@momo-kits/native-kits 0.162.2-beta.21 → 0.162.2-beta.22-debug

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 (138) hide show
  1. package/build.gradle.kts +11 -0
  2. package/compose/build.gradle.kts +190 -0
  3. package/compose/build.gradle.kts.backup +190 -0
  4. package/compose/compose.podspec +47 -0
  5. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +6 -0
  6. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +123 -0
  7. package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
  8. package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
  9. package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
  10. package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
  11. package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
  12. package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
  13. package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
  14. package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
  15. package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
  16. package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
  17. package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
  18. package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
  19. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
  20. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +52 -0
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +101 -0
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +183 -0
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +207 -0
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +39 -0
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +86 -0
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +76 -0
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +76 -0
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +294 -0
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +826 -0
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +107 -0
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +378 -0
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +157 -0
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +82 -0
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +32 -0
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +338 -0
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +175 -0
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +383 -0
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +113 -0
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +89 -0
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +128 -0
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +209 -0
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +543 -0
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +23 -0
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +76 -0
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +140 -0
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +231 -0
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +112 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +477 -0
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +114 -0
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +214 -0
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +240 -0
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +216 -0
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +259 -0
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +182 -0
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +343 -0
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +108 -0
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +56 -0
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +41 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +84 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +40 -0
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +328 -0
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +97 -0
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +310 -0
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +59 -0
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +87 -0
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +17 -0
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +90 -0
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +323 -0
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +217 -0
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +473 -0
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +122 -0
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +199 -0
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +91 -0
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +470 -0
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +88 -0
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +131 -0
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +203 -0
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +498 -0
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +175 -0
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +182 -0
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +193 -0
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +234 -0
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +188 -0
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +306 -0
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +16 -0
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +188 -0
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +270 -0
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +2 -0
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +35 -0
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +2 -0
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +52 -0
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +66 -0
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +104 -0
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +48 -0
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +253 -0
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +129 -0
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +98 -0
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +154 -0
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +360 -0
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +8 -0
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +562 -0
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +154 -0
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +225 -0
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +169 -0
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +320 -0
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +81 -0
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +309 -0
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +34 -0
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +360 -0
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +132 -0
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +128 -0
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +70 -0
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +58 -0
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
  124. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +55 -0
  125. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +15 -0
  126. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +105 -0
  127. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +32 -0
  128. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +184 -0
  129. package/gradle/libs.versions.toml +69 -0
  130. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  131. package/gradle/wrapper/gradle-wrapper.properties +8 -0
  132. package/gradle.properties +26 -0
  133. package/gradlew +252 -0
  134. package/gradlew.bat +94 -0
  135. package/ios/Button/Button.swift +47 -23
  136. package/local.properties +8 -0
  137. package/package.json +1 -3
  138. package/settings.gradle.kts +64 -0
@@ -0,0 +1,383 @@
1
+ package vn.momo.kits.components
2
+
3
+ import vn.momo.kits.modifier.InternalApi
4
+
5
+ import androidx.compose.animation.core.animateDpAsState
6
+ import androidx.compose.animation.core.animateFloatAsState
7
+ import androidx.compose.animation.core.tween
8
+ import androidx.compose.foundation.background
9
+ import androidx.compose.foundation.border
10
+ import androidx.compose.foundation.clickable
11
+ import androidx.compose.foundation.interaction.MutableInteractionSource
12
+ import androidx.compose.foundation.interaction.collectIsPressedAsState
13
+ import androidx.compose.foundation.layout.*
14
+ import androidx.compose.foundation.shape.RoundedCornerShape
15
+ import androidx.compose.runtime.Composable
16
+ import androidx.compose.runtime.getValue
17
+ import androidx.compose.runtime.remember
18
+ import androidx.compose.ui.Alignment
19
+ import androidx.compose.ui.Modifier
20
+ import androidx.compose.ui.draw.alpha
21
+ import androidx.compose.ui.draw.clip
22
+ import androidx.compose.ui.graphics.Color
23
+ import androidx.compose.ui.semantics.contentDescription
24
+ import androidx.compose.ui.semantics.semantics
25
+ import androidx.compose.ui.text.TextStyle
26
+ import androidx.compose.ui.text.style.TextOverflow
27
+ import androidx.compose.ui.unit.Dp
28
+ import androidx.compose.ui.unit.dp
29
+ import vn.momo.kits.application.IsShowBaseLineDebug
30
+ import vn.momo.kits.const.*
31
+ import vn.momo.kits.modifier.conditional
32
+ import vn.momo.kits.platform.LottieAnimation
33
+
34
+ enum class ButtonType {
35
+ PRIMARY,
36
+ SECONDARY,
37
+ TONAL,
38
+ OUTLINE,
39
+ DANGER,
40
+ TEXT,
41
+ DISABLED
42
+ }
43
+
44
+ data class ButtonSpecs(
45
+ val height: Dp,
46
+ val radius: Dp,
47
+ val padding: Dp,
48
+ val width: Dp,
49
+ )
50
+
51
+ enum class Size(val value: ButtonSpecs) {
52
+ LARGE(
53
+ ButtonSpecs(
54
+ height = 48.dp,
55
+ radius = Radius.S,
56
+ padding = Spacing.L,
57
+ width = 128.dp
58
+ )
59
+ ),
60
+ MEDIUM(
61
+ ButtonSpecs(
62
+ height = 36.dp,
63
+ radius = Radius.S,
64
+ padding = Spacing.M,
65
+ width = 80.dp
66
+ )
67
+ ),
68
+ SMALL(
69
+ ButtonSpecs(
70
+ height = 28.dp,
71
+ radius = Radius.S,
72
+ padding = Spacing.S,
73
+ width = 60.dp
74
+ )
75
+ )
76
+ }
77
+
78
+ private val styleCache = mapOf(
79
+ Size.LARGE to Typography.actionDefaultBold,
80
+ Size.MEDIUM to Typography.actionSBold,
81
+ Size.SMALL to Typography.actionXsBold
82
+ )
83
+
84
+ private val iconSizeCache = mapOf(
85
+ Size.LARGE to 24.dp,
86
+ Size.MEDIUM to 16.dp,
87
+ Size.SMALL to 16.dp
88
+ )
89
+
90
+ private val iconSpaceCache = mapOf(
91
+ Size.SMALL to Spacing.XS,
92
+ Size.MEDIUM to Spacing.S,
93
+ Size.LARGE to Spacing.S
94
+ )
95
+
96
+ private fun getStyle(size: Size): TextStyle {
97
+ return styleCache[size] ?: Typography.actionDefaultBold
98
+ }
99
+
100
+ @InternalApi
101
+ fun getIconSize(size: Size): Dp = iconSizeCache[size] ?: 24.dp
102
+ private fun getIconSpace(size: Size): Dp = iconSpaceCache[size] ?: Spacing.S
103
+
104
+ @InternalApi
105
+ @Composable
106
+ fun getTextColor(loading: Boolean, type: ButtonType): Color {
107
+ val theme = AppTheme.current
108
+
109
+ return remember(type, theme, loading) {
110
+ getButtonColor(type, theme).withLoading(loading)
111
+ }
112
+ }
113
+
114
+ internal fun getButtonColor(type: ButtonType, theme: Theme): Color {
115
+ return when (type) {
116
+ ButtonType.DISABLED -> theme.colors.text.disable
117
+ ButtonType.PRIMARY -> Colors.black_01
118
+ ButtonType.SECONDARY -> theme.colors.text.default
119
+ ButtonType.OUTLINE -> theme.colors.primary
120
+ ButtonType.TONAL -> theme.colors.primary
121
+ ButtonType.DANGER -> Colors.black_01
122
+ ButtonType.TEXT -> theme.colors.primary
123
+ }
124
+ }
125
+
126
+
127
+ @Composable
128
+ internal fun RenderTitle(size: Size, title: String = "", textColor: Color) {
129
+ val style = remember(size) { getStyle(size) }
130
+ Text(
131
+ style = style,
132
+ text = title,
133
+ color = textColor,
134
+ overflow = TextOverflow.Ellipsis,
135
+ maxLines = 1
136
+ )
137
+ }
138
+
139
+ @Composable
140
+ @InternalApi
141
+ fun RenderIcon(
142
+ size: Size,
143
+ isIconLeft: Boolean,
144
+ useTintColor: Boolean = true,
145
+ icon: String = "",
146
+ forceLoading: Boolean = false,
147
+ textColor: Color? = null,
148
+ iconOnly: Boolean = false,
149
+ ) {
150
+ val iconSize = remember(size) { getIconSize(size) }
151
+ val margin = remember(size, iconOnly) { if (iconOnly) 0.dp else getIconSpace(size) }
152
+ val color = if (useTintColor) textColor else Color.Unspecified
153
+
154
+ val modifier = Modifier.padding(
155
+ start = if (isIconLeft) 0.dp else margin,
156
+ end = if (isIconLeft) margin else 0.dp
157
+ )
158
+
159
+ if (forceLoading) {
160
+ Box(modifier) {
161
+ LottieAnimation(
162
+ modifier = Modifier.size(iconSize),
163
+ bgColor = null,
164
+ tintColor = color,
165
+ path = "files/lottie_circle_loader",
166
+ useCompose = true
167
+ )
168
+ }
169
+ } else if (icon.isNotEmpty()) {
170
+ Icon(
171
+ source = icon,
172
+ color = color,
173
+ size = iconSize,
174
+ modifier = modifier
175
+ )
176
+ }
177
+ }
178
+
179
+ private fun shouldLoadingOnLeft(iconLeft: String, iconRight: String): Boolean {
180
+ val hasLeft = iconLeft.isNotEmpty()
181
+ val hasRight = iconRight.isNotEmpty()
182
+ return when {
183
+ !hasLeft && !hasRight -> true
184
+ hasLeft && !hasRight -> true
185
+ !hasLeft && hasRight -> false
186
+ hasLeft && hasRight -> false
187
+ else -> true
188
+ }
189
+ }
190
+
191
+ @InternalApi
192
+ @Composable
193
+ fun getTypeStyle(
194
+ type: ButtonType,
195
+ color: Color? = AppTheme.current.colors.primary,
196
+ size: Size,
197
+ bgColor: Color,
198
+ ): Modifier {
199
+ val theme = AppTheme.current
200
+ val radius = remember(size) { size.value.radius }
201
+ val modifier = Modifier.background(bgColor)
202
+
203
+ return remember(type, color, theme, radius, bgColor) {
204
+ when (type) {
205
+ ButtonType.DISABLED -> modifier
206
+ .border(0.dp, Color.Unspecified, RoundedCornerShape(radius))
207
+
208
+ ButtonType.PRIMARY -> modifier
209
+ .border(0.dp, Color.Unspecified, RoundedCornerShape(radius))
210
+
211
+ ButtonType.SECONDARY -> modifier
212
+ .border(1.dp, theme.colors.border.default, RoundedCornerShape(radius))
213
+
214
+ ButtonType.OUTLINE -> modifier
215
+ .border(1.dp, color ?: theme.colors.primary, RoundedCornerShape(radius))
216
+
217
+ ButtonType.TONAL -> modifier
218
+ .border(0.dp, Color.Unspecified, RoundedCornerShape(radius))
219
+
220
+ ButtonType.DANGER -> modifier
221
+ .border(0.dp, Color.Unspecified, RoundedCornerShape(radius))
222
+
223
+ ButtonType.TEXT -> modifier
224
+ }
225
+ }
226
+ }
227
+
228
+ @InternalApi
229
+ @Composable
230
+ fun getButtonBackgroundColor(
231
+ loading: Boolean,
232
+ type: ButtonType
233
+ ): Color {
234
+ val theme = AppTheme.current
235
+
236
+ return remember(loading, type, theme) {
237
+ when (type) {
238
+ ButtonType.DISABLED -> theme.colors.background.disable.withLoading(loading)
239
+ ButtonType.PRIMARY -> theme.colors.primary.withLoading(loading)
240
+ ButtonType.SECONDARY -> theme.colors.background.surface.withLoading(loading)
241
+ ButtonType.OUTLINE -> theme.colors.background.surface.withLoading(loading)
242
+ ButtonType.TONAL -> theme.colors.background.tonal.withLoading(loading)
243
+ ButtonType.DANGER -> theme.colors.error.primary.withLoading(loading)
244
+ ButtonType.TEXT -> Color.Unspecified
245
+ }
246
+ }
247
+ }
248
+
249
+ private fun Color.withLoading(loading: Boolean): Color =
250
+ this.copy(alpha = if (loading) 0.75f else 1f)
251
+
252
+ /**
253
+ * When [iconOnly] is true the label is hidden and the button collapses to a compact,
254
+ * icon-sized box (it ignores [isFull] and always wraps its content). [title] stays
255
+ * required and is exposed as the button's accessibility label. Supply the glyph via
256
+ * [iconLeft] (preferred) or [iconRight]; while [loading] the spinner replaces the icon.
257
+ */
258
+ @Composable
259
+ fun Button(
260
+ onClick: () -> Unit,
261
+ type: ButtonType = ButtonType.PRIMARY,
262
+ size: Size = Size.LARGE,
263
+ iconRight: String = "",
264
+ iconLeft: String = "",
265
+ title: String = "Button",
266
+ loading: Boolean = false,
267
+ useTintColor: Boolean = true,
268
+ isFull: Boolean = true,
269
+ iconOnly: Boolean = false,
270
+ modifier: Modifier = Modifier,
271
+ ) {
272
+ val radius = remember(size) { size.value.radius }
273
+ val isEnabled = remember(type) { type != ButtonType.DISABLED }
274
+ val loadingOnLeft = remember(iconLeft, iconRight) {
275
+ shouldLoadingOnLeft(iconLeft, iconRight)
276
+ }
277
+ val iconOnlyIcon = remember(iconLeft, iconRight) { iconLeft.ifEmpty { iconRight } }
278
+
279
+ val sizeSpecs = remember(size) { size.value }
280
+
281
+ val interactionSource = remember { MutableInteractionSource() }
282
+ val isPressed by interactionSource.collectIsPressedAsState()
283
+
284
+ val animatedPadding by animateDpAsState(
285
+ targetValue = if (isPressed && isEnabled && !loading) 2.dp else 0.dp,
286
+ animationSpec = tween(100),
287
+ label = "pressPadding"
288
+ )
289
+
290
+ val targetAlpha = if (isPressed && isEnabled && !loading) 0.5f else 1f
291
+ val alpha by animateFloatAsState(
292
+ targetValue = targetAlpha,
293
+ animationSpec = tween(100),
294
+ label = "buttonPressAlpha"
295
+ )
296
+
297
+ val clickableModifier =
298
+ if (isEnabled && !loading) {
299
+ modifier
300
+ .clip(RoundedCornerShape(radius))
301
+ .clickable(
302
+ enabled = isEnabled && !loading,
303
+ interactionSource = interactionSource,
304
+ indication = null,
305
+ onClick = onClick
306
+ )
307
+ .alpha(alpha)
308
+ } else {
309
+ modifier
310
+ .clip(RoundedCornerShape(radius))
311
+ }
312
+
313
+ val bgColor = getButtonBackgroundColor(loading, type)
314
+ val textColor = getTextColor(loading, type)
315
+
316
+ val rootModifier = if (isFull && !iconOnly) {
317
+ Modifier
318
+ .fillMaxWidth()
319
+ .wrapContentHeight()
320
+ } else {
321
+ Modifier
322
+ }
323
+
324
+ Box(
325
+ modifier = rootModifier
326
+ .then(clickableModifier)
327
+ .conditional(iconOnly) {
328
+ semantics(mergeDescendants = true) { contentDescription = title }
329
+ },
330
+ contentAlignment = Alignment.Center
331
+ ) {
332
+ // Visual background (shrinks on press)
333
+ Box(
334
+ modifier = Modifier
335
+ .matchParentSize()
336
+ .padding(horizontal = animatedPadding)
337
+ .clip(RoundedCornerShape(radius))
338
+ .then(getTypeStyle(type, size = size, bgColor = bgColor))
339
+ .conditional(IsShowBaseLineDebug) {
340
+ border(1.dp, Colors.blue_03)
341
+ }
342
+ )
343
+
344
+ // Content
345
+ Row(
346
+ modifier = Modifier
347
+ .padding(horizontal = sizeSpecs.padding)
348
+ .height(sizeSpecs.height),
349
+ horizontalArrangement = Arrangement.Center,
350
+ verticalAlignment = Alignment.CenterVertically,
351
+ ) {
352
+ if (iconOnly) {
353
+ RenderIcon(
354
+ size = size,
355
+ isIconLeft = true,
356
+ useTintColor = useTintColor,
357
+ icon = iconOnlyIcon,
358
+ forceLoading = loading,
359
+ textColor = textColor,
360
+ iconOnly = true
361
+ )
362
+ } else {
363
+ RenderIcon(
364
+ size = size,
365
+ isIconLeft = true,
366
+ useTintColor = useTintColor,
367
+ icon = iconLeft,
368
+ forceLoading = loading && loadingOnLeft,
369
+ textColor = textColor
370
+ )
371
+ RenderTitle(size, title, textColor = textColor)
372
+ RenderIcon(
373
+ size = size,
374
+ isIconLeft = false,
375
+ useTintColor = useTintColor,
376
+ icon = iconRight,
377
+ forceLoading = loading && !loadingOnLeft,
378
+ textColor = textColor
379
+ )
380
+ }
381
+ }
382
+ }
383
+ }
@@ -0,0 +1,113 @@
1
+ package vn.momo.kits.components
2
+
3
+ import androidx.compose.animation.core.animateFloatAsState
4
+ import androidx.compose.animation.core.tween
5
+ import androidx.compose.foundation.border
6
+ import androidx.compose.foundation.layout.Box
7
+ import androidx.compose.foundation.layout.PaddingValues
8
+ import androidx.compose.foundation.layout.fillMaxWidth
9
+ import androidx.compose.foundation.pager.HorizontalPager
10
+ import androidx.compose.foundation.pager.rememberPagerState
11
+ import androidx.compose.runtime.*
12
+ import androidx.compose.ui.Modifier
13
+ import androidx.compose.ui.graphics.graphicsLayer
14
+ import androidx.compose.ui.unit.dp
15
+ import kotlinx.coroutines.delay
16
+ import kotlinx.coroutines.isActive
17
+ import vn.momo.kits.application.IsShowBaseLineDebug
18
+ import vn.momo.kits.const.Colors
19
+ import vn.momo.kits.modifier.conditional
20
+ import kotlin.math.absoluteValue
21
+ import kotlin.time.Duration.Companion.milliseconds
22
+
23
+ @Composable
24
+ fun Carousel(
25
+ itemCount: Int,
26
+ modifier: Modifier = Modifier,
27
+ loop: Boolean = false,
28
+ autoplay: Boolean = false,
29
+ autoplayInterval: Long = 3000L,
30
+ scrollEnabled: Boolean = true,
31
+ inactiveSlideOpacity: Float = 1f,
32
+ inactiveSlideScale: Float = 1f,
33
+ contentPadding: PaddingValues = PaddingValues(0.dp),
34
+ onSnapToItem: (Int) -> Unit = {},
35
+ onScrollIndexChanged: (Int) -> Unit = {},
36
+ content: @Composable (index: Int) -> Unit,
37
+ ) {
38
+ if (itemCount <= 0) return
39
+
40
+ val virtualPageCount = if (loop) itemCount * 1000 else itemCount
41
+ val startPage = if (loop) (virtualPageCount / 2) - ((virtualPageCount / 2) % itemCount) else 0
42
+
43
+ val pagerState = rememberPagerState(initialPage = startPage) { virtualPageCount }
44
+
45
+ val realIndex: (Int) -> Int = { page -> page % itemCount }
46
+
47
+ var isUserScrolling by remember { mutableStateOf(false) }
48
+
49
+ if (autoplay && itemCount > 1) {
50
+ LaunchedEffect(autoplayInterval, isUserScrolling) {
51
+ if (isUserScrolling) return@LaunchedEffect
52
+ while (isActive) {
53
+ delay(autoplayInterval.milliseconds)
54
+ if (!isUserScrolling) {
55
+ val nextPage = pagerState.currentPage + 1
56
+ if (nextPage < virtualPageCount) {
57
+ pagerState.animateScrollToPage(nextPage)
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+
64
+ LaunchedEffect(pagerState) {
65
+ snapshotFlow { pagerState.isScrollInProgress }.collect { scrolling ->
66
+ isUserScrolling = scrolling
67
+ }
68
+ }
69
+
70
+ LaunchedEffect(pagerState) {
71
+ snapshotFlow { pagerState.currentPage }.collect { page ->
72
+ val real = realIndex(page)
73
+ onSnapToItem(real)
74
+ onScrollIndexChanged(real)
75
+ }
76
+ }
77
+
78
+ HorizontalPager(
79
+ state = pagerState,
80
+ modifier = modifier
81
+ .fillMaxWidth()
82
+ .conditional(IsShowBaseLineDebug) { border(1.dp, Colors.blue_03) },
83
+ contentPadding = contentPadding,
84
+ userScrollEnabled = scrollEnabled,
85
+ beyondViewportPageCount = if (loop) 1 else 0,
86
+ ) { page ->
87
+ val real = realIndex(page)
88
+
89
+ val pageOffset = ((pagerState.currentPage - page) + pagerState.currentPageOffsetFraction).absoluteValue
90
+
91
+ val scale by animateFloatAsState(
92
+ targetValue = if (pageOffset < 0.5f) 1f else inactiveSlideScale,
93
+ animationSpec = tween(150),
94
+ label = "carouselScale"
95
+ )
96
+ val alpha by animateFloatAsState(
97
+ targetValue = if (pageOffset < 0.5f) 1f else inactiveSlideOpacity,
98
+ animationSpec = tween(150),
99
+ label = "carouselAlpha"
100
+ )
101
+
102
+ Box(
103
+ modifier = Modifier
104
+ .graphicsLayer {
105
+ scaleX = scale
106
+ scaleY = scale
107
+ this.alpha = alpha
108
+ }
109
+ ) {
110
+ content(real)
111
+ }
112
+ }
113
+ }
@@ -0,0 +1,89 @@
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.Spacer
8
+ import androidx.compose.foundation.layout.size
9
+ import androidx.compose.foundation.shape.RoundedCornerShape
10
+ import androidx.compose.runtime.Composable
11
+ import androidx.compose.runtime.remember
12
+ import androidx.compose.ui.Alignment
13
+ import androidx.compose.ui.Modifier
14
+ import androidx.compose.ui.graphics.Color
15
+ import androidx.compose.ui.unit.dp
16
+ import vn.momo.kits.application.IsShowBaseLineDebug
17
+ import vn.momo.kits.const.AppTheme
18
+ import vn.momo.kits.const.Colors
19
+ import vn.momo.kits.const.Radius
20
+ import vn.momo.kits.const.Spacing
21
+ import vn.momo.kits.const.Typography
22
+ import vn.momo.kits.modifier.activeOpacityClickable
23
+ import vn.momo.kits.modifier.conditional
24
+
25
+ private val checkboxShape = RoundedCornerShape(Radius.XS)
26
+
27
+ @Composable
28
+ fun CheckBox(
29
+ disabled: Boolean = false,
30
+ checked: Boolean = false,
31
+ onCheckedChange: (Boolean) -> Unit,
32
+ indeterminate: Boolean = false,
33
+ title: String = "",
34
+ ) {
35
+ val theme = AppTheme.current
36
+
37
+ val colors = remember(checked, disabled, theme) {
38
+ when {
39
+ disabled && checked -> Pair(theme.colors.background.tonal, theme.colors.background.tonal)
40
+ disabled -> Pair(theme.colors.border.disable, Color.Transparent)
41
+ checked -> Pair(theme.colors.primary, theme.colors.primary)
42
+ else -> Pair(theme.colors.text.default, Color.Transparent)
43
+ }
44
+ }
45
+
46
+ val (borderColor, backgroundColor) = colors
47
+
48
+ val iconSource = remember(indeterminate) {
49
+ if (indeterminate) "navigation_minus" else "ic_checked"
50
+ }
51
+
52
+ val onClickHandler = remember(onCheckedChange, checked) {
53
+ { onCheckedChange(!checked) }
54
+ }
55
+
56
+ Row(verticalAlignment = Alignment.CenterVertically) {
57
+ Box(
58
+ modifier = Modifier
59
+ .size(20.dp)
60
+ .background(backgroundColor, checkboxShape)
61
+ .border(Spacing.XXS, borderColor, checkboxShape)
62
+ .conditional(IsShowBaseLineDebug) {
63
+ border(1.dp, Colors.blue_03)
64
+ }
65
+ .activeOpacityClickable(
66
+ activeOpacity = 0.8f,
67
+ onClick = onClickHandler,
68
+ enabled = !disabled,
69
+ ),
70
+ ) {
71
+ if (checked) {
72
+ Icon(
73
+ source = iconSource,
74
+ size = 20.dp,
75
+ color = Colors.black_01
76
+ )
77
+ }
78
+ }
79
+
80
+ if (title.isNotEmpty()) {
81
+ Spacer(Modifier.size(Spacing.S))
82
+ Text(
83
+ style = Typography.descriptionDefaultRegular,
84
+ text = title,
85
+ )
86
+ }
87
+ }
88
+
89
+ }
@@ -0,0 +1,128 @@
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.RoundedCornerShape
7
+ import androidx.compose.runtime.Composable
8
+ import androidx.compose.runtime.Immutable
9
+ import androidx.compose.ui.Alignment
10
+ import androidx.compose.ui.Modifier
11
+ import androidx.compose.ui.draw.clip
12
+ import androidx.compose.ui.graphics.Color
13
+ import androidx.compose.ui.text.style.TextAlign
14
+ import androidx.compose.ui.text.style.TextOverflow
15
+ import androidx.compose.ui.unit.dp
16
+ import vn.momo.kits.application.IsShowBaseLineDebug
17
+ import vn.momo.kits.const.*
18
+ import vn.momo.kits.modifier.activeOpacityClickable
19
+ import vn.momo.kits.modifier.conditional
20
+ import vn.momo.kits.modifier.setAutomationId
21
+
22
+ @Composable
23
+ fun Chip(
24
+ modifier: Modifier = Modifier,
25
+ label: String? = "Label",
26
+ iconLeft: String? = null,
27
+ iconRight: String? = null,
28
+ selected: Boolean = false,
29
+ onClick: () -> Unit = {},
30
+ size: ChipSize = ChipSize.LARGE,
31
+ iconLeftTint: Color? = null,
32
+ iconRightTint: Color? = null,
33
+ backgroundColor: Color? = null,
34
+ accessibilityLabel: String? = null,
35
+ ) {
36
+ val theme = AppTheme.current
37
+ val bg = when {
38
+ selected -> theme.colors.background.selected
39
+ backgroundColor != null -> backgroundColor
40
+ else -> Colors.black_03
41
+ }
42
+
43
+ val textColor = if (selected) theme.colors.primary else theme.colors.text.default
44
+ val leftTint = if (selected) theme.colors.primary else iconLeftTint ?: theme.colors.text.default
45
+ val rightTint = if (selected) theme.colors.primary else iconRightTint ?: theme.colors.text.default
46
+
47
+ val dims = when (size) {
48
+ ChipSize.SMALL -> ChipDefaults.Small
49
+ ChipSize.LARGE -> ChipDefaults.Large
50
+ }
51
+
52
+ val (height, horizontal, iconSize, iconSpacing) =
53
+ listOf(scaleSize(dims.height), scaleSize(dims.horizontal), scaleSize(dims.iconSize), scaleSize(dims.iconSpacing))
54
+
55
+ val radius = scaleSize(Radius.L)
56
+
57
+ Row(
58
+ modifier
59
+ .wrapContentWidth()
60
+ .height(height.dp)
61
+ .clip(RoundedCornerShape(radius))
62
+ .background(bg)
63
+ .conditional(selected) {
64
+ Modifier.border(width = 2.dp, color = theme.colors.secondary, shape = RoundedCornerShape(radius))
65
+ }
66
+ .conditional(IsShowBaseLineDebug) {
67
+ border(1.dp, Colors.blue_03)
68
+ }
69
+ .activeOpacityClickable {
70
+ onClick()
71
+ }
72
+ .padding(horizontal = horizontal.dp)
73
+ .conditional(accessibilityLabel != null) {
74
+ setAutomationId(accessibilityLabel.toString())
75
+ },
76
+ verticalAlignment = Alignment.CenterVertically,
77
+ horizontalArrangement = Arrangement.spacedBy(iconSpacing.dp)
78
+ ) {
79
+ if (iconLeft != null) {
80
+ Icon(source = iconLeft, size = dims.iconSize.dp, color = leftTint)
81
+ }
82
+
83
+ if (!label.isNullOrEmpty()) {
84
+ Text(
85
+ text = label,
86
+ textAlign = TextAlign.Center,
87
+ color = textColor,
88
+ style = when (size) {
89
+ ChipSize.SMALL -> Typography.labelSMedium
90
+ ChipSize.LARGE -> Typography.labelDefaultMedium
91
+ },
92
+ maxLines = 1,
93
+ overflow = TextOverflow.Ellipsis,
94
+ modifier = Modifier.weight(1f, fill = false),
95
+ )
96
+ }
97
+
98
+ if (iconRight != null) {
99
+ Icon(source = iconRight, size = iconSize.dp, color = rightTint)
100
+ }
101
+ }
102
+ }
103
+
104
+ enum class ChipSize { SMALL, LARGE }
105
+
106
+ private object ChipDefaults {
107
+ @Immutable
108
+ data class Dimensions(
109
+ val height: Float,
110
+ val horizontal: Float,
111
+ val iconSize: Float,
112
+ val iconSpacing: Float,
113
+ )
114
+
115
+ val Large = Dimensions(
116
+ height = 32f,
117
+ horizontal = 12f,
118
+ iconSize = 20f,
119
+ iconSpacing = 4f,
120
+ )
121
+
122
+ val Small = Dimensions(
123
+ height = 24f,
124
+ horizontal = 10f,
125
+ iconSize = 16f,
126
+ iconSpacing = 4f,
127
+ )
128
+ }