@momo-kits/native-kits 0.0.2-alpha.1 → 0.0.2-alpha.1-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 (143) hide show
  1. package/build.gradle.kts +11 -0
  2. package/compose/build.gradle.kts +189 -0
  3. package/compose/build.gradle.kts.backup +189 -0
  4. package/compose/compose.podspec +47 -0
  5. package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +19 -0
  6. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +6 -0
  7. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +12 -0
  8. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +7 -0
  9. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +151 -0
  10. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +52 -0
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +124 -0
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +183 -0
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +207 -0
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +39 -0
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +86 -0
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +76 -0
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +76 -0
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +293 -0
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +831 -0
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +110 -0
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +378 -0
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +65 -0
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +157 -0
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +82 -0
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +32 -0
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +338 -0
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +175 -0
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +356 -0
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +113 -0
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +89 -0
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +128 -0
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +209 -0
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +542 -0
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +49 -0
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +86 -0
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +125 -0
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +231 -0
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +189 -0
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +475 -0
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +114 -0
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +266 -0
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +240 -0
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +223 -0
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +264 -0
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +185 -0
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +343 -0
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +108 -0
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +56 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +41 -0
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +84 -0
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +40 -0
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +328 -0
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +97 -0
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +310 -0
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +59 -0
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +87 -0
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +21 -0
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +90 -0
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +323 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +220 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +473 -0
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +122 -0
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +199 -0
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +91 -0
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +552 -0
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +88 -0
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +125 -0
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +207 -0
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +498 -0
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +176 -0
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +182 -0
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +193 -0
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +234 -0
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +188 -0
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +325 -0
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +16 -0
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +18 -0
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +22 -0
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +16 -0
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +194 -0
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +282 -0
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +54 -0
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +51 -0
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +41 -0
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +52 -0
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +66 -0
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +104 -0
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +48 -0
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +299 -0
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +58 -0
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +129 -0
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +106 -0
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +211 -0
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +398 -0
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +8 -0
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +595 -0
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +148 -0
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +40 -0
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +184 -0
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +225 -0
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +169 -0
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +357 -0
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +83 -0
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +106 -0
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +75 -0
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +326 -0
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +39 -0
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +364 -0
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +132 -0
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/ToolkitRequests.kt +33 -0
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +135 -0
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +12 -0
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +91 -0
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +9 -0
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +71 -0
  124. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
  125. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +55 -0
  126. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +15 -0
  127. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +105 -0
  128. package/compose/src/commonTest/kotlin/vn/momo/kits/navigation/component/ToolkitRequestsTest.kt +47 -0
  129. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +40 -0
  130. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +45 -0
  131. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +36 -0
  132. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +38 -0
  133. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +66 -0
  134. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +213 -0
  135. package/gradle/libs.versions.toml +71 -0
  136. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  137. package/gradle/wrapper/gradle-wrapper.properties +8 -0
  138. package/gradle.properties +26 -0
  139. package/gradlew +252 -0
  140. package/gradlew.bat +94 -0
  141. package/ios-demo/README.md +120 -0
  142. package/package.json +1 -1
  143. package/settings.gradle.kts +64 -0
@@ -0,0 +1,364 @@
1
+ package vn.momo.kits.navigation.component
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.BasicText
9
+ import androidx.compose.foundation.text.TextAutoSize
10
+ import androidx.compose.runtime.Composable
11
+ import androidx.compose.runtime.Immutable
12
+ import androidx.compose.runtime.remember
13
+ import androidx.compose.ui.Alignment
14
+ import androidx.compose.ui.Modifier
15
+ import androidx.compose.ui.draw.clip
16
+ import androidx.compose.ui.draw.clipToBounds
17
+ import androidx.compose.ui.layout.ContentScale
18
+ import androidx.compose.ui.text.TextStyle
19
+ import androidx.compose.ui.text.font.FontWeight
20
+ import androidx.compose.ui.text.style.TextOverflow
21
+ import androidx.compose.ui.unit.Dp
22
+ import androidx.compose.ui.unit.dp
23
+ import androidx.compose.ui.unit.sp
24
+ import vn.momo.kits.components.Image
25
+ import vn.momo.kits.components.Options
26
+ import vn.momo.kits.components.Skeleton
27
+ import vn.momo.kits.components.Text
28
+ import vn.momo.kits.const.AppTheme
29
+ import vn.momo.kits.const.Colors
30
+ import vn.momo.kits.const.Spacing
31
+ import vn.momo.kits.const.Typography
32
+ import vn.momo.kits.const.getFontFamily
33
+ import vn.momo.kits.modifier.activeOpacityClickable
34
+ import vn.momo.kits.navigation.component.HeaderTitle.User
35
+ import vn.momo.kits.platform.getScreenDimensions
36
+
37
+ @Composable
38
+ internal fun HeaderUser(
39
+ data: HeaderTitle
40
+ ) {
41
+ if (data !is User) return
42
+
43
+ if (data.isLoading) {
44
+ return TitleUserShimmer()
45
+ }
46
+
47
+ val maxWidth = getScreenDimensions().width.dp - 172.dp
48
+
49
+ Row(
50
+ modifier = Modifier
51
+ .fillMaxWidth()
52
+ .padding(vertical = Spacing.XS)
53
+ .activeOpacityClickable(enabled = data.onPress != null, onClick = { data.onPress?.invoke() }),
54
+ horizontalArrangement = Arrangement.SpaceBetween,
55
+ verticalAlignment = Alignment.CenterVertically
56
+ ) {
57
+ Row(
58
+ modifier = Modifier.widthIn(max = maxWidth),
59
+ verticalAlignment = Alignment.CenterVertically
60
+ ) {
61
+ Box(modifier = Modifier.size(Spacing.XXL)) {
62
+ AvatarGroup(urls = AvatarUrls(data.image ?: listOf()), title = data.title)
63
+ if (data.dotColor != null) {
64
+ Box(
65
+ modifier = Modifier
66
+ .align(Alignment.BottomEnd)
67
+ .size(10.dp)
68
+ .clip(CircleShape)
69
+ .background(data.dotColor)
70
+ )
71
+ }
72
+ }
73
+
74
+ Spacer(Modifier.width(6.dp))
75
+
76
+ Column(modifier = Modifier.weight(1f, fill = false)) {
77
+ Row(verticalAlignment = Alignment.CenterVertically) {
78
+ Text(
79
+ text = data.title,
80
+ color = data.tintColor,
81
+ style = Typography.actionXsBold,
82
+ fontFamily = headerFontFamily,
83
+ maxLines = 1,
84
+ overflow = TextOverflow.Ellipsis
85
+ )
86
+ if (data.icons.isNotEmpty()) {
87
+ Spacer(Modifier.width(6.dp))
88
+ Row(verticalAlignment = Alignment.CenterVertically) {
89
+ data.icons.forEach { icon ->
90
+ RemoteIcon16(url = icon)
91
+ }
92
+ }
93
+ }
94
+ }
95
+
96
+ if (!data.subTitle.isNullOrEmpty()) {
97
+ Spacer(Modifier.height(2.dp))
98
+ Text(
99
+ text = data.subTitle,
100
+ color = data.tintColor,
101
+ style = Typography.descriptionXsRegular,
102
+ maxLines = 1,
103
+ overflow = TextOverflow.Ellipsis
104
+ )
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
110
+
111
+ @Composable
112
+ private fun TitleUserShimmer() {
113
+ val theme = AppTheme.current
114
+ Row(
115
+ modifier = Modifier
116
+ .fillMaxWidth()
117
+ .padding(vertical = Spacing.XS),
118
+ verticalAlignment = Alignment.CenterVertically
119
+ ) {
120
+ Box(
121
+ modifier = Modifier
122
+ .size(Spacing.XXL)
123
+ .clip(CircleShape)
124
+ .background(theme.colors.border.default)
125
+ ) {
126
+ Skeleton()
127
+ }
128
+ Spacer(Modifier.width(Spacing.M / 2))
129
+ Column(modifier = Modifier.fillMaxWidth()) {
130
+ Box(
131
+ modifier = Modifier
132
+ .height(18.dp)
133
+ .width(120.dp)
134
+ .clip(RoundedCornerShape(8.dp))
135
+ ) { Skeleton() }
136
+ Spacer(Modifier.height(Spacing.XXS))
137
+ Box(
138
+ modifier = Modifier
139
+ .height(12.dp)
140
+ .width(120.dp)
141
+ .clip(RoundedCornerShape(8.dp))
142
+ ) { Skeleton() }
143
+ }
144
+ }
145
+ }
146
+
147
+ @Composable
148
+ private fun AvatarGroup(
149
+ urls: AvatarUrls?,
150
+ title: String? = null,
151
+ size: Dp = Spacing.XXL
152
+ ) {
153
+ val theme = AppTheme.current
154
+ val modifierBorder = remember {
155
+ Modifier.clip(CircleShape).border(width = (0.5).dp, color = theme.colors.border.default, shape = CircleShape)
156
+ }
157
+ val options = Options(
158
+ contentScale = ContentScale.Crop,
159
+ alignment = Alignment.Center
160
+ )
161
+
162
+ when (urls?.items?.size) {
163
+ 0 -> InitialsAvatar(
164
+ modifier = modifierBorder,
165
+ name = title ?: "",
166
+ fallbackInitials = null,
167
+ size = size
168
+ )
169
+ 1 -> SingleAvatar(options = options, modifier = modifierBorder, urls.items[0], size)
170
+ 2 -> TwoAvatar(options = options, modifier = modifierBorder, urls, size)
171
+ 3 -> ThreeAvatar(options = options, modifier = modifierBorder, urls, size)
172
+ 4 -> FourAvatar(options = options, modifier = modifierBorder, urls, size)
173
+ else -> ManyAvatar(options = options, modifier = modifierBorder, urls!!, size)
174
+ }
175
+ }
176
+
177
+ @Composable
178
+ private fun SingleAvatar(options: Options, modifier: Modifier = Modifier, url: String, size: Dp) {
179
+ Image(
180
+ source = url,
181
+ options = options,
182
+ modifier = modifier
183
+ .size(size)
184
+ )
185
+ }
186
+
187
+ @Composable
188
+ private fun TwoAvatar(options: Options, modifier: Modifier = Modifier, urls: AvatarUrls, size: Dp) {
189
+ val child = 24.dp
190
+ Box(modifier = Modifier.size(size)) {
191
+ Image(
192
+ source = urls.items[0],
193
+ options = options,
194
+ modifier = modifier
195
+ .size(child)
196
+ .align(Alignment.TopEnd)
197
+ )
198
+ Image(
199
+ source = urls.items[1],
200
+ options = options,
201
+ modifier = modifier
202
+ .size(child)
203
+ .align(Alignment.BottomStart)
204
+ )
205
+ }
206
+ }
207
+
208
+ @Composable
209
+ private fun ThreeAvatar(options: Options, modifier: Modifier = Modifier, urls: AvatarUrls, size: Dp) {
210
+ val child = 16.dp
211
+ Box(modifier = Modifier.size(size).padding(2.dp)) {
212
+ Image(
213
+ source = urls.items[0],
214
+ options = options,
215
+ modifier = modifier
216
+ .size(child)
217
+ .align(Alignment.BottomEnd)
218
+ )
219
+ Image(
220
+ source = urls.items[1],
221
+ options = options,
222
+ modifier = modifier
223
+ .size(child)
224
+ .align(Alignment.TopCenter)
225
+ )
226
+ Image(
227
+ source = urls.items[2],
228
+ options = options,
229
+ modifier = modifier
230
+ .size(child)
231
+ .align(Alignment.BottomStart)
232
+ )
233
+ }
234
+ }
235
+
236
+ @Composable
237
+ private fun FourAvatar(options: Options, modifier: Modifier = Modifier, urls: AvatarUrls, size: Dp) {
238
+ val child = 16.dp
239
+ Box(modifier = Modifier.size(size).padding(1.dp)) {
240
+ Image(
241
+ source = urls.items[0],
242
+ options = options,
243
+ modifier = modifier
244
+ .size(child)
245
+ .align(Alignment.BottomStart)
246
+ )
247
+ Image(
248
+ source = urls.items[1],
249
+ options = options,
250
+ modifier = modifier
251
+ .size(child)
252
+ .align(Alignment.BottomEnd)
253
+ )
254
+ Image(
255
+ source = urls.items[2],
256
+ options = options,
257
+ modifier = modifier
258
+ .size(child)
259
+ .align(Alignment.TopEnd)
260
+ )
261
+ Image(
262
+ source = urls.items[3],
263
+ options = options,
264
+ modifier = modifier
265
+ .size(child)
266
+ .align(Alignment.TopStart)
267
+ )
268
+ }
269
+ }
270
+
271
+ @Composable
272
+ private fun ManyAvatar(options: Options, modifier: Modifier = Modifier, urls: AvatarUrls, size: Dp) {
273
+ val child = 16.dp
274
+ val more = urls.items.size - 3
275
+ Box(modifier = Modifier.size(size).padding(1.dp)) {
276
+ Image(
277
+ source = urls.items[0],
278
+ options = options,
279
+ modifier = modifier
280
+ .size(child)
281
+ .align(Alignment.BottomStart)
282
+ )
283
+ Image(
284
+ source = urls.items[1],
285
+ options = options,
286
+ modifier = modifier
287
+ .size(child)
288
+ .align(Alignment.TopEnd)
289
+ )
290
+ Image(
291
+ source = urls.items[2],
292
+ options = options,
293
+ modifier = modifier
294
+ .size(child)
295
+ .align(Alignment.TopStart)
296
+ )
297
+ Box(
298
+ modifier = modifier
299
+ .size(child)
300
+ .align(Alignment.BottomEnd)
301
+ .background(Colors.pink_09)
302
+ .padding(1.dp)
303
+ .clipToBounds(),
304
+ contentAlignment = Alignment.Center
305
+ ) {
306
+ BasicText(
307
+ text = "+$more",
308
+ style = TextStyle(
309
+ color = Colors.pink_03,
310
+ fontFamily = getFontFamily(AppTheme.current.font, FontWeight.Normal)
311
+ ),
312
+ maxLines = 1,
313
+ autoSize = TextAutoSize.StepBased(
314
+ minFontSize = 1.sp,
315
+ maxFontSize = 10.sp,
316
+ stepSize = 1.sp
317
+ )
318
+ )
319
+ }
320
+ }
321
+ }
322
+
323
+ @Composable
324
+ private fun InitialsAvatar(
325
+ modifier: Modifier = Modifier,
326
+ name: String,
327
+ fallbackInitials: String? = null,
328
+ size: Dp = Spacing.XXL
329
+ ) {
330
+ val initials = remember(name, fallbackInitials) {
331
+ fallbackInitials ?: run {
332
+ val words = name.trim().split(Regex("\\s+")).takeLast(2)
333
+ words.joinToString("") { it.firstOrNull()?.uppercase() ?: "" }
334
+ }
335
+ }
336
+ Box(
337
+ modifier = modifier
338
+ .size(size)
339
+ .background(Colors.pink_09),
340
+ contentAlignment = Alignment.Center
341
+ ) {
342
+ Text(
343
+ text = initials,
344
+ color = Colors.pink_03,
345
+ style = Typography.descriptionXsRegular
346
+ )
347
+ }
348
+ }
349
+
350
+ @Composable
351
+ private fun RemoteIcon16(url: String) {
352
+ Image(
353
+ source = url,
354
+ options = Options(
355
+ contentScale = ContentScale.Crop
356
+ ),
357
+ modifier = Modifier
358
+ .size(16.dp)
359
+ .clip(RoundedCornerShape(4.dp)),
360
+ )
361
+ }
362
+
363
+ @Immutable
364
+ private data class AvatarUrls(val items: List<String>)
@@ -0,0 +1,132 @@
1
+ package vn.momo.kits.navigation.component
2
+
3
+ import androidx.compose.animation.core.Animatable
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.fillMaxWidth
8
+ import androidx.compose.foundation.layout.offset
9
+ import androidx.compose.runtime.Composable
10
+ import androidx.compose.runtime.DisposableEffect
11
+ import androidx.compose.runtime.LaunchedEffect
12
+ import androidx.compose.runtime.getValue
13
+ import androidx.compose.runtime.mutableStateOf
14
+ import androidx.compose.runtime.remember
15
+ import androidx.compose.runtime.rememberCoroutineScope
16
+ import androidx.compose.runtime.setValue
17
+ import androidx.compose.ui.Modifier
18
+ import androidx.compose.ui.layout.onGloballyPositioned
19
+ import androidx.compose.ui.platform.LocalDensity
20
+ import androidx.compose.ui.unit.IntOffset
21
+ import androidx.compose.ui.unit.dp
22
+ import kotlinx.coroutines.delay
23
+ import vn.momo.kits.application.IsShowBaseLineDebug
24
+ import vn.momo.kits.const.Colors
25
+ import vn.momo.kits.modifier.conditional
26
+ import kotlinx.coroutines.launch
27
+ import vn.momo.kits.const.AppNavigationBar
28
+ import vn.momo.kits.navigation.LocalFooterHeightPx
29
+ import vn.momo.kits.navigation.LocalNavigator
30
+ import vn.momo.kits.navigation.LocalOptions
31
+ import vn.momo.kits.navigation.OverplayComponentRegistry
32
+ import kotlin.time.Duration.Companion.milliseconds
33
+
34
+ sealed class SnackBar(open val duration: Long? = null) {
35
+ data class Custom(
36
+ val content: @Composable () -> Unit,
37
+ override val duration: Long? = 3000L
38
+ ) : SnackBar(duration)
39
+
40
+ data class Toast(
41
+ override val duration: Long? = 3000L
42
+ ) : SnackBar(duration)
43
+ }
44
+
45
+ @Composable
46
+ internal fun SnackBar(
47
+ data: SnackBar,
48
+ onDismiss: (() -> Unit)?
49
+ ) {
50
+ val navigator = LocalNavigator.current
51
+ val options = LocalOptions.current
52
+ val density = LocalDensity.current
53
+ val footerHeightPxState = LocalFooterHeightPx.current
54
+ val navigationBar = AppNavigationBar.current
55
+
56
+ val footerHeight = if (options.footerComponent != null) {
57
+ footerHeightPxState.value
58
+ } else {
59
+ with(density){ navigationBar.toPx() }
60
+ }.toInt()
61
+
62
+ var startPosition by remember { mutableStateOf(Float.MAX_VALUE) }
63
+ val targetPosition = 0f
64
+
65
+ var offsetY by remember { mutableStateOf(Animatable(startPosition)) }
66
+ val coroutineScope = rememberCoroutineScope()
67
+
68
+ suspend fun openEvent() {
69
+ offsetY.snapTo(startPosition)
70
+ offsetY.animateTo(
71
+ targetValue = targetPosition,
72
+ animationSpec = tween(
73
+ durationMillis = 350,
74
+ )
75
+ )
76
+ }
77
+
78
+ fun closeEvent() {
79
+ coroutineScope.launch {
80
+ offsetY.animateTo(
81
+ targetValue = startPosition,
82
+ animationSpec = tween(
83
+ durationMillis = 200,
84
+ )
85
+ )
86
+ navigator.hideSnackBar()
87
+ }
88
+ }
89
+
90
+ LaunchedEffect(startPosition) {
91
+ if (startPosition != Float.MAX_VALUE){
92
+ openEvent()
93
+ }
94
+ }
95
+
96
+ LaunchedEffect(data.duration) {
97
+ val duration = data.duration
98
+ if (duration != null) {
99
+ delay(duration.milliseconds)
100
+ navigator.hideSnackBar()
101
+ }
102
+ }
103
+
104
+ DisposableEffect(Unit) {
105
+ OverplayComponentRegistry.bindClose {
106
+ closeEvent()
107
+ }
108
+ onDispose {
109
+ onDismiss?.invoke()
110
+ }
111
+ }
112
+
113
+ Box(
114
+ modifier = Modifier
115
+ .offset { IntOffset(0, offsetY.value.toInt() - footerHeight) }
116
+ .onGloballyPositioned {
117
+ if (startPosition != it.size.height.toFloat()) {
118
+ startPosition = it.size.height.toFloat()
119
+ offsetY = Animatable(startPosition)
120
+ }
121
+ }
122
+ .fillMaxWidth()
123
+ .conditional(IsShowBaseLineDebug) {
124
+ border(1.dp, Colors.blue_03)
125
+ }
126
+ ) {
127
+ when (data) {
128
+ is SnackBar.Custom -> data.content()
129
+ is SnackBar.Toast -> {}
130
+ }
131
+ }
132
+ }
@@ -0,0 +1,33 @@
1
+ // File: ToolkitRequests.kt
2
+ // Created At: 2026-10-12 +07:00 | Created By: AI | AI Agent: Codex | Model: GPT-6
3
+ // AI-GENERATED START: cancel only toolkit requests owned by this composition
4
+ package vn.momo.kits.navigation.component
5
+
6
+ import vn.momo.maxapi.runtime.ApiCallback
7
+ import vn.momo.maxapi.runtime.ApiRequestHandle
8
+
9
+ internal class ToolkitRequests {
10
+ private var active = true
11
+ private val handles = mutableSetOf<ApiRequestHandle>()
12
+
13
+ fun <T> launch(request: (ApiCallback<T>) -> ApiRequestHandle, onResult: ApiCallback<T>) {
14
+ if (!active) return
15
+ var completed = false
16
+ var handle: ApiRequestHandle? = null
17
+ val created = request { result ->
18
+ completed = true
19
+ handle?.let(handles::remove)
20
+ if (active) onResult(result)
21
+ }
22
+ handle = created
23
+ if (!active) created.cancel() else if (!completed) handles.add(created)
24
+ }
25
+
26
+ fun close() {
27
+ active = false
28
+ val pending = handles.toList()
29
+ handles.clear()
30
+ pending.forEach { it.cancel() }
31
+ }
32
+ }
33
+ // AI-GENERATED END: cancel only toolkit requests owned by this composition
@@ -0,0 +1,135 @@
1
+ package vn.momo.kits.navigation.tracking
2
+
3
+ import vn.momo.kits.application.MiniAppContext
4
+ import vn.momo.maxapi.IMaxApi
5
+ // AI-GENERATED START: serialize tracking attributes with their existing value types
6
+ import kotlinx.serialization.json.JsonNull
7
+ import kotlinx.serialization.json.JsonElement
8
+ import kotlinx.serialization.json.JsonObject
9
+ import kotlinx.serialization.json.JsonPrimitive
10
+ // AI-GENERATED END: serialize tracking attributes with their existing value types
11
+ import kotlin.time.Clock
12
+ import kotlin.time.ExperimentalTime
13
+
14
+ class ScreenTrackingState {
15
+ @OptIn(ExperimentalTime::class)
16
+ var time: Long = Clock.System.now().toEpochMilliseconds()
17
+ var releaseNavigated: Boolean = false
18
+ var releaseLoad: Boolean = false
19
+ var releaseInteraction: Boolean = false
20
+ var previousScreenName: String? = null
21
+ var action: String = "push"
22
+ }
23
+
24
+ // AI-GENERATED START: retain screen event names and typed tracking attributes
25
+ object ScreenTracker {
26
+
27
+ private var lastScreenName: String? = null
28
+
29
+ fun trackScreenNavigated(
30
+ maxApi: IMaxApi?,
31
+ context: MiniAppContext?,
32
+ screenName: String,
33
+ action: String,
34
+ state: ScreenTrackingState
35
+ ) {
36
+ if (state.releaseNavigated) return
37
+
38
+ val previousScreen = lastScreenName
39
+ lastScreenName = screenName
40
+
41
+ maxApi?.trackEvent(
42
+ eventName = "auto_screen_navigated",
43
+ attributes = JsonObject(buildBaseParams(context, screenName).apply {
44
+ put("pre_screen_name", JsonPrimitive(previousScreen))
45
+ put("state", JsonPrimitive("navigated"))
46
+ put("action", JsonPrimitive(action))
47
+ })
48
+ )
49
+
50
+ state.releaseNavigated = true
51
+ state.previousScreenName = previousScreen
52
+ }
53
+
54
+ fun trackScreenDisplayed(
55
+ maxApi: IMaxApi?,
56
+ context: MiniAppContext?,
57
+ screenName: String,
58
+ duration: Long,
59
+ state: ScreenTrackingState
60
+ ) {
61
+ if (state.releaseLoad) return
62
+
63
+ maxApi?.trackEvent(
64
+ eventName = "auto_screen_displayed",
65
+ attributes = JsonObject(buildBaseParams(context, screenName).apply {
66
+ put("state", JsonPrimitive("load"))
67
+ put("duration", JsonPrimitive(duration))
68
+ })
69
+ )
70
+
71
+ state.releaseLoad = true
72
+ }
73
+
74
+ fun trackScreenInteracted(
75
+ maxApi: IMaxApi?,
76
+ context: MiniAppContext?,
77
+ screenName: String,
78
+ duration: Long,
79
+ totalDuration: Long,
80
+ state: ScreenTrackingState
81
+ ) {
82
+ if (state.releaseInteraction) return
83
+
84
+ maxApi?.trackEvent(
85
+ eventName = "auto_screen_interacted",
86
+ attributes = JsonObject(buildBaseParams(context, screenName).apply {
87
+ put("state", JsonPrimitive("interaction"))
88
+ put("duration", JsonPrimitive(duration))
89
+ put("total_duration", JsonPrimitive(totalDuration))
90
+ })
91
+ )
92
+
93
+ state.releaseInteraction = true
94
+ }
95
+
96
+ private fun buildBaseParams(
97
+ context: MiniAppContext?,
98
+ screenName: String,
99
+ componentName: String = "Screen"
100
+ ): MutableMap<String, JsonElement> {
101
+ return mutableMapOf(
102
+ "app_id" to JsonPrimitive(context?.appId),
103
+ "feature_code" to JsonPrimitive(context?.appCode),
104
+ "screen_name" to JsonPrimitive(screenName),
105
+ "component_name" to JsonPrimitive(componentName),
106
+ "tracking_source" to JsonPrimitive(1)
107
+ )
108
+ }
109
+
110
+ fun trackPopupDisplayed(
111
+ maxApi: IMaxApi?,
112
+ context: MiniAppContext?,
113
+ parentScreenName: String?
114
+ ) {
115
+ maxApi?.trackEvent(
116
+ eventName = "auto_popup_displayed",
117
+ attributes = JsonObject(mapOf(
118
+ "screen_name" to JsonPrimitive(parentScreenName),
119
+ "tracking_source" to JsonPrimitive(1),
120
+ "app_id" to JsonPrimitive(context?.appId),
121
+ "feature_code" to JsonPrimitive(context?.appCode),
122
+ "kits_version" to JsonNull,
123
+ "component_name" to JsonPrimitive("Modal")
124
+ ))
125
+ )
126
+ }
127
+
128
+ fun getLastScreenName(): String? = lastScreenName
129
+
130
+ @Suppress("UNUSED")
131
+ fun reset() {
132
+ lastScreenName = null
133
+ }
134
+ }
135
+ // AI-GENERATED END: retain screen event names and typed tracking attributes
@@ -0,0 +1,12 @@
1
+ package vn.momo.kits.platform
2
+
3
+ import androidx.compose.runtime.Composable
4
+ import androidx.compose.runtime.State
5
+
6
+ /** Fraction of container width, `null` when idle. Observe only — consuming would steal the
7
+ * gesture from NavHost. Always `null` on Android. */
8
+ @Composable
9
+ expect fun rememberBackGestureProgress(): State<Float?>
10
+
11
+ /** Android is a no-op: no host-owned recogniser to hand the gesture to. */
12
+ expect fun reportFloatingArrowBack(shouldUse: Boolean)