From a6a933b291e892bcd39587bba83b1a5de3e7d1e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=99=E7=A9=BA?= <70465933+YuKongA@users.noreply.github.com> Date: Thu, 22 Aug 2024 21:42:05 +0800 Subject: [PATCH] library: Add MiuixCheckbox --- .../kotlin/component/SecondComponent.kt | 63 +++++++++-- .../main/kotlin/root.publication.gradle.kts | 2 +- .../yukonga/miuix/kmp/MiuixSuperCheckbox.kt | 55 +++++++++ .../top/yukonga/miuix/kmp/MiuixSuperSwitch.kt | 2 +- .../yukonga/miuix/kmp/basic/MiuixCheckbox.kt | 104 ++++++++++++++++++ .../yukonga/miuix/kmp/basic/MiuixSwitch.kt | 25 ++--- .../top/yukonga/miuix/kmp/basic/MiuixText.kt | 18 +-- .../yukonga/miuix/kmp/basic/MiuixTextField.kt | 8 +- 8 files changed, 237 insertions(+), 40 deletions(-) create mode 100644 miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/MiuixSuperCheckbox.kt create mode 100644 miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/MiuixCheckbox.kt diff --git a/composeApp/src/commonMain/kotlin/component/SecondComponent.kt b/composeApp/src/commonMain/kotlin/component/SecondComponent.kt index 7ad9508..19187ca 100644 --- a/composeApp/src/commonMain/kotlin/component/SecondComponent.kt +++ b/composeApp/src/commonMain/kotlin/component/SecondComponent.kt @@ -1,5 +1,6 @@ package component +import MiuixCheckbox import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -13,25 +14,73 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import top.yukonga.miuix.kmp.MiuixSuperCheckbox import top.yukonga.miuix.kmp.MiuixSuperSwitch import top.yukonga.miuix.kmp.basic.MiuixButton import top.yukonga.miuix.kmp.basic.MiuixSwitch @Composable fun SecondComponent() { + var checkbox by remember { mutableStateOf(false) } + var checkboxTrue by remember { mutableStateOf(true) } var switch by remember { mutableStateOf(false) } var switchTrue by remember { mutableStateOf(true) } var buttonText by remember { mutableStateOf("Button") } var submitButtonText by remember { mutableStateOf("Submit") } - var textWithSwitch by remember { mutableStateOf("State: false") } - var textWishSwitchState by remember { mutableStateOf(false) } + var miuixSuperCheckbox by remember { mutableStateOf("State: false") } + var miuixSuperCheckboxState by remember { mutableStateOf(false) } + var miuixSuperSwitch by remember { mutableStateOf("State: false") } + var miuixSuperSwitchState by remember { mutableStateOf(false) } var clickCount by remember { mutableStateOf(0) } var submitClickCount by remember { mutableStateOf(0) } Row( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 28.dp), + .padding(horizontal = 28.dp, vertical = 20.dp), + horizontalArrangement = Arrangement.SpaceBetween + ) { + MiuixCheckbox( + modifier = Modifier, + checked = checkbox, + onCheckedChange = { checkbox = it } + + ) + MiuixCheckbox( + modifier = Modifier.padding(start = 8.dp), + checked = checkboxTrue, + onCheckedChange = { checkboxTrue = it } + ) + MiuixCheckbox( + modifier = Modifier.padding(start = 8.dp), + enabled = false, + checked = false, + onCheckedChange = { } + + ) + MiuixCheckbox( + modifier = Modifier.padding(start = 8.dp), + enabled = false, + checked = true, + onCheckedChange = { } + ) + } + + + MiuixSuperCheckbox( + title = "Checkbox", + summary = miuixSuperCheckbox, + checked = miuixSuperCheckboxState, + onCheckedChange = { + miuixSuperCheckboxState = it + miuixSuperCheckbox = "State: $it" + }, + ) + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 28.dp, vertical = 20.dp), horizontalArrangement = Arrangement.SpaceBetween ) { MiuixSwitch( @@ -59,11 +108,11 @@ fun SecondComponent() { MiuixSuperSwitch( title = "Switch", - summary = textWithSwitch, - checked = textWishSwitchState, + summary = miuixSuperSwitch, + checked = miuixSuperSwitchState, onCheckedChange = { - textWishSwitchState = it - textWithSwitch = "State: $it" + miuixSuperSwitchState = it + miuixSuperSwitch = "State: $it" }, ) diff --git a/convention-plugins/src/main/kotlin/root.publication.gradle.kts b/convention-plugins/src/main/kotlin/root.publication.gradle.kts index 30730b3..98b6385 100644 --- a/convention-plugins/src/main/kotlin/root.publication.gradle.kts +++ b/convention-plugins/src/main/kotlin/root.publication.gradle.kts @@ -1,4 +1,4 @@ allprojects { group = "top.yukonga.miuix.kmp" - version = "0.0.2" + version = "0.0.3" } \ No newline at end of file diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/MiuixSuperCheckbox.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/MiuixSuperCheckbox.kt new file mode 100644 index 0000000..593faa1 --- /dev/null +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/MiuixSuperCheckbox.kt @@ -0,0 +1,55 @@ +package top.yukonga.miuix.kmp + +import MiuixCheckbox +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.RowScope +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.unit.DpSize +import androidx.compose.ui.unit.dp +import top.yukonga.miuix.kmp.basic.MiuixBasicComponent + +@Composable +fun MiuixSuperCheckbox( + title: String, + summary: String? = null, + rightActions: @Composable RowScope.() -> Unit = {}, + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier = Modifier, + insideMargin: DpSize = DpSize(28.dp, 14.dp), + enabled: Boolean = true +) { + var isChecked by remember { mutableStateOf(checked) } + val interactionSource = remember { MutableInteractionSource() } + + if (isChecked != checked) { + isChecked = checked + } + + MiuixBasicComponent( + modifier = modifier, + insideMargin = insideMargin, + title = title, + summary = summary, + leftAction = { + MiuixCheckbox( + checked = checked, + onCheckedChange = onCheckedChange, + enabled = enabled + ) + }, + rightActions = rightActions, + interactionSource = interactionSource, + onClick = { + if (enabled) { + isChecked = !isChecked + onCheckedChange?.invoke(isChecked) + } + } + ) +} \ No newline at end of file diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/MiuixSuperSwitch.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/MiuixSuperSwitch.kt index a50ddce..4e02fae 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/MiuixSuperSwitch.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/MiuixSuperSwitch.kt @@ -32,7 +32,7 @@ fun MiuixSuperSwitch( checked: Boolean, onCheckedChange: ((Boolean) -> Unit)?, modifier: Modifier = Modifier, - insideMargin: DpSize = DpSize(28.dp, 7.dp), + insideMargin: DpSize = DpSize(28.dp, 14.dp), enabled: Boolean = true ) { var isChecked by remember { mutableStateOf(checked) } diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/MiuixCheckbox.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/MiuixCheckbox.kt new file mode 100644 index 0000000..952a91a --- /dev/null +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/MiuixCheckbox.kt @@ -0,0 +1,104 @@ +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.requiredSize +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.selection.toggleable +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Matrix +import androidx.compose.ui.graphics.vector.PathParser +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.unit.dp +import top.yukonga.miuix.kmp.basic.MiuixBox +import top.yukonga.miuix.kmp.theme.MiuixTheme +import top.yukonga.miuix.kmp.utils.squircleshape.SquircleShape + +@Composable +fun MiuixCheckbox( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier = Modifier, + enabled: Boolean = true, + interactionSource: MutableInteractionSource? = remember { MutableInteractionSource() } +) { + val isChecked by rememberUpdatedState(checked) + println("isChecked: $isChecked") + var isPressed by remember { mutableStateOf(false) } + val backgroundColor by animateColorAsState( + if (isChecked) MiuixTheme.colorScheme.primary else MiuixTheme.colorScheme.switchThumb, + animationSpec = tween(durationMillis = 200) + ) + val disabledBackgroundColor by rememberUpdatedState( + if (isChecked) MiuixTheme.colorScheme.disabledBg else MiuixTheme.colorScheme.primaryContainer + ) + val checkboxSize by animateDpAsState(if (isPressed) 20.dp else 22.dp) + val checkmarkColor by animateColorAsState(if (checked) Color.White else Color.Transparent) + val toggleableModifier = remember(onCheckedChange, isChecked, enabled) { + if (onCheckedChange != null) { + Modifier.toggleable( + value = isChecked, + onValueChange = { + onCheckedChange(it) + }, + enabled = enabled, + role = Role.Checkbox, + interactionSource = interactionSource, + indication = null + ) + } else { + Modifier + } + } + + MiuixBox( + modifier = modifier + .then(toggleableModifier) + .wrapContentSize(Alignment.Center) + .size(22.dp) + .requiredSize(checkboxSize) + .clip(SquircleShape(100.dp)) + .background(if (enabled) backgroundColor else disabledBackgroundColor) + .pointerInput(Unit) { + detectTapGestures( + onPress = { + isPressed = true + }, + onTap = { + isPressed = false + if (enabled) { + onCheckedChange?.invoke(!isChecked) + } + } + ) + } + ) { + Canvas( + modifier = Modifier.requiredSize(checkboxSize) + ) { + val svgPath = + "m400-416 236-236q11-11 28-11t28 11q11 11 11 28t-11 28L428-332q-12 12-28 12t-28-12L268-436q-11-11-11-28t11-28q11-11 28-11t28 11l76 76Z" + val path = PathParser().parsePathString(svgPath).toPath() + val scaleFactor = size.minDimension / 960f + path.transform(Matrix().apply { + scale(scaleFactor, scaleFactor) + translate(0f, 960f) + }) + drawPath(path, checkmarkColor) + } + } +} \ No newline at end of file diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/MiuixSwitch.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/MiuixSwitch.kt index 886d351..a5b8333 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/MiuixSwitch.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/MiuixSwitch.kt @@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.requiredSize import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.selection.toggleable -import androidx.compose.material3.minimumInteractiveComponentSize import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -86,18 +85,17 @@ fun MiuixSwitch( ) val toggleableModifier = remember(onCheckedChange, isChecked, enabled) { if (onCheckedChange != null) { - Modifier.minimumInteractiveComponentSize() - .toggleable( - value = isChecked, - onValueChange = { - onCheckedChange(it) - hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) - }, - enabled = enabled, - role = Role.Switch, - interactionSource = interactionSource, - indication = null - ) + Modifier.toggleable( + value = isChecked, + onValueChange = { + onCheckedChange(it) + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + }, + enabled = enabled, + role = Role.Switch, + interactionSource = interactionSource, + indication = null + ) } else { Modifier } @@ -107,6 +105,7 @@ fun MiuixSwitch( modifier = modifier .then(toggleableModifier) .wrapContentSize(Alignment.Center) + .size(52.dp, 28.5.dp) .requiredSize(52.dp, 28.5.dp) .clip(SquircleShape(100.dp)) .background(if (enabled) backgroundColor else disabledBackgroundColor) diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/MiuixText.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/MiuixText.kt index 61c0d33..6f9c63e 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/MiuixText.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/MiuixText.kt @@ -4,6 +4,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.BasicText import androidx.compose.foundation.text.InlineTextContent import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.isSpecified @@ -78,13 +80,7 @@ fun MiuixText( style: TextStyle = MiuixTheme.textStyles.main ) { - val textColor: Color = if (color.isSpecified) { - color - } else if (style.color.isSpecified) { - style.color - } else { - MiuixTheme.colorScheme.primary - } + val textColor by rememberUpdatedState(if (color.isSpecified) color else if (style.color.isSpecified) style.color else MiuixTheme.colorScheme.primary) BasicText( text, @@ -167,13 +163,7 @@ fun MiuixText( onTextLayout: (TextLayoutResult) -> Unit = {}, style: TextStyle = MiuixTheme.textStyles.main ) { - val textColor: Color = if (color.isSpecified) { - color - } else if (style.color.isSpecified) { - style.color - } else { - MiuixTheme.colorScheme.primary - } + val textColor by rememberUpdatedState(if (color.isSpecified) color else if (style.color.isSpecified) style.color else MiuixTheme.colorScheme.primary) BasicText( text = text, diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/MiuixTextField.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/MiuixTextField.kt index cb55211..e140269 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/MiuixTextField.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/MiuixTextField.kt @@ -15,6 +15,7 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.SolidColor @@ -76,9 +77,8 @@ fun MiuixTextField( ) { val isFocused by interactionSource.collectIsFocusedAsState() val borderWidth by animateDpAsState(if (isFocused) 1.6.dp else 0.dp) - val borderColor by animateColorAsState( - if (isFocused) MiuixTheme.colorScheme.primary else MiuixTheme.colorScheme.primaryContainer - ) + val backgroundColor by rememberUpdatedState(if (isSecondary) MiuixTheme.colorScheme.secondaryContainer else MiuixTheme.colorScheme.primaryContainer) + val borderColor by animateColorAsState(if (isFocused) MiuixTheme.colorScheme.primary else backgroundColor) val labelOffsetY by animateDpAsState(if (value.isNotEmpty()) -(insideMargin.height / 2) else 0.dp) val innerTextOffsetY by animateDpAsState(if (value.isNotEmpty()) (insideMargin.height / 2) else 0.dp) val labelFontSize by animateDpAsState(if (value.isNotEmpty()) 10.dp else 16.dp) @@ -104,7 +104,7 @@ fun MiuixTextField( modifier = Modifier .fillMaxWidth() .background( - color = if (isSecondary) MiuixTheme.colorScheme.secondaryContainer else MiuixTheme.colorScheme.primaryContainer, + color = backgroundColor, shape = SquircleShape(cornerRadius) ) .border(