change(design) : introduce PreferenceDropdown component
This commit is contained in:
parent
007555eb56
commit
fd3b99765d
1 changed files with 189 additions and 0 deletions
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
@file:OptIn(ExperimentalMaterial3Api::class)
|
||||
|
||||
package io.element.android.libraries.designsystem.components.preferences
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||
import androidx.compose.material3.MenuAnchorType
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||
import io.element.android.libraries.designsystem.components.preferences.components.preferenceIcon
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
|
||||
import io.element.android.libraries.designsystem.theme.components.ListItem
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.toEnabledColor
|
||||
import io.element.android.libraries.designsystem.toSecondaryEnabledColor
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@Composable
|
||||
fun <T : DropdownOption> PreferenceDropdown(
|
||||
title: String,
|
||||
selectedOption: T?,
|
||||
options: ImmutableList<T>,
|
||||
onSelectOption: (T) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
supportingText: String? = null,
|
||||
enabled: Boolean = true,
|
||||
icon: ImageVector? = null,
|
||||
@DrawableRes iconResourceId: Int? = null,
|
||||
showIconAreaIfNoIcon: Boolean = false,
|
||||
) {
|
||||
var isDropdownExpanded by remember { mutableStateOf(false) }
|
||||
ListItem(
|
||||
modifier = modifier,
|
||||
leadingContent = preferenceIcon(
|
||||
icon = icon,
|
||||
iconResourceId = iconResourceId,
|
||||
enabled = enabled,
|
||||
showIconAreaIfNoIcon = showIconAreaIfNoIcon,
|
||||
),
|
||||
headlineContent = {
|
||||
Text(
|
||||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = title,
|
||||
color = enabled.toEnabledColor(),
|
||||
)
|
||||
},
|
||||
supportingContent = supportingText?.let {
|
||||
{
|
||||
Text(
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
text = it,
|
||||
color = enabled.toSecondaryEnabledColor(),
|
||||
)
|
||||
}
|
||||
},
|
||||
trailingContent = ListItemContent.Custom(
|
||||
content = {
|
||||
DropdownTrailingContent(
|
||||
selectedOption = selectedOption,
|
||||
options = options,
|
||||
onSelectOption = onSelectOption,
|
||||
expanded = isDropdownExpanded,
|
||||
onExpandedChange = { isDropdownExpanded = it },
|
||||
)
|
||||
}
|
||||
),
|
||||
onClick = { isDropdownExpanded = true }.takeIf { !isDropdownExpanded },
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A dropdown option that can be used in a [PreferenceDropdown].
|
||||
*/
|
||||
interface DropdownOption {
|
||||
/**
|
||||
* The text to display for this option.
|
||||
*/
|
||||
val text: String
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun <T : DropdownOption> DropdownTrailingContent(
|
||||
selectedOption: T?,
|
||||
options: ImmutableList<T>,
|
||||
expanded: Boolean,
|
||||
onExpandedChange: (Boolean) -> Unit,
|
||||
onSelectOption: (T) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = expanded,
|
||||
onExpandedChange = onExpandedChange,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = selectedOption?.text.orEmpty(),
|
||||
maxLines = 1,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
)
|
||||
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
|
||||
}
|
||||
ExposedDropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { onExpandedChange(false) },
|
||||
matchTextFieldWidth = false,
|
||||
) {
|
||||
options.forEach { option ->
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
text = option.text,
|
||||
style = ElementTheme.typography.fontBodyMdRegular
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
onSelectOption(option)
|
||||
onExpandedChange(false)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Preferences)
|
||||
@Composable
|
||||
internal fun PreferenceDropdownPreview() = ElementThemedPreview {
|
||||
val options = listOf(
|
||||
object : DropdownOption {
|
||||
override val text = "Option 1"
|
||||
},
|
||||
object : DropdownOption {
|
||||
override val text = "Option 2"
|
||||
},
|
||||
object : DropdownOption {
|
||||
override val text = "Option 3"
|
||||
},
|
||||
).toImmutableList()
|
||||
|
||||
Column {
|
||||
PreferenceDropdown(
|
||||
title = "Dropdown",
|
||||
supportingText = "Options for dropdown",
|
||||
icon = CompoundIcons.Threads(),
|
||||
selectedOption = null,
|
||||
options = options,
|
||||
onSelectOption = {},
|
||||
)
|
||||
PreferenceDropdown(
|
||||
title = "Dropdown",
|
||||
supportingText = "Options for dropdown",
|
||||
icon = CompoundIcons.Threads(),
|
||||
selectedOption = options.first(),
|
||||
options = options,
|
||||
onSelectOption = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue