From 58f161af051d09413fc1ddbec87ead36c46139f4 Mon Sep 17 00:00:00 2001 From: Xavier Molloy <44061143+xavimolloy@users.noreply.github.com> Date: Tue, 29 Aug 2023 13:37:35 +0200 Subject: [PATCH] ANDROAPP-5470-mobile-ui-Create-text-input (#33) * reset button functionality and padding corrections * add documentation, remove unnecessary mutable values , add disabled examples * small correction after rebase --- .../kotlin/org/hisp/dhis/common/App.kt | 5 +- .../hisp/dhis/common/screens/Components.kt | 3 +- .../common/screens/FormsComponentsScreen.kt | 2 +- .../hisp/dhis/common/screens/InputScreen.kt | 8 +- .../dhis/common/screens/InputTextScreen.kt | 49 ++++++++++++ .../mobile/ui/designsystem/component/Input.kt | 76 +++++++++++++++++++ .../ui/designsystem/component/InputField.kt | 19 ++--- .../ui/designsystem/component/InputShell.kt | 20 +++-- .../designsystem/component/SupportingText.kt | 2 + .../mobile/ui/designsystem/component/Text.kt | 44 +++++++++++ 10 files changed, 203 insertions(+), 25 deletions(-) create mode 100644 common/src/commonMain/kotlin/org/hisp/dhis/common/screens/InputTextScreen.kt create mode 100644 designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/Input.kt diff --git a/common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt b/common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt index 36b889332..5b61485c9 100644 --- a/common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt @@ -26,6 +26,7 @@ import org.hisp.dhis.common.screens.Components import org.hisp.dhis.common.screens.FormsComponentsScreen import org.hisp.dhis.common.screens.IconButtonScreen import org.hisp.dhis.common.screens.InputScreen +import org.hisp.dhis.common.screens.InputTextScreen import org.hisp.dhis.common.screens.LegendDescriptionScreen import org.hisp.dhis.common.screens.LegendScreen import org.hisp.dhis.common.screens.NotImplementedScreen @@ -45,7 +46,7 @@ fun App() { @OptIn(ExperimentalMaterial3Api::class) @Composable fun Main() { - val currentScreen = remember { mutableStateOf(Components.LEGEND_DESCRIPTION) } + val currentScreen = remember { mutableStateOf(Components.INPUT_TEXT) } var expanded by remember { mutableStateOf(false) } Column(modifier = Modifier.padding(Spacing.Spacing16)) { @@ -98,8 +99,8 @@ fun Main() { Components.LEGEND -> LegendScreen() Components.INPUT -> InputScreen() Components.SUPPORTING_TEXT -> SupportingTextScreen() + Components.INPUT_TEXT -> InputTextScreen() Components.LEGEND_DESCRIPTION -> LegendDescriptionScreen() - else -> NotImplementedScreen() } } diff --git a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/Components.kt b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/Components.kt index ab342ac68..396b8f068 100644 --- a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/Components.kt +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/Components.kt @@ -10,5 +10,6 @@ enum class Components(val label: String) { SUPPORTING_TEXT("Supporting Text"), LEGEND("Legend"), INPUT("Input"), - LEGEND_DESCRIPTION("Legend description") + LEGEND_DESCRIPTION("Legend description"), + INPUT_TEXT("Input Text") } diff --git a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/FormsComponentsScreen.kt b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/FormsComponentsScreen.kt index c3395e57f..9bb7398bb 100644 --- a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/FormsComponentsScreen.kt +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/FormsComponentsScreen.kt @@ -17,7 +17,7 @@ import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing fun FormsComponentsScreen() { ColumnComponentContainer("Input Shell") { Text("Sample functional Input Shell ", style = MaterialTheme.typography.titleSmall) - InputShellPreview("Label", inputField = { BasicInput("Helper", true, InputStyle.WITH_HELPER_BEFORE) }) + InputShellPreview("Label", inputField = { BasicInput("Helper", true, InputStyle.WITH_HELPER_BEFORE) {} }) Text("Unfocused Input shell ", style = MaterialTheme.typography.titleSmall) InputShellPreview("Label") Text("Focused ", style = MaterialTheme.typography.titleSmall) diff --git a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/InputScreen.kt b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/InputScreen.kt index f6b9c8b6f..9b6930545 100644 --- a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/InputScreen.kt +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/InputScreen.kt @@ -11,13 +11,13 @@ fun InputScreen() { ColumnComponentContainer( content = { Text("With helper before") - BasicInput("Helper", helperStyle = InputStyle.WITH_HELPER_BEFORE, inputText = "Input") + BasicInput("Helper", helperStyle = InputStyle.WITH_HELPER_BEFORE, inputText = "Input") {} Text("With helper after") - BasicInput("Helper", helperStyle = InputStyle.WITH_HELPER_AFTER, inputText = "Input") + BasicInput("Helper", helperStyle = InputStyle.WITH_HELPER_AFTER, inputText = "Input") {} Text("No helper") - BasicInput(inputText = "Input") + BasicInput(inputText = "Input") {} Text("Disabled") - BasicInput(enabled = false, inputText = "Input") + BasicInput(enabled = false, inputText = "Input") {} } ) } diff --git a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/InputTextScreen.kt b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/InputTextScreen.kt new file mode 100644 index 000000000..d7d0c40c8 --- /dev/null +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/InputTextScreen.kt @@ -0,0 +1,49 @@ +package org.hisp.dhis.common.screens + +import androidx.compose.runtime.Composable +import org.hisp.dhis.mobile.ui.designsystem.component.ColumnComponentContainer +import org.hisp.dhis.mobile.ui.designsystem.component.InputShellState +import org.hisp.dhis.mobile.ui.designsystem.component.InputText +import org.hisp.dhis.mobile.ui.designsystem.component.SubTitle +import org.hisp.dhis.mobile.ui.designsystem.component.SupportingTextData +import org.hisp.dhis.mobile.ui.designsystem.component.SupportingTextState +import org.hisp.dhis.mobile.ui.designsystem.component.Title +import org.hisp.dhis.mobile.ui.designsystem.theme.TextColor + +@Composable +fun InputTextScreen() { + ColumnComponentContainer { + Title("Input text component", textColor = TextColor.OnSurfaceVariant) + SubTitle(" Basic Input text", textColor = TextColor.OnSurfaceVariant) + InputText(title = "Label", inputText = "") + + SubTitle("Input text with legend", textColor = TextColor.OnSurfaceVariant) + InputText(title = "Label", inputText = "", legendText = "Legend") + + SubTitle("Input text with Supporting text", textColor = TextColor.OnSurfaceVariant) + InputText(title = "Label", inputText = "", supportingText = listOf(SupportingTextData("Supporting text", SupportingTextState.DEFAULT))) + + SubTitle("Input text with Supporting text and legend", textColor = TextColor.OnSurfaceVariant) + InputText(title = "Label", inputText = "", supportingText = listOf(SupportingTextData("Supporting text", SupportingTextState.DEFAULT)), legendText = "Legend") + + SubTitle("Input text with error and warning text and legend", textColor = TextColor.OnSurfaceVariant) + InputText( + title = "Label", + inputText = "", + supportingText = listOf( + SupportingTextData("Supporting text", SupportingTextState.DEFAULT), + SupportingTextData("Supporting text", SupportingTextState.WARNING), + SupportingTextData("Supporting text", SupportingTextState.ERROR) + + ), + legendText = "Legend", + state = InputShellState.ERROR + ) + + SubTitle("Disabled Input text ", textColor = TextColor.OnSurfaceVariant) + InputText(title = "Label", inputText = "", state = InputShellState.DISABLED) + + SubTitle("Disabled Input text with content ", textColor = TextColor.OnSurfaceVariant) + InputText(title = "Label", inputText = "Content", state = InputShellState.DISABLED) + } +} diff --git a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/Input.kt b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/Input.kt new file mode 100644 index 000000000..ca75bdd67 --- /dev/null +++ b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/Input.kt @@ -0,0 +1,76 @@ +package org.hisp.dhis.mobile.ui.designsystem.component + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Cancel +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor + +/** + * DHIS2 Input Text. Wraps DHIS · [InputShell]. + * @param title controls the text to be shown for the title + * @param state Manages the InputShell state + * @param supportingText is a list of SupportingTextData that + * manages all the messages to be shown + * @param legendText manages the text to be shown with the legend component + * @param inputText manages the value of the text in the input field + * @param modifier allows a modifier to be passed externally + */ +@Composable +fun InputText( + title: String, + state: InputShellState = InputShellState.UNFOCUSED, + supportingText: List? = null, + legendText: String? = null, + inputText: String = "", + modifier: Modifier = Modifier +) { + var inputValue by rememberSaveable { mutableStateOf(inputText) } + var deleteButtonIsVisible by remember { mutableStateOf(false) } + InputShell( + modifier = modifier, + title = title, + primaryButton = { + if (deleteButtonIsVisible) { + IconButton( + icon = { + Icon( + imageVector = Icons.Outlined.Cancel, + contentDescription = "Icon Button" + ) + }, + onClick = { + inputValue = "" + deleteButtonIsVisible = false + }, + enabled = state != InputShellState.DISABLED + ) + } + }, + state = state, + legend = { + legendText?.let { + Legend(SurfaceColor.CustomGreen, legendText) {} + } + }, + supportingText = { + supportingText?.forEach { label -> SupportingText(label.text, label.state) } + }, + inputField = { + BasicInput( + inputText = inputValue, + onInputChanged = { + inputValue = it + deleteButtonIsVisible = inputValue.isNotEmpty() + }, + enabled = state != InputShellState.DISABLED + ) + } + ) +} diff --git a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputField.kt b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputField.kt index 6e1dbde0f..6df0c9760 100644 --- a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputField.kt +++ b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputField.kt @@ -9,10 +9,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.text.BasicTextField import androidx.compose.material3.MaterialTheme 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.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.CornerRadius @@ -50,20 +46,21 @@ fun EmptyInput( /** * DHIS2 Basic Input. Wraps Material· [BasicTextField]. + * @param helper Manages the helper text to be shown * @param enabled Controls the enabled state of the component. When `false`, this component will not be * clickable and will appear disabled to accessibility services. - * @param helper Manages the helper text to be shown * @param helperStyle manages the helper text style, NONE by default + * @param inputText manages the value of the input field text + * @param onInputChanged gives access to the onTextChangedEvent */ @Composable fun BasicInput( helper: String? = null, enabled: Boolean = true, helperStyle: InputStyle = InputStyle.NONE, - inputText: String = "" + inputText: String = "", + onInputChanged: (String) -> Unit ) { - var text by remember { mutableStateOf(inputText) } - var visualTransformation = VisualTransformation.None if (helperStyle != InputStyle.NONE) { @@ -81,10 +78,8 @@ fun BasicInput( Color.Transparent ) .fillMaxWidth(), - value = text, - onValueChange = { - text = it - }, + value = inputText, + onValueChange = onInputChanged, enabled = enabled, textStyle = MaterialTheme.typography.bodyLarge.copy(color = if (enabled) TextColor.OnSurface else TextColor.OnDisabledSurface), singleLine = true, diff --git a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputShell.kt b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputShell.kt index 3f8b18798..74c1b6fcf 100644 --- a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputShell.kt +++ b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputShell.kt @@ -2,12 +2,14 @@ package org.hisp.dhis.mobile.ui.designsystem.component import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Divider @@ -45,9 +47,10 @@ fun InputShell( secondaryButton: @Composable (() -> Unit)? = null, inputField: @Composable (() -> Unit)? = null, supportingText: @Composable (() -> Unit)? = null, - legend: @Composable (() -> Unit)? = null + legend: @Composable (() -> Unit)? = null, + modifier: Modifier = Modifier ) { - Column(modifier = Modifier.fillMaxWidth().clip(shape = RoundedCornerShape(Radius.XS, Radius.XS))) { + Column(modifier = modifier.fillMaxWidth().clip(shape = RoundedCornerShape(Radius.XS, Radius.XS))) { var indicatorColor by remember { mutableStateOf(InputShellState.UNFOCUSED.color) } val backgroundColor = if (state != InputShellState.DISABLED) SurfaceColor.Surface else SurfaceColor.DisabledSurface InputShellRow( @@ -68,13 +71,20 @@ fun InputShell( Row( verticalAlignment = Alignment.CenterVertically ) { - primaryButton?.invoke() + primaryButton?.let { + Box(Modifier.size(Spacing.Spacing48)) { + it.invoke() + } + } if (primaryButton != null && secondaryButton != null) { InputShellButtonSeparator() Spacer(modifier = Modifier.width(Spacing.Spacing4)) } - secondaryButton?.invoke() - Spacer(modifier = Modifier.width(Spacing.Spacing4)) + secondaryButton?.let { + Box(Modifier.size(Spacing.Spacing48)) { + it.invoke() + } + } } } InputShellIndicator(color = indicatorColor) diff --git a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/SupportingText.kt b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/SupportingText.kt index 6453710e3..96b410be2 100644 --- a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/SupportingText.kt +++ b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/SupportingText.kt @@ -139,3 +139,5 @@ enum class SupportingTextState(val color: Color) { WARNING(TextColor.OnWarning), ERROR(SurfaceColor.Error) } + +data class SupportingTextData(val text: String, val state: SupportingTextState) diff --git a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/Text.kt b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/Text.kt index 0c099dc28..1a494b482 100644 --- a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/Text.kt +++ b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/Text.kt @@ -85,3 +85,47 @@ internal fun LegendDescriptionRangeText( textAlign = TextAlign.Start ) } + +@Composable +fun Title( + text: String, + textColor: Color, + modifier: Modifier = Modifier +) { + Text( + text, + modifier = modifier, + color = textColor, + style = MaterialTheme.typography.titleMedium, + textAlign = TextAlign.Start + ) +} + +@Composable fun SubTitle( + text: String, + textColor: Color, + modifier: Modifier = Modifier +) { + Text( + text, + modifier = modifier, + color = textColor, + style = MaterialTheme.typography.titleSmall, + textAlign = TextAlign.Start + ) +} + +@Composable +internal fun Description( + text: String, + textColor: Color, + modifier: Modifier = Modifier +) { + Text( + text, + modifier = modifier, + color = textColor, + style = MaterialTheme.typography.bodyMedium, + textAlign = TextAlign.Start + ) +}