From 90eaa4b9ddd0e0ed102d677ef7e7faf1a6e9ec1b Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 19 Jun 2025 18:44:22 +0300 Subject: [PATCH 01/80] placeholderText -> placeholder --- .../sdk/ui/core/component/field/dropdown/PODropdownField.kt | 4 ++-- .../core/component/field/dropdown/POLabeledDropdownField.kt | 4 ++-- .../sdk/ui/core/component/field/phone/POPhoneNumberField.kt | 4 ++-- .../sdk/ui/core/component/field/text/POLabeledTextField.kt | 4 ++-- .../sdk/ui/core/component/field/text/POTextField.kt | 6 +++--- .../sdk/ui/card/tokenization/CardTokenizationScreen.kt | 4 ++-- .../com/processout/sdk/ui/card/update/CardUpdateScreen.kt | 2 +- .../processout/sdk/ui/checkout/screen/CardTokenization.kt | 4 ++-- .../sdk/ui/checkout/screen/NativeAlternativePayment.kt | 4 ++-- .../sdk/ui/napm/NativeAlternativePaymentScreen.kt | 4 ++-- .../sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt | 4 ++-- 11 files changed, 22 insertions(+), 22 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt index dd1707364..cb6388561 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt @@ -50,7 +50,7 @@ fun PODropdownField( preferFormattedTextSelection: Boolean = false, enabled: Boolean = true, isError: Boolean = false, - placeholderText: String? = null + placeholder: String? = null ) { MaterialTheme( colorScheme = MaterialTheme.colorScheme.copy(surface = Color.Transparent), @@ -87,7 +87,7 @@ fun PODropdownField( readOnly = true, isDropdown = true, isError = isError, - placeholderText = placeholderText, + placeholder = placeholder, trailingIcon = { Icon( painter = painterResource(id = R.drawable.po_dropdown_arrow), diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/POLabeledDropdownField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/POLabeledDropdownField.kt index a28901455..e758566cc 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/POLabeledDropdownField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/POLabeledDropdownField.kt @@ -27,7 +27,7 @@ fun POLabeledDropdownField( preferFormattedTextSelection: Boolean = false, enabled: Boolean = true, isError: Boolean = false, - placeholderText: String? = null + placeholder: String? = null ) { LabeledFieldLayout( title = title, @@ -45,7 +45,7 @@ fun POLabeledDropdownField( preferFormattedTextSelection = preferFormattedTextSelection, enabled = enabled, isError = isError, - placeholderText = placeholderText + placeholder = placeholder ) } } diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt index d59c87930..092600cf0 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt @@ -49,7 +49,7 @@ fun POPhoneNumberField( menuMatchesTextFieldWidth = false, preferFormattedTextSelection = true, isError = state.isError, - placeholderText = state.regionCodePlaceholder + placeholder = state.regionCodePlaceholder ) val phoneNumberUtil = remember { PhoneNumberUtil.getInstance() } POTextField( @@ -87,7 +87,7 @@ fun POPhoneNumberField( enabled = state.enabled, isError = state.isError, forceTextDirectionLtr = state.forceTextDirectionLtr, - placeholderText = state.numberPlaceholder, + placeholder = state.numberPlaceholder, visualTransformation = state.visualTransformation ?: VisualTransformation.None, keyboardOptions = state.keyboardOptions, keyboardActions = keyboardActions diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POLabeledTextField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POLabeledTextField.kt index 95cfab7f5..268e7b214 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POLabeledTextField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POLabeledTextField.kt @@ -36,7 +36,7 @@ fun POLabeledTextField( isDropdown: Boolean = false, isError: Boolean = false, forceTextDirectionLtr: Boolean = false, - placeholderText: String? = null, + placeholder: String? = null, leadingIcon: @Composable (() -> Unit)? = null, trailingIcon: @Composable (() -> Unit)? = null, visualTransformation: VisualTransformation = VisualTransformation.None, @@ -63,7 +63,7 @@ fun POLabeledTextField( isDropdown = isDropdown, isError = isError, forceTextDirectionLtr = forceTextDirectionLtr, - placeholderText = placeholderText, + placeholder = placeholder, leadingIcon = leadingIcon, trailingIcon = trailingIcon, visualTransformation = visualTransformation, diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt index 7f37a7b78..a0ed15ef5 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt @@ -41,7 +41,7 @@ fun POTextField( isDropdown: Boolean = false, isError: Boolean = false, forceTextDirectionLtr: Boolean = false, - placeholderText: String? = null, + placeholder: String? = null, leadingIcon: @Composable (() -> Unit)? = null, trailingIcon: @Composable (() -> Unit)? = null, visualTransformation: VisualTransformation = VisualTransformation.None, @@ -86,9 +86,9 @@ fun POTextField( enabled = enabled, isError = isError, placeholder = { - if (!placeholderText.isNullOrBlank()) { + if (!placeholder.isNullOrBlank()) { POText( - text = placeholderText, + text = placeholder, color = stateStyle.placeholderTextColor, style = stateStyle.text.textStyle ) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationScreen.kt index 0878f5da7..97d402611 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationScreen.kt @@ -300,7 +300,7 @@ private fun TextField( enabled = state.enabled, isError = state.isError, forceTextDirectionLtr = state.forceTextDirectionLtr, - placeholderText = state.placeholder, + placeholder = state.placeholder, trailingIcon = { state.iconResId?.let { AnimatedFieldIcon(id = it) } }, visualTransformation = state.visualTransformation, keyboardOptions = state.keyboardOptions, @@ -373,7 +373,7 @@ private fun DropdownField( menuStyle = menuStyle, enabled = state.enabled, isError = state.isError, - placeholderText = state.placeholder + placeholder = state.placeholder ) } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/update/CardUpdateScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/update/CardUpdateScreen.kt index 99510d703..8a83be8e2 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/card/update/CardUpdateScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/update/CardUpdateScreen.kt @@ -149,7 +149,7 @@ private fun Fields( readOnly = !state.enabled, isError = state.isError, forceTextDirectionLtr = state.forceTextDirectionLtr, - placeholderText = state.placeholder, + placeholder = state.placeholder, trailingIcon = { state.iconResId?.let { AnimatedFieldIcon(id = it) } }, keyboardOptions = state.keyboardOptions, keyboardActions = POField.keyboardActions( diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/CardTokenization.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/CardTokenization.kt index 9905056b0..ac19d546d 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/CardTokenization.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/CardTokenization.kt @@ -245,7 +245,7 @@ private fun TextField( enabled = state.enabled, isError = state.isError, forceTextDirectionLtr = state.forceTextDirectionLtr, - placeholderText = state.placeholder, + placeholder = state.placeholder, trailingIcon = { state.iconResId?.let { AnimatedFieldIcon(id = it) } }, visualTransformation = state.visualTransformation, keyboardOptions = state.keyboardOptions, @@ -330,7 +330,7 @@ private fun DropdownField( menuStyle = menuStyle, enabled = state.enabled, isError = state.isError, - placeholderText = state.placeholder + placeholder = state.placeholder ) } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/NativeAlternativePayment.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/NativeAlternativePayment.kt index 76384b3bf..ebbbe4481 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/NativeAlternativePayment.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/NativeAlternativePayment.kt @@ -167,7 +167,7 @@ private fun TextField( enabled = state.enabled, isError = state.isError, forceTextDirectionLtr = state.forceTextDirectionLtr, - placeholderText = state.placeholder, + placeholder = state.placeholder, visualTransformation = state.visualTransformation, keyboardOptions = state.keyboardOptions, keyboardActions = POField.keyboardActions( @@ -318,7 +318,7 @@ private fun DropdownField( labelsStyle = labelsStyle, menuStyle = menuStyle, isError = state.isError, - placeholderText = state.placeholder + placeholder = state.placeholder ) } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt index 9b2360de8..9f0feca16 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt @@ -246,7 +246,7 @@ private fun TextField( enabled = state.enabled, isError = state.isError, forceTextDirectionLtr = state.forceTextDirectionLtr, - placeholderText = state.placeholder, + placeholder = state.placeholder, visualTransformation = state.visualTransformation, keyboardOptions = state.keyboardOptions, keyboardActions = POField.keyboardActions( @@ -375,7 +375,7 @@ private fun DropdownField( labelsStyle = labelsStyle, menuStyle = menuStyle, isError = state.isError, - placeholderText = state.placeholder + placeholder = state.placeholder ) } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt index 5ebaf7bb7..878c45588 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt @@ -269,7 +269,7 @@ private fun TextField( enabled = state.enabled, isError = state.isError, forceTextDirectionLtr = state.forceTextDirectionLtr, - placeholderText = state.placeholder, + placeholder = state.placeholder, visualTransformation = state.visualTransformation, keyboardOptions = state.keyboardOptions, keyboardActions = POField.keyboardActions( @@ -399,7 +399,7 @@ private fun DropdownField( labelsStyle = labelsStyle, menuStyle = menuStyle, isError = state.isError, - placeholderText = state.placeholder + placeholder = state.placeholder ) } From d53f0db21a66715b94d28fbc771c0d7bc412ef66 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 19 Jun 2025 19:31:36 +0300 Subject: [PATCH 02/80] TextFieldDefaults.DecorationBox with optional label --- .../core/component/field/text/POTextField.kt | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt index a0ed15ef5..faffbb8cf 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt @@ -9,7 +9,7 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.selection.LocalTextSelectionColors import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.OutlinedTextFieldDefaults +import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.focus.onFocusChanged @@ -41,6 +41,7 @@ fun POTextField( isDropdown: Boolean = false, isError: Boolean = false, forceTextDirectionLtr: Boolean = false, + label: String? = null, placeholder: String? = null, leadingIcon: @Composable (() -> Unit)? = null, trailingIcon: @Composable (() -> Unit)? = null, @@ -80,13 +81,22 @@ fun POTextField( visualTransformation = visualTransformation, interactionSource = interactionSource, decorationBox = @Composable { innerTextField -> - OutlinedTextFieldDefaults.DecorationBox( + TextFieldDefaults.DecorationBox( value = value.text, innerTextField = innerTextField, enabled = enabled, isError = isError, - placeholder = { - if (!placeholder.isNullOrBlank()) { + label = if (label.isNullOrBlank()) null else { + { + POText( + text = label, + color = stateStyle.placeholderTextColor, // TODO(v2): label color/style? + style = stateStyle.text.textStyle + ) + } + }, + placeholder = if (placeholder.isNullOrBlank()) null else { + { POText( text = placeholder, color = stateStyle.placeholderTextColor, From c1368b450f9c9897a93b594143d0f11ad969d66f Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 19 Jun 2025 19:34:58 +0300 Subject: [PATCH 03/80] fieldHeight 52.dp --- .../main/kotlin/com/processout/sdk/ui/core/theme/Dimensions.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Dimensions.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Dimensions.kt index 5ad12b4e3..0dc2bc3a9 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Dimensions.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Dimensions.kt @@ -10,6 +10,7 @@ import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi @ProcessOutInternalApi @Immutable data class PODimensions( + val fieldHeight: Dp = 52.dp, val formComponentMinHeight: Dp = 48.dp, val interactiveComponentMinSize: Dp = 44.dp, val iconSizeSmall: Dp = 16.dp, From 963a52466c24475df6784d0eb32835e674d61a69 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 19 Jun 2025 19:43:39 +0300 Subject: [PATCH 04/80] Extract field height as parameter --- .../sdk/ui/core/component/field/text/POTextField.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt index faffbb8cf..23e0dac04 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi import com.processout.sdk.ui.core.component.POText @@ -25,7 +26,7 @@ import com.processout.sdk.ui.core.component.field.POField.ContainerBox import com.processout.sdk.ui.core.component.field.POField.stateStyle import com.processout.sdk.ui.core.component.field.POField.textSelectionColors import com.processout.sdk.ui.core.component.field.POField.textStyle -import com.processout.sdk.ui.core.theme.ProcessOutTheme +import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions /** @suppress */ @ProcessOutInternalApi @@ -34,6 +35,7 @@ fun POTextField( value: TextFieldValue, onValueChange: (TextFieldValue) -> Unit, modifier: Modifier = Modifier, + height: Dp = dimensions.formComponentMinHeight, contentPadding: PaddingValues = POField.contentPadding, style: POField.Style = POField.default, enabled: Boolean = true, @@ -62,7 +64,7 @@ fun POTextField( value = value, onValueChange = onValueChange, modifier = modifier - .requiredHeight(ProcessOutTheme.dimensions.formComponentMinHeight) + .requiredHeight(height) .onFocusChanged { isFocused = it.isFocused }, From a78763c10fccf937b91418abd9133380e3e28435 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 19 Jun 2025 19:48:16 +0300 Subject: [PATCH 05/80] POTextField2 --- .../core/component/field/text/POTextField2.kt | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt new file mode 100644 index 000000000..8477045eb --- /dev/null +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt @@ -0,0 +1,68 @@ +package com.processout.sdk.ui.core.component.field.text + +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.Dp +import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi +import com.processout.sdk.ui.core.component.field.POField +import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions + +/** @suppress */ +@ProcessOutInternalApi +@Composable +fun POTextField2( + value: TextFieldValue, + onValueChange: (TextFieldValue) -> Unit, + modifier: Modifier = Modifier, + height: Dp = dimensions.fieldHeight, + contentPadding: PaddingValues = POField.contentPadding, + style: POField.Style = POField.default, + enabled: Boolean = true, + readOnly: Boolean = false, + isDropdown: Boolean = false, + isError: Boolean = false, + forceTextDirectionLtr: Boolean = false, + label: String? = null, + placeholder: String? = null, + leadingIcon: @Composable (() -> Unit)? = null, + trailingIcon: @Composable (() -> Unit)? = null, + visualTransformation: VisualTransformation = VisualTransformation.None, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + singleLine: Boolean = true, + maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, + minLines: Int = 1, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } +) { + POTextField( + value = value, + onValueChange = onValueChange, + modifier = modifier, + height = height, + contentPadding = contentPadding, + style = style, + enabled = enabled, + readOnly = readOnly, + isDropdown = isDropdown, + isError = isError, + forceTextDirectionLtr = forceTextDirectionLtr, + label = label, + placeholder = placeholder, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + visualTransformation = visualTransformation, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + maxLines = maxLines, + minLines = minLines, + interactionSource = interactionSource + ) +} From 18c2b90eb0f07eed46ae67c9321e2ca63676ccd9 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Fri, 20 Jun 2025 12:30:23 +0300 Subject: [PATCH 06/80] contentPadding2 --- .../com/processout/sdk/ui/core/component/field/POField.kt | 6 ++++++ .../sdk/ui/core/component/field/text/POTextField2.kt | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt index 28c9221f5..68f2d25e2 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt @@ -120,6 +120,12 @@ object POField { vertical = ProcessOutTheme.spacing.medium ) + val contentPadding2: PaddingValues + @Composable get() = PaddingValues( + horizontal = ProcessOutTheme.spacing.medium, + vertical = ProcessOutTheme.spacing.small + ) + internal fun Style.stateStyle( isError: Boolean, isFocused: Boolean diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt index 8477045eb..50aa3f3a5 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt @@ -22,7 +22,7 @@ fun POTextField2( onValueChange: (TextFieldValue) -> Unit, modifier: Modifier = Modifier, height: Dp = dimensions.fieldHeight, - contentPadding: PaddingValues = POField.contentPadding, + contentPadding: PaddingValues = POField.contentPadding2, style: POField.Style = POField.default, enabled: Boolean = true, readOnly: Boolean = false, From 54bb324df9a7b55bd68a64a6ecc35d9f087098de Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Fri, 20 Jun 2025 12:40:01 +0300 Subject: [PATCH 07/80] OutlinedTextFieldDefaults.DecorationBox, remove default label --- .../ui/core/component/field/text/POTextField.kt | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt index 23e0dac04..527604dc0 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt @@ -9,7 +9,7 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.selection.LocalTextSelectionColors import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.TextFieldDefaults +import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.focus.onFocusChanged @@ -83,22 +83,13 @@ fun POTextField( visualTransformation = visualTransformation, interactionSource = interactionSource, decorationBox = @Composable { innerTextField -> - TextFieldDefaults.DecorationBox( + OutlinedTextFieldDefaults.DecorationBox( value = value.text, innerTextField = innerTextField, enabled = enabled, isError = isError, - label = if (label.isNullOrBlank()) null else { - { - POText( - text = label, - color = stateStyle.placeholderTextColor, // TODO(v2): label color/style? - style = stateStyle.text.textStyle - ) - } - }, - placeholder = if (placeholder.isNullOrBlank()) null else { - { + placeholder = { + if (!placeholder.isNullOrBlank()) { POText( text = placeholder, color = stateStyle.placeholderTextColor, From 463ab4ccdf68a00cd35a04afd546668022c24f50 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Fri, 20 Jun 2025 18:36:48 +0300 Subject: [PATCH 08/80] Updated fonts, added work_sans_semibold.ttf --- .../sdk/ui/core/theme/Typography.kt | 3 ++- .../src/main/res/font/work_sans_medium.ttf | Bin 192380 -> 189556 bytes .../src/main/res/font/work_sans_regular.ttf | Bin 191916 -> 188916 bytes .../src/main/res/font/work_sans_semibold.ttf | Bin 0 -> 191016 bytes 4 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 ui-core/src/main/res/font/work_sans_semibold.ttf diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Typography.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Typography.kt index 11fe578aa..4efba2ff7 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Typography.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Typography.kt @@ -15,7 +15,8 @@ import com.processout.sdk.ui.core.style.POTextType.Weight.* private val WorkSans = FontFamily( Font(R.font.work_sans_regular, FontWeight.Normal), - Font(R.font.work_sans_medium, FontWeight.Medium) + Font(R.font.work_sans_medium, FontWeight.Medium), + Font(R.font.work_sans_semibold, FontWeight.SemiBold) ) /** @suppress */ diff --git a/ui-core/src/main/res/font/work_sans_medium.ttf b/ui-core/src/main/res/font/work_sans_medium.ttf index 1800fe2d886dac774a18978fe54d96fd0dde7b9a..21680738207000a7866f5a49981fdef3fd7432e6 100644 GIT binary patch delta 12161 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 k1aD892_}XV)#Am~& zar5@f3El@jagLLZR9OywzxtH(Dkc2*Xw^hZdB=~BRln0P&!10FyA3{E9rx!`)J>DG zg@2EV&D5s>xD8{mDj|?h<19+$SV-LL;QiF=PKdnX;14QKAb(Uf(J}_$4 z@hE%|Vz>(I!6I?hn9dS#`FKBT!6)I)syF`%E@?Gf(tPR(XLKHA!ns^V*|RNbBW5IfypEJ-mxH%Dd&=^rjpkN6=gNyfBj9mZRh- zs+QyAIC5)$k!s{4awcuYr-(AzCf%}z-qRi=?U38#d-T5CA$QPje7x98d+h%ce4u?w zj!E%LEI+S)Y{osD^;dogu*RrnT+u+SyaIP_b6&^k9`~q3o`pXfjdZVV$#ckkHHl9p zn(n^X26pGH$i1d5-%jqr_Ba-~U+KiNB~`hf?8rm7`_di!J||VHB?Dl^>Aks|b5`SS z-H)FHv&$VbfQP{9AG=cq@eaaWHWWAebk=<;&9{`i-9y5VE3{DTZmrmvRV0OC$3V4LY=>zw$QXU89vYsuI-9OFa zGb~)|x))XQEGOsF+!vSd)nM}7)hqZ$FyHHD*ZXsRRX2D)sd1yZLml4ClilZE=MVaE M5#yd+!^a2wFX5{)xc~qF delta 14506 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%0A}%yj(a+BLz2= z9FIuvYKeEG_b^F)WZEnI$g~9&b|gOnrji?god~8!nkJrEn^qvMgokf7y)D#|*NJD1 zl*rrUkMumf0h)Mm=RgxMDPR(ocdX(zWz8Q6pX&6u*juctx_9?7?w731>VZc32VYGM~^GV-gq4~6)&f%aHA;JR!XsW^BH^w zF!IuO(pfxs{$9Ey<|M3qVlC~0bu=Gu6T6Wj(0rt$(pS1*k@^iSzq>6G*xsJ{P6 zl`aZyM7*TqNZ{p|wn&GfA_>QfDpdR}ZzDfTSyB%wi!Tqx(-$ zoAW|K{b7w)f2k5@a<@q5?Ys!*^lr%}bU}QGf%I?qdgANMDeloPKA;R=(#Hi<2Gj)1 z31|#h6tFzt#elT|8w0ikY!BEK@M*yQfWrYN0?q_n3bBP+?C3T38F9l|2n;V~YR-*)xDq44z7(@yMJ}iM56FV8V9l<_b?4$CvznPVS9(|IId7ak3W*ROzCJQ*;XV<$m#c`9H5&j!rn zeE|LOj*>A)57B#8Bzn(^*=)cP_5@&0HV3d5n+sUV<^lE=wqbqPe89f!DZqYse#vm; z#E*{+V2ywS*#f{ptO;;1YX%%5Y{|;-IFqr5SS#RAVOKUx*p&@;u*EcyO7@v(6#%B);2Ke0(_hY!;Duo<6q=4po|j+XXV1+Y=SU2tKfqGC-T97lXw}NzK*{ESkE^C&f>p= z>2O8A3GfO2KY(-iX27}p_ki>Gn}AR9KL9?@R|Br#uK+sv8o-tORlpbcTEJEOH9(cG z1ALMH7Vstfsu+I@H}s6ZjVBh2Z^aV}ane5k_zs>}F#ay?=^c!_@X~_u?RaUy_c;p7tD*{lWr!9hdWIwUUBF!np(D>R&K0=`Nk@NSor zYc!JmfpGRm*$;1YIJr&}*h%1RGy^8**m37@Y-RE0-c#XEY`nz(NfrI2I*$!-%gXJ*bSUE(-hPP6R zlSAbQ5c+Axl!Uv<0kRQG-;d-L?FhG%uyo$0sc=0B%V#_71oxA$gt}=OTu>~FtvFq5 z!1IrGG=oTaXovwP4zXCih4t!Vywv!1#jrrfQ^JAZ2!F(v?l1Yjz|}I zIf3rM0-8iW!8@J~^i%O{gMKDHXw$vYCvd7A{5%#biuL+utU?t`%x_}(Wn?5>&ZZ*Q z$fg4>WBB3H*V$yi#cT%PH8u_MjVP(o@asYkxGOb|DS!@a1*nHCAl{JyRth!^65>ZF zCK=>4C>~HZWAx`q7TyT_1h4~mHgF5@lfd(V>wxh);B$c&0M7w#0G6mUJTrpJNr zOd|237ys6a-vstl;K46=^J~5Me}Fv|d+n?>b!~P zy?C`3zX9y&fd{MJ{5miGJ+P-|9=yt%f6a?G1ABVx!A@^}jTiq87?wzZDYOLcKRoyq uFWxALp^(WAI+~urwTs#LB29=E|Di+-6F&Fgy*|N=06(DObBfrPlK%xvP6w3$ diff --git a/ui-core/src/main/res/font/work_sans_regular.ttf b/ui-core/src/main/res/font/work_sans_regular.ttf index 20c7240371b2036d8444c28afcac2b62d6537c5f..d24586cc0336949d49a1c2bf3fb3b306c47b431e 100644 GIT binary patch delta 12181 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 delta 13963 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
b8Yj10#K{o)$~MiK8ph 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�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 Date: Fri, 20 Jun 2025 19:29:07 +0300 Subject: [PATCH 09/80] Update typography --- .../sdk/ui/core/theme/Typography.kt | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Typography.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Typography.kt index 4efba2ff7..88f422a61 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Typography.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Typography.kt @@ -77,7 +77,72 @@ data class POTypography( fontSize = 14.sp, lineHeight = 18.sp ) -) +) { + + fun s12(fontWeight: FontWeight = FontWeight.Normal) = + TextStyle( + fontFamily = WorkSans, + fontWeight = fontWeight, + fontSize = 12.sp, + lineHeight = 14.sp + ) + + fun s13(fontWeight: FontWeight = FontWeight.Normal) = + TextStyle( + fontFamily = WorkSans, + fontWeight = fontWeight, + fontSize = 13.sp, + lineHeight = 16.sp + ) + + fun s14(fontWeight: FontWeight = FontWeight.Normal) = + TextStyle( + fontFamily = WorkSans, + fontWeight = fontWeight, + fontSize = 14.sp, + lineHeight = 20.sp + ) + + fun s15(fontWeight: FontWeight = FontWeight.Normal) = + TextStyle( + fontFamily = WorkSans, + fontWeight = fontWeight, + fontSize = 15.sp, + lineHeight = 18.sp + ) + + fun s16(fontWeight: FontWeight = FontWeight.Normal) = + TextStyle( + fontFamily = WorkSans, + fontWeight = fontWeight, + fontSize = 16.sp, + lineHeight = 20.sp + ) + + fun s18(fontWeight: FontWeight = FontWeight.Normal) = + TextStyle( + fontFamily = WorkSans, + fontWeight = fontWeight, + fontSize = 18.sp, + lineHeight = 22.sp + ) + + fun s20(fontWeight: FontWeight = FontWeight.Normal) = + TextStyle( + fontFamily = WorkSans, + fontWeight = fontWeight, + fontSize = 20.sp, + lineHeight = 24.sp + ) + + fun s24(fontWeight: FontWeight = FontWeight.Normal) = + TextStyle( + fontFamily = WorkSans, + fontWeight = fontWeight, + fontSize = 24.sp, + lineHeight = 28.sp + ) +} internal val LocalPOTypography = staticCompositionLocalOf { POTypography() } From 5412c16e4dc2fbca3b9a70489596e1fda45833ce Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Fri, 20 Jun 2025 19:43:16 +0300 Subject: [PATCH 10/80] Added Paragraph --- .../com/processout/sdk/ui/core/theme/Typography.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Typography.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Typography.kt index 88f422a61..c07213c1a 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Typography.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Typography.kt @@ -23,6 +23,7 @@ private val WorkSans = FontFamily( @ProcessOutInternalApi @Immutable data class POTypography( + val paragraph: Paragraph = Paragraph, val title: TextStyle = TextStyle( fontFamily = WorkSans, fontWeight = FontWeight.Medium, @@ -142,6 +143,17 @@ data class POTypography( fontSize = 24.sp, lineHeight = 28.sp ) + + object Paragraph { + + fun s16(fontWeight: FontWeight = FontWeight.Normal) = + TextStyle( + fontFamily = WorkSans, + fontWeight = fontWeight, + fontSize = 16.sp, + lineHeight = 26.sp + ) + } } internal val LocalPOTypography = staticCompositionLocalOf { POTypography() } From 51527d7137b087f202bfb06d3a49542c3d92b2f8 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Fri, 20 Jun 2025 20:22:51 +0300 Subject: [PATCH 11/80] letterSpacing --- .../sdk/ui/core/theme/Typography.kt | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Typography.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Typography.kt index c07213c1a..b8eaab73b 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Typography.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Typography.kt @@ -85,7 +85,8 @@ data class POTypography( fontFamily = WorkSans, fontWeight = fontWeight, fontSize = 12.sp, - lineHeight = 14.sp + lineHeight = 14.sp, + letterSpacing = 0.15.sp ) fun s13(fontWeight: FontWeight = FontWeight.Normal) = @@ -101,7 +102,8 @@ data class POTypography( fontFamily = WorkSans, fontWeight = fontWeight, fontSize = 14.sp, - lineHeight = 20.sp + lineHeight = 20.sp, + letterSpacing = 0.15.sp ) fun s15(fontWeight: FontWeight = FontWeight.Normal) = @@ -125,7 +127,12 @@ data class POTypography( fontFamily = WorkSans, fontWeight = fontWeight, fontSize = 18.sp, - lineHeight = 22.sp + lineHeight = 22.sp, + letterSpacing = when (fontWeight) { + FontWeight.Medium, + FontWeight.SemiBold -> 0.1.sp + else -> 0.sp + } ) fun s20(fontWeight: FontWeight = FontWeight.Normal) = @@ -133,7 +140,12 @@ data class POTypography( fontFamily = WorkSans, fontWeight = fontWeight, fontSize = 20.sp, - lineHeight = 24.sp + lineHeight = 24.sp, + letterSpacing = when (fontWeight) { + FontWeight.Medium -> (-0.15).sp + FontWeight.SemiBold -> (-0.1).sp + else -> (-0.2).sp + } ) fun s24(fontWeight: FontWeight = FontWeight.Normal) = From 18f26942f85afd5f2207d4f472b139ce89676097 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 23 Jun 2025 13:39:36 +0300 Subject: [PATCH 12/80] Added 'labelTextColor' to style --- .../sdk/ui/core/component/field/POField.kt | 42 ++++++++++++++++--- .../sdk/ui/core/style/POFieldStyle.kt | 27 +++++++++++- 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt index 68f2d25e2..6899c9f1c 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt @@ -33,6 +33,8 @@ import com.processout.sdk.ui.core.extension.conditional import com.processout.sdk.ui.core.style.POFieldStateStyle import com.processout.sdk.ui.core.style.POFieldStyle import com.processout.sdk.ui.core.theme.ProcessOutTheme +import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors +import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing /** @suppress */ @ProcessOutInternalApi @@ -48,6 +50,7 @@ object POField { @Immutable data class StateStyle( val text: POText.Style, + val labelTextColor: Color, val placeholderTextColor: Color, val backgroundColor: Color, val controlsTintColor: Color, @@ -61,6 +64,7 @@ object POField { Style( normal = StateStyle( text = POText.body2, + labelTextColor = colors.text.primary, placeholderTextColor = colors.text.muted, backgroundColor = colors.input.backgroundDefault, controlsTintColor = colors.text.primary, @@ -70,6 +74,7 @@ object POField { ), error = StateStyle( text = POText.body2, + labelTextColor = colors.text.error, placeholderTextColor = colors.text.muted, backgroundColor = colors.input.backgroundDefault, controlsTintColor = colors.text.primary, @@ -79,6 +84,7 @@ object POField { ), focused = StateStyle( text = POText.body2, + labelTextColor = colors.text.primary, placeholderTextColor = colors.text.muted, backgroundColor = colors.input.backgroundDefault, controlsTintColor = colors.text.primary, @@ -92,21 +98,45 @@ object POField { @Composable fun custom(style: POFieldStyle): Style { val normal = style.normal.toStateStyle() - return Style( + var customStyle = Style( normal = normal, error = style.error.toStateStyle(), focused = style.focused?.toStateStyle() ?: normal ) + if (customStyle.normal.labelTextColor == Color.Unspecified) { + customStyle = customStyle.copy( + normal = customStyle.normal.copy( + labelTextColor = default.normal.labelTextColor + ) + ) + } + if (customStyle.error.labelTextColor == Color.Unspecified) { + customStyle = customStyle.copy( + error = customStyle.error.copy( + labelTextColor = default.error.labelTextColor + ) + ) + } + if (customStyle.focused.labelTextColor == Color.Unspecified) { + customStyle = customStyle.copy( + focused = customStyle.focused.copy( + labelTextColor = default.focused.labelTextColor + ) + ) + } + return customStyle } @Composable private fun POFieldStateStyle.toStateStyle() = StateStyle( text = POText.custom(style = text), + labelTextColor = if (labelTextColorResId != 0) + colorResource(id = labelTextColorResId) else Color.Unspecified, placeholderTextColor = colorResource(id = placeholderTextColorResId), backgroundColor = colorResource(id = backgroundColorResId), controlsTintColor = colorResource(id = controlsTintColorResId), dropdownRippleColor = dropdownRippleColorResId?.let { colorResource(id = it) } - ?: ProcessOutTheme.colors.text.muted, + ?: colors.text.muted, shape = RoundedCornerShape(size = border.radiusDp.dp), border = POBorderStroke( width = border.widthDp.dp, @@ -116,14 +146,14 @@ object POField { val contentPadding: PaddingValues @Composable get() = PaddingValues( - horizontal = ProcessOutTheme.spacing.large, - vertical = ProcessOutTheme.spacing.medium + horizontal = spacing.large, + vertical = spacing.medium ) val contentPadding2: PaddingValues @Composable get() = PaddingValues( - horizontal = ProcessOutTheme.spacing.medium, - vertical = ProcessOutTheme.spacing.small + horizontal = spacing.medium, + vertical = 0.dp ) internal fun Style.stateStyle( diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POFieldStyle.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POFieldStyle.kt index e334c3d54..00f0acf50 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POFieldStyle.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POFieldStyle.kt @@ -15,6 +15,8 @@ data class POFieldStyle( data class POFieldStateStyle( val text: POTextStyle, @ColorRes + val labelTextColorResId: Int, + @ColorRes val placeholderTextColorResId: Int, @ColorRes val backgroundColorResId: Int, @@ -23,4 +25,27 @@ data class POFieldStateStyle( val border: POBorderStyle, @ColorRes val dropdownRippleColorResId: Int? = null -) : Parcelable +) : Parcelable { + + @Deprecated(message = "Use alternative constructor.") + constructor( + text: POTextStyle, + @ColorRes + placeholderTextColorResId: Int, + @ColorRes + backgroundColorResId: Int, + @ColorRes + controlsTintColorResId: Int, + border: POBorderStyle, + @ColorRes + dropdownRippleColorResId: Int? = null + ) : this( + text = text, + labelTextColorResId = 0, + placeholderTextColorResId = placeholderTextColorResId, + backgroundColorResId = backgroundColorResId, + controlsTintColorResId = controlsTintColorResId, + border = border, + dropdownRippleColorResId = dropdownRippleColorResId + ) +} From 4bc56c0b0a9ae9309635f0d9d5789d57af4b1b17 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 23 Jun 2025 13:44:18 +0300 Subject: [PATCH 13/80] Custom label and placeholder in POTextField --- .../core/component/field/text/POTextField.kt | 50 +++++++++++++++---- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt index 527604dc0..7600ab0a2 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt @@ -2,6 +2,10 @@ package com.processout.sdk.ui.core.component.field.text +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.spring import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* import androidx.compose.foundation.text.BasicTextField @@ -19,6 +23,7 @@ import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi import com.processout.sdk.ui.core.component.POText import com.processout.sdk.ui.core.component.field.POField @@ -85,18 +90,45 @@ fun POTextField( decorationBox = @Composable { innerTextField -> OutlinedTextFieldDefaults.DecorationBox( value = value.text, - innerTextField = innerTextField, - enabled = enabled, - isError = isError, - placeholder = { - if (!placeholder.isNullOrBlank()) { - POText( - text = placeholder, - color = stateStyle.placeholderTextColor, - style = stateStyle.text.textStyle + innerTextField = { + val animationStiffness = Spring.StiffnessMedium + Column( + modifier = Modifier.animateContentSize( + animationSpec = spring(stiffness = animationStiffness) ) + ) { + val isLabelFloating = isFocused || value.text.isNotEmpty() + if (!label.isNullOrBlank()) { + val fontSizeValue = stateStyle.text.textStyle.fontSize.value + val animatedFontSizeValue by animateFloatAsState( + targetValue = if (isLabelFloating) fontSizeValue * 0.78f else fontSizeValue, + animationSpec = spring(stiffness = animationStiffness) + ) + POText( + text = label, + color = stateStyle.labelTextColor, + style = stateStyle.text.textStyle.copy(fontSize = animatedFontSizeValue.sp) + ) + if (isLabelFloating) { + Spacer(modifier = Modifier.requiredHeight(2.dp)) + } + } + if (isLabelFloating || label.isNullOrBlank()) { + Box { + if (value.text.isEmpty() && !placeholder.isNullOrBlank()) { + POText( + text = placeholder, + color = stateStyle.placeholderTextColor, + style = stateStyle.text.textStyle + ) + } + innerTextField() + } + } } }, + enabled = enabled, + isError = isError, leadingIcon = leadingIcon, trailingIcon = trailingIcon, singleLine = singleLine, From ffc929d5147d021801cd4093c5c8a58cbd444312 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 23 Jun 2025 13:57:38 +0300 Subject: [PATCH 14/80] Updated POSpacing --- .../kotlin/com/processout/sdk/ui/core/theme/Spacing.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Spacing.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Spacing.kt index dfbc48bf0..cebf2061f 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Spacing.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Spacing.kt @@ -10,6 +10,15 @@ import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi @ProcessOutInternalApi @Immutable data class POSpacing( + val space0: Dp = 0.dp, + val space2: Dp = 2.dp, + val space4: Dp = 4.dp, + val space6: Dp = 6.dp, + val space8: Dp = 8.dp, + val space10: Dp = 10.dp, + val space12: Dp = 12.dp, + val space16: Dp = 16.dp, + val space20: Dp = 20.dp, val extraSmall: Dp = 4.dp, val small: Dp = 8.dp, val medium: Dp = 12.dp, From 9b83be3504fda7d306d7ead8e9043eecee2c0deb Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 23 Jun 2025 14:23:56 +0300 Subject: [PATCH 15/80] Adjust field min height and paddings --- .../com/processout/sdk/ui/core/component/field/POField.kt | 4 ++-- .../sdk/ui/core/component/field/text/POTextField.kt | 7 ++++--- .../sdk/ui/core/component/field/text/POTextField2.kt | 4 ++-- .../kotlin/com/processout/sdk/ui/core/theme/Dimensions.kt | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt index 6899c9f1c..2cdf3037f 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt @@ -152,8 +152,8 @@ object POField { val contentPadding2: PaddingValues @Composable get() = PaddingValues( - horizontal = spacing.medium, - vertical = 0.dp + horizontal = spacing.space12, + vertical = spacing.space6 ) internal fun Style.stateStyle( diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt index 7600ab0a2..5671cb1ea 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt @@ -32,6 +32,7 @@ import com.processout.sdk.ui.core.component.field.POField.stateStyle import com.processout.sdk.ui.core.component.field.POField.textSelectionColors import com.processout.sdk.ui.core.component.field.POField.textStyle import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions +import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing /** @suppress */ @ProcessOutInternalApi @@ -40,7 +41,7 @@ fun POTextField( value: TextFieldValue, onValueChange: (TextFieldValue) -> Unit, modifier: Modifier = Modifier, - height: Dp = dimensions.formComponentMinHeight, + minHeight: Dp = dimensions.formComponentMinHeight, contentPadding: PaddingValues = POField.contentPadding, style: POField.Style = POField.default, enabled: Boolean = true, @@ -69,7 +70,7 @@ fun POTextField( value = value, onValueChange = onValueChange, modifier = modifier - .requiredHeight(height) + .requiredHeightIn(min = minHeight) .onFocusChanged { isFocused = it.isFocused }, @@ -110,7 +111,7 @@ fun POTextField( style = stateStyle.text.textStyle.copy(fontSize = animatedFontSizeValue.sp) ) if (isLabelFloating) { - Spacer(modifier = Modifier.requiredHeight(2.dp)) + Spacer(modifier = Modifier.requiredHeight(spacing.space2)) } } if (isLabelFloating || label.isNullOrBlank()) { diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt index 50aa3f3a5..e4b1f895e 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt @@ -21,7 +21,7 @@ fun POTextField2( value: TextFieldValue, onValueChange: (TextFieldValue) -> Unit, modifier: Modifier = Modifier, - height: Dp = dimensions.fieldHeight, + minHeight: Dp = dimensions.fieldMinHeight, contentPadding: PaddingValues = POField.contentPadding2, style: POField.Style = POField.default, enabled: Boolean = true, @@ -45,7 +45,7 @@ fun POTextField2( value = value, onValueChange = onValueChange, modifier = modifier, - height = height, + minHeight = minHeight, contentPadding = contentPadding, style = style, enabled = enabled, diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Dimensions.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Dimensions.kt index 0dc2bc3a9..5bee82a3b 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Dimensions.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Dimensions.kt @@ -10,7 +10,7 @@ import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi @ProcessOutInternalApi @Immutable data class PODimensions( - val fieldHeight: Dp = 52.dp, + val fieldMinHeight: Dp = 52.dp, val formComponentMinHeight: Dp = 48.dp, val interactiveComponentMinSize: Dp = 44.dp, val iconSizeSmall: Dp = 16.dp, From b2944332e6ff8db2579f5e156ecc7d2ec006ac03 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 23 Jun 2025 14:48:47 +0300 Subject: [PATCH 16/80] Floating scale 0.8f --- .../processout/sdk/ui/core/component/field/text/POTextField.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt index 5671cb1ea..0c0d8c625 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt @@ -102,7 +102,7 @@ fun POTextField( if (!label.isNullOrBlank()) { val fontSizeValue = stateStyle.text.textStyle.fontSize.value val animatedFontSizeValue by animateFloatAsState( - targetValue = if (isLabelFloating) fontSizeValue * 0.78f else fontSizeValue, + targetValue = if (isLabelFloating) fontSizeValue * 0.8f else fontSizeValue, animationSpec = spring(stiffness = animationStiffness) ) POText( From aad1701656642fd86c26e89d305ee1763cac00ae Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 23 Jun 2025 15:59:43 +0300 Subject: [PATCH 17/80] Field style --- .../sdk/ui/core/component/field/POField.kt | 46 +++++++++++++++++++ .../core/component/field/text/POTextField2.kt | 2 +- .../processout/sdk/ui/core/theme/Colors.kt | 6 +++ .../processout/sdk/ui/core/theme/Shapes.kt | 1 + 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt index 2cdf3037f..fb915d821 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.colorResource import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDirection @@ -95,6 +96,51 @@ object POField { ) } + val default2: Style + @Composable get() = with(ProcessOutTheme) { + Style( + normal = StateStyle( + text = POText.Style( + color = colors.text.primary, + textStyle = typography.s15(FontWeight.Medium) + ), + labelTextColor = colors.text.placeholder, + placeholderTextColor = colors.text.placeholder, + backgroundColor = colors.input.backgroundDefault, + controlsTintColor = colors.text.primary, + dropdownRippleColor = colors.text.muted, + shape = shapes.fieldRoundedCorners, + border = POBorderStroke(width = 1.5.dp, color = colors.input.borderDefault2) + ), + error = StateStyle( + text = POText.Style( + color = colors.text.primary, + textStyle = typography.s15(FontWeight.Medium) + ), + labelTextColor = colors.text.placeholder, + placeholderTextColor = colors.text.placeholder, + backgroundColor = colors.input.backgroundDefault, + controlsTintColor = colors.text.primary, + dropdownRippleColor = colors.text.muted, + shape = shapes.fieldRoundedCorners, + border = POBorderStroke(width = 1.5.dp, color = colors.input.borderError) + ), + focused = StateStyle( + text = POText.Style( + color = colors.text.primary, + textStyle = typography.s15(FontWeight.Medium) + ), + labelTextColor = colors.text.placeholder, + placeholderTextColor = colors.text.placeholder, + backgroundColor = colors.input.backgroundDefault, + controlsTintColor = colors.text.primary, + dropdownRippleColor = colors.text.muted, + shape = shapes.fieldRoundedCorners, + border = POBorderStroke(width = 1.5.dp, color = colors.text.primary) + ) + ) + } + @Composable fun custom(style: POFieldStyle): Style { val normal = style.normal.toStateStyle() diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt index e4b1f895e..912175a72 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt @@ -23,7 +23,7 @@ fun POTextField2( modifier: Modifier = Modifier, minHeight: Dp = dimensions.fieldMinHeight, contentPadding: PaddingValues = POField.contentPadding2, - style: POField.Style = POField.default, + style: POField.Style = POField.default2, enabled: Boolean = true, readOnly: Boolean = false, isDropdown: Boolean = false, diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index 62357e77d..6e6b80636 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -21,6 +21,7 @@ data class POColors( val primary: Color, val inverse: Color, val muted: Color, + val placeholder: Color, val disabled: Color, val success: Color, val error: Color @@ -31,6 +32,7 @@ data class POColors( val backgroundDefault: Color, val backgroundDisabled: Color, val borderDefault: Color, + val borderDefault2: Color, val borderDisabled: Color, val borderFocused: Color, val borderError: Color @@ -70,6 +72,7 @@ val POLightColorPalette = POColors( primary = Color(0xFF121821), inverse = Color(0xFFFAFAFA), muted = Color(0xFF5B6576), + placeholder = Color(0xFF707378), disabled = Color(0xFFADB5BD), success = Color(0xFF00291D), error = Color(0xFFD11D2F) @@ -78,6 +81,7 @@ val POLightColorPalette = POColors( backgroundDefault = Color(0xFFFFFFFF), backgroundDisabled = Color(0xFFEDEEEF), borderDefault = Color(0xFF7C8593), + borderDefault2 = Color(0x1F121314), borderDisabled = Color(0xFFADB5BD), borderFocused = Color(0xFF4791FF), borderError = Color(0xFFD11D2F) @@ -111,6 +115,7 @@ val PODarkColorPalette = POColors( primary = Color(0xFFFAFAFA), inverse = Color(0xFF121821), muted = Color(0xFFADB5BD), + placeholder = Color(0xFFA7A9AF), disabled = Color(0xFF5B6576), success = Color(0xFFE5FFF8), error = Color(0xFFFF5263) @@ -119,6 +124,7 @@ val PODarkColorPalette = POColors( backgroundDefault = Color(0xFF121821), backgroundDisabled = Color(0xFF242C38), borderDefault = Color(0xFFCCD1D6), + borderDefault2 = Color(0x29F6F8FB), borderDisabled = Color(0xFF7C8593), borderFocused = Color(0xFFFFE500), borderError = Color(0xFFFF5263) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Shapes.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Shapes.kt index c725e7306..35ec4eab2 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Shapes.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Shapes.kt @@ -11,6 +11,7 @@ import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi @ProcessOutInternalApi @Immutable data class POShapes( + val fieldRoundedCorners: CornerBasedShape = RoundedCornerShape(6.dp), val roundedCornersSmall: CornerBasedShape = RoundedCornerShape(4.dp), val roundedCornersMedium: CornerBasedShape = RoundedCornerShape(8.dp), val roundedCornersLarge: CornerBasedShape = RoundedCornerShape(16.dp), From bfce44d3889f18f04132b2a14e2bf122cd1c305e Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 23 Jun 2025 17:46:44 +0300 Subject: [PATCH 18/80] FieldState: title -> label --- .../sdk/ui/card/tokenization/CardTokenizationScreen.kt | 2 +- .../ui/card/tokenization/CardTokenizationViewModel.kt | 2 +- .../sdk/ui/checkout/DynamicCheckoutViewModel.kt | 2 +- .../sdk/ui/checkout/screen/CardTokenization.kt | 2 +- .../sdk/ui/checkout/screen/DynamicCheckoutScreen.kt | 2 +- .../sdk/ui/checkout/screen/NativeAlternativePayment.kt | 8 ++++---- .../sdk/ui/napm/NativeAlternativePaymentScreen.kt | 8 ++++---- .../sdk/ui/napm/NativeAlternativePaymentViewModel.kt | 8 ++++---- .../sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt | 10 +++++----- .../ui/napm/v2/NativeAlternativePaymentViewModel.kt | 10 +++++----- .../com/processout/sdk/ui/shared/state/FieldState.kt | 4 ++-- 11 files changed, 29 insertions(+), 29 deletions(-) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationScreen.kt index 97d402611..6bd090e2b 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationScreen.kt @@ -385,7 +385,7 @@ private fun CheckboxField( modifier: Modifier = Modifier ) { POCheckbox( - text = state.title ?: String(), + text = state.label ?: String(), checked = state.value.text.toBooleanStrictOrNull() ?: false, onCheckedChange = { if (state.enabled) { diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationViewModel.kt index 29f5cc753..3faee7f0d 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationViewModel.kt @@ -458,7 +458,7 @@ internal class CardTokenizationViewModel private constructor( FieldState( id = field.id, value = field.value, - title = title, + label = title, enabled = field.enabled, isError = !field.isValid ) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutViewModel.kt index 91ecab1de..c60ba2cdb 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutViewModel.kt @@ -374,7 +374,7 @@ internal class DynamicCheckoutViewModel private constructor( FieldState( id = id, value = value, - title = title + label = title ) ) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/CardTokenization.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/CardTokenization.kt index ac19d546d..012a50d07 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/CardTokenization.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/CardTokenization.kt @@ -343,7 +343,7 @@ private fun CheckboxField( modifier: Modifier = Modifier ) { POCheckbox( - text = state.title ?: String(), + text = state.label ?: String(), checked = state.value.text.toBooleanStrictOrNull() ?: false, onCheckedChange = { if (state.enabled) { diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/DynamicCheckoutScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/DynamicCheckoutScreen.kt index 5dc582707..b7f942005 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/DynamicCheckoutScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/DynamicCheckoutScreen.kt @@ -546,7 +546,7 @@ private fun CheckboxField( modifier: Modifier = Modifier ) { POCheckbox( - text = state.title ?: String(), + text = state.label ?: String(), checked = state.value.text.toBooleanStrictOrNull() ?: false, onCheckedChange = { onEvent( diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/NativeAlternativePayment.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/NativeAlternativePayment.kt index ebbbe4481..495797012 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/NativeAlternativePayment.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/NativeAlternativePayment.kt @@ -149,7 +149,7 @@ private fun TextField( ) ) }, - title = state.title ?: String(), + title = state.label ?: String(), description = state.description, modifier = modifier .focusRequester(focusRequester) @@ -213,7 +213,7 @@ private fun CodeField( ) ) }, - title = state.title ?: String(), + title = state.label ?: String(), description = state.description, modifier = modifier .onFocusChanged { @@ -271,7 +271,7 @@ private fun RadioField( ) }, availableValues = state.availableValues ?: POImmutableList(emptyList()), - title = state.title ?: String(), + title = state.label ?: String(), description = state.description, modifier = modifier, radioGroupStyle = radioGroupStyle, @@ -302,7 +302,7 @@ private fun DropdownField( ) }, availableValues = state.availableValues ?: POImmutableList(emptyList()), - title = state.title ?: String(), + title = state.label ?: String(), description = state.description, modifier = modifier .onFocusChanged { diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt index 9f0feca16..05780ad18 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt @@ -229,7 +229,7 @@ private fun TextField( ) ) }, - title = state.title ?: String(), + title = state.label ?: String(), description = state.description, modifier = modifier .focusRequester(focusRequester) @@ -283,7 +283,7 @@ private fun CodeField( ) ) }, - title = state.title ?: String(), + title = state.label ?: String(), description = state.description, modifier = modifier .onFocusChanged { @@ -331,7 +331,7 @@ private fun RadioField( ) }, availableValues = state.availableValues ?: POImmutableList(emptyList()), - title = state.title ?: String(), + title = state.label ?: String(), description = state.description, modifier = modifier, radioGroupStyle = radioGroupStyle, @@ -360,7 +360,7 @@ private fun DropdownField( ) }, availableValues = state.availableValues ?: POImmutableList(emptyList()), - title = state.title ?: String(), + title = state.label ?: String(), description = state.description, modifier = modifier .onFocusChanged { diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt index 217dd9c4c..a3cf0168b 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt @@ -231,7 +231,7 @@ internal class NativeAlternativePaymentViewModel private constructor( FieldState( id = id, value = value, - title = displayName, + label = displayName, description = description, placeholder = type.placeholder(), isError = !isValid, @@ -255,7 +255,7 @@ internal class NativeAlternativePaymentViewModel private constructor( id = id, value = value, length = length, - title = displayName, + label = displayName, description = description, isError = !isValid, keyboardOptions = type.keyboardOptions(keyboardAction.imeAction), @@ -269,7 +269,7 @@ internal class NativeAlternativePaymentViewModel private constructor( id = id, value = value, availableValues = availableValues?.let { POImmutableList(it) }, - title = displayName, + label = displayName, description = description, isError = !isValid ) @@ -281,7 +281,7 @@ internal class NativeAlternativePaymentViewModel private constructor( id = id, value = value, availableValues = availableValues?.let { POImmutableList(it) }, - title = displayName, + label = displayName, description = description, isError = !isValid ) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt index 878c45588..6c9695cd6 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt @@ -252,7 +252,7 @@ private fun TextField( ) ) }, - title = state.title ?: String(), + title = state.label ?: String(), description = state.description, modifier = modifier .focusRequester(focusRequester) @@ -306,7 +306,7 @@ private fun CodeField( ) ) }, - title = state.title ?: String(), + title = state.label ?: String(), description = state.description, modifier = modifier .onFocusChanged { @@ -355,7 +355,7 @@ private fun RadioField( ) }, availableValues = state.availableValues ?: POImmutableList(emptyList()), - title = state.title ?: String(), + title = state.label ?: String(), description = state.description, modifier = modifier, radioGroupStyle = radioGroupStyle, @@ -384,7 +384,7 @@ private fun DropdownField( ) }, availableValues = state.availableValues ?: POImmutableList(emptyList()), - title = state.title ?: String(), + title = state.label ?: String(), description = state.description, modifier = modifier .onFocusChanged { @@ -412,7 +412,7 @@ private fun CheckboxField( modifier: Modifier = Modifier ) { POLabeledCheckboxField( - text = state.title ?: String(), + text = state.label ?: String(), checked = state.value.text.toBooleanStrictOrNull() ?: false, onCheckedChange = { onEvent( diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt index 4c0bd514e..31fa214ae 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt @@ -250,7 +250,7 @@ internal class NativeAlternativePaymentViewModel private constructor( FieldState( id = id, value = value.textFieldValue(), - title = label, + label = label, description = description, placeholder = parameter.placeholder(), isError = !isValid, @@ -270,7 +270,7 @@ internal class NativeAlternativePaymentViewModel private constructor( id = id, value = value.textFieldValue(), length = maxLength, - title = label, + label = label, description = description, isError = !isValid, inputFilter = parameter.inputFilter(), @@ -285,7 +285,7 @@ internal class NativeAlternativePaymentViewModel private constructor( id = id, value = value.textFieldValue(), availableValues = parameter.availableValues(), - title = label, + label = label, description = description, isError = !isValid ) @@ -297,7 +297,7 @@ internal class NativeAlternativePaymentViewModel private constructor( id = id, value = value.textFieldValue(), availableValues = parameter.availableValues(), - title = label, + label = label, description = description, isError = !isValid ) @@ -308,7 +308,7 @@ internal class NativeAlternativePaymentViewModel private constructor( FieldState( id = id, value = value.textFieldValue(), - title = label, + label = label, description = description, isError = !isValid ) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/shared/state/FieldState.kt b/ui/src/main/kotlin/com/processout/sdk/ui/shared/state/FieldState.kt index 3c4bb10f6..f99e648cf 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/shared/state/FieldState.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/shared/state/FieldState.kt @@ -15,9 +15,9 @@ internal data class FieldState( val value: TextFieldValue = TextFieldValue(), val availableValues: POImmutableList? = null, val length: Int? = null, - val title: String? = null, - val description: String? = null, + val label: String? = null, val placeholder: String? = null, + val description: String? = null, @DrawableRes val iconResId: Int? = null, val enabled: Boolean = true, val isError: Boolean = false, From 7547f140999796486a6fd415e0b7f1a1b62cf6e8 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 23 Jun 2025 18:47:43 +0300 Subject: [PATCH 19/80] POTextField2: added error message box, applied on screen --- .../core/component/field/text/POTextField2.kt | 63 +++++++++++-------- ...PONativeAlternativePaymentConfiguration.kt | 2 + .../napm/v2/NativeAlternativePaymentScreen.kt | 22 ++++--- 3 files changed, 53 insertions(+), 34 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt index 912175a72..56a859bf0 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt @@ -1,7 +1,9 @@ package com.processout.sdk.ui.core.component.field.text import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.runtime.Composable @@ -11,8 +13,10 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.Dp import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi +import com.processout.sdk.ui.core.component.POMessageBox import com.processout.sdk.ui.core.component.field.POField import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions +import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing /** @suppress */ @ProcessOutInternalApi @@ -23,7 +27,8 @@ fun POTextField2( modifier: Modifier = Modifier, minHeight: Dp = dimensions.fieldMinHeight, contentPadding: PaddingValues = POField.contentPadding2, - style: POField.Style = POField.default2, + fieldStyle: POField.Style = POField.default2, + descriptionStyle: POMessageBox.Style = POMessageBox.error, enabled: Boolean = true, readOnly: Boolean = false, isDropdown: Boolean = false, @@ -31,6 +36,7 @@ fun POTextField2( forceTextDirectionLtr: Boolean = false, label: String? = null, placeholder: String? = null, + description: String? = null, leadingIcon: @Composable (() -> Unit)? = null, trailingIcon: @Composable (() -> Unit)? = null, visualTransformation: VisualTransformation = VisualTransformation.None, @@ -41,28 +47,35 @@ fun POTextField2( minLines: Int = 1, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } ) { - POTextField( - value = value, - onValueChange = onValueChange, - modifier = modifier, - minHeight = minHeight, - contentPadding = contentPadding, - style = style, - enabled = enabled, - readOnly = readOnly, - isDropdown = isDropdown, - isError = isError, - forceTextDirectionLtr = forceTextDirectionLtr, - label = label, - placeholder = placeholder, - leadingIcon = leadingIcon, - trailingIcon = trailingIcon, - visualTransformation = visualTransformation, - keyboardOptions = keyboardOptions, - keyboardActions = keyboardActions, - singleLine = singleLine, - maxLines = maxLines, - minLines = minLines, - interactionSource = interactionSource - ) + Column { + POTextField( + value = value, + onValueChange = onValueChange, + modifier = modifier, + minHeight = minHeight, + contentPadding = contentPadding, + style = fieldStyle, + enabled = enabled, + readOnly = readOnly, + isDropdown = isDropdown, + isError = isError, + forceTextDirectionLtr = forceTextDirectionLtr, + label = label, + placeholder = placeholder, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + visualTransformation = visualTransformation, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + maxLines = maxLines, + minLines = minLines, + interactionSource = interactionSource + ) + POMessageBox( + text = description, + modifier = Modifier.padding(top = spacing.space8), + style = descriptionStyle + ) + } } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt index eadceb8ff..9b9b316fc 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt @@ -302,6 +302,7 @@ data class PONativeAlternativePaymentConfiguration( * @param[background] Background style. * @param[message] Message style. * @param[errorMessage] Error message style. + * @param[errorMessageBox] Error message box style. * @param[successMessage] Success message style. * @param[successImageResId] Success image drawable resource ID. * @param[progressIndicatorColorResId] Color resource ID for progress indicator. @@ -323,6 +324,7 @@ data class PONativeAlternativePaymentConfiguration( val background: POBackgroundStyle? = null, val message: POTextStyle? = null, val errorMessage: POTextStyle? = null, + val errorMessageBox: POMessageBoxStyle? = null, val successMessage: POTextStyle? = null, @DrawableRes val successImageResId: Int? = null, diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt index 6c9695cd6..e7edd341f 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt @@ -46,7 +46,7 @@ import com.processout.sdk.ui.core.component.field.dropdown.POLabeledDropdownFiel import com.processout.sdk.ui.core.component.field.phone.POLabeledPhoneNumberField import com.processout.sdk.ui.core.component.field.radio.POLabeledRadioField import com.processout.sdk.ui.core.component.field.radio.PORadioGroup -import com.processout.sdk.ui.core.component.field.text.POLabeledTextField +import com.processout.sdk.ui.core.component.field.text.POTextField2 import com.processout.sdk.ui.core.state.POActionState import com.processout.sdk.ui.core.state.POImmutableList import com.processout.sdk.ui.core.state.POPhoneNumberFieldState @@ -179,7 +179,7 @@ private fun UserInput( focusedFieldId = state.focusedFieldId, isPrimaryActionEnabled = isPrimaryActionEnabled, fieldStyle = style.field, - labelsStyle = labelsStyle, + descriptionStyle = style.errorMessageBox, modifier = Modifier.fillMaxWidth() ) is CodeField -> CodeField( @@ -238,11 +238,11 @@ private fun TextField( focusedFieldId: String?, isPrimaryActionEnabled: Boolean, fieldStyle: POField.Style, - labelsStyle: POFieldLabels.Style, + descriptionStyle: POMessageBox.Style, modifier: Modifier = Modifier ) { val focusRequester = remember { FocusRequester() } - POLabeledTextField( + POTextField2( value = state.value, onValueChange = { onEvent( @@ -252,8 +252,6 @@ private fun TextField( ) ) }, - title = state.label ?: String(), - description = state.description, modifier = modifier .focusRequester(focusRequester) .onFocusChanged { @@ -265,11 +263,13 @@ private fun TextField( ) }, fieldStyle = fieldStyle, - labelsStyle = labelsStyle, + descriptionStyle = descriptionStyle, + label = state.label, + placeholder = state.placeholder, + description = state.description, enabled = state.enabled, isError = state.isError, forceTextDirectionLtr = state.forceTextDirectionLtr, - placeholder = state.placeholder, visualTransformation = state.visualTransformation, keyboardOptions = state.keyboardOptions, keyboardActions = POField.keyboardActions( @@ -727,6 +727,7 @@ internal object NativeAlternativePaymentScreen { val successBackgroundColor: Color, val message: AndroidTextView.Style, val errorMessage: POText.Style, + val errorMessageBox: POMessageBox.Style, val successMessage: POText.Style, @DrawableRes val successImageResId: Int, val progressIndicatorColor: Color, @@ -746,7 +747,7 @@ internal object NativeAlternativePaymentScreen { } ?: POText.label1, field = custom?.field?.let { POField.custom(style = it) - } ?: POField.default, + } ?: POField.default2, codeField = custom?.codeField?.let { POField.custom(style = it) } ?: POCodeField.default, @@ -781,6 +782,9 @@ internal object NativeAlternativePaymentScreen { errorMessage = custom?.errorMessage?.let { POText.custom(style = it) } ?: POText.errorLabel, + errorMessageBox = custom?.errorMessageBox?.let { + POMessageBox.custom(style = it) + } ?: POMessageBox.error, successMessage = custom?.successMessage?.let { POText.custom(style = it) } ?: POText.Style( From f5aa8b1f6d2b8bb21eb1b0a7ead4bfc8c1cfcb20 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 23 Jun 2025 19:16:41 +0300 Subject: [PATCH 20/80] POMessageBox paddings --- .../com/processout/sdk/ui/core/component/POMessageBox.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt index 11e546e3f..48c91703f 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt @@ -33,7 +33,7 @@ fun POMessageBox( text: String?, modifier: Modifier = Modifier, style: POMessageBox.Style = POMessageBox.error, - horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, + horizontalArrangement: Arrangement.Horizontal = Arrangement.spacedBy(spacing.space10), enterAnimationDelayMillis: Int = 0 ) { AnimatedVisibility( @@ -51,7 +51,10 @@ fun POMessageBox( ) .clip(style.shape) .background(color = style.backgroundColor) - .padding(spacing.extraLarge) + .padding( + horizontal = spacing.space12, + vertical = spacing.space8 + ) ) { var currentText by remember { mutableStateOf(String()) } if (!text.isNullOrBlank()) { From 7204885971b61ec2c8589fa38db4df9c4325b3a2 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 23 Jun 2025 19:22:51 +0300 Subject: [PATCH 21/80] po_icon_info --- .../kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt | 2 +- .../com/processout/sdk/ui/core/component/POTextWithIcon.kt | 2 +- .../main/res/drawable/{po_info_icon.xml => po_icon_info.xml} | 0 .../processout/sdk/ui/checkout/screen/DynamicCheckoutScreen.kt | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename ui-core/src/main/res/drawable/{po_info_icon.xml => po_icon_info.xml} (100%) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt index 48c91703f..e40173308 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt @@ -98,7 +98,7 @@ object POMessageBox { Style( textWithIcon = POTextWithIcon.Style( text = text, - iconResId = iconResId ?: R.drawable.po_info_icon, + iconResId = iconResId ?: R.drawable.po_icon_info, iconColorFilter = if (iconResId != null) null else ColorFilter.tint(color = text.color) ), diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POTextWithIcon.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POTextWithIcon.kt index 8de3faaf4..aa9f59a42 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POTextWithIcon.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POTextWithIcon.kt @@ -93,7 +93,7 @@ object POTextWithIcon { ) return Style( text = text, - iconResId = R.drawable.po_info_icon, + iconResId = R.drawable.po_icon_info, iconColorFilter = ColorFilter.tint(color = text.color) ) } diff --git a/ui-core/src/main/res/drawable/po_info_icon.xml b/ui-core/src/main/res/drawable/po_icon_info.xml similarity index 100% rename from ui-core/src/main/res/drawable/po_info_icon.xml rename to ui-core/src/main/res/drawable/po_icon_info.xml diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/DynamicCheckoutScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/DynamicCheckoutScreen.kt index b7f942005..7e0fdf078 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/DynamicCheckoutScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/DynamicCheckoutScreen.kt @@ -790,7 +790,7 @@ internal object DynamicCheckoutScreen { title = POText.body1, description = POTextWithIcon.Style( text = description, - iconResId = R.drawable.po_info_icon, + iconResId = R.drawable.po_icon_info, iconColorFilter = ColorFilter.tint(color = description.color) ), shape = shapes.roundedCornersSmall, From c10328fbe34bbf208f6879913f6f8bcd9bfa2200 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 23 Jun 2025 19:33:50 +0300 Subject: [PATCH 22/80] po_icon_warning_diamond.xml --- .../sdk/ui/core/component/POMessageBox.kt | 6 ++++-- .../main/res/drawable/po_icon_warning_diamond.xml | 13 +++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 ui-core/src/main/res/drawable/po_icon_warning_diamond.xml diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt index e40173308..08de5eca4 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt @@ -83,7 +83,9 @@ object POMessageBox { val error: Style @Composable get() = Style( - textWithIcon = POTextWithIcon.default, + textWithIcon = POTextWithIcon.default.copy( + iconResId = R.drawable.po_icon_warning_diamond + ), shape = shapes.roundedCornersSmall, border = POBorderStroke( width = 1.dp, @@ -98,7 +100,7 @@ object POMessageBox { Style( textWithIcon = POTextWithIcon.Style( text = text, - iconResId = iconResId ?: R.drawable.po_icon_info, + iconResId = iconResId ?: error.textWithIcon.iconResId, iconColorFilter = if (iconResId != null) null else ColorFilter.tint(color = text.color) ), diff --git a/ui-core/src/main/res/drawable/po_icon_warning_diamond.xml b/ui-core/src/main/res/drawable/po_icon_warning_diamond.xml new file mode 100644 index 000000000..661700537 --- /dev/null +++ b/ui-core/src/main/res/drawable/po_icon_warning_diamond.xml @@ -0,0 +1,13 @@ + + + From ec2f98c148181ad8fc1ecb65a0b276c92f8a7288 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 23 Jun 2025 19:44:49 +0300 Subject: [PATCH 23/80] error2 style --- .../sdk/ui/core/component/POMessageBox.kt | 13 +++++++++++++ .../sdk/ui/core/component/field/POField.kt | 6 +++--- .../ui/core/component/field/text/POTextField2.kt | 2 +- .../com/processout/sdk/ui/core/theme/Shapes.kt | 3 ++- .../ui/napm/v2/NativeAlternativePaymentScreen.kt | 2 +- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt index 08de5eca4..ff362dc29 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt @@ -94,6 +94,19 @@ object POMessageBox { backgroundColor = colors.surface.error ) + val error2: Style + @Composable get() = Style( + textWithIcon = POTextWithIcon.default.copy( + iconResId = R.drawable.po_icon_warning_diamond + ), + shape = shapes.roundedCorners8, + border = POBorderStroke( + width = 1.dp, + color = colors.input.borderError + ), + backgroundColor = colors.surface.error + ) + @Composable fun custom(style: POMessageBoxStyle) = with(style) { val text = POText.custom(style = text) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt index fb915d821..91c1b05c9 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt @@ -109,7 +109,7 @@ object POField { backgroundColor = colors.input.backgroundDefault, controlsTintColor = colors.text.primary, dropdownRippleColor = colors.text.muted, - shape = shapes.fieldRoundedCorners, + shape = shapes.roundedCorners6, border = POBorderStroke(width = 1.5.dp, color = colors.input.borderDefault2) ), error = StateStyle( @@ -122,7 +122,7 @@ object POField { backgroundColor = colors.input.backgroundDefault, controlsTintColor = colors.text.primary, dropdownRippleColor = colors.text.muted, - shape = shapes.fieldRoundedCorners, + shape = shapes.roundedCorners6, border = POBorderStroke(width = 1.5.dp, color = colors.input.borderError) ), focused = StateStyle( @@ -135,7 +135,7 @@ object POField { backgroundColor = colors.input.backgroundDefault, controlsTintColor = colors.text.primary, dropdownRippleColor = colors.text.muted, - shape = shapes.fieldRoundedCorners, + shape = shapes.roundedCorners6, border = POBorderStroke(width = 1.5.dp, color = colors.text.primary) ) ) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt index 56a859bf0..ec93f8f1d 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt @@ -28,7 +28,7 @@ fun POTextField2( minHeight: Dp = dimensions.fieldMinHeight, contentPadding: PaddingValues = POField.contentPadding2, fieldStyle: POField.Style = POField.default2, - descriptionStyle: POMessageBox.Style = POMessageBox.error, + descriptionStyle: POMessageBox.Style = POMessageBox.error2, enabled: Boolean = true, readOnly: Boolean = false, isDropdown: Boolean = false, diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Shapes.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Shapes.kt index 35ec4eab2..3448157e2 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Shapes.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Shapes.kt @@ -11,7 +11,8 @@ import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi @ProcessOutInternalApi @Immutable data class POShapes( - val fieldRoundedCorners: CornerBasedShape = RoundedCornerShape(6.dp), + val roundedCorners6: CornerBasedShape = RoundedCornerShape(6.dp), + val roundedCorners8: CornerBasedShape = RoundedCornerShape(8.dp), val roundedCornersSmall: CornerBasedShape = RoundedCornerShape(4.dp), val roundedCornersMedium: CornerBasedShape = RoundedCornerShape(8.dp), val roundedCornersLarge: CornerBasedShape = RoundedCornerShape(16.dp), diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt index e7edd341f..8de1d6e24 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt @@ -784,7 +784,7 @@ internal object NativeAlternativePaymentScreen { } ?: POText.errorLabel, errorMessageBox = custom?.errorMessageBox?.let { POMessageBox.custom(style = it) - } ?: POMessageBox.error, + } ?: POMessageBox.error2, successMessage = custom?.successMessage?.let { POText.custom(style = it) } ?: POText.Style( From 98acbe7fe48aee123b595303ba08e57c5ca3be2a Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 23 Jun 2025 20:12:45 +0300 Subject: [PATCH 24/80] Update colors and error2 style --- .../sdk/ui/core/component/POMessageBox.kt | 35 ++++++++++++------- .../processout/sdk/ui/core/theme/Colors.kt | 18 ++++++---- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt index ff362dc29..90853fb12 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POMessageBox.kt @@ -18,13 +18,16 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.Shape import androidx.compose.ui.res.colorResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.processout.sdk.ui.core.R import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi +import com.processout.sdk.ui.core.component.POText.Style import com.processout.sdk.ui.core.style.POMessageBoxStyle import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors import com.processout.sdk.ui.core.theme.ProcessOutTheme.shapes import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing +import com.processout.sdk.ui.core.theme.ProcessOutTheme.typography /** @suppress */ @ProcessOutInternalApi @@ -33,7 +36,7 @@ fun POMessageBox( text: String?, modifier: Modifier = Modifier, style: POMessageBox.Style = POMessageBox.error, - horizontalArrangement: Arrangement.Horizontal = Arrangement.spacedBy(spacing.space10), + horizontalArrangement: Arrangement.Horizontal = Arrangement.spacedBy(spacing.space8), enterAnimationDelayMillis: Int = 0 ) { AnimatedVisibility( @@ -95,17 +98,25 @@ object POMessageBox { ) val error2: Style - @Composable get() = Style( - textWithIcon = POTextWithIcon.default.copy( - iconResId = R.drawable.po_icon_warning_diamond - ), - shape = shapes.roundedCorners8, - border = POBorderStroke( - width = 1.dp, - color = colors.input.borderError - ), - backgroundColor = colors.surface.error - ) + @Composable get() { + val text = Style( + color = colors.text.onTipError, + textStyle = typography.s14(FontWeight.Medium) + ) + return Style( + textWithIcon = POTextWithIcon.Style( + text = text, + iconResId = R.drawable.po_icon_warning_diamond, + iconColorFilter = ColorFilter.tint(color = text.color) + ), + shape = shapes.roundedCorners8, + border = POBorderStroke( + width = 1.dp, + color = colors.input.borderDefault2 + ), + backgroundColor = colors.surface.toastError + ) + } @Composable fun custom(style: POMessageBoxStyle) = with(style) { diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index 6e6b80636..cebee51b2 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -24,7 +24,8 @@ data class POColors( val placeholder: Color, val disabled: Color, val success: Color, - val error: Color + val error: Color, + val onTipError: Color ) @Immutable @@ -57,7 +58,8 @@ data class POColors( val default: Color, val neutral: Color, val success: Color, - val error: Color + val error: Color, + val toastError: Color ) @Immutable @@ -75,7 +77,8 @@ val POLightColorPalette = POColors( placeholder = Color(0xFF707378), disabled = Color(0xFFADB5BD), success = Color(0xFF00291D), - error = Color(0xFFD11D2F) + error = Color(0xFFD11D2F), + onTipError = Color(0xFF630407) ), input = Input( backgroundDefault = Color(0xFFFFFFFF), @@ -102,7 +105,8 @@ val POLightColorPalette = POColors( default = Color(0xFFFFFFFF), neutral = Color(0xFFFAFAFA), success = Color(0xFFBEFAE9), - error = Color(0xFFFFC2C8) + error = Color(0xFFFFC2C8), + toastError = Color(0xFFFDE3DE) ), border = Border( subtle = Color(0xFFCCD1D6) @@ -118,7 +122,8 @@ val PODarkColorPalette = POColors( placeholder = Color(0xFFA7A9AF), disabled = Color(0xFF5B6576), success = Color(0xFFE5FFF8), - error = Color(0xFFFF5263) + error = Color(0xFFFF5263), + onTipError = Color(0xFFF5D9D9) ), input = Input( backgroundDefault = Color(0xFF121821), @@ -145,7 +150,8 @@ val PODarkColorPalette = POColors( default = Color(0xFF121821), neutral = Color(0xFF242C38), success = Color(0xFF1DA37D), - error = Color(0xFFD11D2F) + error = Color(0xFFD11D2F), + toastError = Color(0xFF511511) ), border = Border( subtle = Color(0xFF7C8593) From 6d35fd04b10f6a3dcd45f44791f386bcebafd75f Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 23 Jun 2025 20:31:12 +0300 Subject: [PATCH 25/80] Label error color --- .../com/processout/sdk/ui/core/component/field/POField.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt index 91c1b05c9..72bafd703 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt @@ -117,7 +117,7 @@ object POField { color = colors.text.primary, textStyle = typography.s15(FontWeight.Medium) ), - labelTextColor = colors.text.placeholder, + labelTextColor = colors.text.error, placeholderTextColor = colors.text.placeholder, backgroundColor = colors.input.backgroundDefault, controlsTintColor = colors.text.primary, From cec4caeafd759e796e617226069ed7bbf3fb6b9a Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 23 Jun 2025 20:56:10 +0300 Subject: [PATCH 26/80] Update error colors --- .../kotlin/com/processout/sdk/ui/core/theme/Colors.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index cebee51b2..9715ef0b8 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -77,7 +77,7 @@ val POLightColorPalette = POColors( placeholder = Color(0xFF707378), disabled = Color(0xFFADB5BD), success = Color(0xFF00291D), - error = Color(0xFFD11D2F), + error = Color(0xFFBE011B), onTipError = Color(0xFF630407) ), input = Input( @@ -87,7 +87,7 @@ val POLightColorPalette = POColors( borderDefault2 = Color(0x1F121314), borderDisabled = Color(0xFFADB5BD), borderFocused = Color(0xFF4791FF), - borderError = Color(0xFFD11D2F) + borderError = Color(0xFFBE011B) ), button = Button( primaryBackgroundDefault = Color(0xFF121821), @@ -122,7 +122,7 @@ val PODarkColorPalette = POColors( placeholder = Color(0xFFA7A9AF), disabled = Color(0xFF5B6576), success = Color(0xFFE5FFF8), - error = Color(0xFFFF5263), + error = Color(0xFFFF7D6C), onTipError = Color(0xFFF5D9D9) ), input = Input( @@ -132,7 +132,7 @@ val PODarkColorPalette = POColors( borderDefault2 = Color(0x29F6F8FB), borderDisabled = Color(0xFF7C8593), borderFocused = Color(0xFFFFE500), - borderError = Color(0xFFFF5263) + borderError = Color(0xFFFF7D6C) ), button = Button( primaryBackgroundDefault = Color(0xFFFFFFFF), From 921cafc78147ea46939d7830f9d9643f90f89aad Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 24 Jun 2025 16:15:39 +0300 Subject: [PATCH 27/80] Fix dropdown floating label --- .../processout/sdk/ui/core/component/field/text/POTextField.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt index 0c0d8c625..663b975eb 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt @@ -98,7 +98,7 @@ fun POTextField( animationSpec = spring(stiffness = animationStiffness) ) ) { - val isLabelFloating = isFocused || value.text.isNotEmpty() + val isLabelFloating = value.text.isNotEmpty() || (isFocused && !isDropdown) if (!label.isNullOrBlank()) { val fontSizeValue = stateStyle.text.textStyle.fontSize.value val animatedFontSizeValue by animateFloatAsState( From 3b24bfe31751fcf4b02052a91e490d4dd36280c6 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 24 Jun 2025 16:49:40 +0300 Subject: [PATCH 28/80] PODropdownField2 --- .../field/dropdown/PODropdownField2.kt | 162 ++++++++++++++++++ .../napm/v2/NativeAlternativePaymentScreen.kt | 16 +- 2 files changed, 170 insertions(+), 8 deletions(-) create mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt new file mode 100644 index 000000000..74891eb50 --- /dev/null +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt @@ -0,0 +1,162 @@ +@file:OptIn(ExperimentalMaterial3Api::class) + +package com.processout.sdk.ui.core.component.field.dropdown + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.window.PopupProperties +import com.processout.sdk.ui.core.R +import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi +import com.processout.sdk.ui.core.component.POMessageBox +import com.processout.sdk.ui.core.component.POText +import com.processout.sdk.ui.core.component.field.POField +import com.processout.sdk.ui.core.component.field.POField.stateStyle +import com.processout.sdk.ui.core.component.field.text.POTextField2 +import com.processout.sdk.ui.core.state.POAvailableValue +import com.processout.sdk.ui.core.state.POImmutableList +import com.processout.sdk.ui.core.theme.ProcessOutTheme + +/** @suppress */ +@ProcessOutInternalApi +@Composable +fun PODropdownField2( + value: TextFieldValue, + onValueChange: (TextFieldValue) -> Unit, + availableValues: POImmutableList, + modifier: Modifier = Modifier, + fieldStyle: POField.Style = POField.default2, + menuStyle: PODropdownField.MenuStyle = PODropdownField.defaultMenu, + descriptionStyle: POMessageBox.Style = POMessageBox.error2, + menuMatchesTextFieldWidth: Boolean = true, + preferFormattedTextSelection: Boolean = false, + enabled: Boolean = true, + isError: Boolean = false, + label: String? = null, + placeholder: String? = null, + description: String? = null +) { + MaterialTheme( + colorScheme = MaterialTheme.colorScheme.copy(surface = Color.Transparent), + shapes = MaterialTheme.shapes.copy(extraSmall = menuStyle.shape) + ) { + var expanded by remember { mutableStateOf(false) } + ExposedDropdownMenuBox( + expanded = expanded, + onExpandedChange = { + if (enabled) { + expanded = it + } + } + ) { + var isFocused by remember { mutableStateOf(false) } + val fieldStateStyle = fieldStyle.stateStyle(isError = isError, isFocused = isFocused) + POTextField2( + value = availableValues.elements.find { it.value == value.text } + ?.let { + TextFieldValue( + text = if (preferFormattedTextSelection && it.formattedText != null) + it.formattedText else it.text + ) + } ?: TextFieldValue(), + onValueChange = {}, + modifier = modifier + .menuAnchor(MenuAnchorType.PrimaryNotEditable) + .onFocusChanged { + isFocused = it.isFocused + }, + fieldStyle = fieldStyle, + descriptionStyle = descriptionStyle, + enabled = enabled, + readOnly = true, + isDropdown = true, + isError = isError, + label = label, + placeholder = placeholder, + description = description, + trailingIcon = { + Icon( + painter = painterResource(id = R.drawable.po_dropdown_arrow), + contentDescription = null, + modifier = Modifier.rotate(if (expanded) 180f else 0f), + tint = fieldStateStyle.text.color + ) + } + ) + val menuItemHeight = ProcessOutTheme.dimensions.formComponentMinHeight + val menuVerticalPaddings = ProcessOutTheme.spacing.large + val maxMenuHeight = remember { menuItemHeight * PODropdownField.MaxVisibleMenuItems + menuVerticalPaddings } + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + modifier = Modifier + .exposedDropdownSize(matchTextFieldWidth = menuMatchesTextFieldWidth) + .heightIn(max = maxMenuHeight) + .border( + width = menuStyle.border.width, + color = menuStyle.border.color, + shape = menuStyle.shape + ) + .background(color = menuStyle.backgroundColor), + properties = PopupProperties( + focusable = true, + dismissOnBackPress = true, + dismissOnClickOutside = true + ) + ) { + availableValues.elements.forEach { availableValue -> + MenuItem( + availableValue = availableValue, + onClick = { + expanded = false + onValueChange(TextFieldValue(it.value)) + }, + modifier = Modifier.requiredHeight(menuItemHeight), + style = menuStyle + ) + } + } + } + } +} + +@Composable +private fun MenuItem( + availableValue: POAvailableValue, + onClick: (POAvailableValue) -> Unit, + modifier: Modifier = Modifier, + style: PODropdownField.MenuStyle, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } +) { + Box( + modifier = modifier + .clickable( + onClick = { onClick(availableValue) }, + interactionSource = interactionSource, + indication = ripple(color = style.rippleColor) + ) + .fillMaxWidth() + .padding(horizontal = ProcessOutTheme.spacing.large), + contentAlignment = Alignment.CenterStart + ) { + POText( + text = availableValue.text, + color = style.text.color, + style = style.text.textStyle, + overflow = TextOverflow.Ellipsis, + maxLines = 1 + ) + } +} diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt index 8de1d6e24..f7ab1be8d 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt @@ -42,7 +42,7 @@ import com.processout.sdk.ui.core.component.field.checkbox.POLabeledCheckboxFiel import com.processout.sdk.ui.core.component.field.code.POCodeField import com.processout.sdk.ui.core.component.field.code.POLabeledCodeField import com.processout.sdk.ui.core.component.field.dropdown.PODropdownField -import com.processout.sdk.ui.core.component.field.dropdown.POLabeledDropdownField +import com.processout.sdk.ui.core.component.field.dropdown.PODropdownField2 import com.processout.sdk.ui.core.component.field.phone.POLabeledPhoneNumberField import com.processout.sdk.ui.core.component.field.radio.POLabeledRadioField import com.processout.sdk.ui.core.component.field.radio.PORadioGroup @@ -202,8 +202,8 @@ private fun UserInput( state = field.state, onEvent = onEvent, fieldStyle = style.field, - labelsStyle = labelsStyle, menuStyle = style.dropdownMenu, + descriptionStyle = style.errorMessageBox, modifier = Modifier.fillMaxWidth() ) is CheckboxField -> CheckboxField( @@ -369,11 +369,11 @@ private fun DropdownField( state: FieldState, onEvent: (NativeAlternativePaymentEvent) -> Unit, fieldStyle: POField.Style, - labelsStyle: POFieldLabels.Style, menuStyle: PODropdownField.MenuStyle, + descriptionStyle: POMessageBox.Style, modifier: Modifier = Modifier ) { - POLabeledDropdownField( + PODropdownField2( value = state.value, onValueChange = { onEvent( @@ -384,8 +384,6 @@ private fun DropdownField( ) }, availableValues = state.availableValues ?: POImmutableList(emptyList()), - title = state.label ?: String(), - description = state.description, modifier = modifier .onFocusChanged { onEvent( @@ -396,10 +394,12 @@ private fun DropdownField( ) }, fieldStyle = fieldStyle, - labelsStyle = labelsStyle, menuStyle = menuStyle, + descriptionStyle = descriptionStyle, isError = state.isError, - placeholder = state.placeholder + label = state.label, + placeholder = state.placeholder, + description = state.description ) } From 09647a4b9c7d52b78e5c6f0c539d29a47339ba32 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 24 Jun 2025 16:57:28 +0300 Subject: [PATCH 29/80] Dropdown arrow color --- .../sdk/ui/core/component/field/dropdown/PODropdownField2.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt index 74891eb50..6cc65607d 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt @@ -91,7 +91,7 @@ fun PODropdownField2( painter = painterResource(id = R.drawable.po_dropdown_arrow), contentDescription = null, modifier = Modifier.rotate(if (expanded) 180f else 0f), - tint = fieldStateStyle.text.color + tint = fieldStateStyle.labelTextColor ) } ) From ad08f9cdf47c58292f81c980275659748a4c7a39 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 24 Jun 2025 17:12:11 +0300 Subject: [PATCH 30/80] po_chevron_down --- .../component/field/dropdown/PODropdownField2.kt | 7 +++++-- ui-core/src/main/res/drawable/po_chevron_down.xml | 13 +++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 ui-core/src/main/res/drawable/po_chevron_down.xml diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt index 6cc65607d..5a666e6d0 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt @@ -12,6 +12,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate +import androidx.compose.ui.draw.scale import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource @@ -88,9 +89,11 @@ fun PODropdownField2( description = description, trailingIcon = { Icon( - painter = painterResource(id = R.drawable.po_dropdown_arrow), + painter = painterResource(id = R.drawable.po_chevron_down), contentDescription = null, - modifier = Modifier.rotate(if (expanded) 180f else 0f), + modifier = Modifier + .scale(1.1f) + .rotate(if (expanded) 180f else 0f), tint = fieldStateStyle.labelTextColor ) } diff --git a/ui-core/src/main/res/drawable/po_chevron_down.xml b/ui-core/src/main/res/drawable/po_chevron_down.xml new file mode 100644 index 000000000..9dbbb88f3 --- /dev/null +++ b/ui-core/src/main/res/drawable/po_chevron_down.xml @@ -0,0 +1,13 @@ + + + From 6d610250d9a7940aa3ab88a96340169883e0fe0f Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 24 Jun 2025 17:29:35 +0300 Subject: [PATCH 31/80] Single line label and placeholder --- .../sdk/ui/core/component/field/text/POTextField.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt index 663b975eb..3b567046c 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -108,7 +109,9 @@ fun POTextField( POText( text = label, color = stateStyle.labelTextColor, - style = stateStyle.text.textStyle.copy(fontSize = animatedFontSizeValue.sp) + style = stateStyle.text.textStyle.copy(fontSize = animatedFontSizeValue.sp), + overflow = TextOverflow.Ellipsis, + maxLines = 1 ) if (isLabelFloating) { Spacer(modifier = Modifier.requiredHeight(spacing.space2)) @@ -120,7 +123,9 @@ fun POTextField( POText( text = placeholder, color = stateStyle.placeholderTextColor, - style = stateStyle.text.textStyle + style = stateStyle.text.textStyle, + overflow = TextOverflow.Ellipsis, + maxLines = 1 ) } innerTextField() From 64fe01d31d977220087a260319c1ffab2fff238e Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 24 Jun 2025 18:02:18 +0300 Subject: [PATCH 32/80] defaultMenu2 style --- .../field/dropdown/PODropdownField.kt | 41 +++++++++++++------ .../field/dropdown/PODropdownField2.kt | 11 ++--- .../napm/v2/NativeAlternativePaymentScreen.kt | 2 +- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt index cb6388561..e5ab36edf 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -33,7 +34,11 @@ import com.processout.sdk.ui.core.component.field.text.POTextField import com.processout.sdk.ui.core.state.POAvailableValue import com.processout.sdk.ui.core.state.POImmutableList import com.processout.sdk.ui.core.style.PODropdownMenuStyle -import com.processout.sdk.ui.core.theme.ProcessOutTheme +import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors +import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions +import com.processout.sdk.ui.core.theme.ProcessOutTheme.shapes +import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing +import com.processout.sdk.ui.core.theme.ProcessOutTheme.typography /** @suppress */ @ProcessOutInternalApi @@ -97,8 +102,8 @@ fun PODropdownField( ) } ) - val menuItemHeight = ProcessOutTheme.dimensions.formComponentMinHeight - val menuVerticalPaddings = ProcessOutTheme.spacing.large + val menuItemHeight = dimensions.formComponentMinHeight + val menuVerticalPaddings = spacing.large val maxMenuHeight = remember { menuItemHeight * PODropdownField.MaxVisibleMenuItems + menuVerticalPaddings } DropdownMenu( expanded = expanded, @@ -150,7 +155,7 @@ private fun MenuItem( indication = ripple(color = style.rippleColor) ) .fillMaxWidth() - .padding(horizontal = ProcessOutTheme.spacing.large), + .padding(horizontal = spacing.large), contentAlignment = Alignment.CenterStart ) { POText( @@ -177,15 +182,25 @@ object PODropdownField { ) val defaultMenu: MenuStyle - @Composable get() = with(ProcessOutTheme) { - MenuStyle( - text = POText.body2, - backgroundColor = colors.surface.neutral, - rippleColor = colors.text.muted, - shape = shapes.roundedCornersSmall, - border = POBorderStroke(width = 0.dp, color = Color.Transparent) - ) - } + @Composable get() = MenuStyle( + text = POText.body2, + backgroundColor = colors.surface.neutral, + rippleColor = colors.text.muted, + shape = shapes.roundedCornersSmall, + border = POBorderStroke(width = 0.dp, color = Color.Transparent) + ) + + val defaultMenu2: MenuStyle + @Composable get() = MenuStyle( + text = POText.Style( + color = colors.text.primary, + textStyle = typography.s15(FontWeight.Medium) + ), + backgroundColor = colors.surface.neutral, + rippleColor = colors.text.muted, + shape = shapes.roundedCorners6, + border = POBorderStroke(width = 0.dp, color = Color.Transparent) + ) @Composable fun custom(style: PODropdownMenuStyle) = with(style) { diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt index 5a666e6d0..a6677631f 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt @@ -28,7 +28,8 @@ import com.processout.sdk.ui.core.component.field.POField.stateStyle import com.processout.sdk.ui.core.component.field.text.POTextField2 import com.processout.sdk.ui.core.state.POAvailableValue import com.processout.sdk.ui.core.state.POImmutableList -import com.processout.sdk.ui.core.theme.ProcessOutTheme +import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions +import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing /** @suppress */ @ProcessOutInternalApi @@ -39,7 +40,7 @@ fun PODropdownField2( availableValues: POImmutableList, modifier: Modifier = Modifier, fieldStyle: POField.Style = POField.default2, - menuStyle: PODropdownField.MenuStyle = PODropdownField.defaultMenu, + menuStyle: PODropdownField.MenuStyle = PODropdownField.defaultMenu2, descriptionStyle: POMessageBox.Style = POMessageBox.error2, menuMatchesTextFieldWidth: Boolean = true, preferFormattedTextSelection: Boolean = false, @@ -98,8 +99,8 @@ fun PODropdownField2( ) } ) - val menuItemHeight = ProcessOutTheme.dimensions.formComponentMinHeight - val menuVerticalPaddings = ProcessOutTheme.spacing.large + val menuItemHeight = dimensions.formComponentMinHeight + val menuVerticalPaddings = spacing.large val maxMenuHeight = remember { menuItemHeight * PODropdownField.MaxVisibleMenuItems + menuVerticalPaddings } DropdownMenu( expanded = expanded, @@ -151,7 +152,7 @@ private fun MenuItem( indication = ripple(color = style.rippleColor) ) .fillMaxWidth() - .padding(horizontal = ProcessOutTheme.spacing.large), + .padding(horizontal = spacing.large), contentAlignment = Alignment.CenterStart ) { POText( diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt index f7ab1be8d..79da8c5a8 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt @@ -759,7 +759,7 @@ internal object NativeAlternativePaymentScreen { } ?: POCheckbox.default, dropdownMenu = custom?.dropdownMenu?.let { PODropdownField.custom(style = it) - } ?: PODropdownField.defaultMenu, + } ?: PODropdownField.defaultMenu2, actionsContainer = custom?.actionsContainer?.let { POActionsContainer.custom(style = it) } ?: POActionsContainer.default, From 25638676cb47959da17498f5e12373ad8cb367ea Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 24 Jun 2025 18:13:55 +0300 Subject: [PATCH 33/80] Secondary text color for dropdown menu items text --- .../sdk/ui/core/component/field/dropdown/PODropdownField.kt | 2 +- .../src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt index e5ab36edf..33536cd40 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt @@ -193,7 +193,7 @@ object PODropdownField { val defaultMenu2: MenuStyle @Composable get() = MenuStyle( text = POText.Style( - color = colors.text.primary, + color = colors.text.secondary, textStyle = typography.s15(FontWeight.Medium) ), backgroundColor = colors.surface.neutral, diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index 9715ef0b8..9b19c5847 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -19,6 +19,7 @@ data class POColors( @Immutable data class Text( val primary: Color, + val secondary: Color, val inverse: Color, val muted: Color, val placeholder: Color, @@ -72,6 +73,7 @@ data class POColors( val POLightColorPalette = POColors( text = Text( primary = Color(0xFF121821), + secondary = Color(0xFF585A5F), inverse = Color(0xFFFAFAFA), muted = Color(0xFF5B6576), placeholder = Color(0xFF707378), @@ -117,6 +119,7 @@ val POLightColorPalette = POColors( val PODarkColorPalette = POColors( text = Text( primary = Color(0xFFFAFAFA), + secondary = Color(0xFFC0C3C8), inverse = Color(0xFF121821), muted = Color(0xFFADB5BD), placeholder = Color(0xFFA7A9AF), From 2ee8a8ed0f5e7e3d03511b8302e9a2ab320a1c42 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 24 Jun 2025 18:28:15 +0300 Subject: [PATCH 34/80] MaxVisibleMenuItems = 9 --- .../sdk/ui/core/component/field/dropdown/PODropdownField.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt index 33536cd40..758db3a64 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt @@ -216,5 +216,5 @@ object PODropdownField { ) } - internal val MaxVisibleMenuItems = 6 + internal val MaxVisibleMenuItems = 9 } From d43eec4543db11763284ac2728d52458548c7bef Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 24 Jun 2025 22:12:33 +0300 Subject: [PATCH 35/80] Expand dropdown menu after keyboard is closed to fix resizing issues --- .../processout/sdk/ui/core/component/POIme.kt | 6 ++++-- .../field/dropdown/PODropdownField.kt | 20 +++++++++++++++++-- .../field/dropdown/PODropdownField2.kt | 18 ++++++++++++++++- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POIme.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POIme.kt index b3799bfc0..c629ebb54 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POIme.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POIme.kt @@ -12,11 +12,13 @@ import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi object POIme { @Composable - fun isImeVisibleAsState(): State { + fun isImeVisibleAsState( + policy: SnapshotMutationPolicy = neverEqualPolicy() + ): State { val isImeVisible = remember { mutableStateOf( value = false, - policy = neverEqualPolicy() + policy = policy ) } val view = LocalView.current.rootView diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt index 758db3a64..cf115233e 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight @@ -27,6 +28,7 @@ import androidx.compose.ui.window.PopupProperties import com.processout.sdk.ui.core.R import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi import com.processout.sdk.ui.core.component.POBorderStroke +import com.processout.sdk.ui.core.component.POIme.isImeVisibleAsState import com.processout.sdk.ui.core.component.POText import com.processout.sdk.ui.core.component.field.POField import com.processout.sdk.ui.core.component.field.POField.stateStyle @@ -62,11 +64,25 @@ fun PODropdownField( shapes = MaterialTheme.shapes.copy(extraSmall = menuStyle.shape) ) { var expanded by remember { mutableStateOf(false) } + var expanding by remember { mutableStateOf(false) } + val isImeVisible by isImeVisibleAsState(policy = structuralEqualityPolicy()) + if (expanding && isImeVisible) { + LocalFocusManager.current.clearFocus(force = true) + } + LaunchedEffect(expanding, isImeVisible) { + if (expanding && !isImeVisible) { + expanding = false + expanded = true + } + } ExposedDropdownMenuBox( expanded = expanded, onExpandedChange = { if (enabled) { - expanded = it + when (it) { + true -> expanding = true + false -> expanded = false + } } } ) { @@ -216,5 +232,5 @@ object PODropdownField { ) } - internal val MaxVisibleMenuItems = 9 + internal val MaxVisibleMenuItems = 10 } diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt index a6677631f..c9a1ece1e 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt @@ -15,12 +15,14 @@ import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.scale import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.window.PopupProperties import com.processout.sdk.ui.core.R import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi +import com.processout.sdk.ui.core.component.POIme.isImeVisibleAsState import com.processout.sdk.ui.core.component.POMessageBox import com.processout.sdk.ui.core.component.POText import com.processout.sdk.ui.core.component.field.POField @@ -55,11 +57,25 @@ fun PODropdownField2( shapes = MaterialTheme.shapes.copy(extraSmall = menuStyle.shape) ) { var expanded by remember { mutableStateOf(false) } + var expanding by remember { mutableStateOf(false) } + val isImeVisible by isImeVisibleAsState(policy = structuralEqualityPolicy()) + if (expanding && isImeVisible) { + LocalFocusManager.current.clearFocus(force = true) + } + LaunchedEffect(expanding, isImeVisible) { + if (expanding && !isImeVisible) { + expanding = false + expanded = true + } + } ExposedDropdownMenuBox( expanded = expanded, onExpandedChange = { if (enabled) { - expanded = it + when (it) { + true -> expanding = true + false -> expanded = false + } } } ) { From 474909a819f1730887a6065e7e9395a29149c11c Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 25 Jun 2025 14:14:22 +0300 Subject: [PATCH 36/80] Updated POPhoneNumberField --- .../field/dropdown/PODropdownField.kt | 4 +- .../field/dropdown/PODropdownField2.kt | 7 +- .../field/phone/POLabeledPhoneNumberField.kt | 42 ------ .../field/phone/POPhoneNumberField.kt | 137 ++++++++++-------- .../core/component/field/text/POTextField2.kt | 6 +- .../ui/core/state/POPhoneNumberFieldState.kt | 2 +- .../napm/v2/NativeAlternativePaymentScreen.kt | 16 +- .../v2/NativeAlternativePaymentViewModel.kt | 2 +- 8 files changed, 97 insertions(+), 119 deletions(-) delete mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POLabeledPhoneNumberField.kt diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt index cf115233e..4475c289e 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField.kt @@ -50,7 +50,7 @@ fun PODropdownField( onValueChange: (TextFieldValue) -> Unit, availableValues: POImmutableList, modifier: Modifier = Modifier, - fieldContentPadding: PaddingValues = POField.contentPadding, + contentPadding: PaddingValues = POField.contentPadding, fieldStyle: POField.Style = POField.default, menuStyle: PODropdownField.MenuStyle = PODropdownField.defaultMenu, menuMatchesTextFieldWidth: Boolean = true, @@ -102,7 +102,7 @@ fun PODropdownField( .onFocusChanged { isFocused = it.isFocused }, - contentPadding = fieldContentPadding, + contentPadding = contentPadding, style = fieldStyle, enabled = enabled, readOnly = true, diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt index c9a1ece1e..e93859822 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt @@ -41,6 +41,8 @@ fun PODropdownField2( onValueChange: (TextFieldValue) -> Unit, availableValues: POImmutableList, modifier: Modifier = Modifier, + textFieldModifier: Modifier = Modifier, + contentPadding: PaddingValues = POField.contentPadding2, fieldStyle: POField.Style = POField.default2, menuStyle: PODropdownField.MenuStyle = PODropdownField.defaultMenu2, descriptionStyle: POMessageBox.Style = POMessageBox.error2, @@ -69,6 +71,7 @@ fun PODropdownField2( } } ExposedDropdownMenuBox( + modifier = modifier, expanded = expanded, onExpandedChange = { if (enabled) { @@ -90,11 +93,13 @@ fun PODropdownField2( ) } ?: TextFieldValue(), onValueChange = {}, - modifier = modifier + modifier = Modifier.fillMaxWidth(), + textFieldModifier = textFieldModifier .menuAnchor(MenuAnchorType.PrimaryNotEditable) .onFocusChanged { isFocused = it.isFocused }, + contentPadding = contentPadding, fieldStyle = fieldStyle, descriptionStyle = descriptionStyle, enabled = enabled, diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POLabeledPhoneNumberField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POLabeledPhoneNumberField.kt deleted file mode 100644 index 8c35c954f..000000000 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POLabeledPhoneNumberField.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.processout.sdk.ui.core.component.field.phone - -import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.input.TextFieldValue -import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi -import com.processout.sdk.ui.core.component.field.LabeledFieldLayout -import com.processout.sdk.ui.core.component.field.POField -import com.processout.sdk.ui.core.component.field.POFieldLabels -import com.processout.sdk.ui.core.component.field.dropdown.PODropdownField -import com.processout.sdk.ui.core.state.POPhoneNumberFieldState - -/** @suppress */ -@ProcessOutInternalApi -@Composable -fun POLabeledPhoneNumberField( - state: POPhoneNumberFieldState, - onValueChange: (TextFieldValue, TextFieldValue) -> Unit, - modifier: Modifier = Modifier, - textFieldModifier: Modifier = Modifier, - fieldStyle: POField.Style = POField.default, - dropdownMenuStyle: PODropdownField.MenuStyle = PODropdownField.defaultMenu, - labelsStyle: POFieldLabels.Style = POFieldLabels.default, - keyboardActions: KeyboardActions = KeyboardActions.Default -) { - LabeledFieldLayout( - title = state.title ?: String(), - description = state.description, - style = labelsStyle - ) { - POPhoneNumberField( - state = state, - onValueChange = onValueChange, - modifier = modifier, - textFieldModifier = textFieldModifier, - fieldStyle = fieldStyle, - dropdownMenuStyle = dropdownMenuStyle, - keyboardActions = keyboardActions - ) - } -} diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt index 092600cf0..bf275b1ad 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt @@ -8,13 +8,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation -import androidx.compose.ui.unit.dp import com.google.i18n.phonenumbers.NumberParseException import com.google.i18n.phonenumbers.PhoneNumberUtil import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi +import com.processout.sdk.ui.core.component.POMessageBox import com.processout.sdk.ui.core.component.field.POField import com.processout.sdk.ui.core.component.field.dropdown.PODropdownField -import com.processout.sdk.ui.core.component.field.text.POTextField +import com.processout.sdk.ui.core.component.field.dropdown.PODropdownField2 +import com.processout.sdk.ui.core.component.field.text.POTextField2 import com.processout.sdk.ui.core.state.POPhoneNumberFieldState import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing @@ -26,71 +27,81 @@ fun POPhoneNumberField( onValueChange: (TextFieldValue, TextFieldValue) -> Unit, modifier: Modifier = Modifier, textFieldModifier: Modifier = Modifier, - fieldStyle: POField.Style = POField.default, - dropdownMenuStyle: PODropdownField.MenuStyle = PODropdownField.defaultMenu, + fieldStyle: POField.Style = POField.default2, + dropdownMenuStyle: PODropdownField.MenuStyle = PODropdownField.defaultMenu2, + descriptionStyle: POMessageBox.Style = POMessageBox.error2, keyboardActions: KeyboardActions = KeyboardActions.Default ) { - Row(modifier = modifier) { - PODropdownField( - value = state.regionCode, - onValueChange = { regionCode -> - onValueChange(regionCode, state.number) - }, - availableValues = state.regionCodes, - modifier = Modifier.width(IntrinsicSize.Min), - fieldContentPadding = PaddingValues( - start = spacing.large, - end = 0.dp, - top = spacing.medium, - bottom = spacing.medium - ), - fieldStyle = fieldStyle, - menuStyle = dropdownMenuStyle, - menuMatchesTextFieldWidth = false, - preferFormattedTextSelection = true, - isError = state.isError, - placeholder = state.regionCodePlaceholder - ) - val phoneNumberUtil = remember { PhoneNumberUtil.getInstance() } - POTextField( - value = state.number, - onValueChange = { number -> - if (number.text.startsWith('+')) { - try { - val filteredNumber = number.text.filterIndexed { index, char -> - (index == 0 && char == '+') || char.isDigit() - } - val parsedNumber = phoneNumberUtil.parse(filteredNumber, null) - val parsedRegionCode = phoneNumberUtil.getRegionCodeForCountryCode(parsedNumber.countryCode) - var regionCode = state.regionCode - if (state.regionCodes.elements.any { it.value == parsedRegionCode }) { - regionCode = TextFieldValue(text = parsedRegionCode) + Column(modifier = modifier) { + Row(modifier = Modifier.fillMaxWidth()) { + PODropdownField2( + value = state.regionCode, + onValueChange = { regionCode -> + onValueChange(regionCode, state.number) + }, + availableValues = state.regionCodes, + modifier = Modifier.width(IntrinsicSize.Min), + contentPadding = PaddingValues( + start = spacing.space12, + end = spacing.space0, + top = spacing.space6, + bottom = spacing.space6 + ), + fieldStyle = fieldStyle, + menuStyle = dropdownMenuStyle, + menuMatchesTextFieldWidth = false, + preferFormattedTextSelection = true, + isError = state.isError, + label = state.regionCodePlaceholder + ) + val phoneNumberUtil = remember { PhoneNumberUtil.getInstance() } + POTextField2( + value = state.number, + onValueChange = { number -> + if (number.text.startsWith('+')) { + try { + val filteredNumber = number.text.filterIndexed { index, char -> + (index == 0 && char == '+') || char.isDigit() + } + val parsedNumber = phoneNumberUtil.parse(filteredNumber, null) + val parsedRegionCode = phoneNumberUtil.getRegionCodeForCountryCode(parsedNumber.countryCode) + var regionCode = state.regionCode + if (state.regionCodes.elements.any { it.value == parsedRegionCode }) { + regionCode = TextFieldValue(text = parsedRegionCode) + } + val parsedNationalNumber = parsedNumber.nationalNumber.toString() + val nationalNumber = TextFieldValue( + text = parsedNationalNumber, + selection = TextRange(parsedNationalNumber.length) + ) + onValueChange(regionCode, nationalNumber) + } catch (e: NumberParseException) { + // ignore } - val parsedNationalNumber = parsedNumber.nationalNumber.toString() - val nationalNumber = TextFieldValue( - text = parsedNationalNumber, - selection = TextRange(parsedNationalNumber.length) - ) - onValueChange(regionCode, nationalNumber) - } catch (e: NumberParseException) { - // ignore + } else { + val filteredNumber = state.inputFilter?.filter(number) ?: number + onValueChange(state.regionCode, filteredNumber) } - } else { - val filteredNumber = state.inputFilter?.filter(number) ?: number - onValueChange(state.regionCode, filteredNumber) - } - }, - modifier = textFieldModifier - .padding(start = spacing.extraSmall) - .weight(1f), - style = fieldStyle, - enabled = state.enabled, - isError = state.isError, - forceTextDirectionLtr = state.forceTextDirectionLtr, - placeholder = state.numberPlaceholder, - visualTransformation = state.visualTransformation ?: VisualTransformation.None, - keyboardOptions = state.keyboardOptions, - keyboardActions = keyboardActions + }, + modifier = Modifier + .padding(start = spacing.space4) + .weight(1f), + textFieldModifier = textFieldModifier, + fieldStyle = fieldStyle, + enabled = state.enabled, + isError = state.isError, + forceTextDirectionLtr = state.forceTextDirectionLtr, + label = state.label, + placeholder = state.numberPlaceholder, + visualTransformation = state.visualTransformation ?: VisualTransformation.None, + keyboardOptions = state.keyboardOptions, + keyboardActions = keyboardActions + ) + } + POMessageBox( + text = state.description, + modifier = Modifier.padding(top = spacing.space8), + style = descriptionStyle ) } } diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt index ec93f8f1d..920735337 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt @@ -3,6 +3,7 @@ package com.processout.sdk.ui.core.component.field.text import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions @@ -25,6 +26,7 @@ fun POTextField2( value: TextFieldValue, onValueChange: (TextFieldValue) -> Unit, modifier: Modifier = Modifier, + textFieldModifier: Modifier = Modifier, minHeight: Dp = dimensions.fieldMinHeight, contentPadding: PaddingValues = POField.contentPadding2, fieldStyle: POField.Style = POField.default2, @@ -47,11 +49,11 @@ fun POTextField2( minLines: Int = 1, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } ) { - Column { + Column(modifier = modifier) { POTextField( value = value, onValueChange = onValueChange, - modifier = modifier, + modifier = textFieldModifier.fillMaxWidth(), minHeight = minHeight, contentPadding = contentPadding, style = fieldStyle, diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/state/POPhoneNumberFieldState.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/state/POPhoneNumberFieldState.kt index 623a2db96..743a40403 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/state/POPhoneNumberFieldState.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/state/POPhoneNumberFieldState.kt @@ -16,7 +16,7 @@ data class POPhoneNumberFieldState( val regionCodePlaceholder: String?, val number: TextFieldValue, val numberPlaceholder: String?, - val title: String? = null, + val label: String? = null, val description: String? = null, val enabled: Boolean = true, val isError: Boolean = false, diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt index 79da8c5a8..6398a7826 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt @@ -43,7 +43,7 @@ import com.processout.sdk.ui.core.component.field.code.POCodeField import com.processout.sdk.ui.core.component.field.code.POLabeledCodeField import com.processout.sdk.ui.core.component.field.dropdown.PODropdownField import com.processout.sdk.ui.core.component.field.dropdown.PODropdownField2 -import com.processout.sdk.ui.core.component.field.phone.POLabeledPhoneNumberField +import com.processout.sdk.ui.core.component.field.phone.POPhoneNumberField import com.processout.sdk.ui.core.component.field.radio.POLabeledRadioField import com.processout.sdk.ui.core.component.field.radio.PORadioGroup import com.processout.sdk.ui.core.component.field.text.POTextField2 @@ -221,7 +221,7 @@ private fun UserInput( isPrimaryActionEnabled = isPrimaryActionEnabled, fieldStyle = style.field, dropdownMenuStyle = style.dropdownMenu, - labelsStyle = labelsStyle, + descriptionStyle = style.errorMessageBox, modifier = Modifier.fillMaxWidth() ) } @@ -252,7 +252,8 @@ private fun TextField( ) ) }, - modifier = modifier + modifier = modifier, + textFieldModifier = Modifier .focusRequester(focusRequester) .onFocusChanged { onEvent( @@ -384,7 +385,8 @@ private fun DropdownField( ) }, availableValues = state.availableValues ?: POImmutableList(emptyList()), - modifier = modifier + modifier = modifier, + textFieldModifier = Modifier .onFocusChanged { onEvent( FieldFocusChanged( @@ -442,11 +444,11 @@ private fun PhoneNumberField( isPrimaryActionEnabled: Boolean, fieldStyle: POField.Style, dropdownMenuStyle: PODropdownField.MenuStyle, - labelsStyle: POFieldLabels.Style, + descriptionStyle: POMessageBox.Style, modifier: Modifier = Modifier ) { val focusRequester = remember { FocusRequester() } - POLabeledPhoneNumberField( + POPhoneNumberField( state = state, onValueChange = { regionCode, number -> onEvent( @@ -472,7 +474,7 @@ private fun PhoneNumberField( }, fieldStyle = fieldStyle, dropdownMenuStyle = dropdownMenuStyle, - labelsStyle = labelsStyle, + descriptionStyle = descriptionStyle, keyboardActions = POField.keyboardActions( imeAction = state.keyboardOptions.imeAction, actionId = state.keyboardActionId, diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt index 31fa214ae..ad8da1edf 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt @@ -332,7 +332,7 @@ internal class NativeAlternativePaymentViewModel private constructor( else -> TextFieldValue() }, numberPlaceholder = app.getString(R.string.po_native_apm_phone_placeholder), - title = label, + label = label, description = description, isError = !isValid, forceTextDirectionLtr = true, From 0b42b5295f9546e06fccfe9759a43e04fb6211e6 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 25 Jun 2025 17:22:19 +0300 Subject: [PATCH 37/80] Focus on phone number field when region changed --- .../component/field/phone/POPhoneNumberField.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt index bf275b1ad..d0045d717 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt @@ -2,9 +2,10 @@ package com.processout.sdk.ui.core.component.field.phone import androidx.compose.foundation.layout.* import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember +import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation @@ -12,6 +13,7 @@ import com.google.i18n.phonenumbers.NumberParseException import com.google.i18n.phonenumbers.PhoneNumberUtil import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi import com.processout.sdk.ui.core.component.POMessageBox +import com.processout.sdk.ui.core.component.PORequestFocus import com.processout.sdk.ui.core.component.field.POField import com.processout.sdk.ui.core.component.field.dropdown.PODropdownField import com.processout.sdk.ui.core.component.field.dropdown.PODropdownField2 @@ -34,9 +36,11 @@ fun POPhoneNumberField( ) { Column(modifier = modifier) { Row(modifier = Modifier.fillMaxWidth()) { + var requestFocus by remember { mutableStateOf(false) } PODropdownField2( value = state.regionCode, onValueChange = { regionCode -> + requestFocus = true onValueChange(regionCode, state.number) }, availableValues = state.regionCodes, @@ -54,6 +58,7 @@ fun POPhoneNumberField( isError = state.isError, label = state.regionCodePlaceholder ) + val focusRequester = remember { FocusRequester() } val phoneNumberUtil = remember { PhoneNumberUtil.getInstance() } POTextField2( value = state.number, @@ -86,7 +91,7 @@ fun POPhoneNumberField( modifier = Modifier .padding(start = spacing.space4) .weight(1f), - textFieldModifier = textFieldModifier, + textFieldModifier = textFieldModifier.focusRequester(focusRequester), fieldStyle = fieldStyle, enabled = state.enabled, isError = state.isError, @@ -97,6 +102,10 @@ fun POPhoneNumberField( keyboardOptions = state.keyboardOptions, keyboardActions = keyboardActions ) + if (requestFocus) { + requestFocus = false + PORequestFocus(focusRequester) + } } POMessageBox( text = state.description, From 76fc00e476d4c2946172f57ad019cd1267179e99 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 25 Jun 2025 17:46:34 +0300 Subject: [PATCH 38/80] Phone number labels and placeholders --- .../nativeapm/NativeAlternativePaymentMethodViewModel.kt | 4 ++-- sdk/src/main/res/values-ar/strings.xml | 6 +++--- sdk/src/main/res/values-fr/strings.xml | 6 +++--- sdk/src/main/res/values-pl/strings.xml | 6 +++--- sdk/src/main/res/values-pt/strings.xml | 6 +++--- sdk/src/main/res/values/strings.xml | 6 +++--- .../ui/core/component/field/phone/POPhoneNumberField.kt | 5 ++--- .../sdk/ui/core/state/POPhoneNumberFieldState.kt | 5 ++--- .../sdk/ui/napm/NativeAlternativePaymentViewModel.kt | 4 ++-- .../sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt | 7 +++---- 10 files changed, 26 insertions(+), 29 deletions(-) diff --git a/sdk/src/main/kotlin/com/processout/sdk/ui/nativeapm/NativeAlternativePaymentMethodViewModel.kt b/sdk/src/main/kotlin/com/processout/sdk/ui/nativeapm/NativeAlternativePaymentMethodViewModel.kt index cb5ec8392..26fc5b2d5 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/ui/nativeapm/NativeAlternativePaymentMethodViewModel.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/ui/nativeapm/NativeAlternativePaymentMethodViewModel.kt @@ -697,8 +697,8 @@ internal class NativeAlternativePaymentMethodViewModel private constructor( private fun getInputHint(type: ParameterType) = when (type) { - EMAIL -> app.getString(R.string.po_native_apm_email_placeholder) - PHONE -> app.getString(R.string.po_native_apm_phone_placeholder) + EMAIL -> app.getString(R.string.po_native_apm_placeholder_email) + PHONE -> app.getString(R.string.po_native_apm_placeholder_phone) else -> null } diff --git a/sdk/src/main/res/values-ar/strings.xml b/sdk/src/main/res/values-ar/strings.xml index 7524ab245..17ba6a447 100644 --- a/sdk/src/main/res/values-ar/strings.xml +++ b/sdk/src/main/res/values-ar/strings.xml @@ -29,9 +29,9 @@ فشل حفظ الصورة لم نتمكن من حفظ الصورة. يرجى التحقق من أذوناتك أو محاولة التقاط لقطة شاشة كبديل. فهمت - بلد - أدخل رقم الهاتف - name@example.com + بلد + أدخل رقم الهاتف + name@example.com نجاح! تمت الموافقة على الدفع البيانات مطلوبة الرقم غير صحيح diff --git a/sdk/src/main/res/values-fr/strings.xml b/sdk/src/main/res/values-fr/strings.xml index 6b47a5e26..c969a6e67 100644 --- a/sdk/src/main/res/values-fr/strings.xml +++ b/sdk/src/main/res/values-fr/strings.xml @@ -29,9 +29,9 @@ Échec de l\'enregistrement de l\'image Nous n\'avons pas pu enregistrer l\'image. Veuillez vérifier vos permissions ou alternativement prendre une capture d\'écran. Compris - Pays - Entrez votre numéro de téléphone - nom@exemple.fr + Pays + Entrez votre numéro de téléphone + nom@exemple.fr Succès !\nPaiement confirmé. Paramètre requis. Numéro invalide. diff --git a/sdk/src/main/res/values-pl/strings.xml b/sdk/src/main/res/values-pl/strings.xml index fee0194b2..43013fafd 100644 --- a/sdk/src/main/res/values-pl/strings.xml +++ b/sdk/src/main/res/values-pl/strings.xml @@ -29,9 +29,9 @@ Nie udało się zapisać obrazu Sprawdź uprawnienia systemu lub zrób zrzut ekranu. Rozumiem - Kraj - Twój numer telefonu - imię@przykład.pl + Kraj + Twój numer telefonu + imię@przykład.pl Sukces!\nPłatność przyjęta. Parametr jest wmagany. Niepoprawny numer. diff --git a/sdk/src/main/res/values-pt/strings.xml b/sdk/src/main/res/values-pt/strings.xml index a69e1e9ba..9fce5156a 100644 --- a/sdk/src/main/res/values-pt/strings.xml +++ b/sdk/src/main/res/values-pt/strings.xml @@ -29,9 +29,9 @@ Falha na gravação da imagem Não conseguimos gravar a imagem. Por favor, verifique as suas permissões ou tire uma captura do ecrā como alternativa. Compreendi - País - Insira o seu número de telemóvel - nome@exemplo.pt + País + Insira o seu número de telemóvel + nome@exemplo.pt Successo!\nPagamento aprovado. Campo obrigatório. Número inválido. diff --git a/sdk/src/main/res/values/strings.xml b/sdk/src/main/res/values/strings.xml index 77be47966..a504d87a2 100644 --- a/sdk/src/main/res/values/strings.xml +++ b/sdk/src/main/res/values/strings.xml @@ -29,9 +29,9 @@ Image save failed We couldn\'t save the image. Please check your permissions or try taking a screenshot as an alternative. Got it - Country - Enter phone number - name@example.com + Country + Enter phone number + name@example.com Success!\nPayment approved. Parameter is required. Number is not valid. diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt index d0045d717..76db52d37 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt @@ -56,7 +56,7 @@ fun POPhoneNumberField( menuMatchesTextFieldWidth = false, preferFormattedTextSelection = true, isError = state.isError, - label = state.regionCodePlaceholder + label = state.regionCodeLabel ) val focusRequester = remember { FocusRequester() } val phoneNumberUtil = remember { PhoneNumberUtil.getInstance() } @@ -96,8 +96,7 @@ fun POPhoneNumberField( enabled = state.enabled, isError = state.isError, forceTextDirectionLtr = state.forceTextDirectionLtr, - label = state.label, - placeholder = state.numberPlaceholder, + label = state.numberLabel, visualTransformation = state.visualTransformation ?: VisualTransformation.None, keyboardOptions = state.keyboardOptions, keyboardActions = keyboardActions diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/state/POPhoneNumberFieldState.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/state/POPhoneNumberFieldState.kt index 743a40403..7a1ac1beb 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/state/POPhoneNumberFieldState.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/state/POPhoneNumberFieldState.kt @@ -13,10 +13,9 @@ data class POPhoneNumberFieldState( val id: String, val regionCode: TextFieldValue, val regionCodes: POImmutableList, - val regionCodePlaceholder: String?, + val regionCodeLabel: String, val number: TextFieldValue, - val numberPlaceholder: String?, - val label: String? = null, + val numberLabel: String, val description: String? = null, val enabled: Boolean = true, val isError: Boolean = false, diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt index a3cf0168b..208105149 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt @@ -336,8 +336,8 @@ internal class NativeAlternativePaymentViewModel private constructor( } private fun ParameterType.placeholder(): String? = when (this) { - EMAIL -> app.getString(R.string.po_native_apm_email_placeholder) - PHONE -> app.getString(R.string.po_native_apm_phone_placeholder) + EMAIL -> app.getString(R.string.po_native_apm_placeholder_email) + PHONE -> app.getString(R.string.po_native_apm_placeholder_phone) else -> null } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt index ad8da1edf..7ee110d53 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt @@ -326,13 +326,12 @@ internal class NativeAlternativePaymentViewModel private constructor( id = id, regionCode = regionCode, regionCodes = parameter.phoneNumberRegionCodes(), - regionCodePlaceholder = app.getString(R.string.po_native_apm_country_placeholder), + regionCodeLabel = app.getString(R.string.po_native_apm_label_country), number = when (value) { is FieldValue.PhoneNumber -> value.number else -> TextFieldValue() }, - numberPlaceholder = app.getString(R.string.po_native_apm_phone_placeholder), - label = label, + numberLabel = label, description = description, isError = !isValid, forceTextDirectionLtr = true, @@ -462,7 +461,7 @@ internal class NativeAlternativePaymentViewModel private constructor( private fun Parameter.placeholder(): String? = when (this) { - is Email -> app.getString(R.string.po_native_apm_email_placeholder) + is Email -> app.getString(R.string.po_native_apm_placeholder_email) else -> null } From 566feda0571df6ffa6c48473f496c42be884d87b Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 25 Jun 2025 17:58:29 +0300 Subject: [PATCH 39/80] Code impr --- .../sdk/ui/napm/v2/NativeAlternativePaymentInteractor.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentInteractor.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentInteractor.kt index 182d8e8ba..0fe54dbe0 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentInteractor.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentInteractor.kt @@ -290,10 +290,9 @@ internal class NativeAlternativePaymentInteractor( return } val fields = parameters.toFields() - val focusedFieldId = fields.firstFocusableFieldId() val updatedStateValue = stateValue.copy( fields = fields, - focusedFieldId = focusedFieldId + focusedFieldId = fields.firstFocusableFieldId() ) _state.update { if (_state.value is Loading) { From c2378c9da2cf098be1b98d86b2ffd5459da5c10b Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 25 Jun 2025 18:07:55 +0300 Subject: [PATCH 40/80] Remove placeholders --- .../sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt index 7ee110d53..097c0b8fc 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt @@ -252,7 +252,6 @@ internal class NativeAlternativePaymentViewModel private constructor( value = value.textFieldValue(), label = label, description = description, - placeholder = parameter.placeholder(), isError = !isValid, forceTextDirectionLtr = ltrParameterTypes.contains(parameter::class.java), inputFilter = parameter.inputFilter(), @@ -459,12 +458,6 @@ internal class NativeAlternativePaymentViewModel private constructor( Unknown -> KeyboardOptions.Default } - private fun Parameter.placeholder(): String? = - when (this) { - is Email -> app.getString(R.string.po_native_apm_placeholder_email) - else -> null - } - private fun Invoice.formatPrimaryActionText() = try { val price = NumberFormat.getCurrencyInstance().apply { From 59abf2335f4668d5a2ddb0e580f69a63cb3d7c33 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 26 Jun 2025 13:26:22 +0300 Subject: [PATCH 41/80] Error padding 12 --- .../sdk/ui/core/component/field/phone/POPhoneNumberField.kt | 2 +- .../processout/sdk/ui/core/component/field/text/POTextField2.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt index 76db52d37..aa689b230 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/phone/POPhoneNumberField.kt @@ -108,7 +108,7 @@ fun POPhoneNumberField( } POMessageBox( text = state.description, - modifier = Modifier.padding(top = spacing.space8), + modifier = Modifier.padding(top = spacing.space12), style = descriptionStyle ) } diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt index 920735337..1fb1332dd 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField2.kt @@ -76,7 +76,7 @@ fun POTextField2( ) POMessageBox( text = description, - modifier = Modifier.padding(top = spacing.space8), + modifier = Modifier.padding(top = spacing.space12), style = descriptionStyle ) } From c68e7cb6406abe05567197591997a050de324403 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 26 Jun 2025 15:05:10 +0300 Subject: [PATCH 42/80] POCodeField style --- .../core/component/field/code/POCodeField.kt | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField.kt index 3e8196d6a..ec4c01ff1 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField.kt @@ -26,13 +26,15 @@ import androidx.lifecycle.Lifecycle import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi import com.processout.sdk.ui.core.component.PORequestFocus import com.processout.sdk.ui.core.component.field.POField +import com.processout.sdk.ui.core.component.field.code.POCodeField.align import com.processout.sdk.ui.core.component.field.code.POCodeField.rememberTextFieldWidth -import com.processout.sdk.ui.core.component.field.code.POCodeField.style import com.processout.sdk.ui.core.component.field.code.POCodeField.validLength import com.processout.sdk.ui.core.component.field.text.POTextField import com.processout.sdk.ui.core.component.texttoolbar.ProcessOutTextToolbar import com.processout.sdk.ui.core.state.POInputFilter -import com.processout.sdk.ui.core.theme.ProcessOutTheme +import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions +import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing +import com.processout.sdk.ui.core.theme.ProcessOutTheme.typography /** @suppress */ @ProcessOutInternalApi @@ -53,7 +55,7 @@ fun POCodeField( keyboardActions: KeyboardActions = KeyboardActions.Default ) { var rowWidthPx by remember { mutableIntStateOf(0) } - val horizontalSpace = ProcessOutTheme.spacing.small + val horizontalSpace = spacing.small CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) { Row( modifier = Modifier @@ -145,7 +147,7 @@ fun POCodeField( modifier = modifier .requiredWidth( rememberTextFieldWidth( - defaultWidth = ProcessOutTheme.dimensions.interactiveComponentMinSize, + defaultWidth = dimensions.interactiveComponentMinSize, rowWidth = with(LocalDensity.current) { rowWidthPx.toDp() }, space = horizontalSpace, length = validLength @@ -176,7 +178,7 @@ fun POCodeField( } }, contentPadding = PaddingValues(0.dp), - style = style(style), + style = align(style), enabled = enabled, isError = isError, keyboardOptions = keyboardOptions, @@ -235,27 +237,27 @@ private fun List.codeValue() = TextFieldValue( object POCodeField { val default: POField.Style - @Composable get() = with(POField.default) { - copy( - normal = normal.default(), - error = error.default(), - focused = focused.default() + @Composable get() = POField.default.let { + it.copy( + normal = it.normal.defaultState(), + error = it.error.defaultState(), + focused = it.focused.defaultState() ) } - internal fun style(style: POField.Style) = with(style) { - copy( - normal = normal.textAlignCenter(), - error = error.textAlignCenter(), - focused = focused.textAlignCenter() - ) - } - @Composable - private fun POField.StateStyle.default() = copy( - text = text.copy(textStyle = ProcessOutTheme.typography.title) + private fun POField.StateStyle.defaultState() = copy( + text = text.copy(textStyle = typography.title) ) + internal fun align(style: POField.Style) = style.let { + it.copy( + normal = it.normal.textAlignCenter(), + error = it.error.textAlignCenter(), + focused = it.focused.textAlignCenter() + ) + } + private fun POField.StateStyle.textAlignCenter() = copy( text = text.copy(textStyle = text.textStyle.copy(textAlign = TextAlign.Center)) ) From fb39041c07279e8fb474a03cb1d5b343af042864 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 26 Jun 2025 15:10:01 +0300 Subject: [PATCH 43/80] POCodeField2 (draft) --- .../core/component/field/code/POCodeField2.kt | 273 ++++++++++++++++++ .../napm/v2/NativeAlternativePaymentScreen.kt | 26 +- 2 files changed, 283 insertions(+), 16 deletions(-) create mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField2.kt diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField2.kt new file mode 100644 index 000000000..c420997ca --- /dev/null +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField2.kt @@ -0,0 +1,273 @@ +package com.processout.sdk.ui.core.component.field.code + +import androidx.compose.foundation.focusGroup +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.input.key.* +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.* +import androidx.compose.ui.text.TextRange +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.unit.LayoutDirection +import androidx.lifecycle.Lifecycle +import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi +import com.processout.sdk.ui.core.component.POMessageBox +import com.processout.sdk.ui.core.component.PORequestFocus +import com.processout.sdk.ui.core.component.POText +import com.processout.sdk.ui.core.component.field.POField +import com.processout.sdk.ui.core.component.field.code.POCodeField.align +import com.processout.sdk.ui.core.component.field.code.POCodeField.rememberTextFieldWidth +import com.processout.sdk.ui.core.component.field.code.POCodeField.validLength +import com.processout.sdk.ui.core.component.field.text.POTextField2 +import com.processout.sdk.ui.core.component.texttoolbar.ProcessOutTextToolbar +import com.processout.sdk.ui.core.state.POInputFilter +import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions +import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing + +/** @suppress */ +@ProcessOutInternalApi +@Composable +fun POCodeField2( + value: TextFieldValue, + onValueChange: (TextFieldValue) -> Unit, + modifier: Modifier = Modifier, + textFieldModifier: Modifier = Modifier, + fieldStyle: POField.Style = POCodeField.default, // TODO(v2) + descriptionStyle: POMessageBox.Style = POMessageBox.error2, + length: Int = POCodeField.LengthMax, + label: String? = null, + description: String? = null, + enabled: Boolean = true, + isError: Boolean = false, + isFocused: Boolean = false, + lifecycleEvent: Lifecycle.Event? = null, + inputFilter: POInputFilter? = null, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default +) { + Column(modifier = modifier) { + if (label != null) { + // TODO(v2): color and style + POText( + text = label, + modifier = Modifier.padding(bottom = spacing.space12) + ) + } + Code( + value = value, + onValueChange = onValueChange, + style = fieldStyle, + length = length, + enabled = enabled, + isError = isError, + isFocused = isFocused, + lifecycleEvent = lifecycleEvent, + inputFilter = inputFilter, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + modifier = textFieldModifier + ) + POMessageBox( + text = description, + modifier = Modifier.padding(top = spacing.space12), + style = descriptionStyle + ) + } +} + +@Composable +private fun Code( + value: TextFieldValue, + onValueChange: (TextFieldValue) -> Unit, + style: POField.Style, + length: Int, + enabled: Boolean, + isError: Boolean, + isFocused: Boolean, + lifecycleEvent: Lifecycle.Event?, + inputFilter: POInputFilter?, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions, + modifier: Modifier = Modifier +) { + val validLength = remember(length) { validLength(length) } + var values by remember(validLength) { mutableStateOf(values(value.text, validLength, inputFilter)) } + var focusedIndex by remember(validLength) { mutableIntStateOf(values.focusedIndex()) } + val clipboardManager = LocalClipboardManager.current + CompositionLocalProvider( + LocalLayoutDirection provides LayoutDirection.Ltr, + LocalTextToolbar provides ProcessOutTextToolbar( + view = LocalView.current, + onPasteRequested = { + if (clipboardManager.hasText()) { + val pastedValues = values( + text = clipboardManager.getText()?.text ?: String(), + length = validLength, + inputFilter = inputFilter + ) + if (!pastedValues.all { it.text.isEmpty() }) { + values = pastedValues + focusedIndex = values.focusedIndex() + onValueChange(values.codeValue()) + } + } + }, + hideUnspecifiedActions = true + ) + ) { + var rowWidthPx by remember { mutableIntStateOf(0) } + val horizontalSpace = spacing.space8 + Row( + modifier = Modifier + .focusGroup() + .fillMaxWidth() + .onGloballyPositioned { rowWidthPx = it.size.width }, + horizontalArrangement = Arrangement.spacedBy(horizontalSpace), + verticalAlignment = Alignment.CenterVertically + ) { + val focusManager = LocalFocusManager.current + for (textFieldIndex in values.indices) { + val focusRequester = remember { FocusRequester() } + POTextField2( + value = values[textFieldIndex], + onValueChange = { updatedValue -> + if (updatedValue.selection.length == 0) { + val currentValue = values[textFieldIndex] + val updatedFilteredValue = inputFilter?.filter(updatedValue) ?: updatedValue + values = values.mapIndexed { index, textFieldValue -> + if (index == textFieldIndex) { + val updatedText = updatedFilteredValue.text.firstOrNull()?.toString() ?: String() + val isTextChanged = textFieldValue.text != updatedText + TextFieldValue( + text = updatedText, + selection = if (isTextChanged) { + TextRange(updatedText.length) + } else { + updatedFilteredValue.selection + } + ) + } else { + textFieldValue.copy(selection = TextRange.Zero) + } + } + if (textFieldIndex != values.lastIndex && + updatedFilteredValue.text.length == 2 && + updatedFilteredValue.selection.start == 2 + ) { + val nextText = updatedFilteredValue.text.last().toString() + values = values.mapIndexed { index, textFieldValue -> + if (index == textFieldIndex + 1) { + TextFieldValue( + text = nextText, + selection = TextRange(nextText.length) + ) + } else { + textFieldValue.copy(selection = TextRange.Zero) + } + } + } + val isSelectionChangedOnly = currentValue.text == updatedFilteredValue.text && + currentValue.selection != updatedFilteredValue.selection + if (updatedFilteredValue.text.isNotEmpty() && + !isSelectionChangedOnly && + textFieldIndex != values.lastIndex + ) { + focusedIndex = textFieldIndex + 1 + } + onValueChange(values.codeValue()) + } + }, + modifier = Modifier.requiredWidth( + rememberTextFieldWidth( + defaultWidth = dimensions.interactiveComponentMinSize, + rowWidth = with(LocalDensity.current) { rowWidthPx.toDp() }, + space = horizontalSpace, + length = validLength + ) + ), + textFieldModifier = modifier + .onPreviewKeyEvent { + if (it.key == Key.Backspace && + it.type == KeyEventType.KeyDown && + textFieldIndex != 0 && + values[textFieldIndex].selection.start == 0 + ) { + values = values.mapIndexed { index, textFieldValue -> + if (index == textFieldIndex - 1) { + TextFieldValue() + } else { + textFieldValue.copy(selection = TextRange.Zero) + } + } + focusManager.moveFocus(FocusDirection.Previous) + onValueChange(values.codeValue()) + } + false + } + .focusRequester(focusRequester) + .onFocusChanged { + if (it.isFocused) { + focusedIndex = textFieldIndex + } + }, + contentPadding = PaddingValues(spacing.space0), + fieldStyle = align(style), + enabled = enabled, + isError = isError, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions + ) + if (isFocused && textFieldIndex == focusedIndex) { + if (lifecycleEvent == Lifecycle.Event.ON_RESUME) { + PORequestFocus(focusRequester, lifecycleEvent) + } else { + PORequestFocus(focusRequester) + } + } + } + } + } +} + +private fun values( + text: String, + length: Int, + inputFilter: POInputFilter? +): List { + val values = mutableListOf() + while (values.size < length) { + values.add(TextFieldValue()) + } + val filteredText = inputFilter?.filter(TextFieldValue(text = text))?.text ?: text + filteredText + .take(length) + .forEachIndexed { index, char -> + val value = char.toString() + values[index] = TextFieldValue( + text = value, + selection = TextRange(value.length) + ) + } + return values +} + +private fun List.focusedIndex(): Int { + forEachIndexed { index, textFieldValue -> + if (textFieldValue.text.isEmpty()) { + return index + } + } + return lastIndex +} + +private fun List.codeValue() = TextFieldValue( + text = joinToString(separator = String()) { it.text } +) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt index 6398a7826..89d7a0a5f 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt @@ -40,7 +40,7 @@ import com.processout.sdk.ui.core.component.field.POFieldLabels import com.processout.sdk.ui.core.component.field.checkbox.POCheckbox import com.processout.sdk.ui.core.component.field.checkbox.POLabeledCheckboxField import com.processout.sdk.ui.core.component.field.code.POCodeField -import com.processout.sdk.ui.core.component.field.code.POLabeledCodeField +import com.processout.sdk.ui.core.component.field.code.POCodeField2 import com.processout.sdk.ui.core.component.field.dropdown.PODropdownField import com.processout.sdk.ui.core.component.field.dropdown.PODropdownField2 import com.processout.sdk.ui.core.component.field.phone.POPhoneNumberField @@ -60,7 +60,6 @@ import com.processout.sdk.ui.napm.v2.NativeAlternativePaymentScreen.CaptureImage import com.processout.sdk.ui.napm.v2.NativeAlternativePaymentScreen.CaptureLogoHeight import com.processout.sdk.ui.napm.v2.NativeAlternativePaymentScreen.CrossfadeAnimationDurationMillis import com.processout.sdk.ui.napm.v2.NativeAlternativePaymentScreen.animatedBackgroundColor -import com.processout.sdk.ui.napm.v2.NativeAlternativePaymentScreen.codeFieldHorizontalAlignment import com.processout.sdk.ui.napm.v2.NativeAlternativePaymentScreen.messageGravity import com.processout.sdk.ui.napm.v2.NativeAlternativePaymentViewModelState.* import com.processout.sdk.ui.napm.v2.NativeAlternativePaymentViewModelState.Field.* @@ -189,8 +188,8 @@ private fun UserInput( focusedFieldId = state.focusedFieldId, isPrimaryActionEnabled = isPrimaryActionEnabled, fieldStyle = style.codeField, - labelsStyle = labelsStyle, - horizontalAlignment = codeFieldHorizontalAlignment(state.fields.elements) + descriptionStyle = style.errorMessageBox, + modifier = Modifier.fillMaxWidth() ) is RadioField -> RadioField( state = field.state, @@ -293,11 +292,10 @@ private fun CodeField( focusedFieldId: String?, isPrimaryActionEnabled: Boolean, fieldStyle: POField.Style, - labelsStyle: POFieldLabels.Style, - horizontalAlignment: Alignment.Horizontal, + descriptionStyle: POMessageBox.Style, modifier: Modifier = Modifier ) { - POLabeledCodeField( + POCodeField2( value = state.value, onValueChange = { onEvent( @@ -307,9 +305,8 @@ private fun CodeField( ) ) }, - title = state.label ?: String(), - description = state.description, - modifier = modifier + modifier = modifier, + textFieldModifier = Modifier .onFocusChanged { onEvent( FieldFocusChanged( @@ -319,9 +316,10 @@ private fun CodeField( ) }, fieldStyle = fieldStyle, - labelsStyle = labelsStyle, + descriptionStyle = descriptionStyle, length = state.length ?: POCodeField.LengthMax, - horizontalAlignment = horizontalAlignment, + label = state.label, + description = state.description, enabled = state.enabled, isError = state.isError, isFocused = state.id == focusedFieldId, @@ -830,10 +828,6 @@ internal object NativeAlternativePaymentScreen { ) ).value - fun codeFieldHorizontalAlignment(fields: List): Alignment.Horizontal = - if (fields.size == 1 && fields[0] is CodeField) - Alignment.CenterHorizontally else Alignment.Start - private val ShortMessageMaxLength = 150 fun messageGravity(text: String): Int = From 4a213f9454192589b5dac6b102b2f1b7946ae6bf Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 26 Jun 2025 17:16:16 +0300 Subject: [PATCH 44/80] AGP 8.11.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index eeef5433a..581dc29ea 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ buildscript { ext { - androidGradlePluginVersion = '8.10.1' + androidGradlePluginVersion = '8.11.0' kotlinVersion = '2.1.20' kspVersion = '2.1.20-1.0.32' dokkaVersion = '1.9.20' From 578b517bc0e7e878e4469b590bc4444b847bd1e0 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 26 Jun 2025 17:59:12 +0300 Subject: [PATCH 45/80] Add label field style --- .../sdk/ui/core/component/field/POField.kt | 47 +++++++++++++------ .../field/dropdown/PODropdownField2.kt | 2 +- .../core/component/field/text/POTextField.kt | 4 +- .../sdk/ui/core/style/POFieldStyle.kt | 11 +++-- 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt index 72bafd703..4e4daf2c8 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/POField.kt @@ -36,6 +36,7 @@ import com.processout.sdk.ui.core.style.POFieldStyle import com.processout.sdk.ui.core.theme.ProcessOutTheme import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing +import com.processout.sdk.ui.core.theme.ProcessOutTheme.typography /** @suppress */ @ProcessOutInternalApi @@ -51,7 +52,7 @@ object POField { @Immutable data class StateStyle( val text: POText.Style, - val labelTextColor: Color, + val label: POText.Style, val placeholderTextColor: Color, val backgroundColor: Color, val controlsTintColor: Color, @@ -65,7 +66,7 @@ object POField { Style( normal = StateStyle( text = POText.body2, - labelTextColor = colors.text.primary, + label = POText.label1, placeholderTextColor = colors.text.muted, backgroundColor = colors.input.backgroundDefault, controlsTintColor = colors.text.primary, @@ -75,7 +76,10 @@ object POField { ), error = StateStyle( text = POText.body2, - labelTextColor = colors.text.error, + label = POText.Style( + color = colors.text.error, + textStyle = typography.label1 + ), placeholderTextColor = colors.text.muted, backgroundColor = colors.input.backgroundDefault, controlsTintColor = colors.text.primary, @@ -85,7 +89,7 @@ object POField { ), focused = StateStyle( text = POText.body2, - labelTextColor = colors.text.primary, + label = POText.label1, placeholderTextColor = colors.text.muted, backgroundColor = colors.input.backgroundDefault, controlsTintColor = colors.text.primary, @@ -104,7 +108,10 @@ object POField { color = colors.text.primary, textStyle = typography.s15(FontWeight.Medium) ), - labelTextColor = colors.text.placeholder, + label = POText.Style( + color = colors.text.placeholder, + textStyle = typography.s15(FontWeight.Medium) + ), placeholderTextColor = colors.text.placeholder, backgroundColor = colors.input.backgroundDefault, controlsTintColor = colors.text.primary, @@ -117,7 +124,10 @@ object POField { color = colors.text.primary, textStyle = typography.s15(FontWeight.Medium) ), - labelTextColor = colors.text.error, + label = POText.Style( + color = colors.text.error, + textStyle = typography.s15(FontWeight.Medium) + ), placeholderTextColor = colors.text.placeholder, backgroundColor = colors.input.backgroundDefault, controlsTintColor = colors.text.primary, @@ -130,7 +140,10 @@ object POField { color = colors.text.primary, textStyle = typography.s15(FontWeight.Medium) ), - labelTextColor = colors.text.placeholder, + label = POText.Style( + color = colors.text.placeholder, + textStyle = typography.s15(FontWeight.Medium) + ), placeholderTextColor = colors.text.placeholder, backgroundColor = colors.input.backgroundDefault, controlsTintColor = colors.text.primary, @@ -149,24 +162,24 @@ object POField { error = style.error.toStateStyle(), focused = style.focused?.toStateStyle() ?: normal ) - if (customStyle.normal.labelTextColor == Color.Unspecified) { + if (customStyle.normal.label.color == Color.Unspecified) { customStyle = customStyle.copy( normal = customStyle.normal.copy( - labelTextColor = default.normal.labelTextColor + label = default.normal.label ) ) } - if (customStyle.error.labelTextColor == Color.Unspecified) { + if (customStyle.error.label.color == Color.Unspecified) { customStyle = customStyle.copy( error = customStyle.error.copy( - labelTextColor = default.error.labelTextColor + label = default.error.label ) ) } - if (customStyle.focused.labelTextColor == Color.Unspecified) { + if (customStyle.focused.label.color == Color.Unspecified) { customStyle = customStyle.copy( focused = customStyle.focused.copy( - labelTextColor = default.focused.labelTextColor + label = default.focused.label ) ) } @@ -176,8 +189,12 @@ object POField { @Composable private fun POFieldStateStyle.toStateStyle() = StateStyle( text = POText.custom(style = text), - labelTextColor = if (labelTextColorResId != 0) - colorResource(id = labelTextColorResId) else Color.Unspecified, + label = if (label.colorResId != 0) + POText.custom(style = label) else + POText.Style( + color = Color.Unspecified, + textStyle = typography.s15(FontWeight.Medium) + ), placeholderTextColor = colorResource(id = placeholderTextColorResId), backgroundColor = colorResource(id = backgroundColorResId), controlsTintColor = colorResource(id = controlsTintColorResId), diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt index e93859822..305d417ef 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/dropdown/PODropdownField2.kt @@ -116,7 +116,7 @@ fun PODropdownField2( modifier = Modifier .scale(1.1f) .rotate(if (expanded) 180f else 0f), - tint = fieldStateStyle.labelTextColor + tint = fieldStateStyle.label.color ) } ) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt index 3b567046c..65ac7eca6 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/text/POTextField.kt @@ -108,8 +108,8 @@ fun POTextField( ) POText( text = label, - color = stateStyle.labelTextColor, - style = stateStyle.text.textStyle.copy(fontSize = animatedFontSizeValue.sp), + color = stateStyle.label.color, + style = stateStyle.label.textStyle.copy(fontSize = animatedFontSizeValue.sp), overflow = TextOverflow.Ellipsis, maxLines = 1 ) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POFieldStyle.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POFieldStyle.kt index 00f0acf50..51852a6f8 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POFieldStyle.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POFieldStyle.kt @@ -14,8 +14,7 @@ data class POFieldStyle( @Parcelize data class POFieldStateStyle( val text: POTextStyle, - @ColorRes - val labelTextColorResId: Int, + val label: POTextStyle, @ColorRes val placeholderTextColorResId: Int, @ColorRes @@ -41,7 +40,13 @@ data class POFieldStateStyle( dropdownRippleColorResId: Int? = null ) : this( text = text, - labelTextColorResId = 0, + label = POTextStyle( + colorResId = 0, + type = POTextType( + textSizeSp = 0, + lineHeightSp = 0 + ) + ), placeholderTextColorResId = placeholderTextColorResId, backgroundColorResId = backgroundColorResId, controlsTintColorResId = controlsTintColorResId, From 0f2b04c755380dc4392281a029337de691ef4a2a Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 26 Jun 2025 19:08:09 +0300 Subject: [PATCH 46/80] Apply code field style 'default2' --- .../core/component/field/code/POCodeField.kt | 58 +++++++++++++++---- .../core/component/field/code/POCodeField2.kt | 12 +++- .../napm/v2/NativeAlternativePaymentScreen.kt | 2 +- 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField.kt index ec4c01ff1..b6a20a416 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.input.key.* import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.* import androidx.compose.ui.text.TextRange +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Dp @@ -25,6 +26,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.Lifecycle import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi import com.processout.sdk.ui.core.component.PORequestFocus +import com.processout.sdk.ui.core.component.POText import com.processout.sdk.ui.core.component.field.POField import com.processout.sdk.ui.core.component.field.code.POCodeField.align import com.processout.sdk.ui.core.component.field.code.POCodeField.rememberTextFieldWidth @@ -32,6 +34,7 @@ import com.processout.sdk.ui.core.component.field.code.POCodeField.validLength import com.processout.sdk.ui.core.component.field.text.POTextField import com.processout.sdk.ui.core.component.texttoolbar.ProcessOutTextToolbar import com.processout.sdk.ui.core.state.POInputFilter +import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing import com.processout.sdk.ui.core.theme.ProcessOutTheme.typography @@ -238,17 +241,47 @@ object POCodeField { val default: POField.Style @Composable get() = POField.default.let { + val text = POText.Style( + color = colors.text.primary, + textStyle = typography.title + ) it.copy( - normal = it.normal.defaultState(), - error = it.error.defaultState(), - focused = it.focused.defaultState() + normal = it.normal.copy(text = text), + error = it.error.copy(text = text), + focused = it.focused.copy(text = text) ) } - @Composable - private fun POField.StateStyle.defaultState() = copy( - text = text.copy(textStyle = typography.title) - ) + val default2: POField.Style + @Composable get() = POField.default2.let { + val text = POText.Style( + color = colors.text.primary, + textStyle = typography.s20(FontWeight.Medium) + ) + it.copy( + normal = it.normal.copy( + text = text, + label = POText.Style( + color = colors.text.primary, + textStyle = typography.s16(FontWeight.Medium) + ) + ), + error = it.error.copy( + text = text, + label = POText.Style( + color = colors.text.error, + textStyle = typography.s16(FontWeight.Medium) + ) + ), + focused = it.focused.copy( + text = text, + label = POText.Style( + color = colors.text.primary, + textStyle = typography.s16(FontWeight.Medium) + ) + ) + ) + } internal fun align(style: POField.Style) = style.let { it.copy( @@ -258,9 +291,14 @@ object POCodeField { ) } - private fun POField.StateStyle.textAlignCenter() = copy( - text = text.copy(textStyle = text.textStyle.copy(textAlign = TextAlign.Center)) - ) + private fun POField.StateStyle.textAlignCenter() = + copy( + text = text.copy( + textStyle = text.textStyle.copy( + textAlign = TextAlign.Center + ) + ) + ) val LengthMin = 1 val LengthMax = 8 diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField2.kt index c420997ca..186e868d0 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField2.kt @@ -23,6 +23,7 @@ import com.processout.sdk.ui.core.component.POMessageBox import com.processout.sdk.ui.core.component.PORequestFocus import com.processout.sdk.ui.core.component.POText import com.processout.sdk.ui.core.component.field.POField +import com.processout.sdk.ui.core.component.field.POField.stateStyle import com.processout.sdk.ui.core.component.field.code.POCodeField.align import com.processout.sdk.ui.core.component.field.code.POCodeField.rememberTextFieldWidth import com.processout.sdk.ui.core.component.field.code.POCodeField.validLength @@ -40,7 +41,7 @@ fun POCodeField2( onValueChange: (TextFieldValue) -> Unit, modifier: Modifier = Modifier, textFieldModifier: Modifier = Modifier, - fieldStyle: POField.Style = POCodeField.default, // TODO(v2) + fieldStyle: POField.Style = POCodeField.default2, descriptionStyle: POMessageBox.Style = POMessageBox.error2, length: Int = POCodeField.LengthMax, label: String? = null, @@ -55,10 +56,15 @@ fun POCodeField2( ) { Column(modifier = modifier) { if (label != null) { - // TODO(v2): color and style + val fieldStateStyle = fieldStyle.stateStyle( + isError = isError, + isFocused = isFocused + ) POText( text = label, - modifier = Modifier.padding(bottom = spacing.space12) + modifier = Modifier.padding(bottom = spacing.space12), + color = fieldStateStyle.label.color, + style = fieldStateStyle.label.textStyle ) } Code( diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt index 89d7a0a5f..fc1352a18 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt @@ -750,7 +750,7 @@ internal object NativeAlternativePaymentScreen { } ?: POField.default2, codeField = custom?.codeField?.let { POField.custom(style = it) - } ?: POCodeField.default, + } ?: POCodeField.default2, radioGroup = custom?.radioButton?.let { PORadioGroup.custom(style = it) } ?: PORadioGroup.default, From 96a1598a11eea068013c560e9f931347d5a3e667 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 26 Jun 2025 19:18:47 +0300 Subject: [PATCH 47/80] value -> key --- .../response/napm/v2/PONativeAlternativePaymentElement.kt | 4 ++-- .../sdk/ui/napm/v2/NativeAlternativePaymentInteractor.kt | 2 +- .../sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/model/response/napm/v2/PONativeAlternativePaymentElement.kt b/sdk/src/main/kotlin/com/processout/sdk/api/model/response/napm/v2/PONativeAlternativePaymentElement.kt index 7572219e6..71b6ed1c4 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/api/model/response/napm/v2/PONativeAlternativePaymentElement.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/api/model/response/napm/v2/PONativeAlternativePaymentElement.kt @@ -78,13 +78,13 @@ sealed class PONativeAlternativePaymentElement { /** * Available parameter value. * - * @param[value] Parameter value. + * @param[key] Parameter value. * @param[label] Value display label. * @param[preselected] Indicates whether the value should be preselected by default. */ @JsonClass(generateAdapter = true) data class AvailableValue( - val value: String, + val key: String, val label: String, val preselected: Boolean ) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentInteractor.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentInteractor.kt index 0fe54dbe0..39078855f 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentInteractor.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentInteractor.kt @@ -326,7 +326,7 @@ internal class NativeAlternativePaymentInteractor( map { parameter -> val defaultValue = when (parameter) { is Parameter.SingleSelect -> FieldValue.Text( - TextFieldValue(text = parameter.preselectedValue?.value ?: String()) + TextFieldValue(text = parameter.preselectedValue?.key ?: String()) ) is Parameter.Bool -> FieldValue.Text(TextFieldValue(text = "false")) is Parameter.PhoneNumber -> FieldValue.PhoneNumber() diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt index 097c0b8fc..5b852d98a 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt @@ -356,7 +356,7 @@ internal class NativeAlternativePaymentViewModel private constructor( is SingleSelect -> POImmutableList( availableValues.map { POAvailableValue( - value = it.value, + value = it.key, text = it.label ) } From f70d7d7e5f64e55b889a38b129898cca0fd7c0f2 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 26 Jun 2025 20:35:20 +0300 Subject: [PATCH 48/80] POCheckbox2 --- .../component/field/checkbox/POCheckbox.kt | 19 ++++---- .../component/field/checkbox/POCheckbox2.kt | 45 +++++++++++++++++++ .../napm/v2/NativeAlternativePaymentScreen.kt | 15 +++---- 3 files changed, 61 insertions(+), 18 deletions(-) create mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox2.kt diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt index 671b4e905..80f096a86 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt @@ -17,8 +17,7 @@ import androidx.compose.ui.unit.dp import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi import com.processout.sdk.ui.core.component.POText import com.processout.sdk.ui.core.component.POText.measuredPaddingTop -import com.processout.sdk.ui.core.component.field.checkbox.POCheckbox.CheckboxScale -import com.processout.sdk.ui.core.component.field.checkbox.POCheckbox.CheckboxSize +import com.processout.sdk.ui.core.component.field.checkbox.POCheckbox.MaterialCheckboxSize import com.processout.sdk.ui.core.component.field.checkbox.POCheckbox.colors import com.processout.sdk.ui.core.component.field.checkbox.POCheckbox.textStyle import com.processout.sdk.ui.core.style.POCheckboxStateStyle @@ -26,6 +25,7 @@ import com.processout.sdk.ui.core.style.POCheckboxStyle import com.processout.sdk.ui.core.style.POCheckmarkStyle import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions +import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing import com.processout.sdk.ui.core.theme.ProcessOutTheme.typography /** @suppress */ @@ -37,6 +37,7 @@ fun POCheckbox( onCheckedChange: (Boolean) -> Unit, modifier: Modifier = Modifier, minHeight: Dp = dimensions.formComponentMinHeight, + checkboxSize: Dp = MaterialCheckboxSize, style: POCheckbox.Style = POCheckbox.default, enabled: Boolean = true, isError: Boolean = false, @@ -56,14 +57,14 @@ fun POCheckbox( indication = null ) ) { + val checkboxScale = checkboxSize.value / MaterialCheckboxSize.value Checkbox( checked = checked, onCheckedChange = onCheckedChange, modifier = Modifier - .scale(CheckboxScale) - .requiredWidth(CheckboxSize) - .requiredHeight(minHeight) - .offset(x = (-0.5).dp), + .scale(checkboxScale) + .requiredWidth(checkboxSize) + .requiredHeight(minHeight), enabled = enabled, colors = colors( style = style, @@ -80,7 +81,7 @@ fun POCheckbox( POText( text = text, modifier = Modifier.padding( - start = 10.dp, + start = spacing.space10, top = measuredPaddingTop( textStyle = textStyle.textStyle, componentHeight = minHeight @@ -177,9 +178,7 @@ object POCheckbox { backgroundColor = colorResource(id = backgroundColorResId) ) - private val MaterialCheckboxSize = 20.dp - internal val CheckboxSize = 22.dp - internal val CheckboxScale = CheckboxSize.value / MaterialCheckboxSize.value + internal val MaterialCheckboxSize = 20.dp internal fun colors( style: Style, diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox2.kt new file mode 100644 index 000000000..fd498af59 --- /dev/null +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox2.kt @@ -0,0 +1,45 @@ +package com.processout.sdk.ui.core.component.field.checkbox + +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi +import com.processout.sdk.ui.core.component.POMessageBox + +/** @suppress */ +@ProcessOutInternalApi +@Composable +fun POCheckbox2( + text: String, + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + checkboxStyle: POCheckbox.Style = POCheckbox.default, // TODO(v2) + descriptionStyle: POMessageBox.Style = POMessageBox.error2, + enabled: Boolean = true, + isError: Boolean = false, + description: String? = null, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } +) { + Column { + POCheckbox( + text = text, + checked = checked, + onCheckedChange = onCheckedChange, + modifier = modifier, + minHeight = 40.dp, + checkboxSize = 16.dp, + style = checkboxStyle, + enabled = enabled, + isError = isError, + interactionSource = interactionSource + ) + POMessageBox( + text = description, + style = descriptionStyle + ) + } +} diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt index fc1352a18..3c4fbba56 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt @@ -38,7 +38,7 @@ import com.processout.sdk.ui.core.component.* import com.processout.sdk.ui.core.component.field.POField import com.processout.sdk.ui.core.component.field.POFieldLabels import com.processout.sdk.ui.core.component.field.checkbox.POCheckbox -import com.processout.sdk.ui.core.component.field.checkbox.POLabeledCheckboxField +import com.processout.sdk.ui.core.component.field.checkbox.POCheckbox2 import com.processout.sdk.ui.core.component.field.code.POCodeField import com.processout.sdk.ui.core.component.field.code.POCodeField2 import com.processout.sdk.ui.core.component.field.dropdown.PODropdownField @@ -209,7 +209,7 @@ private fun UserInput( state = field.state, onEvent = onEvent, checkboxStyle = style.checkbox, - labelsStyle = labelsStyle, + descriptionStyle = style.errorMessageBox, modifier = Modifier.fillMaxWidth() ) is PhoneNumberField -> PhoneNumberField( @@ -408,10 +408,10 @@ private fun CheckboxField( state: FieldState, onEvent: (NativeAlternativePaymentEvent) -> Unit, checkboxStyle: POCheckbox.Style, - labelsStyle: POFieldLabels.Style, + descriptionStyle: POMessageBox.Style, modifier: Modifier = Modifier ) { - POLabeledCheckboxField( + POCheckbox2( text = state.label ?: String(), checked = state.value.text.toBooleanStrictOrNull() ?: false, onCheckedChange = { @@ -424,12 +424,11 @@ private fun CheckboxField( ) ) }, - title = null, - description = state.description, modifier = modifier, checkboxStyle = checkboxStyle, - labelsStyle = labelsStyle, - isError = state.isError + descriptionStyle = descriptionStyle, + isError = state.isError, + description = state.description ) } From fcafc9d342a19085db68f35b3a3fadec6cec4946 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Fri, 27 Jun 2025 16:28:00 +0300 Subject: [PATCH 49/80] code2 cell width 40 --- .../sdk/ui/core/component/field/code/POCodeField2.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField2.kt index 186e868d0..022c4107c 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField2.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.platform.* import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp import androidx.lifecycle.Lifecycle import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi import com.processout.sdk.ui.core.component.POMessageBox @@ -30,7 +31,6 @@ import com.processout.sdk.ui.core.component.field.code.POCodeField.validLength import com.processout.sdk.ui.core.component.field.text.POTextField2 import com.processout.sdk.ui.core.component.texttoolbar.ProcessOutTextToolbar import com.processout.sdk.ui.core.state.POInputFilter -import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing /** @suppress */ @@ -193,7 +193,7 @@ private fun Code( }, modifier = Modifier.requiredWidth( rememberTextFieldWidth( - defaultWidth = dimensions.interactiveComponentMinSize, + defaultWidth = 40.dp, rowWidth = with(LocalDensity.current) { rowWidthPx.toDp() }, space = horizontalSpace, length = validLength From 86f84cfaefa918001b07dab8f632c6ac45c2cd1e Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Fri, 27 Jun 2025 17:01:02 +0300 Subject: [PATCH 50/80] Checkbox style default2 --- .../component/field/checkbox/POCheckbox.kt | 49 +++++++++++++++++++ .../component/field/checkbox/POCheckbox2.kt | 2 +- .../processout/sdk/ui/core/theme/Colors.kt | 9 ++-- .../napm/v2/NativeAlternativePaymentScreen.kt | 2 +- 4 files changed, 57 insertions(+), 5 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt index 80f096a86..80d59687c 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.scale import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.colorResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi @@ -157,6 +158,54 @@ object POCheckbox { ) ) + val default2: Style + @Composable get() = Style( + normal = StateStyle( + checkmark = CheckmarkStyle( + color = colors.surface.default, + borderColor = colors.border.checkboxRadioDefault, + backgroundColor = colors.surface.default + ), + text = POText.Style( + color = colors.text.secondary, + textStyle = typography.s15(FontWeight.Medium) + ) + ), + selected = StateStyle( + checkmark = CheckmarkStyle( + color = colors.surface.default, + borderColor = colors.button.primaryBackgroundDefault, + backgroundColor = colors.button.primaryBackgroundDefault + ), + text = POText.Style( + color = colors.text.secondary, + textStyle = typography.s15(FontWeight.Medium) + ) + ), + error = StateStyle( + checkmark = CheckmarkStyle( + color = colors.input.borderError, + borderColor = colors.input.borderError, + backgroundColor = colors.surface.default + ), + text = POText.Style( + color = colors.text.secondary, + textStyle = typography.s15(FontWeight.Medium) + ) + ), + disabled = StateStyle( + checkmark = CheckmarkStyle( + color = colors.input.borderDisabled, + borderColor = colors.input.borderDisabled, + backgroundColor = colors.input.backgroundDisabled + ), + text = POText.Style( + color = colors.text.disabled, + textStyle = typography.s15(FontWeight.Medium) + ) + ) + ) + @Composable fun custom(style: POCheckboxStyle) = Style( normal = style.normal.toStateStyle(), diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox2.kt index fd498af59..3c379679c 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox2.kt @@ -17,7 +17,7 @@ fun POCheckbox2( checked: Boolean, onCheckedChange: (Boolean) -> Unit, modifier: Modifier = Modifier, - checkboxStyle: POCheckbox.Style = POCheckbox.default, // TODO(v2) + checkboxStyle: POCheckbox.Style = POCheckbox.default2, descriptionStyle: POMessageBox.Style = POMessageBox.error2, enabled: Boolean = true, isError: Boolean = false, diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index 9b19c5847..0850e154c 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -65,7 +65,8 @@ data class POColors( @Immutable data class Border( - val subtle: Color + val subtle: Color, + val checkboxRadioDefault: Color ) } @@ -111,7 +112,8 @@ val POLightColorPalette = POColors( toastError = Color(0xFFFDE3DE) ), border = Border( - subtle = Color(0xFFCCD1D6) + subtle = Color(0xFFCCD1D6), + checkboxRadioDefault = Color(0xFFC0C3C8) ) ) @@ -157,7 +159,8 @@ val PODarkColorPalette = POColors( toastError = Color(0xFF511511) ), border = Border( - subtle = Color(0xFF7C8593) + subtle = Color(0xFF7C8593), + checkboxRadioDefault = Color(0x3DF6F8FB) ) ) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt index 3c4fbba56..01c917ec4 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt @@ -755,7 +755,7 @@ internal object NativeAlternativePaymentScreen { } ?: PORadioGroup.default, checkbox = custom?.checkbox?.let { POCheckbox.custom(style = it) - } ?: POCheckbox.default, + } ?: POCheckbox.default2, dropdownMenu = custom?.dropdownMenu?.let { PODropdownField.custom(style = it) } ?: PODropdownField.defaultMenu2, From 63b4b8fa564f392eaa45f84edae253951a70efa2 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Fri, 27 Jun 2025 18:28:44 +0300 Subject: [PATCH 51/80] Updated colors and secondary button style --- .../sdk/ui/core/component/POButton.kt | 6 +-- .../processout/sdk/ui/core/theme/Colors.kt | 39 +++++++------------ 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt index 0a49cb1b9..df47088f3 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt @@ -261,7 +261,7 @@ object POButton { textStyle = typography.button ), shape = shapes.roundedCornersSmall, - border = POBorderStroke(width = 1.dp, color = colors.button.secondaryBorderDefault), + border = POBorderStroke(width = 0.dp, color = Color.Transparent), backgroundColor = colors.button.secondaryBackgroundDefault, elevation = 0.dp ), @@ -271,13 +271,13 @@ object POButton { textStyle = typography.button ), shape = shapes.roundedCornersSmall, - border = POBorderStroke(width = 1.dp, color = colors.button.secondaryBorderDisabled), + border = POBorderStroke(width = 0.dp, color = Color.Transparent), backgroundColor = colors.button.secondaryBackgroundDisabled, elevation = 0.dp ), highlighted = HighlightedStyle( textColor = colors.text.primary, - borderColor = colors.button.secondaryBorderPressed, + borderColor = Color.Transparent, backgroundColor = colors.button.secondaryBackgroundPressed ), progressIndicatorColor = colors.text.primary diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index 0850e154c..7ee02808e 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -48,9 +48,6 @@ data class POColors( val secondaryBackgroundDefault: Color, val secondaryBackgroundDisabled: Color, val secondaryBackgroundPressed: Color, - val secondaryBorderDefault: Color, - val secondaryBorderDisabled: Color, - val secondaryBorderPressed: Color, val ghostBackgroundPressed: Color ) @@ -73,9 +70,9 @@ data class POColors( @ProcessOutInternalApi val POLightColorPalette = POColors( text = Text( - primary = Color(0xFF121821), + primary = Color(0xFF000000), secondary = Color(0xFF585A5F), - inverse = Color(0xFFFAFAFA), + inverse = Color(0xFFFFFFFF), muted = Color(0xFF5B6576), placeholder = Color(0xFF707378), disabled = Color(0xFFADB5BD), @@ -93,15 +90,12 @@ val POLightColorPalette = POColors( borderError = Color(0xFFBE011B) ), button = Button( - primaryBackgroundDefault = Color(0xFF121821), - primaryBackgroundDisabled = Color(0xFFEDEEEF), - primaryBackgroundPressed = Color(0xFF242C38), - secondaryBackgroundDefault = Color(0xFFFFFFFF), - secondaryBackgroundDisabled = Color(0xFFFFFFFF), + primaryBackgroundDefault = Color(0xFF000000), + primaryBackgroundDisabled = Color(0x0A121314), + primaryBackgroundPressed = Color(0xFF26292F), + secondaryBackgroundDefault = Color(0x0F121314), + secondaryBackgroundDisabled = Color(0x0A121314), secondaryBackgroundPressed = Color(0x29212222), - secondaryBorderDefault = Color(0xFF121821), - secondaryBorderDisabled = Color(0xFFEDEEEF), - secondaryBorderPressed = Color(0xFF242C38), ghostBackgroundPressed = Color(0x1F121314) ), surface = Surface( @@ -120,9 +114,9 @@ val POLightColorPalette = POColors( @ProcessOutInternalApi val PODarkColorPalette = POColors( text = Text( - primary = Color(0xFFFAFAFA), + primary = Color(0xFFFFFFFF), secondary = Color(0xFFC0C3C8), - inverse = Color(0xFF121821), + inverse = Color(0xFF000000), muted = Color(0xFFADB5BD), placeholder = Color(0xFFA7A9AF), disabled = Color(0xFF5B6576), @@ -131,7 +125,7 @@ val PODarkColorPalette = POColors( onTipError = Color(0xFFF5D9D9) ), input = Input( - backgroundDefault = Color(0xFF121821), + backgroundDefault = Color(0xFF26292F), backgroundDisabled = Color(0xFF242C38), borderDefault = Color(0xFFCCD1D6), borderDefault2 = Color(0x29F6F8FB), @@ -141,18 +135,15 @@ val PODarkColorPalette = POColors( ), button = Button( primaryBackgroundDefault = Color(0xFFFFFFFF), - primaryBackgroundDisabled = Color(0xFF242C38), - primaryBackgroundPressed = Color(0xFFCCD1D6), - secondaryBackgroundDefault = Color(0xFF121821), - secondaryBackgroundDisabled = Color(0xFF121821), + primaryBackgroundDisabled = Color(0xFF2E3137), + primaryBackgroundPressed = Color(0xFF585A5F), + secondaryBackgroundDefault = Color(0x14F6F8FB), + secondaryBackgroundDisabled = Color(0xFF2E3137), secondaryBackgroundPressed = Color(0x0FF6F8FB), - secondaryBorderDefault = Color(0xFFFFFFFF), - secondaryBorderDisabled = Color(0xFF242C38), - secondaryBorderPressed = Color(0xFFCCD1D6), ghostBackgroundPressed = Color(0x1FF6F8FB) ), surface = Surface( - default = Color(0xFF121821), + default = Color(0xFF26292F), neutral = Color(0xFF242C38), success = Color(0xFF1DA37D), error = Color(0xFFD11D2F), From d9149a4f8247e73ceca28332f0a0e4dea2997080 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Fri, 27 Jun 2025 19:00:48 +0300 Subject: [PATCH 52/80] neutral color for dark theme --- .../src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index 7ee02808e..4f7efe254 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -144,7 +144,7 @@ val PODarkColorPalette = POColors( ), surface = Surface( default = Color(0xFF26292F), - neutral = Color(0xFF242C38), + neutral = Color(0xFF2A2D34), success = Color(0xFF1DA37D), error = Color(0xFFD11D2F), toastError = Color(0xFF511511) From eacf70793c035d5cdbed451f34aa1c2e73003cf5 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 30 Jun 2025 12:10:44 +0300 Subject: [PATCH 53/80] Update backgroundDisabled color --- .../main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index 4f7efe254..a823ac237 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -82,7 +82,7 @@ val POLightColorPalette = POColors( ), input = Input( backgroundDefault = Color(0xFFFFFFFF), - backgroundDisabled = Color(0xFFEDEEEF), + backgroundDisabled = Color(0x0F121314), borderDefault = Color(0xFF7C8593), borderDefault2 = Color(0x1F121314), borderDisabled = Color(0xFFADB5BD), @@ -126,7 +126,7 @@ val PODarkColorPalette = POColors( ), input = Input( backgroundDefault = Color(0xFF26292F), - backgroundDisabled = Color(0xFF242C38), + backgroundDisabled = Color(0x14F6F8FB), borderDefault = Color(0xFFCCD1D6), borderDefault2 = Color(0x29F6F8FB), borderDisabled = Color(0xFF7C8593), From a023dc0567974f62267ccd0be71dfcaae83d8d95 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 30 Jun 2025 12:46:36 +0300 Subject: [PATCH 54/80] radioButtonSize --- .../ui/core/component/field/radio/PORadioButton.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioButton.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioButton.kt index 527328ba7..b39ebe90b 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioButton.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioButton.kt @@ -10,10 +10,10 @@ import androidx.compose.runtime.Immutable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.scale import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi -import com.processout.sdk.ui.core.component.field.radio.PORadioButton.RadioButtonScale -import com.processout.sdk.ui.core.component.field.radio.PORadioButton.RadioButtonSize +import com.processout.sdk.ui.core.component.field.radio.PORadioButton.MaterialRadioButtonSize import com.processout.sdk.ui.core.component.field.radio.PORadioButton.colors import com.processout.sdk.ui.core.theme.ProcessOutTheme @@ -24,16 +24,18 @@ fun PORadioButton( selected: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier, + radioButtonSize: Dp = MaterialRadioButtonSize, style: PORadioButton.Style = PORadioButton.default, enabled: Boolean = true, isError: Boolean = false ) { + val radioButtonScale = radioButtonSize.value / MaterialRadioButtonSize.value RadioButton( selected = selected, onClick = onClick, modifier = modifier - .scale(RadioButtonScale) - .requiredWidth(RadioButtonSize) + .scale(radioButtonScale) + .requiredWidth(radioButtonSize) .requiredHeight(ProcessOutTheme.dimensions.formComponentMinHeight), enabled = enabled, colors = colors(style = style, isError = isError) @@ -62,9 +64,7 @@ object PORadioButton { ) } - private val MaterialRadioButtonSize = 20.dp - internal val RadioButtonSize = 22.dp - internal val RadioButtonScale = RadioButtonSize.value / MaterialRadioButtonSize.value + internal val MaterialRadioButtonSize = 20.dp @Composable internal fun colors( From a4bf4ff5210fe4c29258326346d80414d647325c Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 30 Jun 2025 13:12:00 +0300 Subject: [PATCH 55/80] CheckRadio colors group --- .../component/field/checkbox/POCheckbox.kt | 2 +- .../processout/sdk/ui/core/theme/Colors.kt | 21 +++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt index 80d59687c..a63792e2f 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt @@ -163,7 +163,7 @@ object POCheckbox { normal = StateStyle( checkmark = CheckmarkStyle( color = colors.surface.default, - borderColor = colors.border.checkboxRadioDefault, + borderColor = colors.checkRadio.borderDefault, backgroundColor = colors.surface.default ), text = POText.Style( diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index a823ac237..13aef1c57 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -12,6 +12,7 @@ import com.processout.sdk.ui.core.theme.POColors.* data class POColors( val text: Text, val input: Input, + val checkRadio: CheckRadio, val button: Button, val surface: Surface, val border: Border @@ -40,6 +41,11 @@ data class POColors( val borderError: Color ) + @Immutable + data class CheckRadio( + val borderDefault: Color + ) + @Immutable data class Button( val primaryBackgroundDefault: Color, @@ -62,8 +68,7 @@ data class POColors( @Immutable data class Border( - val subtle: Color, - val checkboxRadioDefault: Color + val subtle: Color ) } @@ -89,6 +94,9 @@ val POLightColorPalette = POColors( borderFocused = Color(0xFF4791FF), borderError = Color(0xFFBE011B) ), + checkRadio = CheckRadio( + borderDefault = Color(0xFFC0C3C8) + ), button = Button( primaryBackgroundDefault = Color(0xFF000000), primaryBackgroundDisabled = Color(0x0A121314), @@ -106,8 +114,7 @@ val POLightColorPalette = POColors( toastError = Color(0xFFFDE3DE) ), border = Border( - subtle = Color(0xFFCCD1D6), - checkboxRadioDefault = Color(0xFFC0C3C8) + subtle = Color(0xFFCCD1D6) ) ) @@ -133,6 +140,9 @@ val PODarkColorPalette = POColors( borderFocused = Color(0xFFFFE500), borderError = Color(0xFFFF7D6C) ), + checkRadio = CheckRadio( + borderDefault = Color(0x3DF6F8FB) + ), button = Button( primaryBackgroundDefault = Color(0xFFFFFFFF), primaryBackgroundDisabled = Color(0xFF2E3137), @@ -150,8 +160,7 @@ val PODarkColorPalette = POColors( toastError = Color(0xFF511511) ), border = Border( - subtle = Color(0xFF7C8593), - checkboxRadioDefault = Color(0x3DF6F8FB) + subtle = Color(0xFF7C8593) ) ) From c241cd53fee41d1a906dbc74d8bb55fa92883d5f Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 30 Jun 2025 14:12:10 +0300 Subject: [PATCH 56/80] Update CheckRadio colors for default2 style --- .../component/field/checkbox/POCheckbox.kt | 22 +++++------ .../component/field/radio/PORadioButton.kt | 10 +++++ .../processout/sdk/ui/core/theme/Colors.kt | 39 +++++++++++++++++-- 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt index a63792e2f..f6a817ade 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt @@ -162,9 +162,9 @@ object POCheckbox { @Composable get() = Style( normal = StateStyle( checkmark = CheckmarkStyle( - color = colors.surface.default, + color = colors.checkRadio.iconDefault, borderColor = colors.checkRadio.borderDefault, - backgroundColor = colors.surface.default + backgroundColor = colors.checkRadio.surfaceDefault ), text = POText.Style( color = colors.text.secondary, @@ -173,9 +173,9 @@ object POCheckbox { ), selected = StateStyle( checkmark = CheckmarkStyle( - color = colors.surface.default, - borderColor = colors.button.primaryBackgroundDefault, - backgroundColor = colors.button.primaryBackgroundDefault + color = colors.checkRadio.iconActive, + borderColor = colors.checkRadio.borderActive, + backgroundColor = colors.checkRadio.surfaceActive ), text = POText.Style( color = colors.text.secondary, @@ -184,9 +184,9 @@ object POCheckbox { ), error = StateStyle( checkmark = CheckmarkStyle( - color = colors.input.borderError, - borderColor = colors.input.borderError, - backgroundColor = colors.surface.default + color = colors.checkRadio.iconError, + borderColor = colors.checkRadio.borderError, + backgroundColor = colors.checkRadio.surfaceError ), text = POText.Style( color = colors.text.secondary, @@ -195,9 +195,9 @@ object POCheckbox { ), disabled = StateStyle( checkmark = CheckmarkStyle( - color = colors.input.borderDisabled, - borderColor = colors.input.borderDisabled, - backgroundColor = colors.input.backgroundDisabled + color = colors.checkRadio.iconDisabled, + borderColor = colors.checkRadio.borderDisabled, + backgroundColor = colors.checkRadio.surfaceDisabled ), text = POText.Style( color = colors.text.disabled, diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioButton.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioButton.kt index b39ebe90b..f5466cf31 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioButton.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioButton.kt @@ -64,6 +64,16 @@ object PORadioButton { ) } + val default2: Style + @Composable get() = with(ProcessOutTheme) { + Style( + normalColor = colors.checkRadio.borderDefault, + selectedColor = colors.checkRadio.borderActive, + errorColor = colors.checkRadio.borderError, + disabledColor = colors.checkRadio.iconDisabled + ) + } + internal val MaterialRadioButtonSize = 20.dp @Composable diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index 13aef1c57..82d44092d 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -43,7 +43,18 @@ data class POColors( @Immutable data class CheckRadio( - val borderDefault: Color + val iconDefault: Color, + val iconActive: Color, + val iconError: Color, + val iconDisabled: Color, + val borderDefault: Color, + val borderActive: Color, + val borderError: Color, + val borderDisabled: Color, + val surfaceDefault: Color, + val surfaceActive: Color, + val surfaceError: Color, + val surfaceDisabled: Color ) @Immutable @@ -95,7 +106,18 @@ val POLightColorPalette = POColors( borderError = Color(0xFFBE011B) ), checkRadio = CheckRadio( - borderDefault = Color(0xFFC0C3C8) + iconDefault = Color(0xFFFFFFFF), + iconActive = Color(0xFFFFFFFF), + iconError = Color(0xFFF03030), + iconDisabled = Color(0xFFC0C3C8), + borderDefault = Color(0xFFC0C3C8), + borderActive = Color(0xFF000000), + borderError = Color(0xFFF03030), + borderDisabled = Color(0xFFE2E2E2), + surfaceDefault = Color(0xFFFFFFFF), + surfaceActive = Color(0xFF000000), + surfaceError = Color(0xFFFDE3DE), + surfaceDisabled = Color(0xFFF1F1F2) ), button = Button( primaryBackgroundDefault = Color(0xFF000000), @@ -141,7 +163,18 @@ val PODarkColorPalette = POColors( borderError = Color(0xFFFF7D6C) ), checkRadio = CheckRadio( - borderDefault = Color(0x3DF6F8FB) + iconDefault = Color(0x33121314), + iconActive = Color(0xFF000000), + iconError = Color(0xFFFF7D6C), + iconDisabled = Color(0xFF707378), + borderDefault = Color(0x3DF6F8FB), + borderActive = Color(0xFFFFFFFF), + borderError = Color(0xFFFF7D6C), + borderDisabled = Color(0xFF3D4149), + surfaceDefault = Color(0x33121314), + surfaceActive = Color(0xFFFFFFFF), + surfaceError = Color(0xFF3D0D04), + surfaceDisabled = Color(0xFF2E3137) ), button = Button( primaryBackgroundDefault = Color(0xFFFFFFFF), From af556ee535623d16f45cccda4a27bf28d17b1822 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 30 Jun 2025 14:23:11 +0300 Subject: [PATCH 57/80] PORadioGroup.default2 --- .../component/field/radio/PORadioGroup.kt | 33 +++++++++++++++++++ .../napm/v2/NativeAlternativePaymentScreen.kt | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioGroup.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioGroup.kt index e27aa6a77..27daaa5ff 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioGroup.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioGroup.kt @@ -9,6 +9,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.colorResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi import com.processout.sdk.ui.core.component.POText @@ -116,6 +117,38 @@ object PORadioGroup { ) ) + val default2: Style + @Composable get() = Style( + normal = StateStyle( + buttonColor = PORadioButton.default2.normalColor, + text = POText.Style( + color = colors.text.secondary, + textStyle = typography.s15(FontWeight.Medium) + ) + ), + selected = StateStyle( + buttonColor = PORadioButton.default2.selectedColor, + text = POText.Style( + color = colors.text.secondary, + textStyle = typography.s15(FontWeight.Medium) + ) + ), + error = StateStyle( + buttonColor = PORadioButton.default2.errorColor, + text = POText.Style( + color = colors.text.secondary, + textStyle = typography.s15(FontWeight.Medium) + ) + ), + disabled = StateStyle( + buttonColor = PORadioButton.default2.disabledColor, + text = POText.Style( + color = colors.text.disabled, + textStyle = typography.s15(FontWeight.Medium) + ) + ) + ) + @Composable fun custom(style: PORadioButtonStyle): Style { val normal = style.normal.toStateStyle() diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt index 01c917ec4..cb00fba01 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt @@ -752,7 +752,7 @@ internal object NativeAlternativePaymentScreen { } ?: POCodeField.default2, radioGroup = custom?.radioButton?.let { PORadioGroup.custom(style = it) - } ?: PORadioGroup.default, + } ?: PORadioGroup.default2, checkbox = custom?.checkbox?.let { POCheckbox.custom(style = it) } ?: POCheckbox.default2, From 4fb3f84995b6b2603c884142308c6c336571e632 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 30 Jun 2025 14:37:48 +0300 Subject: [PATCH 58/80] Fix checkbox error state --- .../sdk/ui/core/component/field/checkbox/POCheckbox.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt index f6a817ade..7b80152c2 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt @@ -245,7 +245,7 @@ object POCheckbox { uncheckedCheckmarkColor = this.color uncheckedBorderColor = this.borderColor uncheckedBoxColor = this.backgroundColor - checkedCheckmarkColor = this.color + checkedCheckmarkColor = if (enabled) this.color else style.disabled.checkmark.color checkedBorderColor = this.borderColor checkedBoxColor = this.backgroundColor } From bc819b3c7c729dd7634712f3864442c1efac2abe Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 30 Jun 2025 14:42:36 +0300 Subject: [PATCH 59/80] Update check/radio error colors --- .../main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index 82d44092d..90776224c 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -108,11 +108,11 @@ val POLightColorPalette = POColors( checkRadio = CheckRadio( iconDefault = Color(0xFFFFFFFF), iconActive = Color(0xFFFFFFFF), - iconError = Color(0xFFF03030), + iconError = Color(0xFFBE011B), iconDisabled = Color(0xFFC0C3C8), borderDefault = Color(0xFFC0C3C8), borderActive = Color(0xFF000000), - borderError = Color(0xFFF03030), + borderError = Color(0xFFBE011B), borderDisabled = Color(0xFFE2E2E2), surfaceDefault = Color(0xFFFFFFFF), surfaceActive = Color(0xFF000000), From 6e975655b6a31108f9e0d5c598fbbf92c2e51a4d Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 30 Jun 2025 16:26:45 +0300 Subject: [PATCH 60/80] ghostBackgroundDisabled --- .../kotlin/com/processout/sdk/ui/core/component/POButton.kt | 2 +- .../src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt index df47088f3..cf1723e42 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt @@ -304,7 +304,7 @@ object POButton { ), shape = shapes.roundedCornersSmall, border = POBorderStroke(width = 0.dp, color = Color.Transparent), - backgroundColor = Color.Transparent, + backgroundColor = colors.button.ghostBackgroundDisabled, elevation = 0.dp ), highlighted = HighlightedStyle( diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index 90776224c..db74b676c 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -65,6 +65,7 @@ data class POColors( val secondaryBackgroundDefault: Color, val secondaryBackgroundDisabled: Color, val secondaryBackgroundPressed: Color, + val ghostBackgroundDisabled: Color, val ghostBackgroundPressed: Color ) @@ -126,6 +127,7 @@ val POLightColorPalette = POColors( secondaryBackgroundDefault = Color(0x0F121314), secondaryBackgroundDisabled = Color(0x0A121314), secondaryBackgroundPressed = Color(0x29212222), + ghostBackgroundDisabled = Color(0x0A121314), ghostBackgroundPressed = Color(0x1F121314) ), surface = Surface( @@ -183,6 +185,7 @@ val PODarkColorPalette = POColors( secondaryBackgroundDefault = Color(0x14F6F8FB), secondaryBackgroundDisabled = Color(0xFF2E3137), secondaryBackgroundPressed = Color(0x0FF6F8FB), + ghostBackgroundDisabled = Color(0xFF2E3137), ghostBackgroundPressed = Color(0x1FF6F8FB) ), surface = Surface( From 9fcfbcce25fe572dfe0be20880abd2399cd6762b Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 30 Jun 2025 16:48:30 +0300 Subject: [PATCH 61/80] onButtonDisabled --- .../kotlin/com/processout/sdk/ui/core/component/POButton.kt | 6 +++--- .../main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt index cf1723e42..830953126 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt @@ -235,7 +235,7 @@ object POButton { ), disabled = StateStyle( text = POText.Style( - color = colors.text.disabled, + color = colors.text.onButtonDisabled, textStyle = typography.button ), shape = shapes.roundedCornersSmall, @@ -267,7 +267,7 @@ object POButton { ), disabled = StateStyle( text = POText.Style( - color = colors.text.disabled, + color = colors.text.onButtonDisabled, textStyle = typography.button ), shape = shapes.roundedCornersSmall, @@ -299,7 +299,7 @@ object POButton { ), disabled = StateStyle( text = POText.Style( - color = colors.text.disabled, + color = colors.text.onButtonDisabled, textStyle = typography.button ), shape = shapes.roundedCornersSmall, diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index db74b676c..ec3702754 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -25,6 +25,7 @@ data class POColors( val muted: Color, val placeholder: Color, val disabled: Color, + val onButtonDisabled: Color, val success: Color, val error: Color, val onTipError: Color @@ -93,6 +94,7 @@ val POLightColorPalette = POColors( muted = Color(0xFF5B6576), placeholder = Color(0xFF707378), disabled = Color(0xFFADB5BD), + onButtonDisabled = Color(0xFFC0C3C8), success = Color(0xFF00291D), error = Color(0xFFBE011B), onTipError = Color(0xFF630407) @@ -151,6 +153,7 @@ val PODarkColorPalette = POColors( muted = Color(0xFFADB5BD), placeholder = Color(0xFFA7A9AF), disabled = Color(0xFF5B6576), + onButtonDisabled = Color(0xFF707378), success = Color(0xFFE5FFF8), error = Color(0xFFFF7D6C), onTipError = Color(0xFFF5D9D9) From 94446c3e827256c11988072ac2347c396f650243 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 30 Jun 2025 16:57:17 +0300 Subject: [PATCH 62/80] PORadioButton code impr --- .../component/field/radio/PORadioButton.kt | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioButton.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioButton.kt index f5466cf31..76f81e3e4 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioButton.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioButton.kt @@ -15,7 +15,8 @@ import androidx.compose.ui.unit.dp import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi import com.processout.sdk.ui.core.component.field.radio.PORadioButton.MaterialRadioButtonSize import com.processout.sdk.ui.core.component.field.radio.PORadioButton.colors -import com.processout.sdk.ui.core.theme.ProcessOutTheme +import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors +import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions /** @suppress */ @ProcessOutInternalApi @@ -36,7 +37,7 @@ fun PORadioButton( modifier = modifier .scale(radioButtonScale) .requiredWidth(radioButtonSize) - .requiredHeight(ProcessOutTheme.dimensions.formComponentMinHeight), + .requiredHeight(dimensions.formComponentMinHeight), enabled = enabled, colors = colors(style = style, isError = isError) ) @@ -55,24 +56,20 @@ object PORadioButton { ) val default: Style - @Composable get() = with(ProcessOutTheme) { - Style( - normalColor = colors.input.borderDefault, - selectedColor = colors.button.primaryBackgroundDefault, - errorColor = colors.input.borderError, - disabledColor = colors.input.borderDisabled - ) - } + @Composable get() = Style( + normalColor = colors.input.borderDefault, + selectedColor = colors.button.primaryBackgroundDefault, + errorColor = colors.input.borderError, + disabledColor = colors.input.borderDisabled + ) val default2: Style - @Composable get() = with(ProcessOutTheme) { - Style( - normalColor = colors.checkRadio.borderDefault, - selectedColor = colors.checkRadio.borderActive, - errorColor = colors.checkRadio.borderError, - disabledColor = colors.checkRadio.iconDisabled - ) - } + @Composable get() = Style( + normalColor = colors.checkRadio.borderDefault, + selectedColor = colors.checkRadio.borderActive, + errorColor = colors.checkRadio.borderError, + disabledColor = colors.checkRadio.iconDisabled + ) internal val MaterialRadioButtonSize = 20.dp From 3503997e711d3a4a83d65aeee78eec16bc3f5f8d Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 30 Jun 2025 17:13:06 +0300 Subject: [PATCH 63/80] Buttons style: primary2 & secondary2 --- .../ui/core/component/POActionsContainer.kt | 11 ++++ .../sdk/ui/core/component/POButton.kt | 65 +++++++++++++++++++ .../napm/v2/NativeAlternativePaymentScreen.kt | 2 +- 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POActionsContainer.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POActionsContainer.kt index 21fdaba19..3ed89ea4b 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POActionsContainer.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POActionsContainer.kt @@ -136,6 +136,17 @@ object POActionsContainer { ) } + val default2: Style + @Composable get() = with(ProcessOutTheme) { + Style( + primary = POButton.primary2, + secondary = POButton.secondary2, + dividerColor = colors.border.subtle, + backgroundColor = colors.surface.default, + axis = POAxis.Vertical + ) + } + @Composable fun custom(style: POActionsContainerStyle) = with(style) { Style( diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt index 830953126..88345a621 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.Shape import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp @@ -252,6 +253,38 @@ object POButton { ) } + val primary2: Style + @Composable get() = with(ProcessOutTheme) { + Style( + normal = StateStyle( + text = POText.Style( + color = colors.text.inverse, + textStyle = typography.s15(FontWeight.Medium) + ), + shape = shapes.roundedCorners6, + border = POBorderStroke(width = 0.dp, color = Color.Transparent), + backgroundColor = colors.button.primaryBackgroundDefault, + elevation = 0.dp + ), + disabled = StateStyle( + text = POText.Style( + color = colors.text.onButtonDisabled, + textStyle = typography.s15(FontWeight.Medium) + ), + shape = shapes.roundedCorners6, + border = POBorderStroke(width = 0.dp, color = Color.Transparent), + backgroundColor = colors.button.primaryBackgroundDisabled, + elevation = 0.dp + ), + highlighted = HighlightedStyle( + textColor = colors.text.inverse, + borderColor = Color.Transparent, + backgroundColor = colors.button.primaryBackgroundPressed + ), + progressIndicatorColor = colors.text.inverse + ) + } + val secondary: Style @Composable get() = with(ProcessOutTheme) { Style( @@ -284,6 +317,38 @@ object POButton { ) } + val secondary2: Style + @Composable get() = with(ProcessOutTheme) { + Style( + normal = StateStyle( + text = POText.Style( + color = colors.text.primary, + textStyle = typography.s15(FontWeight.Medium) + ), + shape = shapes.roundedCorners6, + border = POBorderStroke(width = 0.dp, color = Color.Transparent), + backgroundColor = colors.button.secondaryBackgroundDefault, + elevation = 0.dp + ), + disabled = StateStyle( + text = POText.Style( + color = colors.text.onButtonDisabled, + textStyle = typography.s15(FontWeight.Medium) + ), + shape = shapes.roundedCorners6, + border = POBorderStroke(width = 0.dp, color = Color.Transparent), + backgroundColor = colors.button.secondaryBackgroundDisabled, + elevation = 0.dp + ), + highlighted = HighlightedStyle( + textColor = colors.text.primary, + borderColor = Color.Transparent, + backgroundColor = colors.button.secondaryBackgroundPressed + ), + progressIndicatorColor = colors.text.primary + ) + } + val ghost: Style @Composable get() = with(ProcessOutTheme) { Style( diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt index cb00fba01..0c649169a 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt @@ -761,7 +761,7 @@ internal object NativeAlternativePaymentScreen { } ?: PODropdownField.defaultMenu2, actionsContainer = custom?.actionsContainer?.let { POActionsContainer.custom(style = it) - } ?: POActionsContainer.default, + } ?: POActionsContainer.default2, dialog = custom?.dialog?.let { PODialog.custom(style = it) } ?: PODialog.default, From df8bb532a2bdada5f4dfea3d88c77980c5f23fa3 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 30 Jun 2025 17:35:07 +0300 Subject: [PATCH 64/80] Update dialog style --- .../sdk/ui/core/component/PODialog.kt | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/PODialog.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/PODialog.kt index 7b766a9d1..1d6a41c05 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/PODialog.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/PODialog.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.colorResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties @@ -30,6 +31,7 @@ import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions import com.processout.sdk.ui.core.theme.ProcessOutTheme.shapes import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing +import com.processout.sdk.ui.core.theme.ProcessOutTheme.typography /** @suppress */ @ProcessOutInternalApi @@ -166,8 +168,14 @@ object PODialog { val default: Style @Composable get() = Style( - title = POText.title, - message = POText.body2, + title = POText.Style( + color = colors.text.primary, + textStyle = typography.s20(FontWeight.SemiBold) + ), + message = POText.Style( + color = colors.text.secondary, + textStyle = typography.paragraph.s16() + ), confirmButton = defaultButton, dismissButton = defaultButton, backgroundColor = colors.surface.default @@ -176,8 +184,22 @@ object PODialog { private val defaultButton: POButton.Style @Composable get() = with(POButton.ghost) { copy( - normal = normal.copy(paddingHorizontal = spacing.large), - disabled = disabled.copy(paddingHorizontal = spacing.large) + normal = normal.copy( + text = POText.Style( + color = colors.text.primary, + textStyle = typography.s15(FontWeight.Medium) + ), + shape = shapes.roundedCorners6, + paddingHorizontal = spacing.space16 + ), + disabled = disabled.copy( + text = POText.Style( + color = colors.text.onButtonDisabled, + textStyle = typography.s15(FontWeight.Medium) + ), + shape = shapes.roundedCorners6, + paddingHorizontal = spacing.space16 + ) ) } From 2b0884957a589494e29dbb443036ed25d0982c39 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 30 Jun 2025 18:06:41 +0300 Subject: [PATCH 65/80] Fix POCodeField2 label condition --- .../processout/sdk/ui/core/component/field/code/POCodeField2.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField2.kt index 022c4107c..66c74caea 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/code/POCodeField2.kt @@ -55,7 +55,7 @@ fun POCodeField2( keyboardActions: KeyboardActions = KeyboardActions.Default ) { Column(modifier = modifier) { - if (label != null) { + if (!label.isNullOrBlank()) { val fieldStateStyle = fieldStyle.stateStyle( isError = isError, isFocused = isFocused From 3c9a16370e7d2fb6fa53b0a71eacbbc60876dd30 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 30 Jun 2025 19:27:54 +0300 Subject: [PATCH 66/80] roundedCorners4 --- .../src/main/kotlin/com/processout/sdk/ui/core/theme/Shapes.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Shapes.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Shapes.kt index 3448157e2..c98a6335a 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Shapes.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Shapes.kt @@ -11,6 +11,7 @@ import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi @ProcessOutInternalApi @Immutable data class POShapes( + val roundedCorners4: CornerBasedShape = RoundedCornerShape(4.dp), val roundedCorners6: CornerBasedShape = RoundedCornerShape(6.dp), val roundedCorners8: CornerBasedShape = RoundedCornerShape(8.dp), val roundedCornersSmall: CornerBasedShape = RoundedCornerShape(4.dp), From ec35ca2c348994b8ff2649533c374e1128962744 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 30 Jun 2025 20:18:48 +0300 Subject: [PATCH 67/80] Added 'darkout' color --- .../src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index ec3702754..8e93b67d4 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -74,6 +74,7 @@ data class POColors( data class Surface( val default: Color, val neutral: Color, + val darkout: Color, val success: Color, val error: Color, val toastError: Color @@ -135,6 +136,7 @@ val POLightColorPalette = POColors( surface = Surface( default = Color(0xFFFFFFFF), neutral = Color(0xFFFAFAFA), + darkout = Color(0x0F121314), success = Color(0xFFBEFAE9), error = Color(0xFFFFC2C8), toastError = Color(0xFFFDE3DE) @@ -194,6 +196,7 @@ val PODarkColorPalette = POColors( surface = Surface( default = Color(0xFF26292F), neutral = Color(0xFF2A2D34), + darkout = Color(0x0FF6F8FB), success = Color(0xFF1DA37D), error = Color(0xFFD11D2F), toastError = Color(0xFF511511) From e44111aa0fcf7ad061d3f6cd304927f60c83a0b0 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 1 Jul 2025 17:08:51 +0300 Subject: [PATCH 68/80] Added 'darkoutRipple' color --- .../src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index 8e93b67d4..f9a14da2f 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -75,6 +75,7 @@ data class POColors( val default: Color, val neutral: Color, val darkout: Color, + val darkoutRipple: Color, val success: Color, val error: Color, val toastError: Color @@ -137,6 +138,7 @@ val POLightColorPalette = POColors( default = Color(0xFFFFFFFF), neutral = Color(0xFFFAFAFA), darkout = Color(0x0F121314), + darkoutRipple = Color(0x0F888989), success = Color(0xFFBEFAE9), error = Color(0xFFFFC2C8), toastError = Color(0xFFFDE3DE) @@ -197,6 +199,7 @@ val PODarkColorPalette = POColors( default = Color(0xFF26292F), neutral = Color(0xFF2A2D34), darkout = Color(0x0FF6F8FB), + darkoutRipple = Color(0x0F7B7C7D), success = Color(0xFF1DA37D), error = Color(0xFFD11D2F), toastError = Color(0xFF511511) From ac421d6fff6563f5862a2771dff6563170909b3d Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 1 Jul 2025 17:16:37 +0300 Subject: [PATCH 69/80] CheckRadio error color --- .../main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index f9a14da2f..ffa6c96bd 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -113,11 +113,11 @@ val POLightColorPalette = POColors( checkRadio = CheckRadio( iconDefault = Color(0xFFFFFFFF), iconActive = Color(0xFFFFFFFF), - iconError = Color(0xFFBE011B), + iconError = Color(0xFFF03030), iconDisabled = Color(0xFFC0C3C8), borderDefault = Color(0xFFC0C3C8), borderActive = Color(0xFF000000), - borderError = Color(0xFFBE011B), + borderError = Color(0xFFF03030), borderDisabled = Color(0xFFE2E2E2), surfaceDefault = Color(0xFFFFFFFF), surfaceActive = Color(0xFF000000), From a8dfea4e1ead7ad2960f3c7e26f458b856559b62 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 1 Jul 2025 17:55:53 +0300 Subject: [PATCH 70/80] Update 'darkoutRipple' color --- .../main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt index ffa6c96bd..53a166706 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/theme/Colors.kt @@ -138,7 +138,7 @@ val POLightColorPalette = POColors( default = Color(0xFFFFFFFF), neutral = Color(0xFFFAFAFA), darkout = Color(0x0F121314), - darkoutRipple = Color(0x0F888989), + darkoutRipple = Color(0x0F59595A), success = Color(0xFFBEFAE9), error = Color(0xFFFFC2C8), toastError = Color(0xFFFDE3DE) @@ -199,7 +199,7 @@ val PODarkColorPalette = POColors( default = Color(0xFF26292F), neutral = Color(0xFF2A2D34), darkout = Color(0x0FF6F8FB), - darkoutRipple = Color(0x0F7B7C7D), + darkoutRipple = Color(0x0FACADAF), success = Color(0xFF1DA37D), error = Color(0xFFD11D2F), toastError = Color(0xFF511511) From 66f7cad0c8734546442d8b3f8cf07941ab3cceb6 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 1 Jul 2025 18:15:35 +0300 Subject: [PATCH 71/80] PORadioField --- .../component/field/radio/PORadioField.kt | 237 ++++++++++++++++++ .../napm/v2/NativeAlternativePaymentScreen.kt | 33 +-- 2 files changed, 250 insertions(+), 20 deletions(-) create mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt new file mode 100644 index 000000000..8402874b3 --- /dev/null +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt @@ -0,0 +1,237 @@ +package com.processout.sdk.ui.core.component.field.radio + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.material3.ripple +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.unit.dp +import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi +import com.processout.sdk.ui.core.component.POBorderStroke +import com.processout.sdk.ui.core.component.POMessageBox +import com.processout.sdk.ui.core.component.POText +import com.processout.sdk.ui.core.component.POText.measuredPaddingTop +import com.processout.sdk.ui.core.component.field.radio.PORadioField.optionStateStyle +import com.processout.sdk.ui.core.component.field.radio.PORadioField.radioButtonStyle +import com.processout.sdk.ui.core.component.field.radio.PORadioField.stateStyle +import com.processout.sdk.ui.core.state.POAvailableValue +import com.processout.sdk.ui.core.state.POImmutableList +import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors +import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions +import com.processout.sdk.ui.core.theme.ProcessOutTheme.shapes +import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing +import com.processout.sdk.ui.core.theme.ProcessOutTheme.typography + +/** @suppress */ +@ProcessOutInternalApi +@Composable +fun PORadioField( + value: TextFieldValue, + onValueChange: (TextFieldValue) -> Unit, + availableValues: POImmutableList, + modifier: Modifier = Modifier, + fieldStyle: PORadioField.Style = PORadioField.default, + descriptionStyle: POMessageBox.Style = POMessageBox.error2, + title: String? = null, + description: String? = null, + isError: Boolean = false +) { + Column(modifier = modifier) { + val stateStyle = stateStyle( + style = fieldStyle, + isSelected = value.text.isNotBlank(), + isError = isError + ) + if (!title.isNullOrBlank()) { + POText( + text = title, + modifier = Modifier.padding(bottom = spacing.space12), + color = stateStyle.title.color, + style = stateStyle.title.textStyle + ) + } + Column( + modifier = Modifier + .fillMaxWidth() + .border( + width = stateStyle.border.width, + color = stateStyle.border.color, + shape = stateStyle.shape + ) + .padding(spacing.space4), + verticalArrangement = Arrangement.spacedBy(spacing.space2) + ) { + availableValues.elements.forEach { availableValue -> + val isSelected = availableValue.value == value.text + val optionStateStyle = optionStateStyle( + style = fieldStyle, + isSelected = isSelected, + isError = isError + ) + val onClick = { onValueChange(TextFieldValue(text = availableValue.value)) } + val interactionSource = remember { MutableInteractionSource() } + Row( + modifier = Modifier + .fillMaxWidth() + .requiredHeightIn(min = dimensions.formComponentMinHeight) + .clip(shape = shapes.roundedCorners4) + .clickable( + onClick = onClick, + interactionSource = interactionSource, + indication = optionStateStyle.rowRippleColor?.let { ripple(color = it) } + ) + .background(optionStateStyle.rowBackgroundColor) + .padding(start = spacing.space12) + ) { + PORadioButton( + selected = isSelected, + onClick = onClick, + radioButtonSize = 16.dp, + style = fieldStyle.radioButtonStyle(), + isError = isError + ) + POText( + text = availableValue.text, + modifier = Modifier.padding( + start = spacing.space10, + top = measuredPaddingTop( + textStyle = optionStateStyle.option.textStyle, + componentHeight = dimensions.formComponentMinHeight + ) + ), + color = optionStateStyle.option.color, + style = optionStateStyle.option.textStyle + ) + } + } + } + POMessageBox( + text = description, + modifier = Modifier.padding(top = spacing.space12), + style = descriptionStyle + ) + } +} + +/** @suppress */ +@ProcessOutInternalApi +object PORadioField { + + @Immutable + data class Style( + val normal: StateStyle, + val selected: StateStyle, + val error: StateStyle, + val disabled: StateStyle + ) + + @Immutable + data class StateStyle( + val title: POText.Style, + val option: POText.Style, + val radioButtonColor: Color, + val rowBackgroundColor: Color, + val rowRippleColor: Color?, + val shape: Shape, + val border: POBorderStroke + ) + + val default: Style + @Composable get() = Style( + normal = StateStyle( + title = POText.Style( + color = colors.text.primary, + textStyle = typography.s16(FontWeight.Medium) + ), + option = POText.Style( + color = colors.text.secondary, + textStyle = typography.s15(FontWeight.Medium) + ), + radioButtonColor = PORadioButton.default2.normalColor, + rowBackgroundColor = colors.surface.default, + rowRippleColor = colors.surface.darkoutRipple, + shape = shapes.roundedCorners6, + border = POBorderStroke(width = 1.5.dp, color = colors.input.borderDefault2) + ), + selected = StateStyle( + title = POText.Style( + color = colors.text.primary, + textStyle = typography.s16(FontWeight.Medium) + ), + option = POText.Style( + color = colors.text.primary, + textStyle = typography.s15(FontWeight.Medium) + ), + radioButtonColor = PORadioButton.default2.selectedColor, + rowBackgroundColor = colors.surface.darkout, + rowRippleColor = null, + shape = shapes.roundedCorners6, + border = POBorderStroke(width = 1.5.dp, color = colors.input.borderDefault2) + ), + error = StateStyle( + title = POText.Style( + color = colors.text.error, + textStyle = typography.s16(FontWeight.Medium) + ), + option = POText.Style( + color = colors.text.secondary, + textStyle = typography.s15(FontWeight.Medium) + ), + radioButtonColor = PORadioButton.default2.errorColor, + rowBackgroundColor = colors.surface.default, + rowRippleColor = colors.surface.darkoutRipple, + shape = shapes.roundedCorners6, + border = POBorderStroke(width = 1.5.dp, color = colors.input.borderError) + ), + disabled = StateStyle( + title = POText.Style( + color = colors.text.primary, + textStyle = typography.s16(FontWeight.Medium) + ), + option = POText.Style( + color = colors.text.disabled, + textStyle = typography.s15(FontWeight.Medium) + ), + radioButtonColor = PORadioButton.default2.disabledColor, + rowBackgroundColor = colors.surface.default, + rowRippleColor = null, + shape = shapes.roundedCorners6, + border = POBorderStroke(width = 1.5.dp, color = colors.input.borderDefault2) + ) + ) + + fun stateStyle( + style: Style, + isSelected: Boolean, + isError: Boolean + ): StateStyle = + if (isError) style.error + else if (isSelected) style.selected + else style.normal + + fun optionStateStyle( + style: Style, + isSelected: Boolean, + isError: Boolean + ): StateStyle = + if (isSelected) style.selected + else if (isError) style.error + else style.normal + + fun Style.radioButtonStyle() = PORadioButton.Style( + normalColor = normal.radioButtonColor, + selectedColor = selected.radioButtonColor, + errorColor = error.radioButtonColor, + disabledColor = disabled.radioButtonColor + ) +} diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt index 0c649169a..18cecaf4d 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt @@ -36,7 +36,6 @@ import coil.compose.AsyncImage import com.processout.sdk.ui.R import com.processout.sdk.ui.core.component.* import com.processout.sdk.ui.core.component.field.POField -import com.processout.sdk.ui.core.component.field.POFieldLabels import com.processout.sdk.ui.core.component.field.checkbox.POCheckbox import com.processout.sdk.ui.core.component.field.checkbox.POCheckbox2 import com.processout.sdk.ui.core.component.field.code.POCodeField @@ -44,8 +43,7 @@ import com.processout.sdk.ui.core.component.field.code.POCodeField2 import com.processout.sdk.ui.core.component.field.dropdown.PODropdownField import com.processout.sdk.ui.core.component.field.dropdown.PODropdownField2 import com.processout.sdk.ui.core.component.field.phone.POPhoneNumberField -import com.processout.sdk.ui.core.component.field.radio.POLabeledRadioField -import com.processout.sdk.ui.core.component.field.radio.PORadioGroup +import com.processout.sdk.ui.core.component.field.radio.PORadioField import com.processout.sdk.ui.core.component.field.text.POTextField2 import com.processout.sdk.ui.core.state.POActionState import com.processout.sdk.ui.core.state.POImmutableList @@ -164,10 +162,6 @@ private fun UserInput( verticalArrangement = Arrangement.spacedBy(ProcessOutTheme.spacing.extraLarge) ) { val lifecycleEvent = rememberLifecycleEvent() - val labelsStyle = POFieldLabels.Style( - title = style.label, - description = style.errorMessage - ) val isPrimaryActionEnabled = with(state.primaryAction) { enabled && !loading } state.fields.elements.forEach { field -> when (field) { @@ -194,8 +188,9 @@ private fun UserInput( is RadioField -> RadioField( state = field.state, onEvent = onEvent, - radioGroupStyle = style.radioGroup, - labelsStyle = labelsStyle + fieldStyle = style.radioField, + descriptionStyle = style.errorMessageBox, + modifier = Modifier.fillMaxWidth() ) is DropdownField -> DropdownField( state = field.state, @@ -339,11 +334,11 @@ private fun CodeField( private fun RadioField( state: FieldState, onEvent: (NativeAlternativePaymentEvent) -> Unit, - radioGroupStyle: PORadioGroup.Style, - labelsStyle: POFieldLabels.Style, + fieldStyle: PORadioField.Style, + descriptionStyle: POMessageBox.Style, modifier: Modifier = Modifier ) { - POLabeledRadioField( + PORadioField( value = state.value, onValueChange = { onEvent( @@ -354,11 +349,11 @@ private fun RadioField( ) }, availableValues = state.availableValues ?: POImmutableList(emptyList()), - title = state.label ?: String(), - description = state.description, modifier = modifier, - radioGroupStyle = radioGroupStyle, - labelsStyle = labelsStyle, + fieldStyle = fieldStyle, + descriptionStyle = descriptionStyle, + title = state.label, + description = state.description, isError = state.isError ) } @@ -717,7 +712,7 @@ internal object NativeAlternativePaymentScreen { val label: POText.Style, val field: POField.Style, val codeField: POField.Style, - val radioGroup: PORadioGroup.Style, + val radioField: PORadioField.Style, val checkbox: POCheckbox.Style, val dropdownMenu: PODropdownField.MenuStyle, val actionsContainer: POActionsContainer.Style, @@ -750,9 +745,7 @@ internal object NativeAlternativePaymentScreen { codeField = custom?.codeField?.let { POField.custom(style = it) } ?: POCodeField.default2, - radioGroup = custom?.radioButton?.let { - PORadioGroup.custom(style = it) - } ?: PORadioGroup.default2, + radioField = PORadioField.default, // TODO(v2): map custom style checkbox = custom?.checkbox?.let { POCheckbox.custom(style = it) } ?: POCheckbox.default2, From bf2527e1fe909c7b4111b2feb0468cb8d375eccf Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 1 Jul 2025 18:37:13 +0300 Subject: [PATCH 72/80] val rowHeight --- .../sdk/ui/core/component/field/radio/PORadioField.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt index 8402874b3..8e8648fda 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt @@ -80,10 +80,11 @@ fun PORadioField( ) val onClick = { onValueChange(TextFieldValue(text = availableValue.value)) } val interactionSource = remember { MutableInteractionSource() } + val rowHeight = dimensions.formComponentMinHeight Row( modifier = Modifier .fillMaxWidth() - .requiredHeightIn(min = dimensions.formComponentMinHeight) + .requiredHeightIn(min = rowHeight) .clip(shape = shapes.roundedCorners4) .clickable( onClick = onClick, @@ -106,7 +107,7 @@ fun PORadioField( start = spacing.space10, top = measuredPaddingTop( textStyle = optionStateStyle.option.textStyle, - componentHeight = dimensions.formComponentMinHeight + componentHeight = rowHeight ) ), color = optionStateStyle.option.color, From da5c8a825cc3f8542afc56d058b5767fad975186 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 1 Jul 2025 18:55:36 +0300 Subject: [PATCH 73/80] Better multi-line support in PORadioField --- .../sdk/ui/core/component/field/radio/PORadioField.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt index 8e8648fda..9412e1a1f 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt @@ -80,11 +80,11 @@ fun PORadioField( ) val onClick = { onValueChange(TextFieldValue(text = availableValue.value)) } val interactionSource = remember { MutableInteractionSource() } - val rowHeight = dimensions.formComponentMinHeight + val rowMinHeight = dimensions.formComponentMinHeight Row( modifier = Modifier .fillMaxWidth() - .requiredHeightIn(min = rowHeight) + .requiredHeightIn(min = rowMinHeight) .clip(shape = shapes.roundedCorners4) .clickable( onClick = onClick, @@ -107,8 +107,9 @@ fun PORadioField( start = spacing.space10, top = measuredPaddingTop( textStyle = optionStateStyle.option.textStyle, - componentHeight = rowHeight - ) + componentHeight = rowMinHeight + ), + bottom = spacing.space10 ), color = optionStateStyle.option.color, style = optionStateStyle.option.textStyle From 3f66a17b31850689abe9ebe086bda3fee06d5408 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 1 Jul 2025 19:49:42 +0300 Subject: [PATCH 74/80] POCheckboxField --- .../field/checkbox/{POCheckbox2.kt => POCheckboxField.kt} | 2 +- .../sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/{POCheckbox2.kt => POCheckboxField.kt} (98%) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox2.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckboxField.kt similarity index 98% rename from ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox2.kt rename to ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckboxField.kt index 3c379679c..bbcd007a4 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox2.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckboxField.kt @@ -12,7 +12,7 @@ import com.processout.sdk.ui.core.component.POMessageBox /** @suppress */ @ProcessOutInternalApi @Composable -fun POCheckbox2( +fun POCheckboxField( text: String, checked: Boolean, onCheckedChange: (Boolean) -> Unit, diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt index 18cecaf4d..a29955590 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt @@ -37,7 +37,7 @@ import com.processout.sdk.ui.R import com.processout.sdk.ui.core.component.* import com.processout.sdk.ui.core.component.field.POField import com.processout.sdk.ui.core.component.field.checkbox.POCheckbox -import com.processout.sdk.ui.core.component.field.checkbox.POCheckbox2 +import com.processout.sdk.ui.core.component.field.checkbox.POCheckboxField import com.processout.sdk.ui.core.component.field.code.POCodeField import com.processout.sdk.ui.core.component.field.code.POCodeField2 import com.processout.sdk.ui.core.component.field.dropdown.PODropdownField @@ -406,7 +406,7 @@ private fun CheckboxField( descriptionStyle: POMessageBox.Style, modifier: Modifier = Modifier ) { - POCheckbox2( + POCheckboxField( text = state.label ?: String(), checked = state.value.text.toBooleanStrictOrNull() ?: false, onCheckedChange = { From 815433b6e0408c9cf4258c782b5d5e2bba47e3bd Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 1 Jul 2025 21:49:22 +0300 Subject: [PATCH 75/80] PORadioFieldStyle and config --- .../component/field/radio/PORadioGroup.kt | 31 +++++--- .../sdk/ui/core/style/PORadioFieldStyle.kt | 26 +++++++ .../ui/napm/NativeAlternativePaymentScreen.kt | 2 +- ...PONativeAlternativePaymentConfiguration.kt | 78 ++++++++++++++++++- 4 files changed, 124 insertions(+), 13 deletions(-) create mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/PORadioFieldStyle.kt diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioGroup.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioGroup.kt index 27daaa5ff..9986579dd 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioGroup.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioGroup.kt @@ -20,6 +20,8 @@ import com.processout.sdk.ui.core.state.POAvailableValue import com.processout.sdk.ui.core.state.POImmutableList import com.processout.sdk.ui.core.style.PORadioButtonStateStyle import com.processout.sdk.ui.core.style.PORadioButtonStyle +import com.processout.sdk.ui.core.style.PORadioFieldStateStyle +import com.processout.sdk.ui.core.style.PORadioFieldStyle import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions import com.processout.sdk.ui.core.theme.ProcessOutTheme.typography @@ -150,15 +152,12 @@ object PORadioGroup { ) @Composable - fun custom(style: PORadioButtonStyle): Style { - val normal = style.normal.toStateStyle() - return Style( - normal = normal, - selected = style.selected.toStateStyle(), - error = style.error.toStateStyle(), - disabled = style.disabled?.toStateStyle() ?: normal - ) - } + fun custom(style: PORadioButtonStyle) = Style( + normal = style.normal.toStateStyle(), + selected = style.selected.toStateStyle(), + error = style.error.toStateStyle(), + disabled = style.disabled?.toStateStyle() ?: default.disabled + ) @Composable private fun PORadioButtonStateStyle.toStateStyle() = StateStyle( @@ -166,6 +165,20 @@ object PORadioGroup { text = POText.custom(style = text) ) + @Composable + fun custom(style: PORadioFieldStyle) = Style( + normal = style.normal.toStateStyle(), + selected = style.selected.toStateStyle(), + error = style.error.toStateStyle(), + disabled = style.disabled?.toStateStyle() ?: default.disabled + ) + + @Composable + private fun PORadioFieldStateStyle.toStateStyle() = StateStyle( + buttonColor = colorResource(id = radioButtonColorResId), + text = POText.custom(style = option) + ) + fun Style.toRadioButtonStyle() = PORadioButton.Style( normalColor = normal.buttonColor, selectedColor = selected.buttonColor, diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/PORadioFieldStyle.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/PORadioFieldStyle.kt new file mode 100644 index 000000000..85c36be18 --- /dev/null +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/PORadioFieldStyle.kt @@ -0,0 +1,26 @@ +package com.processout.sdk.ui.core.style + +import android.os.Parcelable +import androidx.annotation.ColorRes +import kotlinx.parcelize.Parcelize + +@Parcelize +data class PORadioFieldStyle( + val normal: PORadioFieldStateStyle, + val selected: PORadioFieldStateStyle, + val error: PORadioFieldStateStyle, + val disabled: PORadioFieldStateStyle? = null +) : Parcelable + +@Parcelize +data class PORadioFieldStateStyle( + val title: POTextStyle, + val option: POTextStyle, + @ColorRes + val radioButtonColorResId: Int, + @ColorRes + val rowBackgroundColorResId: Int, + @ColorRes + val rowRippleColorResId: Int?, + val border: POBorderStyle +) : Parcelable diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt index 05780ad18..532ccec9a 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentScreen.kt @@ -643,7 +643,7 @@ internal object NativeAlternativePaymentScreen { codeField = custom?.codeField?.let { POField.custom(style = it) } ?: POCodeField.default, - radioGroup = custom?.radioButton?.let { + radioGroup = custom?.radioField?.let { PORadioGroup.custom(style = it) } ?: PORadioGroup.default, dropdownMenu = custom?.dropdownMenu?.let { diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt index 9b9b316fc..ad4195774 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt @@ -294,7 +294,7 @@ data class PONativeAlternativePaymentConfiguration( * @param[label] Field label style. * @param[field] Field style. * @param[codeField] Code field style. - * @param[radioButton] Radio button style. + * @param[radioField] Radio field style. * @param[checkbox] Checkbox style. * @param[dropdownMenu] Dropdown menu style. * @param[actionsContainer] Style of action buttons and their container. @@ -316,7 +316,7 @@ data class PONativeAlternativePaymentConfiguration( val label: POTextStyle? = null, val field: POFieldStyle? = null, val codeField: POFieldStyle? = null, - val radioButton: PORadioButtonStyle? = null, + val radioField: PORadioFieldStyle? = null, val checkbox: POCheckboxStyle? = null, val dropdownMenu: PODropdownMenuStyle? = null, val actionsContainer: POActionsContainerStyle? = null, @@ -336,7 +336,79 @@ data class PONativeAlternativePaymentConfiguration( val dividerColorResId: Int? = null, @ColorRes val dragHandleColorResId: Int? = null - ) : Parcelable + ) : Parcelable { + + /** + * Allows to customize the look and feel. + * + * @param[title] Title style. + * @param[label] Field label style. + * @param[field] Field style. + * @param[codeField] Code field style. + * @param[radioButton] Radio button style. __Deprecated__: not used. + * @param[checkbox] Checkbox style. + * @param[dropdownMenu] Dropdown menu style. + * @param[actionsContainer] Style of action buttons and their container. + * @param[dialog] Dialog style. + * @param[background] Background style. + * @param[message] Message style. + * @param[errorMessage] Error message style. + * @param[errorMessageBox] Error message box style. + * @param[successMessage] Success message style. + * @param[successImageResId] Success image drawable resource ID. + * @param[progressIndicatorColorResId] Color resource ID for progress indicator. + * @param[controlsTintColorResId] Color resource ID for tint that applies to generic components (e.g. selectable text). + * @param[dividerColorResId] Color resource ID for title divider. + * @param[dragHandleColorResId] Color resource ID for bottom sheet drag handle. + */ + @Deprecated(message = "Use alternative constructor.") + constructor( + title: POTextStyle? = null, + label: POTextStyle? = null, + field: POFieldStyle? = null, + codeField: POFieldStyle? = null, + radioButton: PORadioButtonStyle? = null, + checkbox: POCheckboxStyle? = null, + dropdownMenu: PODropdownMenuStyle? = null, + actionsContainer: POActionsContainerStyle? = null, + dialog: PODialogStyle? = null, + background: POBackgroundStyle? = null, + message: POTextStyle? = null, + errorMessage: POTextStyle? = null, + errorMessageBox: POMessageBoxStyle? = null, + successMessage: POTextStyle? = null, + @DrawableRes + successImageResId: Int? = null, + @ColorRes + progressIndicatorColorResId: Int? = null, + @ColorRes + controlsTintColorResId: Int? = null, + @ColorRes + dividerColorResId: Int? = null, + @ColorRes + dragHandleColorResId: Int? = null + ) : this( + title = title, + label = label, + field = field, + codeField = codeField, + radioField = null, + checkbox = checkbox, + dropdownMenu = dropdownMenu, + actionsContainer = actionsContainer, + dialog = dialog, + background = background, + message = message, + errorMessage = errorMessage, + errorMessageBox = errorMessageBox, + successMessage = successMessage, + successImageResId = successImageResId, + progressIndicatorColorResId = progressIndicatorColorResId, + controlsTintColorResId = controlsTintColorResId, + dividerColorResId = dividerColorResId, + dragHandleColorResId = dragHandleColorResId + ) + } } private fun SecondaryAction.toCancelButton(): CancelButton = From 9a4dea2eb628e4618e46344e0576cfa5ec2f1d26 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 1 Jul 2025 22:02:27 +0300 Subject: [PATCH 76/80] PORadioField.custom(style) --- .../component/field/radio/PORadioField.kt | 26 +++++++++++++++++++ .../napm/v2/NativeAlternativePaymentScreen.kt | 4 ++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt index 9412e1a1f..520d9afab 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ripple import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable @@ -13,6 +14,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.res.colorResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp @@ -26,6 +28,8 @@ import com.processout.sdk.ui.core.component.field.radio.PORadioField.radioButton import com.processout.sdk.ui.core.component.field.radio.PORadioField.stateStyle import com.processout.sdk.ui.core.state.POAvailableValue import com.processout.sdk.ui.core.state.POImmutableList +import com.processout.sdk.ui.core.style.PORadioFieldStateStyle +import com.processout.sdk.ui.core.style.PORadioFieldStyle import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions import com.processout.sdk.ui.core.theme.ProcessOutTheme.shapes @@ -212,6 +216,28 @@ object PORadioField { ) ) + @Composable + fun custom(style: PORadioFieldStyle) = Style( + normal = style.normal.toStateStyle(), + selected = style.selected.toStateStyle(), + error = style.error.toStateStyle(), + disabled = style.disabled?.toStateStyle() ?: default.disabled + ) + + @Composable + private fun PORadioFieldStateStyle.toStateStyle() = StateStyle( + title = POText.custom(style = title), + option = POText.custom(style = option), + radioButtonColor = colorResource(id = radioButtonColorResId), + rowBackgroundColor = colorResource(id = rowBackgroundColorResId), + rowRippleColor = rowRippleColorResId?.let { colorResource(id = it) }, + shape = RoundedCornerShape(size = border.radiusDp.dp), + border = POBorderStroke( + width = border.widthDp.dp, + color = colorResource(id = border.colorResId) + ) + ) + fun stateStyle( style: Style, isSelected: Boolean, diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt index a29955590..5df874a64 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt @@ -745,7 +745,9 @@ internal object NativeAlternativePaymentScreen { codeField = custom?.codeField?.let { POField.custom(style = it) } ?: POCodeField.default2, - radioField = PORadioField.default, // TODO(v2): map custom style + radioField = custom?.radioField?.let { + PORadioField.custom(style = it) + } ?: PORadioField.default, checkbox = custom?.checkbox?.let { POCheckbox.custom(style = it) } ?: POCheckbox.default2, From 925bb54f648299bdb23cc892b176802a57d4567c Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 1 Jul 2025 22:10:25 +0300 Subject: [PATCH 77/80] Delete POLabeledCheckboxField --- .../field/checkbox/POLabeledCheckboxField.kt | 45 ------------------- 1 file changed, 45 deletions(-) delete mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POLabeledCheckboxField.kt diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POLabeledCheckboxField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POLabeledCheckboxField.kt deleted file mode 100644 index 6cc3ae6a6..000000000 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POLabeledCheckboxField.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.processout.sdk.ui.core.component.field.checkbox - -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi -import com.processout.sdk.ui.core.component.field.LabeledFieldLayout -import com.processout.sdk.ui.core.component.field.POFieldLabels - -/** @suppress */ -@ProcessOutInternalApi -@Composable -fun POLabeledCheckboxField( - text: String, - checked: Boolean, - onCheckedChange: (Boolean) -> Unit, - title: String?, - description: String?, - modifier: Modifier = Modifier, - checkboxStyle: POCheckbox.Style = POCheckbox.default, - labelsStyle: POFieldLabels.Style = POFieldLabels.default, - enabled: Boolean = true, - isError: Boolean = false, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } -) { - LabeledFieldLayout( - title = title, - description = description, - style = labelsStyle - ) { - POCheckbox( - text = text, - checked = checked, - onCheckedChange = onCheckedChange, - modifier = modifier, - minHeight = 30.dp, - style = checkboxStyle, - enabled = enabled, - isError = isError, - interactionSource = interactionSource - ) - } -} From 97a4c408183a6c79420280a48d4e5058aa69b7f9 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 1 Jul 2025 22:17:10 +0300 Subject: [PATCH 78/80] POCheckboxField modifier --- .../sdk/ui/core/component/field/checkbox/POCheckboxField.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckboxField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckboxField.kt index bbcd007a4..504c7a857 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckboxField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckboxField.kt @@ -24,12 +24,11 @@ fun POCheckboxField( description: String? = null, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } ) { - Column { + Column(modifier = modifier) { POCheckbox( text = text, checked = checked, onCheckedChange = onCheckedChange, - modifier = modifier, minHeight = 40.dp, checkboxSize = 16.dp, style = checkboxStyle, From febc2fd9822f0c3c826aabedcb48c03c09671e25 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 2 Jul 2025 14:13:00 +0300 Subject: [PATCH 79/80] internal modifier --- .../sdk/ui/core/component/field/radio/PORadioField.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt index 520d9afab..ecac0ddfe 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/radio/PORadioField.kt @@ -238,7 +238,7 @@ object PORadioField { ) ) - fun stateStyle( + internal fun stateStyle( style: Style, isSelected: Boolean, isError: Boolean @@ -247,7 +247,7 @@ object PORadioField { else if (isSelected) style.selected else style.normal - fun optionStateStyle( + internal fun optionStateStyle( style: Style, isSelected: Boolean, isError: Boolean @@ -256,7 +256,7 @@ object PORadioField { else if (isError) style.error else style.normal - fun Style.radioButtonStyle() = PORadioButton.Style( + internal fun Style.radioButtonStyle() = PORadioButton.Style( normalColor = normal.radioButtonColor, selectedColor = selected.radioButtonColor, errorColor = error.radioButtonColor, From aed5d8f54fa4acc0f3f652f4d00b2b0ffae70485 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 2 Jul 2025 14:54:57 +0300 Subject: [PATCH 80/80] Added ripple to checkbox --- .../component/field/checkbox/POCheckbox.kt | 103 ++++++++++-------- .../field/checkbox/POCheckboxField.kt | 2 + .../sdk/ui/core/style/POCheckboxStyle.kt | 4 +- 3 files changed, 63 insertions(+), 46 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt index 7b80152c2..ad64da76e 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckbox.kt @@ -3,12 +3,15 @@ package com.processout.sdk.ui.core.component.field.checkbox import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CornerBasedShape import androidx.compose.material3.Checkbox import androidx.compose.material3.CheckboxColors +import androidx.compose.material3.ripple import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.scale import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.colorResource @@ -20,12 +23,13 @@ import com.processout.sdk.ui.core.component.POText import com.processout.sdk.ui.core.component.POText.measuredPaddingTop import com.processout.sdk.ui.core.component.field.checkbox.POCheckbox.MaterialCheckboxSize import com.processout.sdk.ui.core.component.field.checkbox.POCheckbox.colors -import com.processout.sdk.ui.core.component.field.checkbox.POCheckbox.textStyle +import com.processout.sdk.ui.core.component.field.checkbox.POCheckbox.stateStyle import com.processout.sdk.ui.core.style.POCheckboxStateStyle import com.processout.sdk.ui.core.style.POCheckboxStyle import com.processout.sdk.ui.core.style.POCheckmarkStyle import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions +import com.processout.sdk.ui.core.theme.ProcessOutTheme.shapes import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing import com.processout.sdk.ui.core.theme.ProcessOutTheme.typography @@ -39,15 +43,22 @@ fun POCheckbox( modifier: Modifier = Modifier, minHeight: Dp = dimensions.formComponentMinHeight, checkboxSize: Dp = MaterialCheckboxSize, + rowShape: CornerBasedShape = shapes.roundedCorners4, style: POCheckbox.Style = POCheckbox.default, enabled: Boolean = true, isError: Boolean = false, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } ) { + val stateStyle = style.stateStyle( + checked = checked, + enabled = enabled, + isError = isError + ) Row( modifier = modifier .fillMaxWidth() .requiredHeightIn(min = minHeight) + .clip(shape = rowShape) .clickable( onClick = { if (enabled) { @@ -55,7 +66,7 @@ fun POCheckbox( } }, interactionSource = interactionSource, - indication = null + indication = stateStyle.rippleColor?.let { ripple(color = it) } ) ) { val checkboxScale = checkboxSize.value / MaterialCheckboxSize.value @@ -67,29 +78,23 @@ fun POCheckbox( .requiredWidth(checkboxSize) .requiredHeight(minHeight), enabled = enabled, - colors = colors( - style = style, + colors = style.colors( enabled = enabled, isError = isError ) ) - val textStyle = textStyle( - style = style, - checked = checked, - enabled = enabled, - isError = isError - ) POText( text = text, modifier = Modifier.padding( start = spacing.space10, top = measuredPaddingTop( - textStyle = textStyle.textStyle, + textStyle = stateStyle.text.textStyle, componentHeight = minHeight - ) + ), + bottom = spacing.space10 ), - color = textStyle.color, - style = textStyle.textStyle + color = stateStyle.text.color, + style = stateStyle.text.textStyle ) } } @@ -109,7 +114,8 @@ object POCheckbox { @Immutable data class StateStyle( val checkmark: CheckmarkStyle, - val text: POText.Style + val text: POText.Style, + val rippleColor: Color? ) @Immutable @@ -127,7 +133,8 @@ object POCheckbox { borderColor = colors.input.borderDefault, backgroundColor = colors.surface.default ), - text = POText.label1 + text = POText.label1, + rippleColor = colors.surface.darkoutRipple ), selected = StateStyle( checkmark = CheckmarkStyle( @@ -135,7 +142,8 @@ object POCheckbox { borderColor = colors.button.primaryBackgroundDefault, backgroundColor = colors.button.primaryBackgroundDefault ), - text = POText.label1 + text = POText.label1, + rippleColor = colors.surface.darkoutRipple ), error = StateStyle( checkmark = CheckmarkStyle( @@ -143,7 +151,8 @@ object POCheckbox { borderColor = colors.input.borderError, backgroundColor = colors.surface.default ), - text = POText.label1 + text = POText.label1, + rippleColor = colors.surface.darkoutRipple ), disabled = StateStyle( checkmark = CheckmarkStyle( @@ -154,7 +163,8 @@ object POCheckbox { text = POText.Style( color = colors.text.disabled, textStyle = typography.label1 - ) + ), + rippleColor = null ) ) @@ -169,7 +179,8 @@ object POCheckbox { text = POText.Style( color = colors.text.secondary, textStyle = typography.s15(FontWeight.Medium) - ) + ), + rippleColor = colors.surface.darkoutRipple ), selected = StateStyle( checkmark = CheckmarkStyle( @@ -180,7 +191,8 @@ object POCheckbox { text = POText.Style( color = colors.text.secondary, textStyle = typography.s15(FontWeight.Medium) - ) + ), + rippleColor = colors.surface.darkoutRipple ), error = StateStyle( checkmark = CheckmarkStyle( @@ -191,7 +203,8 @@ object POCheckbox { text = POText.Style( color = colors.text.secondary, textStyle = typography.s15(FontWeight.Medium) - ) + ), + rippleColor = colors.surface.darkoutRipple ), disabled = StateStyle( checkmark = CheckmarkStyle( @@ -202,7 +215,8 @@ object POCheckbox { text = POText.Style( color = colors.text.disabled, textStyle = typography.s15(FontWeight.Medium) - ) + ), + rippleColor = null ) ) @@ -217,7 +231,8 @@ object POCheckbox { @Composable private fun POCheckboxStateStyle.toStateStyle() = StateStyle( checkmark = checkmark.toCheckmarkStyle(), - text = POText.custom(style = text) + text = POText.custom(style = text), + rippleColor = rippleColorResId?.let { colorResource(id = it) } ) @Composable @@ -229,8 +244,7 @@ object POCheckbox { internal val MaterialCheckboxSize = 20.dp - internal fun colors( - style: Style, + internal fun Style.colors( enabled: Boolean, isError: Boolean ): CheckboxColors { @@ -241,22 +255,22 @@ object POCheckbox { val checkedBorderColor: Color val checkedBoxColor: Color if (isError) { - with(style.error.checkmark) { + with(error.checkmark) { uncheckedCheckmarkColor = this.color uncheckedBorderColor = this.borderColor uncheckedBoxColor = this.backgroundColor - checkedCheckmarkColor = if (enabled) this.color else style.disabled.checkmark.color + checkedCheckmarkColor = if (enabled) this.color else disabled.checkmark.color checkedBorderColor = this.borderColor checkedBoxColor = this.backgroundColor } } else { - with(style.normal.checkmark) { - uncheckedCheckmarkColor = if (enabled) this.color else style.disabled.checkmark.color + with(normal.checkmark) { + uncheckedCheckmarkColor = if (enabled) this.color else disabled.checkmark.color uncheckedBorderColor = this.borderColor uncheckedBoxColor = this.backgroundColor } - with(style.selected.checkmark) { - checkedCheckmarkColor = if (enabled) this.color else style.disabled.checkmark.color + with(selected.checkmark) { + checkedCheckmarkColor = if (enabled) this.color else disabled.checkmark.color checkedBorderColor = this.borderColor checkedBoxColor = this.backgroundColor } @@ -268,23 +282,22 @@ object POCheckbox { checkedCheckmarkColor = checkedCheckmarkColor, checkedBorderColor = checkedBorderColor, checkedBoxColor = checkedBoxColor, - disabledUncheckedBorderColor = style.disabled.checkmark.borderColor, - disabledUncheckedBoxColor = style.disabled.checkmark.backgroundColor, - disabledBorderColor = style.disabled.checkmark.borderColor, - disabledCheckedBoxColor = style.disabled.checkmark.backgroundColor, - disabledIndeterminateBorderColor = style.disabled.checkmark.borderColor, - disabledIndeterminateBoxColor = style.disabled.checkmark.backgroundColor + disabledUncheckedBorderColor = disabled.checkmark.borderColor, + disabledUncheckedBoxColor = disabled.checkmark.backgroundColor, + disabledBorderColor = disabled.checkmark.borderColor, + disabledCheckedBoxColor = disabled.checkmark.backgroundColor, + disabledIndeterminateBorderColor = disabled.checkmark.borderColor, + disabledIndeterminateBoxColor = disabled.checkmark.backgroundColor ) } - internal fun textStyle( - style: Style, + internal fun Style.stateStyle( checked: Boolean, enabled: Boolean, isError: Boolean - ): POText.Style = - if (!enabled) style.disabled.text - else if (isError) style.error.text - else if (checked) style.selected.text - else style.normal.text + ): StateStyle = + if (!enabled) disabled + else if (isError) error + else if (checked) selected + else normal } diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckboxField.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckboxField.kt index 504c7a857..01f5227d6 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckboxField.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/field/checkbox/POCheckboxField.kt @@ -8,6 +8,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi import com.processout.sdk.ui.core.component.POMessageBox +import com.processout.sdk.ui.core.theme.ProcessOutTheme.shapes /** @suppress */ @ProcessOutInternalApi @@ -31,6 +32,7 @@ fun POCheckboxField( onCheckedChange = onCheckedChange, minHeight = 40.dp, checkboxSize = 16.dp, + rowShape = shapes.roundedCorners6, style = checkboxStyle, enabled = enabled, isError = isError, diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POCheckboxStyle.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POCheckboxStyle.kt index ceea75dfa..19d3d8e8e 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POCheckboxStyle.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POCheckboxStyle.kt @@ -15,7 +15,9 @@ data class POCheckboxStyle( @Parcelize data class POCheckboxStateStyle( val checkmark: POCheckmarkStyle, - val text: POTextStyle + val text: POTextStyle, + @ColorRes + val rippleColorResId: Int? = null ) : Parcelable @Parcelize