[Compound] Implement platform components (Switch, RadioButton, Checkbox) (#982)
* Create our custom Switch component * Update RadioButton colors * Update Checkbox colors * Fix padding in `ReplyToContent` * Add `indeterminate` and `hasError` parameters to `CheckBox`. Improve previews. * Improve Switch previews. * Improve RadioButton previews. --------- Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
committed by
GitHub
parent
c68c329538
commit
e351e87dbc
@@ -479,7 +479,7 @@ private fun ReplyToContent(
|
||||
val paddings = if (attachmentThumbnailInfo != null) {
|
||||
PaddingValues(start = 4.dp, end = 12.dp, top = 4.dp, bottom = 4.dp)
|
||||
} else {
|
||||
PaddingValues(start = 12.dp, end = 12.dp, top = 8.dp, bottom = 4.dp)
|
||||
PaddingValues(horizontal = 12.dp, vertical = 4.dp)
|
||||
}
|
||||
Row(
|
||||
modifier
|
||||
|
||||
@@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Announcement
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -37,6 +36,7 @@ import androidx.compose.ui.unit.dp
|
||||
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.Switch
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.toEnabledColor
|
||||
import io.element.android.libraries.designsystem.toSecondaryEnabledColor
|
||||
|
||||
@@ -17,15 +17,25 @@
|
||||
package io.element.android.libraries.designsystem.theme.components
|
||||
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.material3.CheckboxColors
|
||||
import androidx.compose.material3.CheckboxDefaults
|
||||
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.Modifier
|
||||
import androidx.compose.ui.state.ToggleableState
|
||||
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 in https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&mode=design&t=qb99xBP5mwwCtGkN-1
|
||||
|
||||
@Composable
|
||||
fun Checkbox(
|
||||
@@ -33,12 +43,22 @@ fun Checkbox(
|
||||
onCheckedChange: ((Boolean) -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
colors: CheckboxColors = CheckboxDefaults.colors(),
|
||||
hasError: Boolean = false,
|
||||
indeterminate: Boolean = false,
|
||||
colors: CheckboxColors = if (hasError) compoundErrorCheckBoxColors() else compoundCheckBoxColors(),
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
|
||||
) {
|
||||
androidx.compose.material3.Checkbox(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
var indeterminateState by remember { mutableStateOf(indeterminate) }
|
||||
androidx.compose.material3.TriStateCheckbox(
|
||||
state = if (!checked && indeterminateState) ToggleableState.Indeterminate else ToggleableState(checked),
|
||||
onClick = if (onCheckedChange != null) {
|
||||
{
|
||||
indeterminateState = false
|
||||
onCheckedChange(!checked)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
colors = colors,
|
||||
@@ -46,6 +66,30 @@ fun Checkbox(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun compoundCheckBoxColors(): CheckboxColors {
|
||||
return CheckboxDefaults.colors(
|
||||
checkedColor = ElementTheme.materialColors.primary,
|
||||
uncheckedColor = ElementTheme.colors.borderInteractivePrimary,
|
||||
checkmarkColor = ElementTheme.materialColors.onPrimary,
|
||||
disabledUncheckedColor = ElementTheme.colors.borderDisabled,
|
||||
disabledCheckedColor = ElementTheme.colors.iconDisabled,
|
||||
disabledIndeterminateColor = ElementTheme.colors.iconDisabled,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun compoundErrorCheckBoxColors(): CheckboxColors {
|
||||
return CheckboxDefaults.colors(
|
||||
checkedColor = ElementTheme.materialColors.error,
|
||||
uncheckedColor = ElementTheme.materialColors.error,
|
||||
checkmarkColor = ElementTheme.materialColors.onPrimary,
|
||||
disabledUncheckedColor = ElementTheme.colors.borderDisabled,
|
||||
disabledCheckedColor = ElementTheme.colors.iconDisabled,
|
||||
disabledIndeterminateColor = ElementTheme.colors.iconDisabled,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Toggles)
|
||||
@Composable
|
||||
internal fun CheckboxesPreview() = ElementThemedPreview(vertical = false) { ContentToPreview() }
|
||||
@@ -53,9 +97,33 @@ internal fun CheckboxesPreview() = ElementThemedPreview(vertical = false) { Cont
|
||||
@Composable
|
||||
private fun ContentToPreview() {
|
||||
Column {
|
||||
Checkbox(onCheckedChange = {}, enabled = true, checked = true)
|
||||
Checkbox(onCheckedChange = {}, enabled = true, checked = false)
|
||||
Checkbox(onCheckedChange = {}, enabled = false, checked = true)
|
||||
Checkbox(onCheckedChange = {}, enabled = false, checked = false)
|
||||
// Unchecked
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
Checkbox(onCheckedChange = {}, enabled = true, checked = false)
|
||||
Checkbox(onCheckedChange = {}, enabled = false, checked = false)
|
||||
}
|
||||
// Checked
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
Checkbox(onCheckedChange = {}, enabled = true, checked = true)
|
||||
Checkbox(onCheckedChange = {}, enabled = false, checked = true)
|
||||
}
|
||||
// Indeterminate
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
Checkbox(onCheckedChange = {}, enabled = true, checked = false, indeterminate = true)
|
||||
Checkbox(onCheckedChange = {}, enabled = false, checked = false, indeterminate = true)
|
||||
}
|
||||
// Error
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
Checkbox(hasError = true, onCheckedChange = {}, checked = false)
|
||||
Checkbox(hasError = true, onCheckedChange = {}, enabled = false, checked = false)
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
Checkbox(hasError = true, onCheckedChange = {}, enabled = true, checked = true)
|
||||
Checkbox(hasError = true, onCheckedChange = {}, enabled = false, checked = true)
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
Checkbox(onCheckedChange = {}, enabled = true, checked = false, indeterminate = true, hasError = true)
|
||||
Checkbox(onCheckedChange = {}, enabled = false, checked = false, indeterminate = true, hasError = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,15 +17,21 @@
|
||||
package io.element.android.libraries.designsystem.theme.components
|
||||
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.material3.RadioButtonColors
|
||||
import androidx.compose.material3.RadioButtonDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
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 in https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24202&mode=design&t=qb99xBP5mwwCtGkN-1
|
||||
|
||||
@Composable
|
||||
fun RadioButton(
|
||||
@@ -33,7 +39,7 @@ fun RadioButton(
|
||||
onClick: (() -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
colors: RadioButtonColors = RadioButtonDefaults.colors(),
|
||||
colors: RadioButtonColors = compoundRadioButtonColors(),
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
|
||||
) {
|
||||
androidx.compose.material3.RadioButton(
|
||||
@@ -46,6 +52,15 @@ fun RadioButton(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun compoundRadioButtonColors(): RadioButtonColors {
|
||||
return RadioButtonDefaults.colors(
|
||||
unselectedColor = ElementTheme.colors.borderInteractivePrimary,
|
||||
disabledUnselectedColor = ElementTheme.colors.borderDisabled,
|
||||
disabledSelectedColor = ElementTheme.colors.iconDisabled,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Toggles)
|
||||
@Composable
|
||||
internal fun RadioButtonPreview() = ElementThemedPreview(vertical = false) { ContentToPreview() }
|
||||
@@ -53,9 +68,13 @@ internal fun RadioButtonPreview() = ElementThemedPreview(vertical = false) { Con
|
||||
@Composable
|
||||
private fun ContentToPreview() {
|
||||
Column {
|
||||
RadioButton(selected = false, onClick = {})
|
||||
RadioButton(selected = true, onClick = {})
|
||||
RadioButton(selected = false, enabled = false, onClick = {})
|
||||
RadioButton(selected = true, enabled = false, onClick = {})
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
RadioButton(selected = false, onClick = {})
|
||||
RadioButton(selected = false, enabled = false, onClick = {})
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
RadioButton(selected = true, onClick = {})
|
||||
RadioButton(selected = true, enabled = false, onClick = {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.SwitchColors
|
||||
import androidx.compose.material3.SwitchDefaults
|
||||
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.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
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
|
||||
import androidx.compose.material3.Switch as Material3Switch
|
||||
|
||||
// Designs in https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24203&mode=design&t=qb99xBP5mwwCtGkN-1
|
||||
|
||||
@Composable
|
||||
fun Switch(
|
||||
checked: Boolean,
|
||||
onCheckedChange: ((Boolean) -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
colors: SwitchColors = compoundSwitchColors(),
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
thumbContent: (@Composable () -> Unit)? = null,
|
||||
) {
|
||||
Material3Switch(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
colors = colors,
|
||||
interactionSource = interactionSource,
|
||||
thumbContent = thumbContent
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun compoundSwitchColors() = SwitchDefaults.colors(
|
||||
uncheckedThumbColor = ElementTheme.colors.bgActionPrimaryRest,
|
||||
uncheckedTrackColor = Color.Transparent,
|
||||
disabledUncheckedBorderColor = ElementTheme.colors.borderDisabled,
|
||||
disabledUncheckedThumbColor = ElementTheme.colors.iconDisabled,
|
||||
disabledCheckedTrackColor = ElementTheme.colors.iconDisabled,
|
||||
disabledCheckedBorderColor = ElementTheme.colors.iconDisabled,
|
||||
)
|
||||
|
||||
@Preview(group = PreviewGroup.Toggles)
|
||||
@Composable
|
||||
internal fun SwitchPreview() {
|
||||
var checked by remember { mutableStateOf(false) }
|
||||
ElementThemedPreview {
|
||||
Column(modifier = Modifier.padding(10.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
Switch(checked = checked, onCheckedChange = { checked = !checked })
|
||||
Switch(enabled = false, checked = checked, onCheckedChange = { checked = !checked })
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
Switch(checked = !checked, onCheckedChange = { checked = !checked })
|
||||
Switch(enabled = false, checked = !checked, onCheckedChange = { checked = !checked })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +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.theme.components.previews
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Check
|
||||
import androidx.compose.material3.Switch
|
||||
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.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.designsystem.theme.components.Icon
|
||||
|
||||
@Preview(group = PreviewGroup.Toggles)
|
||||
@Composable
|
||||
internal fun SwitchPreview() {
|
||||
ElementThemedPreview {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
var checked by remember { mutableStateOf(false) }
|
||||
Switch(checked = checked, onCheckedChange = { checked = !checked })
|
||||
Switch(checked = checked, onCheckedChange = { checked = !checked }, thumbContent = {
|
||||
Icon(imageVector = Icons.Outlined.Check, contentDescription = null)
|
||||
})
|
||||
Switch(checked = checked, enabled = false, onCheckedChange = { checked = !checked })
|
||||
Switch(checked = checked, enabled = false, onCheckedChange = { checked = !checked }, thumbContent = {
|
||||
Icon(imageVector = Icons.Outlined.Check, contentDescription = null)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user