@momo-kits/native-kits 0.167.1-duo.17-debug → 0.167.1-duo.18

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 (154) hide show
  1. package/CLAUDE.md +1 -0
  2. package/ios/Layout/ScreenLayout.swift +49 -3
  3. package/ios/Navigation/Component/Header.swift +48 -11
  4. package/ios/Navigation/Component/HeaderRight.swift +34 -28
  5. package/ios/Navigation/Component/SideBar.swift +15 -8
  6. package/ios/Navigation/StackScreen.swift +13 -12
  7. package/package.json +1 -1
  8. package/build.gradle.kts +0 -11
  9. package/compose/build.gradle.kts +0 -204
  10. package/compose/build.gradle.kts.backup +0 -204
  11. package/compose/compose.podspec +0 -47
  12. package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +0 -19
  13. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +0 -6
  14. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +0 -12
  15. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +0 -7
  16. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -153
  17. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +0 -6
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -52
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -124
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -183
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -207
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -39
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -293
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -916
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +0 -269
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -110
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -378
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +0 -56
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +0 -157
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -81
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -32
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -338
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +0 -175
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -357
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -115
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -89
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -128
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +0 -209
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -542
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -49
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -86
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -125
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -231
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -189
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -478
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -114
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -265
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -235
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -223
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -265
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -185
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -343
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +0 -108
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -56
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -41
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -84
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -40
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -361
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -108
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -308
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -59
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +0 -87
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +0 -21
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -90
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +0 -330
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +0 -220
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +0 -473
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +0 -122
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +0 -207
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -92
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -560
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -86
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -125
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -207
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +0 -501
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -176
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +0 -182
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -193
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -236
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -180
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -325
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +0 -20
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +0 -18
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -16
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -194
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -283
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -54
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -50
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +0 -260
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -41
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -52
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -66
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +0 -104
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -48
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -338
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +0 -58
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -137
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -106
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -205
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -393
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +0 -8
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -708
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +0 -148
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +0 -35
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -218
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -231
  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/bottomtab/DuoBarCompression.kt +0 -98
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +0 -312
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -169
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -408
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -103
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +0 -106
  124. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +0 -74
  125. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -366
  126. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -39
  127. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -363
  128. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +0 -185
  129. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -133
  130. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +0 -127
  131. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +0 -12
  132. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -91
  133. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +0 -9
  134. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -74
  135. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +0 -13
  136. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
  137. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -55
  138. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
  139. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -118
  140. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +0 -78
  141. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +0 -45
  142. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +0 -32
  143. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +0 -38
  144. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +0 -66
  145. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -217
  146. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +0 -94
  147. package/gradle/libs.versions.toml +0 -69
  148. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  149. package/gradle/wrapper/gradle-wrapper.properties +0 -8
  150. package/gradle.properties +0 -26
  151. package/gradlew +0 -252
  152. package/gradlew.bat +0 -94
  153. package/ios-demo/README.md +0 -120
  154. package/settings.gradle.kts +0 -64
@@ -1,330 +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.*
8
- import androidx.compose.foundation.shape.RoundedCornerShape
9
- import androidx.compose.runtime.*
10
- import androidx.compose.ui.Alignment
11
- import androidx.compose.ui.Modifier
12
- import androidx.compose.ui.draw.clip
13
- import androidx.compose.ui.input.pointer.pointerInput
14
- import androidx.compose.ui.layout.onGloballyPositioned
15
- import androidx.compose.ui.platform.LocalDensity
16
- import androidx.compose.ui.text.style.TextAlign
17
- import androidx.compose.ui.unit.IntOffset
18
- import androidx.compose.ui.unit.dp
19
- import vn.momo.kits.application.IsShowBaseLineDebug
20
- import vn.momo.kits.const.*
21
- import vn.momo.kits.modifier.conditional
22
- import kotlin.math.abs
23
- import kotlin.math.min
24
- import kotlin.math.roundToInt
25
-
26
- private val THUMB_SIZE = 20.dp
27
- private val THUMB_BORDER = 3.dp
28
- private val TRACK_HEIGHT = 4.dp
29
- private val LABEL_PAD_H = Spacing.XS
30
- private val LABEL_PAD_V = Spacing.XXS
31
- private val LABEL_GAP = Spacing.XS // space between label and thumb top
32
-
33
-
34
- private fun clamp(value: Float, min: Float, max: Float): Float =
35
- min(kotlin.math.max(value, min), max)
36
-
37
- private fun getValueForPosition(
38
- positionInView: Float,
39
- containerWidth: Float,
40
- thumbWidth: Float,
41
- min: Float,
42
- max: Float,
43
- step: Float,
44
- ): Float {
45
- val availableSpace = containerWidth - thumbWidth
46
- if (availableSpace <= 0f || max <= min) return min
47
- var relPosition = (positionInView - thumbWidth / 2f) / availableSpace
48
- // A zero step would make the snap math NaN, and NaN.roundToInt() throws.
49
- if (step <= 0f) return clamp(min + relPosition * (max - min), min, max)
50
- val relStepUnit = step / (max - min)
51
- val relOffset = relPosition % relStepUnit
52
- relPosition -= relOffset
53
- if (relOffset / relStepUnit >= 0.5f) relPosition += relStepUnit
54
- return clamp(min + (relPosition / relStepUnit).roundToInt() * step, min, max)
55
- }
56
-
57
- /** Direct port of RN isLowCloser(). */
58
- private fun isLowCloser(downX: Float, lowPosition: Float, highPosition: Float): Boolean {
59
- if (lowPosition == highPosition) return downX < lowPosition
60
- return abs(downX - lowPosition) < abs(downX - highPosition)
61
- }
62
-
63
- private fun valueToFraction(value: Float, min: Float, max: Float): Float {
64
- if (max == min) return 0f
65
- return clamp((value - min) / (max - min), 0f, 1f)
66
- }
67
-
68
- private fun formatLabel(value: Float): String =
69
- if (value == value.toLong().toFloat()) value.toLong().toString()
70
- else ((value * 10).toLong() / 10.0).toString()
71
-
72
- // ── Component ─────────────────────────────────────────────────────────────────
73
-
74
- /**
75
- * Range slider (or single-thumb when [disableRange] = true), migrated from React Native.
76
- *
77
- * @param min Minimum selectable value.
78
- * @param max Maximum selectable value.
79
- * @param step Snap increment between values.
80
- * @param modifier Modifier applied to the root container.
81
- * @param low Initial lower-thumb value (defaults to [min]).
82
- * @param high Initial upper-thumb value (defaults to [max]).
83
- * @param minRange Minimum required gap between low and high (default 0).
84
- * @param disableRange When true only the low thumb is shown (single-value mode).
85
- * @param floatingLabel When true a value label floats above the active thumb during drag.
86
- * @param disabled Non-interactive; rendered in the disabled colour.
87
- * @param onValueChanged Invoked with (low, high, byUser) on every value change.
88
- * @param onSliderTouchStart Invoked when a drag gesture begins.
89
- * @param onSliderTouchEnd Invoked when a drag gesture ends.
90
- */
91
- @Composable
92
- fun Slider(
93
- min: Float,
94
- max: Float,
95
- step: Float,
96
- modifier: Modifier = Modifier,
97
- low: Float? = null,
98
- high: Float? = null,
99
- minRange: Float = 0f,
100
- disableRange: Boolean = false,
101
- floatingLabel: Boolean = false,
102
- disabled: Boolean = false,
103
- onValueChanged: (low: Float, high: Float, byUser: Boolean) -> Unit = { _, _, _ -> },
104
- onSliderTouchStart: (low: Float, high: Float) -> Unit = { _, _ -> },
105
- onSliderTouchEnd: (low: Float, high: Float) -> Unit = { _, _ -> },
106
- ) {
107
- val theme = AppTheme.current
108
- val density = LocalDensity.current
109
-
110
- // Validated, step-snapped initial values
111
- val initLow = remember(low, min, max, step) {
112
- clamp(low ?: min, min, max)
113
- }
114
- val initHigh = remember(high, min, max, step, disableRange) {
115
- if (disableRange) max else clamp(high ?: max, min, max)
116
- }
117
-
118
- var lowValue by remember { mutableFloatStateOf(initLow) }
119
- var highValue by remember { mutableFloatStateOf(initHigh) }
120
-
121
- // Smoothly animated display fractions
122
- val lowFraction by animateFloatAsState(
123
- targetValue = valueToFraction(lowValue, min, max),
124
- animationSpec = spring(stiffness = 1200f),
125
- label = "lowFraction"
126
- )
127
- val highFraction by animateFloatAsState(
128
- targetValue = valueToFraction(highValue, min, max),
129
- animationSpec = spring(stiffness = 1200f),
130
- label = "highFraction"
131
- )
132
-
133
- var draggingLow by remember { mutableStateOf(true) }
134
- var isDragging by remember { mutableStateOf(false) }
135
-
136
- // pointerInput is not keyed on the callbacks, so read their latest values.
137
- val currentOnValueChanged by rememberUpdatedState(onValueChanged)
138
- val currentOnTouchStart by rememberUpdatedState(onSliderTouchStart)
139
- val currentOnTouchEnd by rememberUpdatedState(onSliderTouchEnd)
140
-
141
- val thumbColor = if (disabled) theme.colors.text.disable else theme.colors.primary
142
- val trackActiveColor = if (disabled) theme.colors.text.disable else theme.colors.primary
143
- val trackInactiveColor = theme.colors.background.default
144
-
145
- // trackWidthPx is the full pixel width of the BoxWithConstraints (incl. thumb padding)
146
- var trackWidthPx by remember { mutableFloatStateOf(0f) }
147
- val thumbSizePx = with(density) { THUMB_SIZE.toPx() }
148
-
149
- Box(
150
- modifier = modifier
151
- .fillMaxWidth()
152
- .conditional(IsShowBaseLineDebug) { border(1.dp, Colors.blue_03) }
153
- ) {
154
- BoxWithConstraints(
155
- modifier = Modifier
156
- .fillMaxWidth()
157
- .onGloballyPositioned { coords -> trackWidthPx = coords.size.width.toFloat() }
158
- // ── Gesture handling ──────────────────────────────────────────
159
- .pointerInput(disabled, min, max, step, minRange, disableRange) {
160
- if (disabled) return@pointerInput
161
-
162
- awaitPointerEventScope {
163
- while (true) {
164
- // Finger down
165
- val downEvent = awaitPointerEvent()
166
- val downChange = downEvent.changes.firstOrNull() ?: continue
167
- val touchX = downChange.position.x
168
-
169
- // Pick the closer thumb
170
- val lowCenterX = thumbSizePx / 2f +
171
- valueToFraction(lowValue, min, max) * (trackWidthPx - thumbSizePx)
172
- val highCenterX = thumbSizePx / 2f +
173
- valueToFraction(highValue, min, max) * (trackWidthPx - thumbSizePx)
174
-
175
- draggingLow = disableRange || isLowCloser(touchX, lowCenterX, highCenterX)
176
- isDragging = true
177
- currentOnTouchStart(lowValue, highValue)
178
-
179
- // Apply initial press
180
- applyDrag(touchX, trackWidthPx, thumbSizePx,
181
- min, max, step, minRange, draggingLow,
182
- lowValue, highValue) { nl, nh ->
183
- lowValue = nl
184
- highValue = nh
185
- currentOnValueChanged(nl, nh, true)
186
- }
187
-
188
- // Track move / release
189
- while (true) {
190
- val moveEvent = awaitPointerEvent()
191
- val moveChange = moveEvent.changes.firstOrNull() ?: break
192
- moveChange.consume()
193
-
194
- if (!moveChange.pressed) {
195
- isDragging = false
196
- currentOnTouchEnd(lowValue, highValue)
197
- break
198
- }
199
-
200
- applyDrag(moveChange.position.x, trackWidthPx, thumbSizePx,
201
- min, max, step, minRange, draggingLow,
202
- lowValue, highValue) { nl, nh ->
203
- lowValue = nl
204
- highValue = nh
205
- currentOnValueChanged(nl, nh, true)
206
- }
207
- }
208
- }
209
- }
210
- }
211
- ) {
212
- val trackWidth = maxWidth
213
- val availableWidth = trackWidth - THUMB_SIZE
214
-
215
- // ── Floating label ────────────────────────────────────────────────
216
- if (floatingLabel && isDragging) {
217
- val activeFraction = if (draggingLow) lowFraction else highFraction
218
- val activeValue = if (draggingLow) lowValue else highValue
219
- val labelOffsetDp = availableWidth * activeFraction
220
-
221
- Box(
222
- modifier = Modifier
223
- .align(Alignment.TopStart)
224
- .offset { IntOffset(x = with(density) { labelOffsetDp.toPx().roundToInt() }, y = 0) }
225
- ) {
226
- Box(
227
- modifier = Modifier
228
- .background(
229
- color = Colors.black_01,
230
- shape = RoundedCornerShape(Radius.XS)
231
- )
232
- .padding(horizontal = LABEL_PAD_H, vertical = LABEL_PAD_V)
233
- ) {
234
- Text(
235
- text = formatLabel(activeValue),
236
- style = Typography.descriptionXsRegular,
237
- color = theme.colors.text.default,
238
- textAlign = TextAlign.Center
239
- )
240
- }
241
- }
242
- }
243
-
244
- // Top padding pushes track + thumbs below the label when floatingLabel is on
245
- val topPad = if (floatingLabel) THUMB_SIZE + LABEL_GAP else 0.dp
246
-
247
- // ── Inactive track (full width) ───────────────────────────────────
248
- Box(
249
- modifier = Modifier
250
- .align(Alignment.TopStart)
251
- .padding(top = topPad + (THUMB_SIZE - TRACK_HEIGHT) / 2)
252
- .padding(horizontal = THUMB_SIZE / 2)
253
- .fillMaxWidth()
254
- .height(TRACK_HEIGHT)
255
- .clip(RoundedCornerShape(percent = 50))
256
- .background(trackInactiveColor)
257
- )
258
-
259
- // ── Active track (selected range / single value) ──────────────────
260
- val activeStartFraction = if (disableRange) 0f else lowFraction
261
- val activeEndFraction = if (disableRange) lowFraction else highFraction
262
- val activeStartDp = THUMB_SIZE / 2 + availableWidth * activeStartFraction
263
- val activeWidthDp = availableWidth * (activeEndFraction - activeStartFraction)
264
-
265
- if (activeWidthDp > 0.dp) {
266
- Box(
267
- modifier = Modifier
268
- .align(Alignment.TopStart)
269
- .padding(top = topPad + (THUMB_SIZE - TRACK_HEIGHT) / 2)
270
- .offset(x = activeStartDp)
271
- .width(activeWidthDp)
272
- .height(TRACK_HEIGHT)
273
- .clip(RoundedCornerShape(percent = 50))
274
- .background(trackActiveColor)
275
- )
276
- }
277
-
278
- // ── Low thumb ─────────────────────────────────────────────────────
279
- Box(
280
- modifier = Modifier
281
- .align(Alignment.TopStart)
282
- .padding(top = topPad)
283
- .offset(x = availableWidth * lowFraction)
284
- .size(THUMB_SIZE)
285
- .clip(RoundedCornerShape(percent = 100))
286
- .background(thumbColor)
287
- .border(THUMB_BORDER, theme.colors.background.surface, RoundedCornerShape(percent = 100))
288
- )
289
-
290
- // ── High thumb (range mode only) ──────────────────────────────────
291
- if (!disableRange) {
292
- Box(
293
- modifier = Modifier
294
- .align(Alignment.TopStart)
295
- .padding(top = topPad)
296
- .offset(x = availableWidth * highFraction)
297
- .size(THUMB_SIZE)
298
- .clip(RoundedCornerShape(percent = 100))
299
- .background(thumbColor)
300
- .border(THUMB_BORDER, theme.colors.background.surface, RoundedCornerShape(percent = 100))
301
- )
302
- }
303
- }
304
- }
305
- }
306
-
307
- // ── Private drag helper ───────────────────────────────────────────────────────
308
-
309
- private fun applyDrag(
310
- positionX: Float,
311
- trackWidthPx: Float,
312
- thumbSizePx: Float,
313
- min: Float,
314
- max: Float,
315
- step: Float,
316
- minRange: Float,
317
- isLow: Boolean,
318
- currentLow: Float,
319
- currentHigh: Float,
320
- onUpdate: (Float, Float) -> Unit,
321
- ) {
322
- val snapped = getValueForPosition(positionX, trackWidthPx, thumbSizePx, min, max, step)
323
- if (isLow) {
324
- val newLow = clamp(snapped, min, currentHigh - minRange)
325
- if (newLow != currentLow) onUpdate(newLow, currentHigh)
326
- } else {
327
- val newHigh = clamp(snapped, currentLow + minRange, max)
328
- if (newHigh != currentHigh) onUpdate(currentLow, newHigh)
329
- }
330
- }
@@ -1,220 +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.*
6
- import androidx.compose.foundation.shape.CircleShape
7
- import androidx.compose.foundation.shape.RoundedCornerShape
8
- import androidx.compose.foundation.text.BasicTextField
9
- import androidx.compose.foundation.text.KeyboardOptions
10
- import androidx.compose.runtime.*
11
- import androidx.compose.ui.Alignment
12
- import androidx.compose.ui.Modifier
13
- import androidx.compose.ui.graphics.Color
14
- import androidx.compose.ui.text.TextStyle
15
- import androidx.compose.ui.text.font.FontWeight
16
- import androidx.compose.ui.text.input.KeyboardType
17
- import androidx.compose.ui.text.style.TextAlign
18
- import androidx.compose.ui.unit.Dp
19
- import androidx.compose.ui.unit.dp
20
- import androidx.compose.ui.unit.sp
21
- import vn.momo.kits.application.IsShowBaseLineDebug
22
- import vn.momo.kits.const.*
23
- import vn.momo.kits.const.getFontFamily
24
- import vn.momo.kits.modifier.activeOpacityClickable
25
- import vn.momo.kits.modifier.conditional
26
-
27
- enum class StepperSize {
28
- LARGE,
29
- SMALL
30
- }
31
-
32
- private data class StepperSizeSpec(
33
- val buttonSize: Dp,
34
- val iconSize: Dp,
35
- val numberMinWidth: Dp,
36
- val numberHeight: Dp,
37
- )
38
-
39
- private val stepperSizeSpecs: Map<StepperSize, StepperSizeSpec> = mapOf(
40
- StepperSize.LARGE to StepperSizeSpec(
41
- buttonSize = 36.dp,
42
- iconSize = 22.dp,
43
- numberMinWidth = 32.dp,
44
- numberHeight = 28.dp,
45
- ),
46
- StepperSize.SMALL to StepperSizeSpec(
47
- buttonSize = 28.dp,
48
- iconSize = 18.dp,
49
- numberMinWidth = 28.dp,
50
- numberHeight = 24.dp,
51
- ),
52
- )
53
-
54
- @Composable
55
- private fun StepperButton(
56
- sign: String = "+",
57
- size: StepperSize = StepperSize.LARGE,
58
- disabled: Boolean = false,
59
- onPress: () -> Unit = {},
60
- modifier: Modifier = Modifier,
61
- ) {
62
- val theme = AppTheme.current
63
- val specs = remember(size) { stepperSizeSpecs[size] ?: stepperSizeSpecs[StepperSize.LARGE]!! }
64
-
65
- val iconColor: Color = if (disabled) theme.colors.text.disable else theme.colors.primary
66
- val iconName: String =
67
- if (sign == "-") "24_navigation_minus_circle" else "24_navigation_plus_circle"
68
-
69
- val clickableModifier: Modifier = if (!disabled) {
70
- Modifier.activeOpacityClickable(onClick = onPress)
71
- } else {
72
- Modifier
73
- }
74
-
75
- Box(
76
- modifier = modifier
77
- .size(specs.buttonSize)
78
- .background(color = theme.colors.background.disable, shape = CircleShape)
79
- .then(clickableModifier)
80
- .conditional(IsShowBaseLineDebug) { border(1.dp, Colors.blue_03) },
81
- contentAlignment = Alignment.Center,
82
- ) {
83
- Icon(
84
- source = iconName,
85
- size = specs.iconSize,
86
- color = iconColor,
87
- )
88
- }
89
- }
90
-
91
- @Composable
92
- private fun StepperNumberView(
93
- value: Int,
94
- size: StepperSize = StepperSize.LARGE,
95
- disabled: Boolean = false,
96
- editable: Boolean = false,
97
- onValueChange: (String) -> Unit = {},
98
- modifier: Modifier = Modifier,
99
- ) {
100
- val theme = AppTheme.current
101
- val specs = remember(size) { stepperSizeSpecs[size] ?: stepperSizeSpecs[StepperSize.LARGE]!! }
102
-
103
- val textColor: Color = if (disabled) theme.colors.text.disable else theme.colors.text.default
104
- val borderColor: Color =
105
- if (disabled) theme.colors.border.disable else theme.colors.border.default
106
-
107
- val scaledFontSize = scaleSize(12.sp)
108
-
109
- val textStyle = TextStyle(
110
- color = textColor,
111
- fontSize = scaledFontSize,
112
- textAlign = TextAlign.Center,
113
- fontFamily = getFontFamily(theme.font, FontWeight.Normal),
114
- )
115
-
116
- Box(
117
- modifier = modifier
118
- .defaultMinSize(minWidth = specs.numberMinWidth)
119
- .height(specs.numberHeight)
120
- .border(
121
- width = 1.dp,
122
- color = borderColor,
123
- shape = RoundedCornerShape(Radius.S),
124
- )
125
- .padding(horizontal = Spacing.XS)
126
- .conditional(IsShowBaseLineDebug) { border(1.dp, Colors.blue_03) },
127
- contentAlignment = Alignment.Center,
128
- ) {
129
- BasicTextField(
130
- value = value.toString(),
131
- onValueChange = { if (editable) onValueChange(it) },
132
- enabled = editable && !disabled,
133
- readOnly = !editable,
134
- singleLine = true,
135
- textStyle = textStyle,
136
- keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
137
- decorationBox = { innerTextField -> innerTextField() },
138
- )
139
- }
140
- }
141
-
142
- @Composable
143
- fun Stepper(
144
- defaultValue: Int = 0,
145
- min: Int = 0,
146
- max: Int = 100,
147
- step: Int = 1,
148
- disabled: Any = false,
149
- size: StepperSize = StepperSize.LARGE,
150
- editable: Boolean = false,
151
- onValueChange: (Int) -> Unit = {},
152
- modifier: Modifier = Modifier,
153
- ) {
154
- var value by remember(defaultValue) { mutableStateOf(defaultValue.coerceIn(min, max)) }
155
-
156
- fun applyValue(raw: String) {
157
- var parsed = raw.toIntOrNull() ?: 0
158
- parsed = parsed.coerceIn(min, max)
159
- value = parsed
160
- onValueChange(parsed)
161
- }
162
-
163
- // Resolve per-part disabled state — mirrors RN getViewDisabledStatus()
164
- val disabledMinus: Boolean
165
- val disabledPlus: Boolean
166
- val disabledNumber: Boolean
167
-
168
- when (disabled) {
169
- is BooleanArray if disabled.size >= 2 -> {
170
- disabledMinus = disabled[0]
171
- disabledPlus = disabled[1]
172
- disabledNumber = disabled[0] && disabled[1]
173
- }
174
-
175
- is Boolean if disabled -> {
176
- disabledMinus = true
177
- disabledPlus = true
178
- disabledNumber = true
179
- }
180
-
181
- else -> {
182
- disabledMinus = false
183
- disabledPlus = false
184
- disabledNumber = false
185
- }
186
- }
187
-
188
- // Auto-disable buttons when the value is already at a bound
189
- val effectiveMinusDisabled = disabledMinus || value <= min
190
- val effectivePlusDisabled = disabledPlus || value >= max
191
-
192
- Row(
193
- modifier = modifier
194
- .conditional(IsShowBaseLineDebug) { border(1.dp, Colors.blue_03) },
195
- verticalAlignment = Alignment.CenterVertically,
196
- ) {
197
- StepperButton(
198
- sign = "-",
199
- size = size,
200
- disabled = effectiveMinusDisabled,
201
- onPress = { applyValue((value - step).toString()) },
202
- )
203
-
204
- StepperNumberView(
205
- value = value,
206
- size = size,
207
- disabled = disabledNumber,
208
- editable = editable,
209
- onValueChange = { applyValue(it) },
210
- modifier = Modifier.padding(horizontal = Spacing.S),
211
- )
212
-
213
- StepperButton(
214
- sign = "+",
215
- size = size,
216
- disabled = effectivePlusDisabled,
217
- onPress = { applyValue((value + step).toString()) },
218
- )
219
- }
220
- }