@momo-kits/native-kits 0.161.1-beta.15-debug → 0.161.2-beta.1

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.
Files changed (139) hide show
  1. package/ios/Application/ApplicationEnvironment.swift +2 -6
  2. package/ios/Input/Input.swift +50 -21
  3. package/ios/Input/InputPhoneNumber.swift +17 -17
  4. package/ios/StatusBarTap/StatusBarTap.h +13 -0
  5. package/ios/StatusBarTap/StatusBarTap.m +75 -0
  6. package/ios/Typography/Text.swift +19 -14
  7. package/ios/Typography/Typography.swift +22 -1
  8. package/ios/native-kits.podspec +2 -1
  9. package/package.json +1 -1
  10. package/build.gradle.kts +0 -11
  11. package/compose/build.gradle.kts +0 -180
  12. package/compose/build.gradle.kts.backup +0 -180
  13. package/compose/compose.podspec +0 -47
  14. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -116
  15. package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
  16. package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
  17. package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
  18. package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
  19. package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
  20. package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
  21. package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
  22. package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
  23. package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
  24. package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
  25. package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
  26. package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
  27. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
  28. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -57
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -115
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -201
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -222
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -48
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -305
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -720
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -121
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -402
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +0 -157
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -85
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -32
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -340
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +0 -194
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -357
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -123
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -94
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -136
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +0 -224
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -543
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -23
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -76
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -148
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -188
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -116
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -448
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -172
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -255
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -231
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -233
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -254
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -241
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -364
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +0 -108
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -56
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -41
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -92
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -40
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -352
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -103
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -338
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -70
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +0 -87
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -96
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +0 -348
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +0 -256
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +0 -494
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +0 -131
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +0 -215
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -96
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -531
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -92
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -130
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -214
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +0 -590
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -177
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +0 -192
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -205
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -239
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -191
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -306
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -16
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -188
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -270
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -2
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -35
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -2
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -57
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -68
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +0 -14
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -50
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -253
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -133
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -99
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -168
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -333
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -552
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -161
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -243
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -187
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -279
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -80
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -306
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -32
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -370
  124. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -131
  125. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +0 -167
  126. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -45
  127. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
  128. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -62
  129. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
  130. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -105
  131. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -176
  132. package/gradle/libs.versions.toml +0 -58
  133. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  134. package/gradle/wrapper/gradle-wrapper.properties +0 -8
  135. package/gradle.properties +0 -26
  136. package/gradlew +0 -252
  137. package/gradlew.bat +0 -94
  138. package/local.properties +0 -8
  139. package/settings.gradle.kts +0 -52
@@ -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
- }