From 3cc55f275de71b8c35df291b7a30398f3d3faca7 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Mon, 7 Jul 2025 20:48:26 +0300 Subject: [PATCH 01/10] POStepIcon --- .../ui/core/component/stepper/POStepIcon.kt | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt new file mode 100644 index 000000000..595372278 --- /dev/null +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt @@ -0,0 +1,166 @@ +package com.processout.sdk.ui.core.component.stepper + +import androidx.compose.animation.core.* +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.StrokeJoin +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.platform.LocalDensity +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.POBorderStroke + +/** @suppress */ +@ProcessOutInternalApi +@Composable +fun POStepIcon( + iconSize: Dp = 24.dp, + padding: Dp = 6.dp, + style: POStepIcon.Style = POStepIcon.active +) { + val density = LocalDensity.current + val iconRadiusPx = with(density) { iconSize.toPx() / 2 } + val borderWidth = style.border?.width ?: 0.dp + val borderWidthPx = with(density) { borderWidth.toPx() } + val haloWidth = style.halo?.width ?: 0.dp + val haloWidthPx = with(density) { haloWidth.toPx() } + val checkmarkWidth = style.checkmark?.width ?: 0.dp + val checkmarkWidthPx = with(density) { checkmarkWidth.toPx() } + + val infiniteTransition = rememberInfiniteTransition() + val animatedHaloWidthPx by infiniteTransition.animateFloat( + initialValue = 0f, + targetValue = haloWidthPx, + animationSpec = infiniteRepeatable( + animation = tween(durationMillis = 1000, easing = LinearEasing), + repeatMode = RepeatMode.Reverse + ) + ) + Canvas( + modifier = Modifier + .padding(padding) + .requiredSize(size = iconSize) + ) { + val center = Offset(x = size.width / 2, y = size.height / 2) + // Halo + if (style.halo != null) { + drawCircle( + color = style.halo.color, + radius = iconRadiusPx + animatedHaloWidthPx, + center = center + ) + } + // Icon + drawCircle( + color = style.backgroundColor, + radius = iconRadiusPx, + center = center + ) + // Border + if (style.border != null) { + drawCircle( + color = style.border.color, + radius = iconRadiusPx - borderWidthPx / 2, + center = center, + style = Stroke(width = borderWidthPx) + ) + } + // Checkmark + if (style.checkmark != null) { + val checkmarkPath = Path().apply { + val scale = 1.3f + val start = Offset( + x = center.x - size.minDimension * 0.15f * scale, + y = center.y + ) + val mid = Offset( + x = center.x - size.minDimension * 0.05f * scale, + y = center.y + size.minDimension * 0.15f * scale + ) + val end = Offset( + x = center.x + size.minDimension * 0.2f * scale, + y = center.y - size.minDimension * 0.15f * scale + ) + moveTo(start.x, start.y) + lineTo(mid.x, mid.y) + lineTo(end.x, end.y) + } + drawPath( + path = checkmarkPath, + color = style.checkmark.color, + style = Stroke( + width = checkmarkWidthPx, + cap = StrokeCap.Round, + join = StrokeJoin.Round + ) + ) + } + } +} + +/** @suppress */ +@ProcessOutInternalApi +object POStepIcon { + + data class Style( + val backgroundColor: Color, + val border: POBorderStroke?, + val halo: Halo?, + val checkmark: Checkmark? + ) + + data class Halo( + val width: Dp, + val color: Color + ) + + data class Checkmark( + val width: Dp, + val color: Color + ) + + val pending: Style + @Composable get() = Style( + backgroundColor = Color.Transparent, + border = POBorderStroke( + width = 1.5.dp, + color = Color(0xFFA3A3A3) + ), + halo = null, + checkmark = null + ) + + val active: Style + @Composable get() = Style( + backgroundColor = Color.White, + border = POBorderStroke( + width = 1.5.dp, + color = Color(0xFFA3A3A3) + ), + halo = Halo( + width = 6.dp, + color = Color.Black.copy(alpha = 0.07f) + ), + checkmark = null + ) + + val completed: Style + @Composable get() = Style( + backgroundColor = Color(0xFF4CA259), + border = null, + halo = null, + checkmark = Checkmark( + width = 2.dp, + color = Color.White + ) + ) +} From 9de0c66852e90af13b81e6f55dc3272b94c7619a Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 8 Jul 2025 09:12:33 +0300 Subject: [PATCH 02/10] borderColor (rebase) --- .../processout/sdk/ui/core/component/stepper/POStepIcon.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt index 595372278..5e20f4ade 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt @@ -128,12 +128,14 @@ object POStepIcon { val color: Color ) + private val borderColor = Color(0xFFA3A3A3) + val pending: Style @Composable get() = Style( backgroundColor = Color.Transparent, border = POBorderStroke( width = 1.5.dp, - color = Color(0xFFA3A3A3) + color = borderColor ), halo = null, checkmark = null @@ -144,7 +146,7 @@ object POStepIcon { backgroundColor = Color.White, border = POBorderStroke( width = 1.5.dp, - color = Color(0xFFA3A3A3) + color = borderColor ), halo = Halo( width = 6.dp, From 0c899547c1b7d77c55f8baad8b88c513b6aa7d43 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 10 Jul 2025 15:19:51 +0300 Subject: [PATCH 03/10] tertiary text 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 53a166706..0fed484e3 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( data class Text( val primary: Color, val secondary: Color, + val tertiary: Color, val inverse: Color, val muted: Color, val placeholder: Color, @@ -92,6 +93,7 @@ val POLightColorPalette = POColors( text = Text( primary = Color(0xFF000000), secondary = Color(0xFF585A5F), + tertiary = Color(0xFF8A8D93), inverse = Color(0xFFFFFFFF), muted = Color(0xFF5B6576), placeholder = Color(0xFF707378), @@ -153,6 +155,7 @@ val PODarkColorPalette = POColors( text = Text( primary = Color(0xFFFFFFFF), secondary = Color(0xFFC0C3C8), + tertiary = Color(0xFF8A8D93), inverse = Color(0xFF000000), muted = Color(0xFFADB5BD), placeholder = Color(0xFFA7A9AF), From 032a8138eac648a91dc974a1b056f095ca95be02 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 10 Jul 2025 15:25:21 +0300 Subject: [PATCH 04/10] positive text 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 0fed484e3..e6bb12761 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 @@ -23,6 +23,7 @@ data class POColors( val secondary: Color, val tertiary: Color, val inverse: Color, + val positive: Color, val muted: Color, val placeholder: Color, val disabled: Color, @@ -95,6 +96,7 @@ val POLightColorPalette = POColors( secondary = Color(0xFF585A5F), tertiary = Color(0xFF8A8D93), inverse = Color(0xFFFFFFFF), + positive = Color(0xFF139947), muted = Color(0xFF5B6576), placeholder = Color(0xFF707378), disabled = Color(0xFFADB5BD), @@ -157,6 +159,7 @@ val PODarkColorPalette = POColors( secondary = Color(0xFFC0C3C8), tertiary = Color(0xFF8A8D93), inverse = Color(0xFF000000), + positive = Color(0xFF28DE6B), muted = Color(0xFFADB5BD), placeholder = Color(0xFFA7A9AF), disabled = Color(0xFF5B6576), From b07c882de52fe50a76a19a088d33e9494f271fab Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 10 Jul 2025 17:46:27 +0300 Subject: [PATCH 05/10] Updated POStepIcon --- .../sdk/ui/core/component/stepper/POStepIcon.kt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt index 5e20f4ade..d2c0537a6 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt @@ -41,7 +41,7 @@ fun POStepIcon( initialValue = 0f, targetValue = haloWidthPx, animationSpec = infiniteRepeatable( - animation = tween(durationMillis = 1000, easing = LinearEasing), + animation = tween(durationMillis = 800, easing = LinearEasing), repeatMode = RepeatMode.Reverse ) ) @@ -128,14 +128,15 @@ object POStepIcon { val color: Color ) - private val borderColor = Color(0xFFA3A3A3) + private val defaultBorderColor = Color(0xFFA3A3A3) + internal val defaultCompletedColor = Color(0xFF4CA259) val pending: Style @Composable get() = Style( backgroundColor = Color.Transparent, border = POBorderStroke( width = 1.5.dp, - color = borderColor + color = defaultBorderColor ), halo = null, checkmark = null @@ -146,7 +147,7 @@ object POStepIcon { backgroundColor = Color.White, border = POBorderStroke( width = 1.5.dp, - color = borderColor + color = defaultBorderColor ), halo = Halo( width = 6.dp, @@ -157,7 +158,7 @@ object POStepIcon { val completed: Style @Composable get() = Style( - backgroundColor = Color(0xFF4CA259), + backgroundColor = defaultCompletedColor, border = null, halo = null, checkmark = Checkmark( From f06375052d87e5ca6be80a8d1abd587f478a63aa Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 10 Jul 2025 22:13:17 +0300 Subject: [PATCH 06/10] Stepper --- .../sdk/ui/core/component/POStroke.kt | 29 ++++ .../ui/core/component/stepper/POStepIcon.kt | 25 ++++ .../ui/core/component/stepper/POStepper.kt | 132 +++++++++++++++++ .../component/stepper/POVerticalStepper.kt | 139 ++++++++++++++++++ .../sdk/ui/core/style/POStepperStyle.kt | 52 +++++++ .../sdk/ui/core/style/POStrokeStyle.kt | 13 ++ 6 files changed, 390 insertions(+) create mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POStroke.kt create mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepper.kt create mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POVerticalStepper.kt create mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POStepperStyle.kt create mode 100644 ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POStrokeStyle.kt diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POStroke.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POStroke.kt new file mode 100644 index 000000000..021388a41 --- /dev/null +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POStroke.kt @@ -0,0 +1,29 @@ +package com.processout.sdk.ui.core.component + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.colorResource +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.style.POStrokeStyle + +/** @suppress */ +@ProcessOutInternalApi +object POStroke { + + @Immutable + data class Style( + val width: Dp, + val color: Color, + val dashInterval: Dp? = null + ) + + @Composable + fun custom(style: POStrokeStyle) = Style( + width = style.widthDp.dp, + color = colorResource(id = style.colorResId), + dashInterval = style.dashIntervalDp?.dp + ) +} diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt index d2c0537a6..8c38dbb29 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt @@ -14,10 +14,12 @@ import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.StrokeJoin import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.colorResource 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.POBorderStroke +import com.processout.sdk.ui.core.style.POStepperStyle /** @suppress */ @ProcessOutInternalApi @@ -166,4 +168,27 @@ object POStepIcon { color = Color.White ) ) + + @Composable + fun custom(style: POStepperStyle.IconStyle) = Style( + backgroundColor = colorResource(id = style.backgroundColorResId), + border = style.border?.let { + POBorderStroke( + width = it.widthDp.dp, + color = colorResource(id = it.colorResId) + ) + }, + halo = style.halo?.let { + Halo( + width = it.widthDp.dp, + color = colorResource(id = it.colorResId) + ) + }, + checkmark = style.checkmark?.let { + Checkmark( + width = it.widthDp.dp, + color = colorResource(id = it.colorResId) + ) + } + ) } diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepper.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepper.kt new file mode 100644 index 000000000..552403e90 --- /dev/null +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepper.kt @@ -0,0 +1,132 @@ +package com.processout.sdk.ui.core.component.stepper + +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +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.POStroke +import com.processout.sdk.ui.core.component.POText +import com.processout.sdk.ui.core.component.stepper.POStepper.StepState.* +import com.processout.sdk.ui.core.style.POStepperStyle +import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors +import com.processout.sdk.ui.core.theme.ProcessOutTheme.typography + +/** @suppress */ +@ProcessOutInternalApi +object POStepper { + + data class Step( + val title: String, + val description: String? = null + ) + + enum class StepState { + PENDING, + ACTIVE, + COMPLETED + } + + data class Style( + val pending: StepStyle, + val active: StepStyle, + val completed: StepStyle + ) + + data class StepStyle( + val title: POText.Style, + val description: POText.Style, + val icon: POStepIcon.Style, + val connector: POStroke.Style + ) + + val default: Style + @Composable get() = Style( + pending = StepStyle( + title = POText.Style( + color = colors.text.tertiary, + textStyle = typography.s15(FontWeight.Medium) + ), + description = POText.Style( + color = colors.text.tertiary, + textStyle = typography.s12(FontWeight.Medium) + ), + icon = POStepIcon.pending, + connector = POStroke.Style( + width = 2.dp, + color = Color(0xFFCECECE), + dashInterval = 3.dp + ) + ), + active = StepStyle( + title = POText.Style( + color = colors.text.primary, + textStyle = typography.s15(FontWeight.Medium) + ), + description = POText.Style( + color = colors.text.secondary, + textStyle = typography.s12(FontWeight.Medium) + ), + icon = POStepIcon.active, + connector = POStroke.Style( + width = 2.dp, + color = POStepIcon.defaultCompletedColor, + dashInterval = 3.dp + ) + ), + completed = StepStyle( + title = POText.Style( + color = colors.text.positive, + textStyle = typography.s15(FontWeight.Medium) + ), + description = POText.Style( + color = colors.text.secondary, + textStyle = typography.s12(FontWeight.Medium) + ), + icon = POStepIcon.completed, + connector = POStroke.Style( + width = 2.dp, + color = POStepIcon.defaultCompletedColor + ) + ) + ) + + @Composable + fun custom(style: POStepperStyle) = Style( + pending = style.pending.toStepStyle(), + active = style.active.toStepStyle(), + completed = style.completed.toStepStyle() + ) + + @Composable + private fun POStepperStyle.StepStyle.toStepStyle() = StepStyle( + title = POText.custom(style = title), + description = POText.custom(style = description), + icon = POStepIcon.custom(style = icon), + connector = POStroke.custom(style = connector) + ) + + internal fun stepStyle( + style: Style, + state: StepState + ): StepStyle = + when (state) { + PENDING -> style.pending + ACTIVE -> style.active + COMPLETED -> style.completed + } + + internal fun connectorStyle( + style: Style, + states: List, + currentStepIndex: Int + ): POStroke.Style { + val current = states.getOrNull(index = currentStepIndex) + val next = states.getOrNull(index = currentStepIndex + 1) + return when { + current == COMPLETED && next == COMPLETED -> style.completed.connector + current == COMPLETED && next == ACTIVE -> style.active.connector + else -> style.pending.connector + } + } +} diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POVerticalStepper.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POVerticalStepper.kt new file mode 100644 index 000000000..a0d97ca1c --- /dev/null +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POVerticalStepper.kt @@ -0,0 +1,139 @@ +package com.processout.sdk.ui.core.component.stepper + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.layout.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.PathEffect +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi +import com.processout.sdk.ui.core.component.POExpandableText +import com.processout.sdk.ui.core.component.POText +import com.processout.sdk.ui.core.component.stepper.POStepper.StepState.* +import com.processout.sdk.ui.core.state.POImmutableList +import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing + +/** @suppress */ +@ProcessOutInternalApi +@Composable +fun POVerticalStepper( + steps: POImmutableList, + modifier: Modifier = Modifier, + style: POStepper.Style = POStepper.default, + activeStepIndex: Int = 0 +) { + Column(modifier = modifier) { + val states = List(steps.elements.size) { index -> + when { + index < activeStepIndex -> COMPLETED + index == activeStepIndex -> ACTIVE + else -> PENDING + } + } + steps.elements.forEachIndexed { index, step -> + Row( + modifier = Modifier + .fillMaxWidth() + .height(IntrinsicSize.Min) + ) { + val stepStyle = POStepper.stepStyle( + style = style, + state = states[index] + ) + Column( + modifier = Modifier.fillMaxHeight(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + POStepIcon( + style = stepStyle.icon + ) + if (index != steps.elements.lastIndex) { + val connectorStyle = POStepper.connectorStyle( + style = style, + states = states, + currentStepIndex = index + ) + Canvas( + modifier = Modifier + .requiredWidth(connectorStyle.width) + .fillMaxHeight() + .defaultMinSize(minHeight = 28.dp) + ) { + val pathEffect = connectorStyle.dashInterval?.toPx() + ?.let { dashIntervalPx -> + PathEffect.dashPathEffect( + intervals = floatArrayOf(dashIntervalPx, dashIntervalPx) + ) + } + drawLine( + color = connectorStyle.color, + start = Offset(x = 0f, y = 0f), + end = Offset(x = 0f, y = size.height), + strokeWidth = size.width, + pathEffect = pathEffect + ) + } + } + } + Column( + modifier = Modifier + .padding(start = spacing.space6) + .fillMaxWidth() + .fillMaxHeight(), + verticalArrangement = Arrangement.spacedBy(spacing.space4) + ) { + val titleTextStyle = stepStyle.title.textStyle + POText( + text = step.title, + color = stepStyle.title.color, + style = titleTextStyle, + modifier = Modifier.padding( + top = POText.measuredPaddingTop( + textStyle = titleTextStyle, + componentHeight = 36.dp + ) + ) + ) + POExpandableText( + text = step.description, + style = stepStyle.description, + modifier = Modifier.fillMaxWidth() + ) + } + } + } + } +} + +/** @suppress */ +@ProcessOutInternalApi +@Composable +@Preview(showBackground = true) +private fun POVerticalStepperPreview() { + POVerticalStepper( + steps = POImmutableList( + listOf( + POStepper.Step( + title = "Step 1", + description = "Description" + ), + POStepper.Step( + title = "Step 2", + description = "Description" + ), + POStepper.Step( + title = "Step 3", + description = "Description" + ), + POStepper.Step( + title = "Step 4", + description = "Description" + ) + ) + ), + activeStepIndex = 2 + ) +} diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POStepperStyle.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POStepperStyle.kt new file mode 100644 index 000000000..5a7e50dbf --- /dev/null +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POStepperStyle.kt @@ -0,0 +1,52 @@ +package com.processout.sdk.ui.core.style + +import android.os.Parcelable +import androidx.annotation.ColorRes +import kotlinx.parcelize.Parcelize + +@Parcelize +data class POStepperStyle( + val pending: StepStyle, + val active: StepStyle, + val completed: StepStyle +) : Parcelable { + + @Parcelize + data class StepStyle( + val title: POTextStyle, + val description: POTextStyle, + val icon: IconStyle, + val connector: POStrokeStyle + ) : Parcelable + + @Parcelize + data class IconStyle( + @ColorRes + val backgroundColorResId: Int, + val border: Border?, + val halo: Halo?, + val checkmark: Checkmark? + ) : Parcelable { + + @Parcelize + data class Border( + val widthDp: Int, + @ColorRes + val colorResId: Int + ) : Parcelable + + @Parcelize + data class Halo( + val widthDp: Int, + @ColorRes + val colorResId: Int + ) : Parcelable + + @Parcelize + data class Checkmark( + val widthDp: Int, + @ColorRes + val colorResId: Int + ) : Parcelable + } +} diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POStrokeStyle.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POStrokeStyle.kt new file mode 100644 index 000000000..d328f8667 --- /dev/null +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/style/POStrokeStyle.kt @@ -0,0 +1,13 @@ +package com.processout.sdk.ui.core.style + +import android.os.Parcelable +import androidx.annotation.ColorRes +import kotlinx.parcelize.Parcelize + +@Parcelize +data class POStrokeStyle( + val widthDp: Int, + @ColorRes + val colorResId: Int, + val dashIntervalDp: Int? = null +) : Parcelable From 1bd2bb9d34b8ac9bdfcc99741cb508a06c8b62f8 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 10 Jul 2025 22:24:32 +0300 Subject: [PATCH 07/10] Update POStepIcon --- .../sdk/ui/core/component/stepper/POStepIcon.kt | 17 ++++++++++------- .../sdk/ui/core/component/stepper/POStepper.kt | 4 ++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt index 8c38dbb29..2723bea68 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepIcon.kt @@ -25,8 +25,8 @@ import com.processout.sdk.ui.core.style.POStepperStyle @ProcessOutInternalApi @Composable fun POStepIcon( - iconSize: Dp = 24.dp, - padding: Dp = 6.dp, + iconSize: Dp = POStepIcon.DefaultIconSize, + padding: Dp = POStepIcon.DefaultPadding, style: POStepIcon.Style = POStepIcon.active ) { val density = LocalDensity.current @@ -130,15 +130,18 @@ object POStepIcon { val color: Color ) - private val defaultBorderColor = Color(0xFFA3A3A3) - internal val defaultCompletedColor = Color(0xFF4CA259) + internal val DefaultIconSize: Dp = 24.dp + internal val DefaultPadding: Dp = 6.dp + + private val DefaultBorderColor = Color(0xFFA3A3A3) + internal val DefaultCompletedColor = Color(0xFF4CA259) val pending: Style @Composable get() = Style( backgroundColor = Color.Transparent, border = POBorderStroke( width = 1.5.dp, - color = defaultBorderColor + color = DefaultBorderColor ), halo = null, checkmark = null @@ -149,7 +152,7 @@ object POStepIcon { backgroundColor = Color.White, border = POBorderStroke( width = 1.5.dp, - color = defaultBorderColor + color = DefaultBorderColor ), halo = Halo( width = 6.dp, @@ -160,7 +163,7 @@ object POStepIcon { val completed: Style @Composable get() = Style( - backgroundColor = defaultCompletedColor, + backgroundColor = DefaultCompletedColor, border = null, halo = null, checkmark = Checkmark( diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepper.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepper.kt index 552403e90..c2d74bc37 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepper.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepper.kt @@ -70,7 +70,7 @@ object POStepper { icon = POStepIcon.active, connector = POStroke.Style( width = 2.dp, - color = POStepIcon.defaultCompletedColor, + color = POStepIcon.DefaultCompletedColor, dashInterval = 3.dp ) ), @@ -86,7 +86,7 @@ object POStepper { icon = POStepIcon.completed, connector = POStroke.Style( width = 2.dp, - color = POStepIcon.defaultCompletedColor + color = POStepIcon.DefaultCompletedColor ) ) ) From ca37921c3becff8d2c31ee530c467c430922d7c2 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 10 Jul 2025 22:36:31 +0300 Subject: [PATCH 08/10] Update POVerticalStepper --- .../sdk/ui/core/component/stepper/POVerticalStepper.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POVerticalStepper.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POVerticalStepper.kt index a0d97ca1c..72dda4da9 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POVerticalStepper.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POVerticalStepper.kt @@ -39,6 +39,8 @@ fun POVerticalStepper( .fillMaxWidth() .height(IntrinsicSize.Min) ) { + val iconSize = POStepIcon.DefaultIconSize + val iconPadding = POStepIcon.DefaultPadding val stepStyle = POStepper.stepStyle( style = style, state = states[index] @@ -48,6 +50,8 @@ fun POVerticalStepper( horizontalAlignment = Alignment.CenterHorizontally ) { POStepIcon( + iconSize = iconSize, + padding = iconPadding, style = stepStyle.icon ) if (index != steps.elements.lastIndex) { @@ -93,7 +97,7 @@ fun POVerticalStepper( modifier = Modifier.padding( top = POText.measuredPaddingTop( textStyle = titleTextStyle, - componentHeight = 36.dp + componentHeight = iconSize + iconPadding * 2 ) ) ) From c38d673c8b2e2d31d85831f23401b8462af9230c Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 10 Jul 2025 22:55:44 +0300 Subject: [PATCH 09/10] verticalArrangement --- .../processout/sdk/ui/napm/v2/NativeAlternativePaymentScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 24818bc48..ae3ba9aff 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 @@ -122,7 +122,7 @@ internal fun NativeAlternativePaymentScreen( horizontal = ProcessOutTheme.spacing.extraLarge, vertical = if (state is Capture) 0.dp else verticalSpacing ), - verticalArrangement = if (state is Capture) Arrangement.Top else Arrangement.Center, + verticalArrangement = if (state is Loading) Arrangement.Center else Arrangement.Top, horizontalAlignment = Alignment.CenterHorizontally ) { when (state) { From 4658949a4e92714a2f686e17ad4e69557d899ce2 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 10 Jul 2025 23:12:01 +0300 Subject: [PATCH 10/10] Add stepper style to screen style --- .../napm/PONativeAlternativePaymentConfiguration.kt | 6 ++++-- .../ui/napm/v2/NativeAlternativePaymentScreen.kt | 13 +++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) 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 af9b5087e..8ebed286b 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 @@ -380,8 +380,9 @@ data class PONativeAlternativePaymentConfiguration( val radioField: PORadioFieldStyle? = null, val checkbox: POCheckboxStyle? = null, val dropdownMenu: PODropdownMenuStyle? = null, - val actionsContainer: POActionsContainerStyle? = null, val dialog: PODialogStyle? = null, + val stepper: POStepperStyle? = null, + val actionsContainer: POActionsContainerStyle? = null, val background: POBackgroundStyle? = null, val message: POTextStyle? = null, val errorMessage: POTextStyle? = null, // TODO(v2): remove, not used @@ -452,8 +453,9 @@ data class PONativeAlternativePaymentConfiguration( radioField = null, checkbox = null, dropdownMenu = dropdownMenu, - actionsContainer = actionsContainer, dialog = dialog, + stepper = null, + actionsContainer = actionsContainer, background = background, message = message, errorMessage = errorMessage, 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 ae3ba9aff..27016d264 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 @@ -45,6 +45,7 @@ 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.PORadioField import com.processout.sdk.ui.core.component.field.text.POTextField2 +import com.processout.sdk.ui.core.component.stepper.POStepper import com.processout.sdk.ui.core.state.POActionState import com.processout.sdk.ui.core.state.POImmutableList import com.processout.sdk.ui.core.state.POPhoneNumberFieldState @@ -714,8 +715,9 @@ internal object NativeAlternativePaymentScreen { val radioField: PORadioField.Style, val checkbox: POCheckbox.Style, val dropdownMenu: PODropdownField.MenuStyle, - val actionsContainer: POActionsContainer.Style, val dialog: PODialog.Style, + val stepper: POStepper.Style, + val actionsContainer: POActionsContainer.Style, val normalBackgroundColor: Color, val successBackgroundColor: Color, val message: AndroidTextView.Style, @@ -749,12 +751,15 @@ internal object NativeAlternativePaymentScreen { dropdownMenu = custom?.dropdownMenu?.let { PODropdownField.custom(style = it) } ?: PODropdownField.defaultMenu2, - actionsContainer = custom?.actionsContainer?.let { - POActionsContainer.custom(style = it) - } ?: POActionsContainer.default2, dialog = custom?.dialog?.let { PODialog.custom(style = it) } ?: PODialog.default, + stepper = custom?.stepper?.let { + POStepper.custom(style = it) + } ?: POStepper.default, + actionsContainer = custom?.actionsContainer?.let { + POActionsContainer.custom(style = it) + } ?: POActionsContainer.default2, normalBackgroundColor = custom?.background?.normalColorResId?.let { colorResource(id = it) } ?: colors.surface.default,