@momo-kits/native-kits 0.157.7-debug → 0.157.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/build.gradle.kts +0 -11
- package/compose/build.gradle.kts +0 -180
- package/compose/build.gradle.kts.backup +0 -180
- package/compose/compose.podspec +0 -54
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -117
- package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
- package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -57
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -107
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -201
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -222
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -48
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -305
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -720
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -121
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -405
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +0 -157
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -85
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -32
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -340
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +0 -198
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -357
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -123
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -94
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -136
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +0 -224
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -543
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -23
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -76
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -148
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -188
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -116
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -448
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -172
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -255
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -231
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -233
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -254
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -241
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -364
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +0 -108
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -56
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -41
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -92
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -40
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -352
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -103
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -338
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -70
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +0 -87
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +0 -17
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -96
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +0 -348
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +0 -256
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +0 -494
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +0 -131
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +0 -215
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -96
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -531
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -92
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -130
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -214
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +0 -590
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -177
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +0 -192
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -205
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -239
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -191
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -306
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -16
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -188
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -285
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -35
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -50
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -68
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +0 -14
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -50
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -239
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -119
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -98
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -161
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -331
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -497
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -162
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -243
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -187
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -279
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -80
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -306
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -32
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -370
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -132
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -46
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -62
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -88
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -161
- package/gradle/libs.versions.toml +0 -57
- package/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/gradle/wrapper/gradle-wrapper.properties +0 -8
- package/gradle.properties +0 -26
- package/gradlew +0 -252
- package/gradlew.bat +0 -94
- package/settings.gradle.kts +0 -52
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
package vn.momo.kits.components
|
|
2
|
-
|
|
3
|
-
import androidx.compose.animation.core.LinearEasing
|
|
4
|
-
import androidx.compose.animation.core.RepeatMode
|
|
5
|
-
import androidx.compose.animation.core.animateFloat
|
|
6
|
-
import androidx.compose.animation.core.infiniteRepeatable
|
|
7
|
-
import androidx.compose.animation.core.rememberInfiniteTransition
|
|
8
|
-
import androidx.compose.animation.core.tween
|
|
9
|
-
import androidx.compose.foundation.background
|
|
10
|
-
import androidx.compose.foundation.border
|
|
11
|
-
import androidx.compose.foundation.layout.Box
|
|
12
|
-
import androidx.compose.foundation.layout.BoxWithConstraints
|
|
13
|
-
import androidx.compose.foundation.layout.fillMaxSize
|
|
14
|
-
import androidx.compose.runtime.Composable
|
|
15
|
-
import androidx.compose.runtime.remember
|
|
16
|
-
import androidx.compose.ui.Modifier
|
|
17
|
-
import androidx.compose.ui.draw.drawBehind
|
|
18
|
-
import androidx.compose.ui.geometry.Offset
|
|
19
|
-
import androidx.compose.ui.graphics.Brush
|
|
20
|
-
import androidx.compose.ui.unit.dp
|
|
21
|
-
import vn.momo.kits.application.IsShowBaseLineDebug
|
|
22
|
-
import vn.momo.kits.const.Colors
|
|
23
|
-
import vn.momo.kits.modifier.conditional
|
|
24
|
-
|
|
25
|
-
// Pre-computed shimmer colors to avoid repeated list creation
|
|
26
|
-
private val shimmerColors = listOf(
|
|
27
|
-
Colors.black_05,
|
|
28
|
-
Colors.black_03,
|
|
29
|
-
Colors.black_05,
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
// Constants for better performance
|
|
33
|
-
private const val SHADOW_BRUSH_PERCENTAGE = 0.6f
|
|
34
|
-
private const val DEFAULT_DURATION_MILLIS = 1000
|
|
35
|
-
|
|
36
|
-
@Composable
|
|
37
|
-
fun Skeleton() {
|
|
38
|
-
BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
|
|
39
|
-
// Memoize width calculation
|
|
40
|
-
val maxWidthValue = remember(maxWidth) { maxWidth.value.toInt() }
|
|
41
|
-
|
|
42
|
-
Box(
|
|
43
|
-
modifier = Modifier
|
|
44
|
-
.fillMaxSize()
|
|
45
|
-
.background(Colors.black_05)
|
|
46
|
-
.conditional(IsShowBaseLineDebug) {
|
|
47
|
-
border(1.dp, Colors.blue_03)
|
|
48
|
-
}
|
|
49
|
-
.shimmerLoadingAnimation(maxWidth = maxWidthValue)
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
@Composable
|
|
55
|
-
fun Modifier.shimmerLoadingAnimation(
|
|
56
|
-
maxWidth: Int,
|
|
57
|
-
angleOfAxisY: Float = 0f,
|
|
58
|
-
durationMillis: Int = DEFAULT_DURATION_MILLIS,
|
|
59
|
-
): Modifier {
|
|
60
|
-
// Memoize calculations to avoid repeated computations
|
|
61
|
-
val widthOfShadowBrush = remember(maxWidth) {
|
|
62
|
-
(maxWidth * SHADOW_BRUSH_PERCENTAGE).toInt()
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
val animationTarget = remember(durationMillis, widthOfShadowBrush) {
|
|
66
|
-
(durationMillis + widthOfShadowBrush).toFloat()
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
val transition = rememberInfiniteTransition(label = "shimmer")
|
|
70
|
-
val translateAnimation = transition.animateFloat(
|
|
71
|
-
initialValue = 0f,
|
|
72
|
-
targetValue = animationTarget,
|
|
73
|
-
animationSpec = infiniteRepeatable(
|
|
74
|
-
animation = tween(
|
|
75
|
-
durationMillis = durationMillis,
|
|
76
|
-
easing = LinearEasing,
|
|
77
|
-
),
|
|
78
|
-
repeatMode = RepeatMode.Restart,
|
|
79
|
-
),
|
|
80
|
-
label = "shimmerTranslate"
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
return drawBehind {
|
|
84
|
-
// Memoize brush creation by using remember in the calling composable
|
|
85
|
-
val startX = translateAnimation.value - widthOfShadowBrush
|
|
86
|
-
val endX = translateAnimation.value
|
|
87
|
-
|
|
88
|
-
drawRect(
|
|
89
|
-
brush = Brush.linearGradient(
|
|
90
|
-
colors = shimmerColors,
|
|
91
|
-
start = Offset(x = startX, y = 0f),
|
|
92
|
-
end = Offset(x = endX, y = angleOfAxisY)
|
|
93
|
-
)
|
|
94
|
-
)
|
|
95
|
-
}
|
|
96
|
-
}
|
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
package vn.momo.kits.components
|
|
2
|
-
|
|
3
|
-
import androidx.compose.animation.core.animateFloatAsState
|
|
4
|
-
import androidx.compose.animation.core.spring
|
|
5
|
-
import androidx.compose.foundation.background
|
|
6
|
-
import androidx.compose.foundation.border
|
|
7
|
-
import androidx.compose.foundation.layout.Box
|
|
8
|
-
import androidx.compose.foundation.layout.BoxWithConstraints
|
|
9
|
-
import androidx.compose.foundation.layout.fillMaxWidth
|
|
10
|
-
import androidx.compose.foundation.layout.height
|
|
11
|
-
import androidx.compose.foundation.layout.offset
|
|
12
|
-
import androidx.compose.foundation.layout.padding
|
|
13
|
-
import androidx.compose.foundation.layout.size
|
|
14
|
-
import androidx.compose.foundation.layout.width
|
|
15
|
-
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
16
|
-
import androidx.compose.runtime.Composable
|
|
17
|
-
import androidx.compose.runtime.getValue
|
|
18
|
-
import androidx.compose.runtime.mutableFloatStateOf
|
|
19
|
-
import androidx.compose.runtime.mutableStateOf
|
|
20
|
-
import androidx.compose.runtime.remember
|
|
21
|
-
import androidx.compose.runtime.setValue
|
|
22
|
-
import androidx.compose.ui.Alignment
|
|
23
|
-
import androidx.compose.ui.Modifier
|
|
24
|
-
import androidx.compose.ui.draw.clip
|
|
25
|
-
import androidx.compose.ui.graphics.Color
|
|
26
|
-
import androidx.compose.ui.input.pointer.pointerInput
|
|
27
|
-
import androidx.compose.ui.layout.onGloballyPositioned
|
|
28
|
-
import androidx.compose.ui.platform.LocalDensity
|
|
29
|
-
import androidx.compose.ui.text.style.TextAlign
|
|
30
|
-
import androidx.compose.ui.unit.IntOffset
|
|
31
|
-
import androidx.compose.ui.unit.dp
|
|
32
|
-
import vn.momo.kits.application.IsShowBaseLineDebug
|
|
33
|
-
import vn.momo.kits.const.AppTheme
|
|
34
|
-
import vn.momo.kits.const.Colors
|
|
35
|
-
import vn.momo.kits.const.Radius
|
|
36
|
-
import vn.momo.kits.const.Spacing
|
|
37
|
-
import vn.momo.kits.const.Typography
|
|
38
|
-
import vn.momo.kits.modifier.conditional
|
|
39
|
-
import vn.momo.kits.modifier.shadow
|
|
40
|
-
import kotlin.math.abs
|
|
41
|
-
import kotlin.math.min
|
|
42
|
-
import kotlin.math.roundToInt
|
|
43
|
-
|
|
44
|
-
// ── Design constants ─────────────────────────────────────────────────────────
|
|
45
|
-
|
|
46
|
-
private val THUMB_SIZE = 20.dp
|
|
47
|
-
private val THUMB_BORDER = 3.dp
|
|
48
|
-
private val TRACK_HEIGHT = 4.dp
|
|
49
|
-
private val LABEL_PAD_H = Spacing.XS
|
|
50
|
-
private val LABEL_PAD_V = Spacing.XXS
|
|
51
|
-
private val LABEL_GAP = Spacing.XS // space between label and thumb top
|
|
52
|
-
|
|
53
|
-
// ── Pure helpers (mirrors RN helpers.ts) ─────────────────────────────────────
|
|
54
|
-
|
|
55
|
-
private fun clamp(value: Float, min: Float, max: Float): Float =
|
|
56
|
-
min(kotlin.math.max(value, min), max)
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Converts a raw touch position into a snapped slider value.
|
|
60
|
-
* Direct port of RN getValueForPosition().
|
|
61
|
-
*/
|
|
62
|
-
private fun getValueForPosition(
|
|
63
|
-
positionInView: Float,
|
|
64
|
-
containerWidth: Float,
|
|
65
|
-
thumbWidth: Float,
|
|
66
|
-
min: Float,
|
|
67
|
-
max: Float,
|
|
68
|
-
step: Float,
|
|
69
|
-
): Float {
|
|
70
|
-
val availableSpace = containerWidth - thumbWidth
|
|
71
|
-
if (availableSpace <= 0f) return min
|
|
72
|
-
val relStepUnit = step / (max - min)
|
|
73
|
-
var relPosition = (positionInView - thumbWidth / 2f) / availableSpace
|
|
74
|
-
val relOffset = relPosition % relStepUnit
|
|
75
|
-
relPosition -= relOffset
|
|
76
|
-
if (relOffset / relStepUnit >= 0.5f) relPosition += relStepUnit
|
|
77
|
-
return clamp(min + (relPosition / relStepUnit).roundToInt() * step, min, max)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/** Direct port of RN isLowCloser(). */
|
|
81
|
-
private fun isLowCloser(downX: Float, lowPosition: Float, highPosition: Float): Boolean {
|
|
82
|
-
if (lowPosition == highPosition) return downX < lowPosition
|
|
83
|
-
return abs(downX - lowPosition) < abs(downX - highPosition)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
private fun valueToFraction(value: Float, min: Float, max: Float): Float {
|
|
87
|
-
if (max == min) return 0f
|
|
88
|
-
return clamp((value - min) / (max - min), 0f, 1f)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
private fun formatLabel(value: Float): String =
|
|
92
|
-
if (value == value.toLong().toFloat()) value.toLong().toString()
|
|
93
|
-
else ((value * 10).toLong() / 10.0).toString()
|
|
94
|
-
|
|
95
|
-
// ── Component ─────────────────────────────────────────────────────────────────
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Range slider (or single-thumb when [disableRange] = true), migrated from React Native.
|
|
99
|
-
*
|
|
100
|
-
* @param min Minimum selectable value.
|
|
101
|
-
* @param max Maximum selectable value.
|
|
102
|
-
* @param step Snap increment between values.
|
|
103
|
-
* @param modifier Modifier applied to the root container.
|
|
104
|
-
* @param low Initial lower-thumb value (defaults to [min]).
|
|
105
|
-
* @param high Initial upper-thumb value (defaults to [max]).
|
|
106
|
-
* @param minRange Minimum required gap between low and high (default 0).
|
|
107
|
-
* @param disableRange When true only the low thumb is shown (single-value mode).
|
|
108
|
-
* @param floatingLabel When true a value label floats above the active thumb during drag.
|
|
109
|
-
* @param disabled Non-interactive; rendered in the disabled colour.
|
|
110
|
-
* @param onValueChanged Invoked with (low, high, byUser) on every value change.
|
|
111
|
-
* @param onSliderTouchStart Invoked when a drag gesture begins.
|
|
112
|
-
* @param onSliderTouchEnd Invoked when a drag gesture ends.
|
|
113
|
-
*/
|
|
114
|
-
@Composable
|
|
115
|
-
fun Slider(
|
|
116
|
-
min: Float,
|
|
117
|
-
max: Float,
|
|
118
|
-
step: Float,
|
|
119
|
-
modifier: Modifier = Modifier,
|
|
120
|
-
low: Float? = null,
|
|
121
|
-
high: Float? = null,
|
|
122
|
-
minRange: Float = 0f,
|
|
123
|
-
disableRange: Boolean = false,
|
|
124
|
-
floatingLabel: Boolean = false,
|
|
125
|
-
disabled: Boolean = false,
|
|
126
|
-
onValueChanged: (low: Float, high: Float, byUser: Boolean) -> Unit = { _, _, _ -> },
|
|
127
|
-
onSliderTouchStart: (low: Float, high: Float) -> Unit = { _, _ -> },
|
|
128
|
-
onSliderTouchEnd: (low: Float, high: Float) -> Unit = { _, _ -> },
|
|
129
|
-
) {
|
|
130
|
-
val theme = AppTheme.current
|
|
131
|
-
val density = LocalDensity.current
|
|
132
|
-
|
|
133
|
-
// Validated, step-snapped initial values
|
|
134
|
-
val initLow = remember(low, min, max, step) {
|
|
135
|
-
clamp(low ?: min, min, max)
|
|
136
|
-
}
|
|
137
|
-
val initHigh = remember(high, min, max, step, disableRange) {
|
|
138
|
-
if (disableRange) max else clamp(high ?: max, min, max)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
var lowValue by remember { mutableFloatStateOf(initLow) }
|
|
142
|
-
var highValue by remember { mutableFloatStateOf(initHigh) }
|
|
143
|
-
|
|
144
|
-
// Smoothly animated display fractions
|
|
145
|
-
val lowFraction by animateFloatAsState(
|
|
146
|
-
targetValue = valueToFraction(lowValue, min, max),
|
|
147
|
-
animationSpec = spring(stiffness = 1200f),
|
|
148
|
-
label = "lowFraction"
|
|
149
|
-
)
|
|
150
|
-
val highFraction by animateFloatAsState(
|
|
151
|
-
targetValue = valueToFraction(highValue, min, max),
|
|
152
|
-
animationSpec = spring(stiffness = 1200f),
|
|
153
|
-
label = "highFraction"
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
var draggingLow by remember { mutableStateOf(true) }
|
|
157
|
-
var isDragging by remember { mutableStateOf(false) }
|
|
158
|
-
|
|
159
|
-
val thumbColor = if (disabled) theme.colors.text.disable else theme.colors.primary
|
|
160
|
-
val trackActiveColor = if (disabled) theme.colors.text.disable else theme.colors.primary
|
|
161
|
-
val trackInactiveColor = theme.colors.background.default
|
|
162
|
-
|
|
163
|
-
// trackWidthPx is the full pixel width of the BoxWithConstraints (incl. thumb padding)
|
|
164
|
-
var trackWidthPx by remember { mutableFloatStateOf(0f) }
|
|
165
|
-
val thumbSizePx = with(density) { THUMB_SIZE.toPx() }
|
|
166
|
-
|
|
167
|
-
Box(
|
|
168
|
-
modifier = modifier
|
|
169
|
-
.fillMaxWidth()
|
|
170
|
-
.conditional(IsShowBaseLineDebug) { border(1.dp, Colors.blue_03) }
|
|
171
|
-
) {
|
|
172
|
-
BoxWithConstraints(
|
|
173
|
-
modifier = Modifier
|
|
174
|
-
.fillMaxWidth()
|
|
175
|
-
.onGloballyPositioned { coords -> trackWidthPx = coords.size.width.toFloat() }
|
|
176
|
-
// ── Gesture handling ──────────────────────────────────────────
|
|
177
|
-
.pointerInput(disabled, min, max, step, minRange, disableRange) {
|
|
178
|
-
if (disabled) return@pointerInput
|
|
179
|
-
|
|
180
|
-
awaitPointerEventScope {
|
|
181
|
-
while (true) {
|
|
182
|
-
// Finger down
|
|
183
|
-
val downEvent = awaitPointerEvent()
|
|
184
|
-
val downChange = downEvent.changes.firstOrNull() ?: continue
|
|
185
|
-
val touchX = downChange.position.x
|
|
186
|
-
|
|
187
|
-
// Pick the closer thumb
|
|
188
|
-
val lowCenterX = thumbSizePx / 2f +
|
|
189
|
-
valueToFraction(lowValue, min, max) * (trackWidthPx - thumbSizePx)
|
|
190
|
-
val highCenterX = thumbSizePx / 2f +
|
|
191
|
-
valueToFraction(highValue, min, max) * (trackWidthPx - thumbSizePx)
|
|
192
|
-
|
|
193
|
-
draggingLow = disableRange || isLowCloser(touchX, lowCenterX, highCenterX)
|
|
194
|
-
isDragging = true
|
|
195
|
-
onSliderTouchStart(lowValue, highValue)
|
|
196
|
-
|
|
197
|
-
// Apply initial press
|
|
198
|
-
applyDrag(touchX, trackWidthPx, thumbSizePx,
|
|
199
|
-
min, max, step, minRange, draggingLow,
|
|
200
|
-
lowValue, highValue) { nl, nh ->
|
|
201
|
-
lowValue = nl
|
|
202
|
-
highValue = nh
|
|
203
|
-
onValueChanged(nl, nh, true)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Track move / release
|
|
207
|
-
while (true) {
|
|
208
|
-
val moveEvent = awaitPointerEvent()
|
|
209
|
-
val moveChange = moveEvent.changes.firstOrNull() ?: break
|
|
210
|
-
moveChange.consume()
|
|
211
|
-
|
|
212
|
-
if (!moveChange.pressed) {
|
|
213
|
-
isDragging = false
|
|
214
|
-
onSliderTouchEnd(lowValue, highValue)
|
|
215
|
-
break
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
applyDrag(moveChange.position.x, trackWidthPx, thumbSizePx,
|
|
219
|
-
min, max, step, minRange, draggingLow,
|
|
220
|
-
lowValue, highValue) { nl, nh ->
|
|
221
|
-
lowValue = nl
|
|
222
|
-
highValue = nh
|
|
223
|
-
onValueChanged(nl, nh, true)
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
) {
|
|
230
|
-
val trackWidth = maxWidth
|
|
231
|
-
val availableWidth = trackWidth - THUMB_SIZE
|
|
232
|
-
|
|
233
|
-
// ── Floating label ────────────────────────────────────────────────
|
|
234
|
-
if (floatingLabel && isDragging) {
|
|
235
|
-
val activeFraction = if (draggingLow) lowFraction else highFraction
|
|
236
|
-
val activeValue = if (draggingLow) lowValue else highValue
|
|
237
|
-
val labelOffsetDp = availableWidth * activeFraction
|
|
238
|
-
|
|
239
|
-
Box(
|
|
240
|
-
modifier = Modifier
|
|
241
|
-
.align(Alignment.TopStart)
|
|
242
|
-
.offset { IntOffset(x = with(density) { labelOffsetDp.toPx().roundToInt() }, y = 0) }
|
|
243
|
-
) {
|
|
244
|
-
Box(
|
|
245
|
-
modifier = Modifier
|
|
246
|
-
.background(
|
|
247
|
-
color = Colors.black_01,
|
|
248
|
-
shape = RoundedCornerShape(Radius.XS)
|
|
249
|
-
)
|
|
250
|
-
.padding(horizontal = LABEL_PAD_H, vertical = LABEL_PAD_V)
|
|
251
|
-
) {
|
|
252
|
-
Text(
|
|
253
|
-
text = formatLabel(activeValue),
|
|
254
|
-
style = Typography.descriptionXsRegular,
|
|
255
|
-
color = theme.colors.text.default,
|
|
256
|
-
textAlign = TextAlign.Center
|
|
257
|
-
)
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Top padding pushes track + thumbs below the label when floatingLabel is on
|
|
263
|
-
val topPad = if (floatingLabel) THUMB_SIZE + LABEL_GAP else 0.dp
|
|
264
|
-
|
|
265
|
-
// ── Inactive track (full width) ───────────────────────────────────
|
|
266
|
-
Box(
|
|
267
|
-
modifier = Modifier
|
|
268
|
-
.align(Alignment.TopStart)
|
|
269
|
-
.padding(top = topPad + (THUMB_SIZE - TRACK_HEIGHT) / 2)
|
|
270
|
-
.padding(horizontal = THUMB_SIZE / 2)
|
|
271
|
-
.fillMaxWidth()
|
|
272
|
-
.height(TRACK_HEIGHT)
|
|
273
|
-
.clip(RoundedCornerShape(percent = 50))
|
|
274
|
-
.background(trackInactiveColor)
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
// ── Active track (selected range / single value) ──────────────────
|
|
278
|
-
val activeStartFraction = if (disableRange) 0f else lowFraction
|
|
279
|
-
val activeEndFraction = if (disableRange) lowFraction else highFraction
|
|
280
|
-
val activeStartDp = THUMB_SIZE / 2 + availableWidth * activeStartFraction
|
|
281
|
-
val activeWidthDp = availableWidth * (activeEndFraction - activeStartFraction)
|
|
282
|
-
|
|
283
|
-
if (activeWidthDp > 0.dp) {
|
|
284
|
-
Box(
|
|
285
|
-
modifier = Modifier
|
|
286
|
-
.align(Alignment.TopStart)
|
|
287
|
-
.padding(top = topPad + (THUMB_SIZE - TRACK_HEIGHT) / 2)
|
|
288
|
-
.offset(x = activeStartDp)
|
|
289
|
-
.width(activeWidthDp)
|
|
290
|
-
.height(TRACK_HEIGHT)
|
|
291
|
-
.clip(RoundedCornerShape(percent = 50))
|
|
292
|
-
.background(trackActiveColor)
|
|
293
|
-
)
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// ── Low thumb ─────────────────────────────────────────────────────
|
|
297
|
-
Box(
|
|
298
|
-
modifier = Modifier
|
|
299
|
-
.align(Alignment.TopStart)
|
|
300
|
-
.padding(top = topPad)
|
|
301
|
-
.offset(x = availableWidth * lowFraction)
|
|
302
|
-
.size(THUMB_SIZE)
|
|
303
|
-
.clip(RoundedCornerShape(percent = 100))
|
|
304
|
-
.background(thumbColor)
|
|
305
|
-
.border(THUMB_BORDER, theme.colors.background.surface, RoundedCornerShape(percent = 100))
|
|
306
|
-
)
|
|
307
|
-
|
|
308
|
-
// ── High thumb (range mode only) ──────────────────────────────────
|
|
309
|
-
if (!disableRange) {
|
|
310
|
-
Box(
|
|
311
|
-
modifier = Modifier
|
|
312
|
-
.align(Alignment.TopStart)
|
|
313
|
-
.padding(top = topPad)
|
|
314
|
-
.offset(x = availableWidth * highFraction)
|
|
315
|
-
.size(THUMB_SIZE)
|
|
316
|
-
.clip(RoundedCornerShape(percent = 100))
|
|
317
|
-
.background(thumbColor)
|
|
318
|
-
.border(THUMB_BORDER, theme.colors.background.surface, RoundedCornerShape(percent = 100))
|
|
319
|
-
)
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// ── Private drag helper ───────────────────────────────────────────────────────
|
|
326
|
-
|
|
327
|
-
private fun applyDrag(
|
|
328
|
-
positionX: Float,
|
|
329
|
-
trackWidthPx: Float,
|
|
330
|
-
thumbSizePx: Float,
|
|
331
|
-
min: Float,
|
|
332
|
-
max: Float,
|
|
333
|
-
step: Float,
|
|
334
|
-
minRange: Float,
|
|
335
|
-
isLow: Boolean,
|
|
336
|
-
currentLow: Float,
|
|
337
|
-
currentHigh: Float,
|
|
338
|
-
onUpdate: (Float, Float) -> Unit,
|
|
339
|
-
) {
|
|
340
|
-
val snapped = getValueForPosition(positionX, trackWidthPx, thumbSizePx, min, max, step)
|
|
341
|
-
if (isLow) {
|
|
342
|
-
val newLow = clamp(snapped, min, currentHigh - minRange)
|
|
343
|
-
if (newLow != currentLow) onUpdate(newLow, currentHigh)
|
|
344
|
-
} else {
|
|
345
|
-
val newHigh = clamp(snapped, currentLow + minRange, max)
|
|
346
|
-
if (newHigh != currentHigh) onUpdate(currentLow, newHigh)
|
|
347
|
-
}
|
|
348
|
-
}
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
package vn.momo.kits.components
|
|
2
|
-
|
|
3
|
-
import androidx.compose.foundation.background
|
|
4
|
-
import androidx.compose.foundation.border
|
|
5
|
-
import androidx.compose.foundation.layout.Box
|
|
6
|
-
import androidx.compose.foundation.layout.Row
|
|
7
|
-
import androidx.compose.foundation.layout.defaultMinSize
|
|
8
|
-
import androidx.compose.foundation.layout.height
|
|
9
|
-
import androidx.compose.foundation.layout.padding
|
|
10
|
-
import androidx.compose.foundation.layout.size
|
|
11
|
-
import androidx.compose.foundation.shape.CircleShape
|
|
12
|
-
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
13
|
-
import androidx.compose.foundation.text.BasicTextField
|
|
14
|
-
import androidx.compose.foundation.text.KeyboardOptions
|
|
15
|
-
import androidx.compose.runtime.Composable
|
|
16
|
-
import androidx.compose.runtime.getValue
|
|
17
|
-
import androidx.compose.runtime.mutableStateOf
|
|
18
|
-
import androidx.compose.runtime.remember
|
|
19
|
-
import androidx.compose.runtime.setValue
|
|
20
|
-
import androidx.compose.ui.Alignment
|
|
21
|
-
import androidx.compose.ui.Modifier
|
|
22
|
-
import androidx.compose.ui.graphics.Color
|
|
23
|
-
import androidx.compose.ui.text.TextStyle
|
|
24
|
-
import androidx.compose.ui.text.input.KeyboardType
|
|
25
|
-
import androidx.compose.ui.text.style.TextAlign
|
|
26
|
-
import androidx.compose.ui.unit.Dp
|
|
27
|
-
import androidx.compose.ui.unit.dp
|
|
28
|
-
import androidx.compose.ui.unit.sp
|
|
29
|
-
import vn.momo.kits.application.IsShowBaseLineDebug
|
|
30
|
-
import vn.momo.kits.const.AppTheme
|
|
31
|
-
import vn.momo.kits.const.Colors
|
|
32
|
-
import vn.momo.kits.const.Radius
|
|
33
|
-
import vn.momo.kits.const.Spacing
|
|
34
|
-
import vn.momo.kits.const.scaleSize
|
|
35
|
-
import vn.momo.kits.modifier.activeOpacityClickable
|
|
36
|
-
import vn.momo.kits.modifier.conditional
|
|
37
|
-
|
|
38
|
-
// ---------------------------------------------------------------------------
|
|
39
|
-
// Size variant
|
|
40
|
-
// ---------------------------------------------------------------------------
|
|
41
|
-
|
|
42
|
-
enum class StepperSize {
|
|
43
|
-
LARGE,
|
|
44
|
-
SMALL
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
private data class StepperSizeSpec(
|
|
48
|
-
val buttonSize: Dp,
|
|
49
|
-
val iconSize: Dp,
|
|
50
|
-
val numberMinWidth: Dp,
|
|
51
|
-
val numberHeight: Dp,
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
private val stepperSizeSpecs: Map<StepperSize, StepperSizeSpec> = mapOf(
|
|
55
|
-
StepperSize.LARGE to StepperSizeSpec(
|
|
56
|
-
buttonSize = 36.dp,
|
|
57
|
-
iconSize = 22.dp,
|
|
58
|
-
numberMinWidth = 32.dp,
|
|
59
|
-
numberHeight = 28.dp,
|
|
60
|
-
),
|
|
61
|
-
StepperSize.SMALL to StepperSizeSpec(
|
|
62
|
-
buttonSize = 28.dp,
|
|
63
|
-
iconSize = 18.dp,
|
|
64
|
-
numberMinWidth = 28.dp,
|
|
65
|
-
numberHeight = 24.dp,
|
|
66
|
-
),
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
// ---------------------------------------------------------------------------
|
|
70
|
-
// Private sub-composables
|
|
71
|
-
// ---------------------------------------------------------------------------
|
|
72
|
-
|
|
73
|
-
@Composable
|
|
74
|
-
private fun StepperButton(
|
|
75
|
-
sign: String = "+",
|
|
76
|
-
size: StepperSize = StepperSize.LARGE,
|
|
77
|
-
disabled: Boolean = false,
|
|
78
|
-
onPress: () -> Unit = {},
|
|
79
|
-
modifier: Modifier = Modifier,
|
|
80
|
-
) {
|
|
81
|
-
val theme = AppTheme.current
|
|
82
|
-
val specs = remember(size) { stepperSizeSpecs[size] ?: stepperSizeSpecs[StepperSize.LARGE]!! }
|
|
83
|
-
|
|
84
|
-
val iconColor: Color = if (disabled) theme.colors.text.disable else theme.colors.primary
|
|
85
|
-
val iconName: String =
|
|
86
|
-
if (sign == "-") "24_navigation_minus_circle" else "24_navigation_plus_circle"
|
|
87
|
-
|
|
88
|
-
val clickableModifier: Modifier = if (!disabled) {
|
|
89
|
-
Modifier.activeOpacityClickable(onClick = onPress)
|
|
90
|
-
} else {
|
|
91
|
-
Modifier
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
Box(
|
|
95
|
-
modifier = modifier
|
|
96
|
-
.size(specs.buttonSize)
|
|
97
|
-
.background(color = theme.colors.background.disable, shape = CircleShape)
|
|
98
|
-
.then(clickableModifier)
|
|
99
|
-
.conditional(IsShowBaseLineDebug) { border(1.dp, Colors.blue_03) },
|
|
100
|
-
contentAlignment = Alignment.Center,
|
|
101
|
-
) {
|
|
102
|
-
Icon(
|
|
103
|
-
source = iconName,
|
|
104
|
-
size = specs.iconSize,
|
|
105
|
-
color = iconColor,
|
|
106
|
-
)
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
@Composable
|
|
111
|
-
private fun StepperNumberView(
|
|
112
|
-
value: Int,
|
|
113
|
-
size: StepperSize = StepperSize.LARGE,
|
|
114
|
-
disabled: Boolean = false,
|
|
115
|
-
editable: Boolean = false,
|
|
116
|
-
onValueChange: (String) -> Unit = {},
|
|
117
|
-
modifier: Modifier = Modifier,
|
|
118
|
-
) {
|
|
119
|
-
val theme = AppTheme.current
|
|
120
|
-
val specs = remember(size) { stepperSizeSpecs[size] ?: stepperSizeSpecs[StepperSize.LARGE]!! }
|
|
121
|
-
|
|
122
|
-
val textColor: Color = if (disabled) theme.colors.text.disable else theme.colors.text.default
|
|
123
|
-
val borderColor: Color =
|
|
124
|
-
if (disabled) theme.colors.border.disable else theme.colors.border.default
|
|
125
|
-
|
|
126
|
-
val scaledFontSize = scaleSize(12.sp)
|
|
127
|
-
|
|
128
|
-
val textStyle = TextStyle(
|
|
129
|
-
color = textColor,
|
|
130
|
-
fontSize = scaledFontSize,
|
|
131
|
-
textAlign = TextAlign.Center,
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
Box(
|
|
135
|
-
modifier = modifier
|
|
136
|
-
.defaultMinSize(minWidth = specs.numberMinWidth)
|
|
137
|
-
.height(specs.numberHeight)
|
|
138
|
-
.border(
|
|
139
|
-
width = 1.dp,
|
|
140
|
-
color = borderColor,
|
|
141
|
-
shape = RoundedCornerShape(Radius.S),
|
|
142
|
-
)
|
|
143
|
-
.padding(horizontal = Spacing.XS)
|
|
144
|
-
.conditional(IsShowBaseLineDebug) { border(1.dp, Colors.blue_03) },
|
|
145
|
-
contentAlignment = Alignment.Center,
|
|
146
|
-
) {
|
|
147
|
-
BasicTextField(
|
|
148
|
-
value = value.toString(),
|
|
149
|
-
onValueChange = { if (editable) onValueChange(it) },
|
|
150
|
-
enabled = editable && !disabled,
|
|
151
|
-
readOnly = !editable,
|
|
152
|
-
singleLine = true,
|
|
153
|
-
textStyle = textStyle,
|
|
154
|
-
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
|
155
|
-
decorationBox = { innerTextField -> innerTextField() },
|
|
156
|
-
)
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// ---------------------------------------------------------------------------
|
|
161
|
-
// Public API
|
|
162
|
-
// ---------------------------------------------------------------------------
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Stepper — migrated from React Native Stepper.
|
|
166
|
-
*
|
|
167
|
-
* Layout: [− button] [value display] [+ button]
|
|
168
|
-
*
|
|
169
|
-
* @param defaultValue Initial value (default 0). Clamped to [min]..[max] on first composition.
|
|
170
|
-
* @param min Minimum allowed value (default 0). Minus button is auto-disabled at this bound.
|
|
171
|
-
* @param max Maximum allowed value (default 100). Plus button is auto-disabled at this bound.
|
|
172
|
-
* @param step Amount added/subtracted per button press (default 1).
|
|
173
|
-
* @param disabled Disable state. Pass `true` to disable everything, or a [BooleanArray] of
|
|
174
|
-
* size >= 2 where index 0 = minus button, index 1 = plus button.
|
|
175
|
-
* @param size Visual size variant: [StepperSize.LARGE] (default) or [StepperSize.SMALL].
|
|
176
|
-
* @param editable When `true` the number display becomes a direct keyboard input (default false).
|
|
177
|
-
* @param onValueChange Callback invoked with the new [Int] value on every change.
|
|
178
|
-
* @param modifier Modifier applied to the root [Row].
|
|
179
|
-
*/
|
|
180
|
-
@Composable
|
|
181
|
-
fun Stepper(
|
|
182
|
-
defaultValue: Int = 0,
|
|
183
|
-
min: Int = 0,
|
|
184
|
-
max: Int = 100,
|
|
185
|
-
step: Int = 1,
|
|
186
|
-
disabled: Any = false,
|
|
187
|
-
size: StepperSize = StepperSize.LARGE,
|
|
188
|
-
editable: Boolean = false,
|
|
189
|
-
onValueChange: (Int) -> Unit = {},
|
|
190
|
-
modifier: Modifier = Modifier,
|
|
191
|
-
) {
|
|
192
|
-
var value by remember(defaultValue) { mutableStateOf(defaultValue.coerceIn(min, max)) }
|
|
193
|
-
|
|
194
|
-
fun applyValue(raw: String) {
|
|
195
|
-
var parsed = raw.toIntOrNull() ?: 0
|
|
196
|
-
parsed = parsed.coerceIn(min, max)
|
|
197
|
-
value = parsed
|
|
198
|
-
onValueChange(parsed)
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Resolve per-part disabled state — mirrors RN getViewDisabledStatus()
|
|
202
|
-
val disabledMinus: Boolean
|
|
203
|
-
val disabledPlus: Boolean
|
|
204
|
-
val disabledNumber: Boolean
|
|
205
|
-
|
|
206
|
-
when {
|
|
207
|
-
disabled is BooleanArray && disabled.size >= 2 -> {
|
|
208
|
-
disabledMinus = disabled[0]
|
|
209
|
-
disabledPlus = disabled[1]
|
|
210
|
-
disabledNumber = disabled[0] && disabled[1]
|
|
211
|
-
}
|
|
212
|
-
disabled is Boolean && disabled -> {
|
|
213
|
-
disabledMinus = true
|
|
214
|
-
disabledPlus = true
|
|
215
|
-
disabledNumber = true
|
|
216
|
-
}
|
|
217
|
-
else -> {
|
|
218
|
-
disabledMinus = false
|
|
219
|
-
disabledPlus = false
|
|
220
|
-
disabledNumber = false
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Auto-disable buttons when the value is already at a bound
|
|
225
|
-
val effectiveMinusDisabled = disabledMinus || value <= min
|
|
226
|
-
val effectivePlusDisabled = disabledPlus || value >= max
|
|
227
|
-
|
|
228
|
-
Row(
|
|
229
|
-
modifier = modifier
|
|
230
|
-
.conditional(IsShowBaseLineDebug) { border(1.dp, Colors.blue_03) },
|
|
231
|
-
verticalAlignment = Alignment.CenterVertically,
|
|
232
|
-
) {
|
|
233
|
-
StepperButton(
|
|
234
|
-
sign = "-",
|
|
235
|
-
size = size,
|
|
236
|
-
disabled = effectiveMinusDisabled,
|
|
237
|
-
onPress = { applyValue((value - step).toString()) },
|
|
238
|
-
)
|
|
239
|
-
|
|
240
|
-
StepperNumberView(
|
|
241
|
-
value = value,
|
|
242
|
-
size = size,
|
|
243
|
-
disabled = disabledNumber,
|
|
244
|
-
editable = editable,
|
|
245
|
-
onValueChange = { applyValue(it) },
|
|
246
|
-
modifier = Modifier.padding(horizontal = Spacing.S),
|
|
247
|
-
)
|
|
248
|
-
|
|
249
|
-
StepperButton(
|
|
250
|
-
sign = "+",
|
|
251
|
-
size = size,
|
|
252
|
-
disabled = effectivePlusDisabled,
|
|
253
|
-
onPress = { applyValue((value + step).toString()) },
|
|
254
|
-
)
|
|
255
|
-
}
|
|
256
|
-
}
|