@momo-kits/native-kits 0.162.6-debug → 0.163.1-beta.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 (123) hide show
  1. package/CLAUDE.md +78 -0
  2. package/compose/build.gradle.kts +8 -11
  3. package/compose/build.gradle.kts.backup +7 -10
  4. package/compose/compose.podspec +1 -1
  5. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +7 -2
  6. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +5 -12
  7. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +24 -6
  8. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +19 -4
  9. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +10 -1
  10. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +2 -2
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +1 -1
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +1 -1
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +16 -5
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +1 -1
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +101 -6
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +15 -4
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +33 -8
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +1 -1
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +25 -22
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +29 -27
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +26 -3
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +30 -27
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +13 -3
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +5 -0
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +11 -3
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +18 -3
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +11 -11
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +11 -3
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +5 -1
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +59 -84
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +71 -13
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +51 -10
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +1 -6
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +21 -4
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +1 -1
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +75 -16
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +26 -5
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +1 -1
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +11 -3
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +36 -15
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +9 -3
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +28 -0
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +13 -2
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +17 -0
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +10 -4
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +28 -3
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +47 -8
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +25 -4
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +11 -2
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +17 -1
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +6 -1
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +111 -50
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +5 -1
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +8 -7
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +12 -1
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +96 -4
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +12 -2
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +21 -9
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +2 -2
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +14 -9
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +11 -8
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +1 -1
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +40 -24
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +1 -1
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +5 -0
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +2 -0
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +4 -94
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +2 -0
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +5 -1
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +15 -5
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +14 -65
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +33 -48
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +55 -56
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +9 -3
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +31 -13
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +1 -1
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +24 -6
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +6 -11
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +1 -2
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +3 -6
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +1 -1
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +13 -3
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +2 -3
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +40 -1
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +2 -0
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +8 -0
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +17 -2
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +1 -1
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +3 -3
  91. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +3 -2
  92. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +2 -0
  93. package/example/ios/Example.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
  94. package/example/ios/Example.xcworkspace/xcuserdata/huynhdung.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  95. package/example/ios/Example.xcworkspace/xcuserdata/huynhdung.xcuserdatad/xcschemes/xcschememanagement.plist +5 -0
  96. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +58 -0
  97. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/Pods-Example.xcscheme +58 -0
  98. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/SDWebImage.xcscheme +58 -0
  99. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/SDWebImageSwiftUI.xcscheme +58 -0
  100. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/SkeletonUI.xcscheme +58 -0
  101. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/xcschememanagement.plist +46 -0
  102. package/gradle.properties +1 -1
  103. package/ios/Application/ApplicationEnvironment.swift +0 -1
  104. package/ios/Application/Components.swift +0 -1
  105. package/ios/Application/FloatingButton.swift +0 -1
  106. package/ios/Badge/Badge.swift +0 -1
  107. package/ios/Badge/BadgeRibbon.swift +0 -2
  108. package/ios/Button/Button.swift +1 -1
  109. package/ios/Checkbox/Checkbox.swift +0 -1
  110. package/ios/Input/Input.swift +0 -1
  111. package/ios/Input/InputPhoneNumber.swift +0 -1
  112. package/ios/Input/InputSearch.swift +0 -3
  113. package/ios/Input/InputTextArea.swift +0 -1
  114. package/ios/OTPKeyboard/KeyboardButton.swift +1 -1
  115. package/ios/Popup/PopupDisplay.swift +0 -6
  116. package/ios/Popup/PopupInput.swift +0 -2
  117. package/ios/Template/TrustBanner/TrustBanner.swift +0 -2
  118. package/ios/Typography/Text.swift +7 -13
  119. package/ios/Typography/Typography.swift +8 -22
  120. package/local.properties +8 -0
  121. package/package.json +1 -1
  122. package/ios/Application/FontScaleStore.swift +0 -59
  123. package/ios/Information/Information.swift +0 -151
@@ -9,7 +9,6 @@ import androidx.compose.runtime.staticCompositionLocalOf
9
9
  import androidx.compose.ui.Alignment
10
10
  import androidx.compose.ui.Modifier
11
11
  import androidx.navigation.NavController
12
- import androidx.navigation.toRoute
13
12
  import kotlinx.coroutines.CoroutineScope
14
13
  import kotlinx.coroutines.Dispatchers
15
14
  import kotlinx.coroutines.SupervisorJob
@@ -22,38 +21,30 @@ import vn.momo.kits.components.PopupNotify
22
21
  import vn.momo.kits.navigation.BottomHeader.*
23
22
  import vn.momo.kits.navigation.component.SnackBar
24
23
  import vn.momo.maxapi.IMaxApi
25
- import kotlin.time.Duration.Companion.milliseconds
26
24
 
27
25
  class Navigator(
28
26
  private val navController: NavController,
29
- private val maxApi: IMaxApi?,
30
- private val registry: DynamicScreenRegistry
27
+ private val maxApi: IMaxApi?
31
28
  ) {
32
29
  private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
33
30
 
34
- private fun currentScreen(): DynamicScreen? {
35
- val entry = navController.currentBackStackEntry ?: return null
36
- val id = runCatching { entry.toRoute<DynamicScreenRoute>().id }.getOrNull()
37
- ?: runCatching { entry.toRoute<DynamicDialogRoute>().id }.getOrNull()
38
- ?: return null
39
- return registry.getScreen(id)
40
- }
41
-
42
31
  fun push(screenName: String, content: @Composable () -> Unit, options: NavigationOptions? = null) {
43
- val route = registry.register(screenName, content, options)
32
+ val route = DynamicScreenRegistry.register(screenName, content, options)
44
33
  navController.navigate(DynamicScreenRoute(route.id))
45
34
  }
46
35
 
47
36
  fun replace(screenName: String, content: @Composable () -> Unit, options: NavigationOptions? = null) {
48
- val replacedScreen = currentScreen()
49
- replacedScreen?.let { registry.unregisterScreen(it.id) }
50
- navController.popBackStack()
51
- push(screenName, content, options)
37
+ if (navController.previousBackStackEntry != null){
38
+ val latestScreen = DynamicScreenRegistry.getLatestScreen()
39
+ latestScreen?.let { DynamicScreenRegistry.unregisterScreen(it.id) }
40
+ navController.popBackStack()
41
+ push(screenName, content, options)
42
+ }
52
43
  }
53
44
 
54
45
  fun onBackSafe(callBack: (() -> Unit)? = null) {
55
- val currentScreen = currentScreen()
56
- val options = currentScreen?.options
46
+ val latestScreen = DynamicScreenRegistry.getLatestScreen()
47
+ val options = latestScreen?.options
57
48
  if (options?.onBackHandler != null) {
58
49
  options.onBackHandler.invoke()
59
50
  return
@@ -96,30 +87,31 @@ class Navigator(
96
87
  }
97
88
  private suspend fun dismissOverplay(isDelay: Boolean = true) {
98
89
  OverplayComponentRegistry.clear()
99
- if (isDelay) delay(300L.milliseconds)
90
+ if (isDelay) delay(300L)
100
91
  OverplayComponentRegistry.hardClearAfterDismiss()
101
92
  }
102
93
 
103
94
  private suspend fun dismissScreen() {
104
95
  if (navController.previousBackStackEntry != null){
105
- val dismissing = currentScreen()
106
96
  navController.popBackStack()
107
- delay(300L.milliseconds)
108
- dismissing?.let { registry.unregisterScreen(it.id) }
97
+ delay(300L)
98
+ DynamicScreenRegistry.getLatestScreen()?.let { it1 ->
99
+ DynamicScreenRegistry.unregisterScreen(it1.id)
100
+ }
109
101
  } else {
110
102
  maxApi?.dismiss { }
111
103
  }
112
104
  }
113
105
 
114
106
  fun present(screenName: String, content: @Composable () -> Unit, options: NavigationOptions? = null) {
115
- val route = registry.register(screenName, content, options)
107
+ val route = DynamicScreenRegistry.register(screenName, content, options)
116
108
  navController.navigate(DynamicDialogRoute(route.id))
117
109
  }
118
110
 
119
111
  fun reset(screenName: String, content: @Composable () -> Unit, options: NavigationOptions? = null) {
120
- registry.unregisterAll()
112
+ DynamicScreenRegistry.unregisterAll()
121
113
 
122
- val route = registry.register(screenName, content, options)
114
+ val route = DynamicScreenRegistry.register(screenName, content, options)
123
115
  navController.navigate(DynamicScreenRoute(route.id)) {
124
116
  popUpTo(0) { inclusive = true }
125
117
  }
@@ -130,7 +122,7 @@ class Navigator(
130
122
  barrierDismissible: Boolean = true,
131
123
  onDismiss: (() -> Unit)? = null
132
124
  ){
133
- val id = currentScreen()?.id ?: -1
125
+ val id = DynamicScreenRegistry.getLatestScreen()?.id ?: -1
134
126
  OverplayComponentRegistry.registerOverplay(id, content, OverplayComponentType.MODAL, false, barrierDismissible, onDismiss)
135
127
  }
136
128
 
@@ -141,12 +133,12 @@ class Navigator(
141
133
  onDismiss: (() -> Unit)? = null,
142
134
  bottomSheetHeader: BottomHeader? = null
143
135
  ){
144
- val id = currentScreen()?.id ?: -1
136
+ val id = DynamicScreenRegistry.getLatestScreen()?.id ?: -1
145
137
  OverplayComponentRegistry.registerOverplay(id, content, OverplayComponentType.BOTTOM_SHEET, isSurface, barrierDismissible, onDismiss, bottomSheetHeader)
146
138
  }
147
139
 
148
140
  fun showSnackBar(snackBar: SnackBar, onDismiss: (() -> Unit)? = null) {
149
- val id = currentScreen()?.id ?: -1
141
+ val id = DynamicScreenRegistry.getLatestScreen()?.id ?: -1
150
142
  scope.launch {
151
143
  OverplayComponentRegistry.registerOverplay(
152
144
  id = id,
@@ -162,7 +154,7 @@ class Navigator(
162
154
  fun hideSnackBar() {
163
155
  scope.launch {
164
156
  OverplayComponentRegistry.clear()
165
- delay(250L.milliseconds)
157
+ delay(250L)
166
158
  OverplayComponentRegistry.hardClearAfterDismiss()
167
159
  }
168
160
  }
@@ -189,11 +181,12 @@ data class DynamicScreen(
189
181
  var options: NavigationOptions? = null
190
182
  )
191
183
 
192
- class DynamicScreenRegistry {
184
+ object DynamicScreenRegistry {
193
185
  private val screens = mutableMapOf<Int, DynamicScreen>()
186
+ private var idCounter = 1
194
187
 
195
188
  fun register(screenName: String, content: @Composable () -> Unit, options: NavigationOptions?): DynamicScreenRoute {
196
- val id = nextGlobalId()
189
+ val id = idCounter++
197
190
  screens[id] = DynamicScreen(
198
191
  id = id,
199
192
  name = screenName,
@@ -203,7 +196,7 @@ class DynamicScreenRegistry {
203
196
  return DynamicScreenRoute(id)
204
197
  }
205
198
 
206
- fun nextId(): Int = nextGlobalId()
199
+ fun nextId(): Int = idCounter++
207
200
 
208
201
  fun bind(id: Int, screenName: String, content: @Composable () -> Unit, options: NavigationOptions?) {
209
202
  screens[id] = DynamicScreen(
@@ -212,13 +205,17 @@ class DynamicScreenRegistry {
212
205
  content = content,
213
206
  options = options
214
207
  )
215
- advanceGlobalIdPast(id) // keep counter ahead after process-death restore
208
+ if (id >= idCounter) idCounter = id + 1 // keep counter ahead after process-death restore
216
209
  }
217
210
 
218
211
  fun unregisterScreen(id: Int) {
219
212
  screens.remove(id)
220
213
  }
221
214
 
215
+ fun getLatestScreen(): DynamicScreen?{
216
+ return screens.entries.lastOrNull()?.value
217
+ }
218
+
222
219
  fun unregisterAll(){
223
220
  val ids = screens.keys.toList()
224
221
  for (id in ids) {
@@ -231,20 +228,8 @@ class DynamicScreenRegistry {
231
228
  fun setOptions(id: Int, options: NavigationOptions){
232
229
  screens[id]?.options = options
233
230
  }
234
-
235
- companion object {
236
- private var globalIdCounter = 1
237
-
238
- private fun nextGlobalId(): Int = globalIdCounter++
239
-
240
- private fun advanceGlobalIdPast(id: Int) {
241
- if (id >= globalIdCounter) globalIdCounter = id + 1
242
- }
243
- }
244
231
  }
245
232
 
246
- val LocalDynamicScreenRegistry = staticCompositionLocalOf<DynamicScreenRegistry?> { null }
247
-
248
233
 
249
234
  sealed class OverplayComponentParams {
250
235
  class Modal(
@@ -259,7 +244,7 @@ sealed class OverplayComponentParams {
259
244
  val bottomSheetHeader: BottomHeader? = null,
260
245
  ) : OverplayComponentParams()
261
246
 
262
- class SnackBar : OverplayComponentParams()
247
+ class SnackBar() : OverplayComponentParams()
263
248
  }
264
249
 
265
250
  data class OverplayComponent(
@@ -323,7 +308,7 @@ object OverplayComponentRegistry {
323
308
  }
324
309
 
325
310
  @Composable
326
- internal fun OverlayComponent(){
311
+ fun OverlayComponent(){
327
312
  val overplay = currentOverlayComponent.value ?: return
328
313
 
329
314
  when (val params = overplay.params) {
@@ -45,8 +45,8 @@ import androidx.compose.ui.layout.onGloballyPositioned
45
45
  import androidx.compose.ui.platform.LocalDensity
46
46
  import androidx.compose.ui.unit.Dp
47
47
  import androidx.compose.ui.unit.dp
48
+ import androidx.compose.ui.unit.min
48
49
  import androidx.compose.ui.zIndex
49
- import kotlinx.coroutines.flow.Flow
50
50
  import vn.momo.kits.components.InputSearch
51
51
  import vn.momo.kits.const.AppNavigationBar
52
52
  import vn.momo.kits.const.AppStatusBar
@@ -71,7 +71,6 @@ import vn.momo.kits.navigation.tracking.ScreenTrackingState
71
71
  import kotlinx.coroutines.delay
72
72
  import vn.momo.kits.application.ApplicationContext
73
73
  import kotlin.time.Clock
74
- import kotlin.time.Duration.Companion.milliseconds
75
74
  import kotlin.time.ExperimentalTime
76
75
 
77
76
  internal val LocalFooterHeightPx = staticCompositionLocalOf { mutableIntStateOf(0) }
@@ -92,15 +91,14 @@ internal fun StackScreen(
92
91
  val context = ApplicationContext.current
93
92
  val statusBar = AppStatusBar.current
94
93
  val density = LocalDensity.current
95
- val screenRegistry = LocalDynamicScreenRegistry.current
96
- val navigation = remember { Navigation(id = id, bottomTabIndex = bottomTabIndex, initOptions = navigationOptions, registry = screenRegistry) }
94
+ val navigation = remember { Navigation(id = id, bottomTabIndex = bottomTabIndex, initOptions = navigationOptions) }
97
95
 
98
96
  val options by navigation.currentOptions
99
97
 
100
98
  // Auto tracking state
101
99
  val trackingState = remember {
102
100
  ScreenTrackingState().apply {
103
- time = Clock.System.now().toEpochMilliseconds()
101
+ startTime = Clock.System.now().toEpochMilliseconds()
104
102
  }
105
103
  }
106
104
 
@@ -121,8 +119,8 @@ internal fun StackScreen(
121
119
  )
122
120
 
123
121
  // Track screen displayed after 2 seconds (debounce)
124
- delay(2000.milliseconds)
125
- val loadTime = Clock.System.now().toEpochMilliseconds() - trackingState.time
122
+ delay(2000)
123
+ val loadTime = Clock.System.now().toEpochMilliseconds() - trackingState.startTime
126
124
  ScreenTracker.trackScreenDisplayed(
127
125
  maxApi = maxApi,
128
126
  context = context,
@@ -132,7 +130,7 @@ internal fun StackScreen(
132
130
  )
133
131
 
134
132
  // Track screen interacted after displayed
135
- val interactionTime = Clock.System.now().toEpochMilliseconds() - trackingState.time
133
+ val interactionTime = Clock.System.now().toEpochMilliseconds() - trackingState.startTime
136
134
  ScreenTracker.trackScreenInteracted(
137
135
  maxApi = maxApi,
138
136
  context = context,
@@ -206,9 +204,8 @@ internal fun StackScreen(
206
204
  }
207
205
  }
208
206
 
209
- @Suppress("FrequentlyChangingValue")
210
207
  @Composable
211
- internal fun FloatingContent() {
208
+ fun FloatingContent() {
212
209
  val options = LocalOptions.current
213
210
  val density = LocalDensity.current
214
211
  val footerHeightPx = LocalFooterHeightPx.current
@@ -232,7 +229,7 @@ internal fun FloatingContent() {
232
229
  }
233
230
 
234
231
  @Composable
235
- internal fun ColumnScope.MainContent(content: @Composable () -> Unit) {
232
+ fun ColumnScope.MainContent(content: @Composable () -> Unit) {
236
233
  val options = LocalOptions.current
237
234
  val inputSearchType = getInputSearchType(options)
238
235
  val density = LocalDensity.current
@@ -267,7 +264,7 @@ internal fun ColumnScope.MainContent(content: @Composable () -> Unit) {
267
264
  }
268
265
 
269
266
  @Composable
270
- internal fun ScreenContent(content: @Composable () -> Unit) {
267
+ fun ScreenContent(content: @Composable () -> Unit) {
271
268
  val scrollState = LocalScrollState.current
272
269
  val options = LocalOptions.current
273
270
 
@@ -287,7 +284,7 @@ internal fun ScreenContent(content: @Composable () -> Unit) {
287
284
  }
288
285
 
289
286
  @Composable
290
- internal fun FooterContent() {
287
+ fun FooterContent() {
291
288
  val options = LocalOptions.current
292
289
  if (options.footerComponent != null) {
293
290
  val keyboardSize = keyboardSizeState()
@@ -297,13 +294,13 @@ internal fun FooterContent() {
297
294
  }
298
295
 
299
296
  @Composable
300
- internal fun keyboardSizeState(): State<Dp> {
297
+ fun keyboardSizeState(): State<Dp> {
301
298
  val bottom = WindowInsets.ime.asPaddingValues()
302
299
  return rememberUpdatedState(bottom.calculateBottomPadding())
303
300
  }
304
301
 
305
302
  @Composable
306
- internal fun OverplayView(bottomTabIndex: Int, id: Int) {
303
+ fun OverplayView(bottomTabIndex: Int, id: Int) {
307
304
  val overplayType = OverplayComponentRegistry.getOverplayType()
308
305
 
309
306
  if (overplayType != null) {
@@ -358,9 +355,8 @@ data class InputSearchLayoutParams(
358
355
  val endPadding: Dp
359
356
  )
360
357
 
361
- @Suppress("FrequentlyChangingValue")
362
358
  @Composable
363
- internal fun SearchAnimated(
359
+ fun SearchAnimated(
364
360
  isScrollInProgress: Boolean
365
361
  ) {
366
362
  val scrollState = LocalScrollState.current
@@ -473,61 +469,64 @@ private fun quantize(value: Int, stepPx: Int): Int {
473
469
  }
474
470
 
475
471
  @Composable
476
- internal fun LazyListState.proxyScrollState(
472
+ fun LazyListState.proxyScrollState(
477
473
  thresholdPx: Int = 200,
478
474
  stepPx: Int = 4
479
- ): Pair<ScrollState, Boolean> = proxyScroll(
480
- source = this,
481
- thresholdPx = thresholdPx,
482
- stepPx = stepPx,
483
- isScrollInProgress = { isScrollInProgress },
484
- rawTargetFlow = {
485
- snapshotFlow {
486
- if (firstVisibleItemIndex == 0) firstVisibleItemScrollOffset
487
- else SCROLLED_PAST_FIRST_ITEM_THRESHOLD
488
- }
489
- },
490
- )
475
+ ): Pair<ScrollState, Boolean> {
476
+ val scrollState = rememberScrollState()
477
+ val locked = remember { mutableStateOf(false) }
478
+
479
+ LaunchedEffect(this, scrollState, thresholdPx, stepPx) {
480
+ snapshotFlow { firstVisibleItemIndex to firstVisibleItemScrollOffset }
481
+ .collect { (index, offset) ->
482
+ val rawTarget = if (index == 0) offset else SCROLLED_PAST_FIRST_ITEM_THRESHOLD
483
+ val shouldLock = rawTarget > thresholdPx
484
+ if (locked.value != shouldLock) locked.value = shouldLock
485
+
486
+ val desired = if (shouldLock) thresholdPx else quantize(rawTarget, stepPx)
487
+
488
+ if (scrollState.value != desired) {
489
+ scrollState.scrollTo(desired.coerceAtLeast(0))
490
+ }
491
+ }
492
+ }
493
+
494
+ val inProgress = remember { mutableStateOf(false) }
495
+ LaunchedEffect(this, thresholdPx) {
496
+ snapshotFlow { isScrollInProgress }
497
+ .collect { progressing ->
498
+ inProgress.value = if (locked.value) false else progressing
499
+ }
500
+ }
501
+
502
+ return scrollState to inProgress.value
503
+ }
491
504
 
492
505
  @Composable
493
- internal fun ScrollState.proxyLimitedScrollState(
506
+ fun ScrollState.proxyLimitedScrollState(
494
507
  thresholdPx: Int = 200,
495
508
  stepPx: Int = 4
496
- ): Pair<ScrollState, Boolean> = proxyScroll(
497
- source = this,
498
- thresholdPx = thresholdPx,
499
- stepPx = stepPx,
500
- isScrollInProgress = { isScrollInProgress },
501
- rawTargetFlow = { snapshotFlow { value } },
502
- )
503
-
504
- @Composable
505
- private fun proxyScroll(
506
- source: Any,
507
- thresholdPx: Int,
508
- stepPx: Int,
509
- isScrollInProgress: () -> Boolean,
510
- rawTargetFlow: () -> Flow<Int>,
511
509
  ): Pair<ScrollState, Boolean> {
512
510
  val proxy = rememberScrollState()
513
511
  val locked = remember { mutableStateOf(false) }
514
512
 
515
- LaunchedEffect(source, proxy, thresholdPx, stepPx) {
516
- rawTargetFlow().collect { rawTarget ->
517
- val shouldLock = rawTarget > thresholdPx
518
- if (locked.value != shouldLock) locked.value = shouldLock
513
+ LaunchedEffect(this, proxy, thresholdPx, stepPx) {
514
+ snapshotFlow { value }
515
+ .collect { rawTarget ->
516
+ val shouldLock = rawTarget > thresholdPx
517
+ if (locked.value != shouldLock) locked.value = shouldLock
519
518
 
520
- val desired = if (shouldLock) thresholdPx else quantize(rawTarget, stepPx)
519
+ val desired = if (shouldLock) thresholdPx else quantize(rawTarget, stepPx)
521
520
 
522
- if (proxy.value != desired) {
523
- proxy.scrollTo(desired.coerceAtLeast(0))
521
+ if (proxy.value != desired) {
522
+ proxy.scrollTo(desired.coerceAtLeast(0))
523
+ }
524
524
  }
525
- }
526
525
  }
527
526
 
528
527
  val inProgress = remember { mutableStateOf(false) }
529
- LaunchedEffect(source, thresholdPx) {
530
- snapshotFlow { isScrollInProgress() }
528
+ LaunchedEffect(this, thresholdPx) {
529
+ snapshotFlow { isScrollInProgress }
531
530
  .collect { progressing ->
532
531
  inProgress.value = if (locked.value) false else progressing
533
532
  }
@@ -6,7 +6,13 @@ import androidx.compose.animation.fadeOut
6
6
  import androidx.compose.animation.scaleIn
7
7
  import androidx.compose.animation.scaleOut
8
8
  import androidx.compose.foundation.background
9
- import androidx.compose.foundation.layout.*
9
+ import androidx.compose.foundation.layout.Box
10
+ import androidx.compose.foundation.layout.Column
11
+ import androidx.compose.foundation.layout.Spacer
12
+ import androidx.compose.foundation.layout.fillMaxSize
13
+ import androidx.compose.foundation.layout.fillMaxWidth
14
+ import androidx.compose.foundation.layout.height
15
+ import androidx.compose.foundation.layout.padding
10
16
  import androidx.compose.runtime.Composable
11
17
  import androidx.compose.runtime.LaunchedEffect
12
18
  import androidx.compose.ui.Alignment
@@ -26,12 +32,12 @@ import vn.momo.kits.navigation.component.HeaderType
26
32
  import vn.momo.kits.platform.getScreenHeight
27
33
 
28
34
  private var bottomTabOptionItems : MutableList<NavigationOptions?> = mutableListOf()
29
- internal fun setBottomTabOption(index: Int, options: NavigationOptions){
35
+ fun setBottomTabOption(index: Int, options: NavigationOptions){
30
36
  if (index in bottomTabOptionItems.indices) {
31
37
  bottomTabOptionItems[index] = options
32
38
  }
33
39
  }
34
- internal fun getBottomTabOption(index: Int): NavigationOptions? {
40
+ fun getBottomTabOption(index: Int): NavigationOptions? {
35
41
  return if (index in bottomTabOptionItems.indices) {
36
42
  bottomTabOptionItems[index]
37
43
  } else null
@@ -5,7 +5,19 @@ import androidx.compose.animation.core.animateDpAsState
5
5
  import androidx.compose.animation.core.tween
6
6
  import androidx.compose.foundation.background
7
7
  import androidx.compose.foundation.border
8
- import androidx.compose.foundation.layout.*
8
+ import androidx.compose.foundation.layout.Arrangement
9
+ import androidx.compose.foundation.layout.Box
10
+ import androidx.compose.foundation.layout.Column
11
+ import androidx.compose.foundation.layout.Row
12
+ import androidx.compose.foundation.layout.RowScope
13
+ import androidx.compose.foundation.layout.Spacer
14
+ import androidx.compose.foundation.layout.fillMaxSize
15
+ import androidx.compose.foundation.layout.fillMaxWidth
16
+ import androidx.compose.foundation.layout.height
17
+ import androidx.compose.foundation.layout.offset
18
+ import androidx.compose.foundation.layout.padding
19
+ import androidx.compose.foundation.layout.size
20
+ import androidx.compose.foundation.layout.width
9
21
  import androidx.compose.foundation.shape.CircleShape
10
22
  import androidx.compose.foundation.shape.RoundedCornerShape
11
23
  import androidx.compose.runtime.Composable
@@ -20,20 +32,26 @@ import androidx.compose.ui.text.style.TextOverflow
20
32
  import androidx.compose.ui.unit.dp
21
33
  import androidx.navigation.NavController
22
34
  import androidx.navigation.compose.currentBackStackEntryAsState
35
+ import vn.momo.kits.components.Badge
36
+ import vn.momo.kits.components.Icon
37
+ import vn.momo.kits.components.Text
38
+ import vn.momo.kits.const.AppTheme
39
+ import vn.momo.kits.const.Colors
40
+ import vn.momo.kits.const.Radius
41
+ import vn.momo.kits.const.Spacing
23
42
  import vn.momo.kits.application.IsShowBaseLineDebug
24
- import vn.momo.kits.components.*
25
- import vn.momo.kits.const.*
26
- import vn.momo.kits.modifier.InternalApi
43
+ import vn.momo.kits.components.BadgeDot
44
+ import vn.momo.kits.components.DotSize
45
+ import vn.momo.kits.const.Typography
27
46
  import vn.momo.kits.modifier.conditional
28
47
  import vn.momo.kits.modifier.noFeedbackClickable
29
48
  import vn.momo.kits.platform.getScreenDimensions
30
49
 
31
- internal val floatingButtonWidth = 75.dp
32
- @InternalApi
50
+ val floatingButtonWidth = 75.dp
33
51
  const val BOTTOM_TAB_BAR_HEIGHT = 64
34
52
 
35
53
  @Composable
36
- internal fun BottomTabBar(
54
+ fun BottomTabBar(
37
55
  items: List<BottomTabItem>,
38
56
  floatingButton: BottomTabFloatingButton? = null,
39
57
  navController: NavController,
@@ -73,11 +91,11 @@ internal fun BottomTabBar(
73
91
  horizontalArrangement = Arrangement.SpaceAround,
74
92
  ) {
75
93
  if (floatingButton == null) {
76
- RenderTabBarItem(items, 0, items.size, selectedIndex, onTabSelected)
94
+ renderTabBarItem(items, 0, items.size, selectedIndex, onTabSelected)
77
95
  } else {
78
- RenderTabBarItem(items, 0, mid, selectedIndex, onTabSelected)
96
+ renderTabBarItem(items, 0, mid, selectedIndex, onTabSelected)
79
97
  Spacer(modifier = Modifier.width(floatingButtonWidth).padding(horizontal = Spacing.XXS))
80
- RenderTabBarItem(items, mid, items.size, selectedIndex, onTabSelected)
98
+ renderTabBarItem(items, mid, items.size, selectedIndex, onTabSelected)
81
99
  }
82
100
  }
83
101
 
@@ -106,7 +124,7 @@ internal fun BottomTabBar(
106
124
  }
107
125
 
108
126
  @Composable
109
- internal fun RowScope.RenderTabBarItem(
127
+ fun RowScope.renderTabBarItem(
110
128
  items: List<BottomTabItem>,
111
129
  fromIndex: Int,
112
130
  toIndex: Int,
@@ -125,7 +143,7 @@ internal fun RowScope.RenderTabBarItem(
125
143
  }
126
144
 
127
145
  @Composable
128
- internal fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> Unit) {
146
+ fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> Unit) {
129
147
  fun isNumber(label: String): Boolean {
130
148
  val numberRegex = "^\\d+$".toRegex()
131
149
  return numberRegex.matches(label)
@@ -185,7 +203,7 @@ internal fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> U
185
203
  }
186
204
 
187
205
  @Composable
188
- internal fun FloatingButton(data: BottomTabFloatingButton) {
206
+ fun FloatingButton(data: BottomTabFloatingButton) {
189
207
  Column(
190
208
  modifier = Modifier
191
209
  .width(floatingButtonWidth)
@@ -17,7 +17,7 @@ import androidx.compose.ui.zIndex
17
17
  import vn.momo.kits.const.AppTheme
18
18
 
19
19
  @Composable
20
- internal fun CurvedContainer(
20
+ fun CurvedContainer(
21
21
  circleSize: Dp = 48.dp,
22
22
  tabBarItemIconSize: Dp = 28.dp,
23
23
  circleOverSize: Dp = 2.dp,
@@ -1,21 +1,40 @@
1
1
  package vn.momo.kits.navigation.component
2
2
 
3
- import androidx.compose.animation.*
3
+ import androidx.compose.animation.AnimatedVisibility
4
4
  import androidx.compose.animation.core.CubicBezierEasing
5
5
  import androidx.compose.animation.core.LinearEasing
6
6
  import androidx.compose.animation.core.animateDpAsState
7
7
  import androidx.compose.animation.core.tween
8
+ import androidx.compose.animation.expandHorizontally
9
+ import androidx.compose.animation.fadeIn
10
+ import androidx.compose.animation.fadeOut
11
+ import androidx.compose.animation.shrinkHorizontally
8
12
  import androidx.compose.foundation.ScrollState
9
13
  import androidx.compose.foundation.background
10
14
  import androidx.compose.foundation.border
11
15
  import androidx.compose.foundation.interaction.MutableInteractionSource
12
- import androidx.compose.foundation.layout.*
16
+ import androidx.compose.foundation.layout.Arrangement
17
+ import androidx.compose.foundation.layout.Box
18
+ import androidx.compose.foundation.layout.Row
19
+ import androidx.compose.foundation.layout.Spacer
20
+ import androidx.compose.foundation.layout.fillMaxSize
21
+ import androidx.compose.foundation.layout.height
22
+ import androidx.compose.foundation.layout.offset
23
+ import androidx.compose.foundation.layout.padding
24
+ import androidx.compose.foundation.layout.sizeIn
25
+ import androidx.compose.foundation.layout.width
13
26
  import androidx.compose.material3.AlertDialogDefaults
14
27
  import androidx.compose.material3.FloatingActionButton
15
28
  import androidx.compose.material3.FloatingActionButtonDefaults
16
29
  import androidx.compose.material3.FloatingActionButtonElevation
17
- import androidx.compose.runtime.*
30
+ import androidx.compose.runtime.Composable
31
+ import androidx.compose.runtime.LaunchedEffect
32
+ import androidx.compose.runtime.getValue
33
+ import androidx.compose.runtime.mutableStateOf
34
+ import androidx.compose.runtime.remember
35
+ import androidx.compose.runtime.rememberCoroutineScope
18
36
  import androidx.compose.runtime.saveable.rememberSaveable
37
+ import androidx.compose.runtime.setValue
19
38
  import androidx.compose.ui.Alignment
20
39
  import androidx.compose.ui.Modifier
21
40
  import androidx.compose.ui.graphics.Color
@@ -31,7 +50,6 @@ import vn.momo.kits.components.Text
31
50
  import vn.momo.kits.const.Colors
32
51
  import vn.momo.kits.const.Typography
33
52
  import vn.momo.kits.modifier.conditional
34
- import kotlin.time.Duration.Companion.milliseconds
35
53
 
36
54
  enum class FABSize { SMALL, DEFAULT }
37
55
  enum class FABPosition { END, CENTER }
@@ -55,7 +73,7 @@ private val FabIconSizeDefault = 24.dp
55
73
  private val FabPaddingHorizontal = 12.dp
56
74
 
57
75
  @Composable
58
- internal fun FloatingButton(
76
+ fun FloatingButton(
59
77
  scrollPosition: Int,
60
78
  bottom: Dp,
61
79
  onClick: () -> Unit,
@@ -88,7 +106,7 @@ internal fun FloatingButton(
88
106
  coroutineScope.launch {
89
107
  scrollStateFlow.collectLatest { newScroll ->
90
108
  isExpanded = newScroll <= lastScrollPosition
91
- delay(100.milliseconds)
109
+ delay(100)
92
110
  lastScrollPosition = newScroll
93
111
  }
94
112
  }