[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:
parent
ede935cdd9
commit
914235228d
52 changed files with 282 additions and 157 deletions
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue