Let Preference composables use ListItem.

Let PreferenceCheckbox use ListItem and add missing previews.
Let PreferenceCategory use ListSectionHeader
Let PreferenceSlide use ListItem
Let PreferenceRow use ListItem
Let PreferenceText use ListItem
This commit is contained in:
Benoit Marty 2024-05-27 12:18:06 +02:00
parent bc4bfe5d40
commit a57b9cb49f
15 changed files with 232 additions and 260 deletions

View file

@ -43,7 +43,7 @@ fun LockScreenSettingsView(
onBackPressed = onBackPressed, onBackPressed = onBackPressed,
modifier = modifier modifier = modifier
) { ) {
PreferenceCategory(showDivider = false) { PreferenceCategory(showTopDivider = false) {
PreferenceText( PreferenceText(
title = stringResource(id = R.string.screen_app_lock_settings_change_pin), title = stringResource(id = R.string.screen_app_lock_settings_change_pin),
onClick = onChangePinClicked onClick = onChangePinClicked

View file

@ -48,7 +48,10 @@ fun DeveloperSettingsView(
title = stringResource(id = CommonStrings.common_developer_options) title = stringResource(id = CommonStrings.common_developer_options)
) { ) {
// Note: this is OK to hardcode strings in this debug screen. // Note: this is OK to hardcode strings in this debug screen.
PreferenceCategory(title = "Feature flags") { PreferenceCategory(
title = "Feature flags",
showTopDivider = false,
) {
FeatureListContent(state) FeatureListContent(state)
} }
ElementCallCategory(state = state) ElementCallCategory(state = state)
@ -67,14 +70,14 @@ fun DeveloperSettingsView(
RageshakePreferencesView( RageshakePreferencesView(
state = state.rageshakeState, state = state.rageshakeState,
) )
PreferenceCategory(title = "Crash", showDivider = false) { PreferenceCategory(title = "Crash", showTopDivider = false) {
PreferenceText( PreferenceText(
title = "Crash the app 💥", title = "Crash the app 💥",
onClick = { error("This crash is a test.") } onClick = { error("This crash is a test.") }
) )
} }
val cache = state.cacheSize val cache = state.cacheSize
PreferenceCategory(title = "Cache", showDivider = false) { PreferenceCategory(title = "Cache", showTopDivider = false) {
PreferenceText( PreferenceText(
title = "Clear cache", title = "Clear cache",
currentValue = cache.dataOrNull(), currentValue = cache.dataOrNull(),
@ -93,11 +96,12 @@ fun DeveloperSettingsView(
private fun ElementCallCategory( private fun ElementCallCategory(
state: DeveloperSettingsState, state: DeveloperSettingsState,
) { ) {
PreferenceCategory(title = "Element Call", showDivider = true) { PreferenceCategory(title = "Element Call", showTopDivider = true) {
val callUrlState = state.customElementCallBaseUrlState val callUrlState = state.customElementCallBaseUrlState
fun isUsingDefaultUrl(value: String?): Boolean { fun isUsingDefaultUrl(value: String?): Boolean {
return value.isNullOrEmpty() || value == callUrlState.defaultUrl return value.isNullOrEmpty() || value == callUrlState.defaultUrl
} }
val supportingText = if (isUsingDefaultUrl(callUrlState.baseUrl)) { val supportingText = if (isUsingDefaultUrl(callUrlState.baseUrl)) {
stringResource(R.string.screen_advanced_settings_element_call_base_url_description) stringResource(R.string.screen_advanced_settings_element_call_base_url_description)
} else { } else {

View file

@ -17,7 +17,6 @@
package io.element.android.features.preferences.impl.notifications package io.element.android.features.preferences.impl.notifications
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -121,7 +120,6 @@ private fun NotificationSettingsContentView(
PreferenceSwitch( PreferenceSwitch(
title = stringResource(id = R.string.screen_notification_settings_enable_notifications), title = stringResource(id = R.string.screen_notification_settings_enable_notifications),
isChecked = systemSettings.appNotificationsEnabled, isChecked = systemSettings.appNotificationsEnabled,
switchAlignment = Alignment.Top,
onCheckedChange = onNotificationsEnabledChanged onCheckedChange = onNotificationsEnabledChanged
) )
@ -145,7 +143,6 @@ private fun NotificationSettingsContentView(
modifier = Modifier, modifier = Modifier,
title = stringResource(id = R.string.screen_notification_settings_room_mention_label), title = stringResource(id = R.string.screen_notification_settings_room_mention_label),
isChecked = matrixSettings.atRoomNotificationsEnabled, isChecked = matrixSettings.atRoomNotificationsEnabled,
switchAlignment = Alignment.Top,
onCheckedChange = onMentionNotificationsChanged onCheckedChange = onMentionNotificationsChanged
) )
} }
@ -162,7 +159,6 @@ private fun NotificationSettingsContentView(
modifier = Modifier, modifier = Modifier,
title = stringResource(id = R.string.screen_notification_settings_invite_for_me_label), title = stringResource(id = R.string.screen_notification_settings_invite_for_me_label),
isChecked = matrixSettings.inviteForMeNotificationsEnabled, isChecked = matrixSettings.inviteForMeNotificationsEnabled,
switchAlignment = Alignment.Top,
onCheckedChange = onInviteForMeNotificationsChanged onCheckedChange = onInviteForMeNotificationsChanged
) )
} }

View file

@ -68,7 +68,10 @@ fun EditDefaultNotificationSettingView(
} else { } else {
R.string.screen_notification_settings_edit_screen_group_section_header R.string.screen_notification_settings_edit_screen_group_section_header
} }
PreferenceCategory(title = stringResource(id = categoryTitle)) { PreferenceCategory(
title = stringResource(id = categoryTitle),
showTopDivider = false,
) {
if (state.mode != null) { if (state.mode != null) {
Column(modifier = Modifier.selectableGroup()) { Column(modifier = Modifier.selectableGroup()) {
validModes.forEach { item -> validModes.forEach { item ->
@ -83,7 +86,7 @@ fun EditDefaultNotificationSettingView(
} }
} }
if (state.roomsWithUserDefinedMode.isNotEmpty()) { if (state.roomsWithUserDefinedMode.isNotEmpty()) {
PreferenceCategory(title = stringResource(id = R.string.screen_notification_settings_edit_custom_settings_section_title)) { PreferenceCategory(title = stringResource(id = R.string.screen_notification_settings_edit_custom_settings_section_title),) {
state.roomsWithUserDefinedMode.forEach { summary -> state.roomsWithUserDefinedMode.forEach { summary ->
val subtitle = when (summary.details.userDefinedNotificationMode) { val subtitle = when (summary.details.userDefinedNotificationMode) {
RoomNotificationMode.ALL_MESSAGES -> stringResource(id = R.string.screen_notification_settings_edit_mode_all_messages) RoomNotificationMode.ALL_MESSAGES -> stringResource(id = R.string.screen_notification_settings_edit_mode_all_messages)

View file

@ -399,7 +399,10 @@ private fun TopicSection(
roomTopic: RoomTopicState, roomTopic: RoomTopicState,
onActionClicked: (RoomDetailsAction) -> Unit, onActionClicked: (RoomDetailsAction) -> Unit,
) { ) {
PreferenceCategory(title = stringResource(CommonStrings.common_topic)) { PreferenceCategory(
title = stringResource(CommonStrings.common_topic),
showTopDivider = false,
) {
if (roomTopic is RoomTopicState.CanAddTopic) { if (roomTopic is RoomTopicState.CanAddTopic) {
PreferenceText( PreferenceText(
title = stringResource(R.string.screen_room_details_add_topic_title), title = stringResource(R.string.screen_room_details_add_topic_title),
@ -489,7 +492,7 @@ private fun SecuritySection() {
@Composable @Composable
private fun OtherActionsSection(isDm: Boolean, onLeaveRoom: () -> Unit) { private fun OtherActionsSection(isDm: Boolean, onLeaveRoom: () -> Unit) {
PreferenceCategory(showDivider = false) { PreferenceCategory(showTopDivider = true) {
ListItem( ListItem(
headlineContent = { headlineContent = {
val leaveText = stringResource( val leaveText = stringResource(

View file

@ -45,7 +45,7 @@ fun BlockUserSection(
) { ) {
PreferenceCategory( PreferenceCategory(
modifier = modifier, modifier = modifier,
showDivider = false, showTopDivider = false,
) { ) {
when (state.isBlocked) { when (state.isBlocked) {
is AsyncData.Failure -> PreferenceBlockUser(isBlocked = state.isBlocked.prevData, isLoading = false, eventSink = state.eventSink) is AsyncData.Failure -> PreferenceBlockUser(isBlocked = state.isBlocked.prevData, isLoading = false, eventSink = state.eventSink)

View file

@ -1,23 +0,0 @@
/*
* 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.components.preferences
import androidx.compose.ui.unit.dp
internal val preferenceMinHeightOnlyTitle = 56.dp
internal val preferenceMinHeight = 56.dp
internal val preferencePaddingHorizontal = 16.dp

View file

@ -19,22 +19,20 @@ package io.element.android.libraries.designsystem.components.preferences
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.ListSectionHeader
@Composable @Composable
fun PreferenceCategory( fun PreferenceCategory(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
title: String? = null, title: String? = null,
showDivider: Boolean = true, showTopDivider: Boolean = true,
showBottomDivider: Boolean = false,
content: @Composable ColumnScope.() -> Unit, content: @Composable ColumnScope.() -> Unit,
) { ) {
Column( Column(
@ -42,30 +40,20 @@ fun PreferenceCategory(
.fillMaxWidth() .fillMaxWidth()
) { ) {
if (title != null) { if (title != null) {
PreferenceCategoryTitle(title = title) ListSectionHeader(
title = title,
hasDivider = showTopDivider,
)
} else if (showTopDivider) {
PreferenceDivider()
} }
content() content()
if (showDivider) { if (showBottomDivider) {
PreferenceDivider() PreferenceDivider()
} }
} }
} }
@Composable
private fun PreferenceCategoryTitle(title: String) {
Text(
modifier = Modifier.padding(
top = 20.dp,
bottom = 8.dp,
start = preferencePaddingHorizontal,
end = preferencePaddingHorizontal,
),
style = ElementTheme.typography.fontBodyLgMedium,
color = ElementTheme.materialColors.primary,
text = title,
)
}
@Preview(group = PreviewGroup.Preferences) @Preview(group = PreviewGroup.Preferences)
@Composable @Composable
internal fun PreferenceCategoryPreview() = ElementThemedPreview { internal fun PreferenceCategoryPreview() = ElementThemedPreview {

View file

@ -17,25 +17,18 @@
package io.element.android.libraries.designsystem.components.preferences package io.element.android.libraries.designsystem.components.preferences
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.components.preferences.components.PreferenceIcon 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.icons.CompoundDrawables import io.element.android.libraries.designsystem.icons.CompoundDrawables
import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.Checkbox import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.toEnabledColor import io.element.android.libraries.designsystem.toEnabledColor
import io.element.android.libraries.designsystem.toSecondaryEnabledColor import io.element.android.libraries.designsystem.toSecondaryEnabledColor
@ -52,29 +45,22 @@ fun PreferenceCheckbox(
@DrawableRes iconResourceId: Int? = null, @DrawableRes iconResourceId: Int? = null,
showIconAreaIfNoIcon: Boolean = false, showIconAreaIfNoIcon: Boolean = false,
) { ) {
Row( ListItem(
modifier = modifier modifier = modifier,
.fillMaxWidth() leadingContent = preferenceIcon(
.defaultMinSize(minHeight = preferenceMinHeight)
.clickable { onCheckedChange(!isChecked) }
.padding(vertical = 4.dp, horizontal = preferencePaddingHorizontal),
verticalAlignment = Alignment.CenterVertically
) {
PreferenceIcon(
icon = icon, icon = icon,
iconResourceId = iconResourceId, iconResourceId = iconResourceId,
enabled = enabled, enabled = enabled,
isVisible = showIconAreaIfNoIcon showIconAreaIfNoIcon = showIconAreaIfNoIcon,
) ),
Column( headlineContent = {
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
Text( Text(
style = ElementTheme.typography.fontBodyLgRegular, style = ElementTheme.typography.fontBodyLgRegular,
text = title, text = title,
color = enabled.toEnabledColor(), color = enabled.toEnabledColor(),
) )
},
supportingContent = {
if (supportingText != null) { if (supportingText != null) {
Text( Text(
style = ElementTheme.typography.fontBodyMdRegular, style = ElementTheme.typography.fontBodyMdRegular,
@ -82,15 +68,13 @@ fun PreferenceCheckbox(
color = enabled.toSecondaryEnabledColor(), color = enabled.toSecondaryEnabledColor(),
) )
} }
} },
Checkbox( trailingContent = ListItemContent.Checkbox(
modifier = Modifier
.align(Alignment.CenterVertically),
checked = isChecked, checked = isChecked,
onChange = onCheckedChange,
enabled = enabled, enabled = enabled,
onCheckedChange = onCheckedChange ),
) )
}
} }
@Preview(group = PreviewGroup.Preferences) @Preview(group = PreviewGroup.Preferences)
@ -112,5 +96,31 @@ internal fun PreferenceCheckboxPreview() = ElementThemedPreview {
isChecked = true, isChecked = true,
onCheckedChange = {}, onCheckedChange = {},
) )
PreferenceCheckbox(
title = "Checkbox with supporting text",
supportingText = "Supporting text",
iconResourceId = CompoundDrawables.ic_compound_threads,
enabled = false,
isChecked = true,
onCheckedChange = {},
)
PreferenceCheckbox(
title = "Checkbox with supporting text",
supportingText = "Supporting text",
iconResourceId = null,
showIconAreaIfNoIcon = true,
enabled = true,
isChecked = true,
onCheckedChange = {},
)
PreferenceCheckbox(
title = "Checkbox with supporting text",
supportingText = "Supporting text",
iconResourceId = null,
showIconAreaIfNoIcon = false,
enabled = true,
isChecked = true,
onCheckedChange = {},
)
} }
} }

View file

@ -19,7 +19,6 @@ package io.element.android.libraries.designsystem.components.preferences
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
@ -28,10 +27,7 @@ import io.element.android.libraries.designsystem.theme.components.HorizontalDivi
fun PreferenceDivider( fun PreferenceDivider(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
HorizontalDivider( HorizontalDivider(modifier = modifier)
modifier = modifier,
color = ElementTheme.colors.borderDisabled,
)
} }
@Preview(group = PreviewGroup.Preferences) @Preview(group = PreviewGroup.Preferences)

View file

@ -19,14 +19,13 @@ package io.element.android.libraries.designsystem.components.preferences
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
/** /**
@ -37,15 +36,17 @@ fun PreferenceRow(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
content: @Composable RowScope.() -> Unit, content: @Composable RowScope.() -> Unit,
) { ) {
Row( ListItem(
modifier = modifier modifier = modifier,
.padding(horizontal = preferencePaddingHorizontal) headlineContent = {
.heightIn(min = preferenceMinHeight) Row(
.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
content() content()
} }
}
)
} }
@Preview(group = PreviewGroup.Preferences) @Preview(group = PreviewGroup.Preferences)

View file

@ -19,23 +19,18 @@ package io.element.android.libraries.designsystem.components.preferences
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.annotation.FloatRange import androidx.annotation.FloatRange
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.libraries.designsystem.components.preferences.components.PreferenceIcon 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.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.Slider import io.element.android.libraries.designsystem.theme.components.Slider
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.toEnabledColor
@Composable @Composable
fun PreferenceSlide( fun PreferenceSlide(
@ -51,51 +46,57 @@ fun PreferenceSlide(
summary: String? = null, summary: String? = null,
steps: Int = 0, steps: Int = 0,
) { ) {
Row( ListItem(
modifier = modifier modifier = modifier,
.fillMaxWidth() enabled = enabled,
.defaultMinSize(minHeight = preferenceMinHeight) leadingContent = preferenceIcon(
.padding(vertical = 4.dp, horizontal = preferencePaddingHorizontal),
) {
PreferenceIcon(
icon = icon, icon = icon,
iconResourceId = iconResourceId, iconResourceId = iconResourceId,
isVisible = showIconAreaIfNoIcon, enabled = enabled,
) showIconAreaIfNoIcon = showIconAreaIfNoIcon,
Column( ),
modifier = Modifier headlineContent = {
.weight(1f), Column {
) {
Text(
style = ElementTheme.typography.fontBodyLgRegular,
text = title,
color = enabled.toEnabledColor(),
)
summary?.let {
Text( Text(
style = ElementTheme.typography.fontBodyMdRegular, style = ElementTheme.typography.fontBodyLgRegular,
text = summary, text = title,
color = enabled.toEnabledColor(), )
summary?.let {
Text(
style = ElementTheme.typography.fontBodyMdRegular,
text = summary,
)
}
Slider(
value = value,
steps = steps,
onValueChange = onValueChange,
enabled = enabled,
) )
} }
Slider(
value = value,
steps = steps,
onValueChange = onValueChange,
enabled = enabled,
)
} }
} )
} }
@Preview(group = PreviewGroup.Preferences) @Preview(group = PreviewGroup.Preferences)
@Composable @Composable
internal fun PreferenceSlidePreview() = ElementThemedPreview { internal fun PreferenceSlidePreview() = ElementThemedPreview {
PreferenceSlide( Column {
icon = CompoundIcons.UserProfile(), PreferenceSlide(
title = "Slide", icon = CompoundIcons.UserProfile(),
summary = "Summary", title = "Slide",
value = 0.75F, summary = "Summary",
onValueChange = {}, enabled = true,
) value = 0.75F,
onValueChange = {},
)
PreferenceSlide(
icon = CompoundIcons.UserProfile(),
title = "Slide",
summary = "Summary",
enabled = false,
value = 0.75F,
onValueChange = {},
)
}
} }

View file

@ -17,30 +17,19 @@
package io.element.android.libraries.designsystem.components.preferences package io.element.android.libraries.designsystem.components.preferences
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.libraries.designsystem.components.preferences.components.PreferenceIcon 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.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.Switch import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.toEnabledColor
import io.element.android.libraries.designsystem.toSecondaryEnabledColor
@Composable @Composable
fun PreferenceSwitch( fun PreferenceSwitch(
@ -53,62 +42,57 @@ fun PreferenceSwitch(
icon: ImageVector? = null, icon: ImageVector? = null,
@DrawableRes iconResourceId: Int? = null, @DrawableRes iconResourceId: Int? = null,
showIconAreaIfNoIcon: Boolean = false, showIconAreaIfNoIcon: Boolean = false,
switchAlignment: Alignment.Vertical = Alignment.CenterVertically
) { ) {
Row( ListItem(
modifier = modifier modifier = modifier,
.fillMaxWidth() enabled = enabled,
.defaultMinSize(minHeight = preferenceMinHeight) leadingContent = preferenceIcon(
.clickable { onCheckedChange(!isChecked) }
.padding(vertical = 4.dp, horizontal = preferencePaddingHorizontal),
verticalAlignment = Alignment.CenterVertically
) {
PreferenceIcon(
icon = icon, icon = icon,
iconResourceId = iconResourceId, iconResourceId = iconResourceId,
enabled = enabled, enabled = enabled,
isVisible = showIconAreaIfNoIcon showIconAreaIfNoIcon = showIconAreaIfNoIcon,
) ),
Column( headlineContent = {
modifier = Modifier
.weight(1f)
.align(Alignment.CenterVertically)
) {
Text( Text(
style = ElementTheme.typography.fontBodyLgRegular, style = ElementTheme.typography.fontBodyLgRegular,
text = title, text = title,
color = enabled.toEnabledColor(),
) )
},
supportingContent = {
if (subtitle != null) { if (subtitle != null) {
Spacer(modifier = Modifier.height(4.dp))
Text( Text(
style = ElementTheme.typography.fontBodyMdRegular, style = ElementTheme.typography.fontBodyMdRegular,
text = subtitle, text = subtitle,
color = enabled.toSecondaryEnabledColor(),
) )
} }
} },
Spacer(modifier = Modifier.width(16.dp)) trailingContent = ListItemContent.Switch(
// TODO Create a wrapper for Switch
Switch(
modifier = Modifier
.align(switchAlignment),
checked = isChecked, checked = isChecked,
onChange = onCheckedChange,
enabled = enabled, enabled = enabled,
onCheckedChange = onCheckedChange
) )
} )
} }
@Preview(group = PreviewGroup.Preferences) @Preview(group = PreviewGroup.Preferences)
@Composable @Composable
internal fun PreferenceSwitchPreview() = ElementThemedPreview { internal fun PreferenceSwitchPreview() = ElementThemedPreview {
PreferenceSwitch( Column {
title = "Switch", PreferenceSwitch(
subtitle = "Subtitle Switch", title = "Switch",
icon = CompoundIcons.Threads(), subtitle = "Subtitle Switch",
enabled = true, icon = CompoundIcons.Threads(),
isChecked = true, enabled = true,
onCheckedChange = {}, isChecked = true,
) onCheckedChange = {},
)
PreferenceSwitch(
title = "Switch",
subtitle = "Subtitle Switch",
icon = CompoundIcons.Threads(),
enabled = false,
isChecked = true,
onCheckedChange = {},
)
}
} }

View file

@ -17,12 +17,9 @@
package io.element.android.libraries.designsystem.components.preferences package io.element.android.libraries.designsystem.components.preferences
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.progressSemantics import androidx.compose.foundation.progressSemantics
@ -38,18 +35,17 @@ import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage
import io.element.android.libraries.designsystem.atomic.atoms.RedIndicatorAtom import io.element.android.libraries.designsystem.atomic.atoms.RedIndicatorAtom
import io.element.android.libraries.designsystem.components.preferences.components.PreferenceIcon 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.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.toEnabledColor import io.element.android.libraries.designsystem.toEnabledColor
import io.element.android.libraries.designsystem.toSecondaryEnabledColor import io.element.android.libraries.designsystem.toSecondaryEnabledColor
/**
* Tried to use ListItem, but it cannot really match the design. Keep custom Layout for now.
*/
@Composable @Composable
fun PreferenceText( fun PreferenceText(
title: String, title: String,
@ -67,34 +63,26 @@ fun PreferenceText(
tintColor: Color? = null, tintColor: Color? = null,
onClick: () -> Unit = {}, onClick: () -> Unit = {},
) { ) {
val minHeight = if (subtitle == null && subtitleAnnotated == null) preferenceMinHeightOnlyTitle else preferenceMinHeight ListItem(
modifier = modifier,
Row( enabled = enabled,
modifier = modifier onClick = onClick,
.fillMaxWidth() leadingContent = preferenceIcon(
.defaultMinSize(minHeight = minHeight)
.clickable { onClick() }
.padding(horizontal = preferencePaddingHorizontal, vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
PreferenceIcon(
icon = icon, icon = icon,
iconResourceId = iconResourceId, iconResourceId = iconResourceId,
showIconBadge = showIconBadge, showIconBadge = showIconBadge,
enabled = enabled, enabled = enabled,
isVisible = showIconAreaIfNoIcon, showIconAreaIfNoIcon = showIconAreaIfNoIcon,
tintColor = tintColor ?: enabled.toSecondaryEnabledColor(), tintColor = tintColor,
) ),
Column( headlineContent = {
modifier = Modifier
.weight(1f)
.align(Alignment.CenterVertically)
) {
Text( Text(
style = ElementTheme.typography.fontBodyLgRegular, style = ElementTheme.typography.fontBodyLgRegular,
text = title, text = title,
color = tintColor ?: enabled.toEnabledColor(), color = tintColor ?: enabled.toEnabledColor(),
) )
},
supportingContent = {
if (subtitle != null) { if (subtitle != null) {
Text( Text(
style = ElementTheme.typography.fontBodyMdRegular, style = ElementTheme.typography.fontBodyMdRegular,
@ -108,35 +96,35 @@ fun PreferenceText(
color = tintColor ?: enabled.toSecondaryEnabledColor(), color = tintColor ?: enabled.toSecondaryEnabledColor(),
) )
} }
} },
if (currentValue != null) { trailingContent = ListItemContent.Custom {
Text( Row(
modifier = Modifier verticalAlignment = Alignment.CenterVertically,
.align(Alignment.CenterVertically) ) {
.padding(start = 16.dp, end = 8.dp), if (currentValue != null) {
text = currentValue, Text(
style = ElementTheme.typography.fontBodyXsMedium, text = currentValue,
color = enabled.toSecondaryEnabledColor(), style = ElementTheme.typography.fontBodyXsMedium,
) color = enabled.toSecondaryEnabledColor(),
} else if (loadingCurrentValue) { )
CircularProgressIndicator( } else if (loadingCurrentValue) {
modifier = Modifier CircularProgressIndicator(
.progressSemantics() modifier = Modifier
.padding(start = 16.dp, end = 8.dp) .progressSemantics()
.size(20.dp) .size(20.dp),
.align(Alignment.CenterVertically), strokeWidth = 2.dp
strokeWidth = 2.dp )
) }
} if (showEndBadge) {
if (showEndBadge) { val endBadgeStartPadding = if (currentValue != null || loadingCurrentValue) 16.dp else 0.dp
val endBadgeStartPadding = if (currentValue != null || loadingCurrentValue) 8.dp else 16.dp RedIndicatorAtom(
RedIndicatorAtom( modifier = Modifier
modifier = Modifier .padding(start = endBadgeStartPadding)
.align(Alignment.CenterVertically) )
.padding(start = endBadgeStartPadding) }
) }
} },
} )
} }
@Preview(group = PreviewGroup.Preferences) @Preview(group = PreviewGroup.Preferences)

View file

@ -19,7 +19,6 @@ package io.element.android.libraries.designsystem.components.preferences.compone
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -31,13 +30,37 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.atomic.atoms.RedIndicatorAtom import io.element.android.libraries.designsystem.atomic.atoms.RedIndicatorAtom
import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.toSecondaryEnabledColor import io.element.android.libraries.designsystem.toSecondaryEnabledColor
@Composable @Composable
fun PreferenceIcon( fun preferenceIcon(
icon: ImageVector? = null,
@DrawableRes iconResourceId: Int? = null,
showIconBadge: Boolean = false,
tintColor: Color? = null,
enabled: Boolean = true,
showIconAreaIfNoIcon: Boolean = false,
): ListItemContent.Custom? {
return if (icon != null || iconResourceId != null || showIconAreaIfNoIcon) {
ListItemContent.Custom {
PreferenceIcon(
icon = icon,
iconResourceId = iconResourceId,
showIconBadge = showIconBadge,
enabled = enabled,
isVisible = showIconAreaIfNoIcon,
tintColor = tintColor,
)
}
} else null
}
@Composable
private fun PreferenceIcon(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
icon: ImageVector? = null, icon: ImageVector? = null,
@DrawableRes iconResourceId: Int? = null, @DrawableRes iconResourceId: Int? = null,
@ -54,19 +77,17 @@ fun PreferenceIcon(
contentDescription = null, contentDescription = null,
tint = tintColor ?: enabled.toSecondaryEnabledColor(), tint = tintColor ?: enabled.toSecondaryEnabledColor(),
modifier = Modifier modifier = Modifier
.padding(end = 16.dp)
.size(24.dp), .size(24.dp),
) )
if (showIconBadge) { if (showIconBadge) {
RedIndicatorAtom( RedIndicatorAtom(
modifier = Modifier modifier = Modifier
.align(Alignment.TopEnd) .align(Alignment.TopEnd)
.padding(end = 16.dp)
) )
} }
} }
} else if (isVisible) { } else if (isVisible) {
Spacer(modifier = modifier.width(40.dp)) Spacer(modifier = modifier.width(24.dp))
} }
} }