[Compound] Platform components (Lists) (#990)
Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
parent
cc3829380d
commit
0f77b4557b
48 changed files with 850 additions and 0 deletions
1
changelog.d/990.misc
Normal file
1
changelog.d/990.misc
Normal file
|
|
@ -0,0 +1 @@
|
|||
Compound: add `ListItem` and `ListSectionHeader` components.
|
||||
|
|
@ -26,6 +26,8 @@ object PreviewGroup {
|
|||
const val Dividers = "Dividers"
|
||||
const val FABs = "Floating Action Buttons"
|
||||
const val Icons = "Icons"
|
||||
const val ListItems = "List items"
|
||||
const val ListSections = "List sections"
|
||||
const val Menus = "Menus"
|
||||
const val Preferences = "Preferences"
|
||||
const val Progress = "Progress Indicators"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,426 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.theme.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Share
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
|
||||
// Designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24208&mode=design&t=G5hCfkLB6GgXDuWe-1
|
||||
|
||||
/**
|
||||
* A List Item component to be used in lists and menus with simple layouts, matching the Material 3 guidelines.
|
||||
* @param headlineContent The main content of the list item, usually a text.
|
||||
* @param modifier The modifier to be applied to the list item.
|
||||
* @param supportingContent The content to be displayed below the headline content.
|
||||
* @param leadingContent The content to be displayed before the headline content.
|
||||
* @param trailingContent The content to be displayed after the headline content.
|
||||
* @param style The style to use for the list item. This may change the color and text styles of the contents. [ListItemStyle.Default] is used by default.
|
||||
* @param enabled Whether the list item is enabled. When disabled, will change the color of the headline content and the leading content to use disabled tokens.
|
||||
* @param onClick The callback to be called when the list item is clicked.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
fun ListItem(
|
||||
headlineContent: @Composable () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
supportingContent: @Composable (() -> Unit)? = null,
|
||||
leadingContent: @Composable (() -> Unit)? = null,
|
||||
trailingContent: @Composable (() -> Unit)? = null,
|
||||
style: ListItemStyle = ListItemStyle.Default,
|
||||
enabled: Boolean = true,
|
||||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
val headlineColor = if (enabled) when (style) {
|
||||
ListItemStyle.Destructive -> ElementTheme.colors.textCriticalPrimary
|
||||
else -> ElementTheme.colors.textPrimary
|
||||
} else {
|
||||
// We cannot apply a disabled color by default: https://issuetracker.google.com/issues/280480132
|
||||
ElementTheme.colors.textDisabled
|
||||
}
|
||||
|
||||
val supportingContentColor = if (enabled) {
|
||||
ElementTheme.materialColors.onSurfaceVariant
|
||||
} else {
|
||||
// We cannot apply a disabled color by default: https://issuetracker.google.com/issues/280480132
|
||||
ElementTheme.colors.textDisabled
|
||||
}
|
||||
|
||||
val leadingTrailingContentColor = if (enabled) when (style) {
|
||||
ListItemStyle.Primary -> ElementTheme.colors.iconPrimary
|
||||
ListItemStyle.Destructive -> ElementTheme.colors.iconCriticalPrimary
|
||||
else -> ElementTheme.colors.iconTertiary
|
||||
} else {
|
||||
// We cannot apply a disabled color by default: https://issuetracker.google.com/issues/280480132
|
||||
ElementTheme.colors.iconDisabled
|
||||
}
|
||||
|
||||
val decoratedHeadlineContent: @Composable () -> Unit = {
|
||||
CompositionLocalProvider(
|
||||
LocalTextStyle provides ElementTheme.materialTypography.bodyLarge,
|
||||
LocalContentColor provides headlineColor,
|
||||
) {
|
||||
headlineContent()
|
||||
}
|
||||
}
|
||||
val decoratedSupportingContent: (@Composable () -> Unit)? = supportingContent?.let { content ->
|
||||
{
|
||||
CompositionLocalProvider(
|
||||
LocalTextStyle provides ElementTheme.materialTypography.bodyMedium,
|
||||
LocalContentColor provides supportingContentColor,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
val decoratedLeadingContent: (@Composable () -> Unit)? = leadingContent?.let { content ->
|
||||
{
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides leadingTrailingContentColor,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
val decoratedTrailingContent: (@Composable () -> Unit)? = trailingContent?.let { content ->
|
||||
{
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides leadingTrailingContentColor,
|
||||
LocalTextStyle provides ElementTheme.typography.fontBodyMdRegular,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
androidx.compose.material3.ListItem(
|
||||
headlineContent = decoratedHeadlineContent,
|
||||
modifier = modifier.clickable(enabled = enabled && onClick != null, onClick = onClick ?: {}),
|
||||
overlineContent = null,
|
||||
supportingContent = decoratedSupportingContent,
|
||||
leadingContent = decoratedLeadingContent,
|
||||
trailingContent = decoratedTrailingContent,
|
||||
colors = ListItemDefaults.colors(), // These aren't really used since we need the workaround for the disabled state color
|
||||
tonalElevation = 0.dp,
|
||||
shadowElevation = 0.dp,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The style to use for a [ListItem].
|
||||
*/
|
||||
sealed interface ListItemStyle {
|
||||
object Default : ListItemStyle
|
||||
object Primary: ListItemStyle
|
||||
object Destructive : ListItemStyle
|
||||
}
|
||||
|
||||
// region: Simple list item
|
||||
@Preview(name = "List item (3 lines) - Simple", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemThreeLinesSimplePreview() = PreviewItems.ThreeLinesListItemPreview()
|
||||
|
||||
@Preview(name = "List item (2 lines) - Simple", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemTwoLinesSimplePreview() = PreviewItems.TwoLinesListItemPreview()
|
||||
|
||||
@Preview(name = "List item (1 line) - Simple", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemSingleLineSimplePreview() = PreviewItems.OneLineListItemPreview()
|
||||
// endregion
|
||||
|
||||
// region: Trailing Checkbox
|
||||
@Preview(name = "List item (3 lines) - Trailing Checkbox", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemThreeLinesTrailingCheckBoxPreview() = PreviewItems.ThreeLinesListItemPreview(trailingContent = PreviewItems.checkbox())
|
||||
|
||||
@Preview(name = "List item (2 lines) - Trailing Checkbox", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemTwoLinesTrailingCheckBoxPreview() = PreviewItems.TwoLinesListItemPreview(trailingContent = PreviewItems.checkbox())
|
||||
|
||||
@Preview(name = "List item (1 line) - Trailing Checkbox", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemSingleLineTrailingCheckBoxPreview() = PreviewItems.OneLineListItemPreview(trailingContent = PreviewItems.checkbox())
|
||||
// endregion
|
||||
|
||||
// region: Trailing RadioButton
|
||||
@Preview(name = "List item (3 lines) - Trailing RadioButton", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemThreeLinesTrailingRadioButtonPreview() = PreviewItems.ThreeLinesListItemPreview(trailingContent = PreviewItems.radioButton())
|
||||
|
||||
@Preview(name = "List item (2 lines) - Trailing RadioButton", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemTwoLinesTrailingRadioButtonPreview() = PreviewItems.TwoLinesListItemPreview(trailingContent = PreviewItems.radioButton())
|
||||
|
||||
@Preview(name = "List item (1 line) - Trailing RadioButton", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemSingleLineTrailingRadioButtonPreview() = PreviewItems.OneLineListItemPreview(trailingContent = PreviewItems.radioButton())
|
||||
// endregion
|
||||
|
||||
// region: Trailing Switch
|
||||
@Preview(name = "List item (3 lines) - Trailing Switch", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemThreeLinesTrailingSwitchPreview() = PreviewItems.ThreeLinesListItemPreview(trailingContent = PreviewItems.switch())
|
||||
|
||||
@Preview(name = "List item (2 lines) - Trailing Switch", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemTwoLinesTrailingSwitchPreview() = PreviewItems.TwoLinesListItemPreview(trailingContent = PreviewItems.switch())
|
||||
|
||||
@Preview(name = "List item (1 line) - Trailing Switch", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemSingleLineTrailingSwitchPreview() = PreviewItems.OneLineListItemPreview(trailingContent = PreviewItems.switch())
|
||||
// endregion
|
||||
|
||||
// region: Trailing Icon
|
||||
@Preview(name = "List item (3 lines) - Trailing Icon", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemThreeLinesTrailingIconPreview() = PreviewItems.ThreeLinesListItemPreview(trailingContent = PreviewItems.icon())
|
||||
|
||||
@Preview(name = "List item (2 lines) - Trailing Icon", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemTwoLinesTrailingIconPreview() = PreviewItems.TwoLinesListItemPreview(trailingContent = PreviewItems.icon())
|
||||
|
||||
@Preview(name = "List item (1 line) - Trailing Icon", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemSingleLineTrailingIconPreview() = PreviewItems.OneLineListItemPreview(trailingContent = PreviewItems.icon())
|
||||
// endregion
|
||||
|
||||
// region: Leading Checkbox
|
||||
@Preview(name = "List item (3 lines) - Leading Checkbox", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemThreeLinesLeadingCheckboxPreview() = PreviewItems.ThreeLinesListItemPreview(leadingContent = PreviewItems.checkbox())
|
||||
|
||||
@Preview(name = "List item (2 lines) - Leading Checkbox", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemTwoLinesLeadingCheckboxPreview() = PreviewItems.TwoLinesListItemPreview(leadingContent = PreviewItems.checkbox())
|
||||
|
||||
@Preview(name = "List item (1 line) - Leading Checkbox", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemSingleLineLeadingCheckboxPreview() = PreviewItems.OneLineListItemPreview(leadingContent = PreviewItems.checkbox())
|
||||
// endregion
|
||||
|
||||
// region: Leading RadioButton
|
||||
@Preview(name = "List item (3 lines) - Leading RadioButton", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemThreeLinesLeadingRadioButtonPreview() = PreviewItems.ThreeLinesListItemPreview(leadingContent = PreviewItems.radioButton())
|
||||
|
||||
@Preview(name = "List item (2 lines) - Leading RadioButton", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemTwoLinesLeadingRadioButtonPreview() = PreviewItems.TwoLinesListItemPreview(leadingContent = PreviewItems.radioButton())
|
||||
|
||||
@Preview(name = "List item (1 line) - Leading RadioButton", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemSingleLineLeadingRadioButtonPreview() = PreviewItems.OneLineListItemPreview(leadingContent = PreviewItems.radioButton())
|
||||
// endregion
|
||||
|
||||
// region: Leading Switch
|
||||
@Preview(name = "List item (3 lines) - Leading Switch", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemThreeLinesLeadingSwitchPreview() = PreviewItems.ThreeLinesListItemPreview(leadingContent = PreviewItems.switch())
|
||||
|
||||
@Preview(name = "List item (2 lines) - Leading Switch", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemTwoLinesLeadingSwitchPreview() = PreviewItems.TwoLinesListItemPreview(leadingContent = PreviewItems.switch())
|
||||
|
||||
@Preview(name = "List item (1 line) - Leading Switch", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemSingleLineLeadingSwitchPreview() = PreviewItems.OneLineListItemPreview(leadingContent = PreviewItems.switch())
|
||||
// endregion
|
||||
|
||||
// region: Leading Icon
|
||||
@Preview(name = "List item (3 lines) - Leading Icon", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemThreeLinesLeadingIconPreview() = PreviewItems.ThreeLinesListItemPreview(leadingContent = PreviewItems.icon())
|
||||
|
||||
@Preview(name = "List item (2 lines) - Leading Icon", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemTwoLinesLeadingIconPreview() = PreviewItems.TwoLinesListItemPreview(leadingContent = PreviewItems.icon())
|
||||
|
||||
@Preview(name = "List item (1 line) - Leading Icon", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemSingleLineLeadingIconPreview() = PreviewItems.OneLineListItemPreview(leadingContent = PreviewItems.icon())
|
||||
// endregion
|
||||
|
||||
// region: Both Icons
|
||||
@Preview(name = "List item (3 lines) - Both Icons", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemThreeLinesBothIconsPreview() = PreviewItems.ThreeLinesListItemPreview(
|
||||
leadingContent = PreviewItems.icon(),
|
||||
trailingContent = PreviewItems.icon()
|
||||
)
|
||||
|
||||
@Preview(name = "List item (2 lines) - Both Icons", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemTwoLinesBothIconsPreview() = PreviewItems.TwoLinesListItemPreview(
|
||||
leadingContent = PreviewItems.icon(),
|
||||
trailingContent = PreviewItems.icon()
|
||||
)
|
||||
|
||||
@Preview(name = "List item (1 line) - Both Icons", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemSingleLineBothIconsPreview() = PreviewItems.OneLineListItemPreview(
|
||||
leadingContent = PreviewItems.icon(),
|
||||
trailingContent = PreviewItems.icon()
|
||||
)
|
||||
// endregion
|
||||
|
||||
// region: Primary action
|
||||
@Preview(name = "List item - Primary action & Icon", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemPrimaryActionWithIconPreview() = PreviewItems.OneLineListItemPreview(
|
||||
style = ListItemStyle.Primary,
|
||||
leadingContent = PreviewItems.icon(),
|
||||
)
|
||||
// endregion
|
||||
|
||||
// region: Error state
|
||||
@Preview(name = "List item - Error", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemErrorPreview() = PreviewItems.OneLineListItemPreview(style = ListItemStyle.Destructive)
|
||||
|
||||
@Preview(name = "List item - Error & Icon", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemErrorWithIconPreview() = PreviewItems.OneLineListItemPreview(
|
||||
style = ListItemStyle.Destructive,
|
||||
leadingContent = PreviewItems.icon(),
|
||||
)
|
||||
// endregion
|
||||
|
||||
// region: Disabled state
|
||||
@Preview(name = "List item - Disabled", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemDisabledPreview() = PreviewItems.OneLineListItemPreview(enabled = false)
|
||||
|
||||
@Preview(name = "List item - Disabled & Icon", group = PreviewGroup.ListItems)
|
||||
@Composable
|
||||
internal fun ListItemDisabledWithIconPreview() = PreviewItems.OneLineListItemPreview(
|
||||
enabled = false,
|
||||
leadingContent = PreviewItems.icon(),
|
||||
)
|
||||
// endregion
|
||||
|
||||
@Suppress("ModifierMissing")
|
||||
private object PreviewItems {
|
||||
|
||||
@Composable
|
||||
fun ThreeLinesListItemPreview(
|
||||
modifier: Modifier = Modifier,
|
||||
leadingContent: @Composable (() -> Unit)? = null,
|
||||
trailingContent: @Composable (() -> Unit)? = null,
|
||||
) {
|
||||
ElementThemedPreview {
|
||||
ListItem(
|
||||
headlineContent = PreviewItems.headline(),
|
||||
supportingContent = PreviewItems.text(),
|
||||
leadingContent = leadingContent,
|
||||
trailingContent = trailingContent,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TwoLinesListItemPreview(
|
||||
modifier: Modifier = Modifier,
|
||||
leadingContent: @Composable (() -> Unit)? = null,
|
||||
trailingContent: @Composable (() -> Unit)? = null,
|
||||
) {
|
||||
ElementThemedPreview {
|
||||
ListItem(
|
||||
headlineContent = PreviewItems.headline(),
|
||||
supportingContent = PreviewItems.textSingleLine(),
|
||||
leadingContent = leadingContent,
|
||||
trailingContent = trailingContent,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun OneLineListItemPreview(
|
||||
modifier: Modifier = Modifier,
|
||||
leadingContent: @Composable (() -> Unit)? = null,
|
||||
trailingContent: @Composable (() -> Unit)? = null,
|
||||
style: ListItemStyle = ListItemStyle.Default,
|
||||
enabled: Boolean = true,
|
||||
) {
|
||||
ElementThemedPreview {
|
||||
ListItem(
|
||||
headlineContent = PreviewItems.headline(),
|
||||
leadingContent = leadingContent,
|
||||
trailingContent = trailingContent,
|
||||
enabled = enabled,
|
||||
style = style,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun headline() = @Composable {
|
||||
Text("List item")
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun text() = @Composable {
|
||||
Text("Supporting line text lorem ipsum dolor sit amet, consectetur.")
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun textSingleLine() = @Composable {
|
||||
Text("Supporting line text lorem ipsum dolor sit amet, consectetur.", overflow = TextOverflow.Ellipsis, maxLines = 1)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun checkbox() = @Composable {
|
||||
var checked by remember { mutableStateOf(false) }
|
||||
Checkbox(checked = checked, onCheckedChange = { checked = !checked })
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun radioButton() = @Composable {
|
||||
var checked by remember { mutableStateOf(false) }
|
||||
RadioButton(selected = checked, onClick = { checked = !checked })
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun switch() = @Composable {
|
||||
var checked by remember { mutableStateOf(false) }
|
||||
Switch(checked = checked, onCheckedChange = { checked = !checked })
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun icon() = @Composable {
|
||||
Icon(imageVector = Icons.Outlined.Share, contentDescription = null)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.theme.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
|
||||
// Designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24208&mode=design&t=G5hCfkLB6GgXDuWe-1
|
||||
|
||||
/**
|
||||
* List section header.
|
||||
* @param title The title of the section.
|
||||
* @param modifier The modifier to be applied to the section.
|
||||
* @param hasDivider Whether to show a divider above the section or not. Default is `true`.
|
||||
* @param description A description for the section. It's empty by default.
|
||||
*/
|
||||
@Composable
|
||||
fun ListSectionHeader(
|
||||
title: String,
|
||||
modifier: Modifier = Modifier,
|
||||
hasDivider: Boolean = true,
|
||||
description: @Composable () -> Unit = {},
|
||||
) {
|
||||
Column(modifier.fillMaxWidth()) {
|
||||
if (hasDivider) {
|
||||
HorizontalDivider(modifier = Modifier.padding(top = 16.dp))
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
style = ElementTheme.typography.fontBodyLgMedium,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
)
|
||||
CompositionLocalProvider(
|
||||
LocalTextStyle provides ElementTheme.typography.fontBodySmRegular,
|
||||
LocalContentColor provides ElementTheme.colors.textSecondary,
|
||||
) {
|
||||
description()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List supporting text item. Used to display an explanation in the list with a pre-formatted style.
|
||||
* @param text The text to display.
|
||||
* @param modifier The modifier to be applied to the text.
|
||||
* @param contentPadding The padding to apply to the text. Default is [ListSupportingTextDefaults.Padding.Default].
|
||||
*/
|
||||
@Composable
|
||||
fun ListSupportingText(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentPadding: ListSupportingTextDefaults.Padding = ListSupportingTextDefaults.Padding.Default,
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
modifier = modifier.padding(contentPadding.paddingValues()),
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* List supporting text item. Used to display an explanation in the list with a pre-formatted style.
|
||||
* @param annotatedString The annotated string to display.
|
||||
* @param modifier The modifier to be applied to the text.
|
||||
* @param contentPadding The padding to apply to the text. Default is [ListSupportingTextDefaults.Padding.Default].
|
||||
*/
|
||||
@Composable
|
||||
fun ListSupportingText(
|
||||
annotatedString: AnnotatedString,
|
||||
modifier: Modifier = Modifier,
|
||||
contentPadding: ListSupportingTextDefaults.Padding = ListSupportingTextDefaults.Padding.Default,
|
||||
) {
|
||||
Text(
|
||||
text = annotatedString,
|
||||
modifier = modifier.padding(contentPadding.paddingValues()),
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
)
|
||||
}
|
||||
|
||||
object ListSupportingTextDefaults {
|
||||
|
||||
/** Specifies the padding to use for the supporting text. */
|
||||
sealed interface Padding {
|
||||
/** No padding. */
|
||||
object None : Padding
|
||||
/** Default padding, it will align fine with a [ListItem] with no leading content. */
|
||||
object Default : Padding
|
||||
/** It will align to a [ListItem] with an [Icon] or [Checkbox] as leading content. */
|
||||
object SmallLeadingContent : Padding
|
||||
/** It will align to with a [ListItem] with a [Switch] as leading content. */
|
||||
object LargeLeadingContent : Padding
|
||||
/** It will align to with a [ListItem] with a custom start [padding]. */
|
||||
data class Custom(val padding: Dp) : Padding
|
||||
|
||||
private fun startPadding(): Dp = when (this) {
|
||||
None -> 0.dp
|
||||
Default -> 16.dp
|
||||
SmallLeadingContent -> 56.dp
|
||||
LargeLeadingContent -> 84.dp
|
||||
is Custom -> padding
|
||||
}
|
||||
|
||||
private fun endPadding(): Dp = when (this) {
|
||||
None -> 0.dp
|
||||
else -> 24.dp
|
||||
}
|
||||
|
||||
private fun bottomPadding(): Dp = when (this) {
|
||||
None -> 0.dp
|
||||
else -> 12.dp
|
||||
}
|
||||
|
||||
fun paddingValues() = PaddingValues(
|
||||
top = 0.dp,
|
||||
bottom = bottomPadding(),
|
||||
start = startPadding(),
|
||||
end = endPadding()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region: List header previews
|
||||
|
||||
@Preview(group = PreviewGroup.ListSections, name = "List section header")
|
||||
@Composable
|
||||
internal fun ListSectionHeaderPreview() {
|
||||
ElementThemedPreview {
|
||||
ListSectionHeader(
|
||||
title = "List section",
|
||||
hasDivider = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.ListSections, name = "List section header with divider")
|
||||
@Composable
|
||||
internal fun ListSectionHeaderWithDividerPreview() {
|
||||
ElementThemedPreview {
|
||||
ListSectionHeader(
|
||||
title = "List section",
|
||||
hasDivider = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.ListSections, name = "List section header with description")
|
||||
@Composable
|
||||
internal fun ListSectionHeaderWithDescriptionPreview() {
|
||||
ElementThemedPreview {
|
||||
ListSectionHeader(
|
||||
title = "List section",
|
||||
description = {
|
||||
ListSupportingText(
|
||||
text = "Supporting line text lorem ipsum dolor sit amet, consectetur. Read more",
|
||||
contentPadding = ListSupportingTextDefaults.Padding.None,
|
||||
)
|
||||
},
|
||||
hasDivider = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.ListSections, name = "List section header with description and divider")
|
||||
@Composable
|
||||
internal fun ListSectionHeaderWithDescriptionAndDividerPreview() {
|
||||
ElementThemedPreview {
|
||||
ListSectionHeader(
|
||||
title = "List section",
|
||||
description = {
|
||||
ListSupportingText(
|
||||
text = "Supporting line text lorem ipsum dolor sit amet, consectetur. Read more",
|
||||
contentPadding = ListSupportingTextDefaults.Padding.None,
|
||||
)
|
||||
},
|
||||
hasDivider = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region: List supporting text previews
|
||||
|
||||
@Preview(group = PreviewGroup.ListSections, name = "List supporting text - no padding")
|
||||
@Composable
|
||||
internal fun ListSupportingTextNoPaddingPreview() {
|
||||
ElementThemedPreview {
|
||||
ListSupportingText(
|
||||
text = "Supporting line text lorem ipsum dolor sit amet, consectetur. Read more",
|
||||
contentPadding = ListSupportingTextDefaults.Padding.None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.ListSections, name = "List supporting text - default padding")
|
||||
@Composable
|
||||
internal fun ListSupportingTextDefaultPaddingPreview() {
|
||||
ElementThemedPreview {
|
||||
Column {
|
||||
ListItem(headlineContent = { Text("A title") })
|
||||
ListSupportingText(
|
||||
text = "Supporting line text lorem ipsum dolor sit amet, consectetur. Read more",
|
||||
contentPadding = ListSupportingTextDefaults.Padding.Default,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.ListSections, name = "List supporting text - small padding")
|
||||
@Composable
|
||||
internal fun ListSupportingTextSmallPaddingPreview() {
|
||||
ElementThemedPreview {
|
||||
Column {
|
||||
ListItem(headlineContent = { Text("A title") }, leadingContent = { Icon(Icons.Default.Share, null) })
|
||||
ListSupportingText(
|
||||
text = "Supporting line text lorem ipsum dolor sit amet, consectetur. Read more",
|
||||
contentPadding = ListSupportingTextDefaults.Padding.SmallLeadingContent,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.ListSections, name = "List supporting text - large padding")
|
||||
@Composable
|
||||
internal fun ListSupportingTextLargePaddingPreview() {
|
||||
ElementThemedPreview {
|
||||
Column {
|
||||
ListItem(headlineContent = { Text("A title") }, leadingContent = { Switch(checked = true, onCheckedChange = null) })
|
||||
ListSupportingText(
|
||||
text = "Supporting line text lorem ipsum dolor sit amet, consectetur. Read more",
|
||||
contentPadding = ListSupportingTextDefaults.Padding.LargeLeadingContent,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.ListSections, name = "List supporting text - custom padding")
|
||||
@Composable
|
||||
internal fun ListSupportingTextCustomPaddingPreview() {
|
||||
ElementThemedPreview {
|
||||
Column {
|
||||
ListItem(headlineContent = { Text("A title") })
|
||||
ListSupportingText(
|
||||
text = "Supporting line text lorem ipsum dolor sit amet, consectetur. Read more",
|
||||
contentPadding = ListSupportingTextDefaults.Padding.Custom(24.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b876fc23d35a7f5867ee23483ab4157e436fb0e3e30b364502a2a513df2246db
|
||||
size 11223
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e8effadab2d277b71e87b5717786eb8fd28df0922864c7eb0cb5f7336bd986e3
|
||||
size 8623
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e61341edb5605cfd12a81844ed221553afe6a89ad4e8d570f4a8ec27fda81bec
|
||||
size 9723
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c9bdde1d1798132a933c572610e2f85b905b3e0e98ff4cd8724a7832dafbc659
|
||||
size 10098
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:99a033b51b2eb667b2970ba62db9e698411a9158012568e44388e23f71095ef5
|
||||
size 12485
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d16fc167400eef809cf682e9e948f20a8839438c6532acda348088d05e0b4d08
|
||||
size 8036
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8c8574785958bacf0eb45b3d135d3caae7c6fe045df773b5f7b6f03dce942130
|
||||
size 8584
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6c315182baa7ab1c47bcfc877722b2f51bde7e132af52af2daad5b3dea6059c6
|
||||
size 9718
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8d5d62ab71228dd0042560487fc754066a71309d28884184ee5f0e4383885f07
|
||||
size 9990
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3b956cd168b4698d187236b0a9684f090c1ea4ab02371ac821d167c8b94a7ad2
|
||||
size 12351
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ada64d53648a2ff884ab2a19ed5f7194419c5b1491f406231a2857a931fad9cd
|
||||
size 21125
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ca93eef86e2215ccfa04e632589fd90dec23388c393a7c55d757a365c54fd054
|
||||
size 19173
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:287cdabed03bd0652fb004d946bedaee7d21c974fc3a2314eaedb6c021e2ff56
|
||||
size 21334
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4f76b18b75b3eff14677c046d725953e86030b113f01fda8bf29cc83705e23f7
|
||||
size 20472
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f1689a328667fa1d7269b827b7e9bef415a642c5cf5163477730a37f95cd3931
|
||||
size 22746
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d9dec4ea21a07697fccf7d6d5f6f9132089d56d6debec66502754bc88aa9a149
|
||||
size 21247
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:14302bebf36302001f4694b3594b5616a3e332be52572051ba55866416fa626b
|
||||
size 19313
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:eed5943478f8c2f779cf81700affa9dc351b97439e1b43ec7ae58d45dcf88b54
|
||||
size 21524
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:383fe0be9d0e110735642e663be3cb8e39960900e306b0eadd836c62cab5a7de
|
||||
size 20606
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:792ca2bab34a395065d912cd6827f0da7b79a2df49b59bf54646c86527e78116
|
||||
size 22673
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:79e62ed5a4a628c9ee458e4cc4b91f9bd1f3a1d09a6d6844eee041797d075eb5
|
||||
size 27925
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:54741cb2099ea8dc48f27ed7b47ef1d11e134b7053db1fa00ceb1b5c24841ecc
|
||||
size 25788
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:bc169b112a966f6aceaf8e876907a207d5c102d703bc16b7f22c88f40b802472
|
||||
size 26683
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fc8d4588c9c89e5f10bdbc7f62f9d269f0956592d8ac352844d978c1b2be03f2
|
||||
size 26763
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3f523d5c94ef05f62337494dfec70cd671bbc30359c4ef403796324c3821e835
|
||||
size 28706
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:62c9105c79dd555c1dd240a0841c7ddedf1f980f9b4147ec87f02a1f0fc5db6a
|
||||
size 25402
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:46f8f65468d81744980f72e9e472c8aae86cfa5ba1ef8b02260aa2c925c0ef31
|
||||
size 25848
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c61f46ffe12bc61640176e30189e07e2d0cbd259de714fbddc33ce71170aa316
|
||||
size 26761
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b5b7661174bf47510f3a639030aa85187d7b3c41facadc94cf02e15e976f5923
|
||||
size 26836
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3dbba64c730837cc376dca1b37a65ae1335199c3209ad1ae7c28aa55377a778e
|
||||
size 28588
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2791dcc27d133c8071c570ebf701cc90be1ce2c87b62f67ebbe5108beae34878
|
||||
size 9435
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:bc96a9942ff751201e47d6ddab4f7e86ddc5718962593a5c5767236f05f52f3d
|
||||
size 7861
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:68538824c68e226b340965533bc7a587b5f485a881dcc63c1b678c16a1f573ca
|
||||
size 9611
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:55eb1efd2e484fe6719d50c0e6abea0d20f0950ab6dcd74688407c45baab144b
|
||||
size 8008
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a410afa415f88ddf85bb33128ed0c2a935c63bd8a93937b301582dd5208a5f0b
|
||||
size 9613
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:350d8f97aa66bcf62f82dface39b254bd795efb307dfa06b45f4dc8b4a3713cd
|
||||
size 9670
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b043c8b580ba5f13675a1598f03fe6dbdd2f359e9edf8808254a8cf16d1a556b
|
||||
size 26533
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:cadb5cb0ea0986a1d6588df63dc053c30256040b8535faeed5840c53db256e6a
|
||||
size 26816
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:53a1b0eab84837509c2ed0a708921cf1728bcfd594b6f539ea34726d4183f5cf
|
||||
size 9788
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c4b6f914265f45741a2095496c7eb4ad917119f7eea9228a520ee8c9e026fbcb
|
||||
size 24994
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:dafd6a93c178689237f423f9de09bced9f6e60b05f9c8560499fecce6dcf40bb
|
||||
size 25019
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5ba37602f8c2519c1854776ea349fd53fb9fb6fd4b1d94a55d297b297854656b
|
||||
size 27584
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:08c2d41fdda5eda4db3e165ca4e57631da040e423420ca04b61aa777858b7f8d
|
||||
size 22104
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1d2a776e9724abd16b1692f0fdac43e39dedcd8c003128d1ea354f1f8df245ac
|
||||
size 26147
|
||||
Loading…
Add table
Add a link
Reference in a new issue