@momo-kits/native-kits 0.162.2-debug → 0.162.2-lottie.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 (117) hide show
  1. package/.claude/settings.local.json +14 -0
  2. package/CLAUDE.md +78 -0
  3. package/compose/build.gradle.kts +12 -8
  4. package/compose/build.gradle.kts.backup +11 -7
  5. package/compose/compose.podspec +1 -1
  6. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +17 -8
  7. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +2 -7
  8. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +3 -11
  9. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +6 -24
  10. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +4 -19
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +1 -10
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +2 -2
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +1 -1
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +1 -1
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +6 -17
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +1 -1
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +6 -101
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +5 -19
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +8 -33
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +1 -1
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +22 -25
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +27 -29
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +3 -26
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +30 -31
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +3 -13
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -5
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +3 -11
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +3 -18
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +11 -11
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +3 -11
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +69 -26
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +1 -5
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +84 -59
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +13 -71
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +10 -51
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +6 -1
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +4 -21
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +1 -1
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +16 -75
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +5 -26
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +1 -1
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +3 -11
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +16 -40
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +3 -9
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -28
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +2 -13
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +4 -10
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +3 -28
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +8 -47
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +4 -25
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +2 -11
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +1 -17
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +1 -6
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +50 -111
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +1 -5
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +1 -5
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +1 -12
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +4 -96
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +5 -7
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +2 -12
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +9 -21
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +2 -2
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +9 -14
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +8 -11
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +1 -1
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +3 -18
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +1 -1
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -5
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -2
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +94 -4
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -2
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +2 -2
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +3 -7
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +12 -16
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +33 -24
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +48 -33
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +88 -64
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +9 -14
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +13 -31
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +1 -1
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +6 -24
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +11 -6
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +2 -1
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +9 -6
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +1 -1
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +3 -13
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +3 -2
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +1 -40
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +15 -7
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +6 -7
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +2 -17
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +1 -1
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +3 -3
  95. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +2 -3
  96. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +26 -5
  97. package/gradle/libs.versions.toml +2 -0
  98. package/gradle.properties +1 -1
  99. package/ios/Application/Components.swift +1 -0
  100. package/ios/Application/FloatingButton.swift +1 -0
  101. package/ios/Badge/Badge.swift +1 -0
  102. package/ios/Badge/BadgeRibbon.swift +2 -0
  103. package/ios/Button/Button.swift +1 -1
  104. package/ios/Checkbox/Checkbox.swift +1 -0
  105. package/ios/Information/Information.swift +151 -0
  106. package/ios/Input/Input.swift +1 -0
  107. package/ios/Input/InputPhoneNumber.swift +1 -0
  108. package/ios/Input/InputSearch.swift +3 -0
  109. package/ios/Input/InputTextArea.swift +1 -0
  110. package/ios/OTPKeyboard/KeyboardButton.swift +1 -1
  111. package/ios/Popup/PopupDisplay.swift +6 -0
  112. package/ios/Popup/PopupInput.swift +2 -0
  113. package/ios/Template/TrustBanner/TrustBanner.swift +2 -0
  114. package/ios/Typography/Text.swift +1 -0
  115. package/ios/Typography/Typography.swift +14 -5
  116. package/local.properties +8 -0
  117. package/package.json +1 -1
@@ -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
49
48
  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
@@ -56,6 +56,7 @@ import vn.momo.kits.const.Colors
56
56
  import vn.momo.kits.const.Spacing
57
57
  import vn.momo.kits.modifier.conditional
58
58
  import vn.momo.kits.modifier.hideKeyboardOnTap
59
+ import vn.momo.kits.navigation.bottomtab.BOTTOM_TAB_BAR_HEIGHT
59
60
  import vn.momo.kits.navigation.component.FABPosition
60
61
  import vn.momo.kits.navigation.component.FloatingButton
61
62
  import vn.momo.kits.navigation.component.HEADER_HEIGHT
@@ -69,13 +70,21 @@ import vn.momo.kits.platform.supportsImePadding
69
70
  import vn.momo.kits.navigation.tracking.ScreenTracker
70
71
  import vn.momo.kits.navigation.tracking.ScreenTrackingState
71
72
  import kotlinx.coroutines.delay
72
- import vn.momo.kits.application.ApplicationContext
73
+ import vn.momo.kits.application.LocalContext
73
74
  import kotlin.time.Clock
75
+ import kotlin.time.Duration.Companion.milliseconds
74
76
  import kotlin.time.ExperimentalTime
75
77
 
76
78
  internal val LocalFooterHeightPx = staticCompositionLocalOf { mutableIntStateOf(0) }
77
79
  internal val LocalHeaderRightWidthPx = staticCompositionLocalOf { mutableIntStateOf(0) }
78
80
 
81
+ @Composable
82
+ private fun bottomTabBarInset(): Dp {
83
+ if (LocalNavigation.current.bottomTabIndex == -1) return 0.dp
84
+ val keyboard = keyboardSizeState().value
85
+ return (BOTTOM_TAB_BAR_HEIGHT.dp + Spacing.S - keyboard).coerceAtLeast(0.dp)
86
+ }
87
+
79
88
  @OptIn(ExperimentalMaterialApi::class, ExperimentalTime::class)
80
89
  @Composable
81
90
  internal fun StackScreen(
@@ -88,17 +97,20 @@ internal fun StackScreen(
88
97
  ) {
89
98
  val navigator = LocalNavigator.current
90
99
  val maxApi = LocalMaxApi.current
91
- val context = ApplicationContext.current
100
+ val context = LocalContext.current
92
101
  val statusBar = AppStatusBar.current
93
102
  val density = LocalDensity.current
94
- val navigation = remember { Navigation(id = id, bottomTabIndex = bottomTabIndex, initOptions = navigationOptions) }
103
+ val screenRegistry = LocalDynamicScreenRegistry.current
104
+ val navigation = remember { Navigation(id = id, bottomTabIndex = bottomTabIndex, initOptions = navigationOptions, registry = screenRegistry) }
95
105
 
96
106
  val options by navigation.currentOptions
107
+ val isBottomTabChild = bottomTabIndex != -1
108
+ val isBottomTabRoot = navigation.isBottomTabRoot
97
109
 
98
110
  // Auto tracking state
99
111
  val trackingState = remember {
100
112
  ScreenTrackingState().apply {
101
- startTime = Clock.System.now().toEpochMilliseconds()
113
+ time = Clock.System.now().toEpochMilliseconds()
102
114
  }
103
115
  }
104
116
 
@@ -119,8 +131,8 @@ internal fun StackScreen(
119
131
  )
120
132
 
121
133
  // Track screen displayed after 2 seconds (debounce)
122
- delay(2000)
123
- val loadTime = Clock.System.now().toEpochMilliseconds() - trackingState.startTime
134
+ delay(2000.milliseconds)
135
+ val loadTime = Clock.System.now().toEpochMilliseconds() - trackingState.time
124
136
  ScreenTracker.trackScreenDisplayed(
125
137
  maxApi = maxApi,
126
138
  context = context,
@@ -130,7 +142,7 @@ internal fun StackScreen(
130
142
  )
131
143
 
132
144
  // Track screen interacted after displayed
133
- val interactionTime = Clock.System.now().toEpochMilliseconds() - trackingState.startTime
145
+ val interactionTime = Clock.System.now().toEpochMilliseconds() - trackingState.time
134
146
  ScreenTracker.trackScreenInteracted(
135
147
  maxApi = maxApi,
136
148
  context = context,
@@ -191,29 +203,35 @@ internal fun StackScreen(
191
203
  }
192
204
 
193
205
  Column(Modifier.zIndex(2f).fillMaxSize()) {
194
- MainContent(content = content)
206
+ MainContent(content = content, isBottomTabRoot = isBottomTabRoot, isBottomTabChild = isBottomTabChild)
195
207
  FooterContent()
196
208
  }
197
209
 
198
210
  Box(Modifier.zIndex(6f)) {
199
- FloatingContent()
211
+ FloatingContent(isBottomTabChild = isBottomTabChild)
200
212
  }
201
213
 
202
- OverplayView(bottomTabIndex = bottomTabIndex, id = id)
214
+ OverplayView(isBottomTabChild = isBottomTabChild, id = id)
203
215
  }
204
216
  }
205
217
  }
206
218
 
219
+ @Suppress("FrequentlyChangingValue")
207
220
  @Composable
208
- fun FloatingContent() {
221
+ internal fun FloatingContent(isBottomTabChild: Boolean) {
209
222
  val options = LocalOptions.current
210
223
  val density = LocalDensity.current
211
224
  val footerHeightPx = LocalFooterHeightPx.current
212
225
  val scrollState = LocalScrollState.current
226
+ val navigationBar = AppNavigationBar.current
213
227
 
214
228
  val fabProps = options.floatingButtonProps ?: return
215
229
  val bottomPadding = fabProps.bottom
216
- ?: (Spacing.M + if (options.footerComponent != null) with(density) { footerHeightPx.intValue.toDp() } else 0.dp)
230
+ ?: (Spacing.M + when {
231
+ options.hasFooter() -> with(density) { footerHeightPx.intValue.toDp() }
232
+ isBottomTabChild -> bottomTabBarInset() + (navigationBar - keyboardSizeState().value).coerceAtLeast(0.dp)
233
+ else -> navigationBar
234
+ })
217
235
 
218
236
  FloatingButton(
219
237
  scrollPosition = fabProps.scrollState?.value ?: scrollState.value,
@@ -229,11 +247,12 @@ fun FloatingContent() {
229
247
  }
230
248
 
231
249
  @Composable
232
- fun ColumnScope.MainContent(content: @Composable () -> Unit) {
250
+ internal fun ColumnScope.MainContent(content: @Composable () -> Unit, isBottomTabRoot: Boolean, isBottomTabChild: Boolean) {
233
251
  val options = LocalOptions.current
234
252
  val inputSearchType = getInputSearchType(options)
235
253
  val density = LocalDensity.current
236
254
  val scrollState = LocalScrollState.current
255
+ val navigationBar = AppNavigationBar.current
237
256
 
238
257
  Spacer(
239
258
  Modifier.height(
@@ -257,6 +276,13 @@ fun ColumnScope.MainContent(content: @Composable () -> Unit) {
257
276
  .conditional(options.scrollData.scrollable && options.scrollData.scrollState is ScrollState) {
258
277
  verticalScroll(scrollState)
259
278
  }
279
+ .padding(
280
+ bottom = when {
281
+ options.hasFooter() || isBottomTabRoot -> 0.dp
282
+ isBottomTabChild -> bottomTabBarInset() + (navigationBar - keyboardSizeState().value).coerceAtLeast(0.dp)
283
+ else -> navigationBar
284
+ }
285
+ )
260
286
  ) {
261
287
  ScreenContent(content = content)
262
288
  }
@@ -264,7 +290,7 @@ fun ColumnScope.MainContent(content: @Composable () -> Unit) {
264
290
  }
265
291
 
266
292
  @Composable
267
- fun ScreenContent(content: @Composable () -> Unit) {
293
+ internal fun ScreenContent(content: @Composable () -> Unit) {
268
294
  val scrollState = LocalScrollState.current
269
295
  val options = LocalOptions.current
270
296
 
@@ -284,28 +310,28 @@ fun ScreenContent(content: @Composable () -> Unit) {
284
310
  }
285
311
 
286
312
  @Composable
287
- fun FooterContent() {
313
+ internal fun FooterContent() {
288
314
  val options = LocalOptions.current
289
- if (options.footerComponent != null) {
315
+ if (options.hasFooter()) {
290
316
  val keyboardSize = keyboardSizeState()
291
- val bottomPadding = (AppNavigationBar.current - keyboardSize.value).coerceAtLeast(0.dp)
317
+ val bottomPadding = (AppNavigationBar.current + bottomTabBarInset() - keyboardSize.value).coerceAtLeast(0.dp)
292
318
  Footer(footerComponent = options.footerComponent, bottomPadding = bottomPadding)
293
319
  }
294
320
  }
295
321
 
296
322
  @Composable
297
- fun keyboardSizeState(): State<Dp> {
323
+ internal fun keyboardSizeState(): State<Dp> {
298
324
  val bottom = WindowInsets.ime.asPaddingValues()
299
325
  return rememberUpdatedState(bottom.calculateBottomPadding())
300
326
  }
301
327
 
302
328
  @Composable
303
- fun OverplayView(bottomTabIndex: Int, id: Int) {
329
+ internal fun OverplayView(isBottomTabChild: Boolean, id: Int) {
304
330
  val overplayType = OverplayComponentRegistry.getOverplayType()
305
331
 
306
332
  if (overplayType != null) {
307
333
  Box(Modifier.zIndex(if (overplayType == OverplayComponentType.SNACK_BAR) 3f else 7f).fillMaxSize()) {
308
- if (bottomTabIndex != -1) return@Box
334
+ if (isBottomTabChild) return@Box
309
335
  if (OverplayComponentRegistry.currentRootId() != id) return@Box
310
336
  OverplayComponentRegistry.OverlayComponent()
311
337
  }
@@ -355,8 +381,9 @@ data class InputSearchLayoutParams(
355
381
  val endPadding: Dp
356
382
  )
357
383
 
384
+ @Suppress("FrequentlyChangingValue")
358
385
  @Composable
359
- fun SearchAnimated(
386
+ internal fun SearchAnimated(
360
387
  isScrollInProgress: Boolean
361
388
  ) {
362
389
  val scrollState = LocalScrollState.current
@@ -469,64 +496,61 @@ private fun quantize(value: Int, stepPx: Int): Int {
469
496
  }
470
497
 
471
498
  @Composable
472
- fun LazyListState.proxyScrollState(
499
+ internal fun LazyListState.proxyScrollState(
473
500
  thresholdPx: Int = 200,
474
501
  stepPx: Int = 4
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
- }
502
+ ): Pair<ScrollState, Boolean> = proxyScroll(
503
+ source = this,
504
+ thresholdPx = thresholdPx,
505
+ stepPx = stepPx,
506
+ isScrollInProgress = { isScrollInProgress },
507
+ rawTargetFlow = {
508
+ snapshotFlow {
509
+ if (firstVisibleItemIndex == 0) firstVisibleItemScrollOffset
510
+ else SCROLLED_PAST_FIRST_ITEM_THRESHOLD
511
+ }
512
+ },
513
+ )
504
514
 
505
515
  @Composable
506
- fun ScrollState.proxyLimitedScrollState(
516
+ internal fun ScrollState.proxyLimitedScrollState(
507
517
  thresholdPx: Int = 200,
508
518
  stepPx: Int = 4
519
+ ): Pair<ScrollState, Boolean> = proxyScroll(
520
+ source = this,
521
+ thresholdPx = thresholdPx,
522
+ stepPx = stepPx,
523
+ isScrollInProgress = { isScrollInProgress },
524
+ rawTargetFlow = { snapshotFlow { value } },
525
+ )
526
+
527
+ @Composable
528
+ private fun proxyScroll(
529
+ source: Any,
530
+ thresholdPx: Int,
531
+ stepPx: Int,
532
+ isScrollInProgress: () -> Boolean,
533
+ rawTargetFlow: () -> Flow<Int>,
509
534
  ): Pair<ScrollState, Boolean> {
510
535
  val proxy = rememberScrollState()
511
536
  val locked = remember { mutableStateOf(false) }
512
537
 
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
538
+ LaunchedEffect(source, proxy, thresholdPx, stepPx) {
539
+ rawTargetFlow().collect { rawTarget ->
540
+ val shouldLock = rawTarget > thresholdPx
541
+ if (locked.value != shouldLock) locked.value = shouldLock
518
542
 
519
- val desired = if (shouldLock) thresholdPx else quantize(rawTarget, stepPx)
543
+ val desired = if (shouldLock) thresholdPx else quantize(rawTarget, stepPx)
520
544
 
521
- if (proxy.value != desired) {
522
- proxy.scrollTo(desired.coerceAtLeast(0))
523
- }
545
+ if (proxy.value != desired) {
546
+ proxy.scrollTo(desired.coerceAtLeast(0))
524
547
  }
548
+ }
525
549
  }
526
550
 
527
551
  val inProgress = remember { mutableStateOf(false) }
528
- LaunchedEffect(this, thresholdPx) {
529
- snapshotFlow { isScrollInProgress }
552
+ LaunchedEffect(source, thresholdPx) {
553
+ snapshotFlow { isScrollInProgress() }
530
554
  .collect { progressing ->
531
555
  inProgress.value = if (locked.value) false else progressing
532
556
  }
@@ -6,13 +6,7 @@ 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.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
9
+ import androidx.compose.foundation.layout.*
16
10
  import androidx.compose.runtime.Composable
17
11
  import androidx.compose.runtime.LaunchedEffect
18
12
  import androidx.compose.ui.Alignment
@@ -24,20 +18,20 @@ import androidx.navigation.compose.rememberNavController
24
18
  import vn.momo.kits.const.AppNavigationBar
25
19
  import vn.momo.kits.const.AppTheme
26
20
  import vn.momo.kits.const.Spacing
21
+ import vn.momo.kits.navigation.KeyboardOptions
27
22
  import vn.momo.kits.navigation.LocalNavigation
28
23
  import vn.momo.kits.navigation.LocalNavigator
29
24
  import vn.momo.kits.navigation.NavigationOptions
30
25
  import vn.momo.kits.navigation.StackScreen
31
26
  import vn.momo.kits.navigation.component.HeaderType
32
- import vn.momo.kits.platform.getScreenHeight
33
27
 
34
28
  private var bottomTabOptionItems : MutableList<NavigationOptions?> = mutableListOf()
35
- fun setBottomTabOption(index: Int, options: NavigationOptions){
29
+ internal fun setBottomTabOption(index: Int, options: NavigationOptions){
36
30
  if (index in bottomTabOptionItems.indices) {
37
31
  bottomTabOptionItems[index] = options
38
32
  }
39
33
  }
40
- fun getBottomTabOption(index: Int): NavigationOptions? {
34
+ internal fun getBottomTabOption(index: Int): NavigationOptions? {
41
35
  return if (index in bottomTabOptionItems.indices) {
42
36
  bottomTabOptionItems[index]
43
37
  } else null
@@ -58,15 +52,16 @@ fun BottomTab(
58
52
 
59
53
 
60
54
  LaunchedEffect(Unit){
55
+ navigation.markAsBottomTabRoot()
61
56
  navigation.setOptions(
62
- headerType = HeaderType.None
57
+ headerType = HeaderType.None,
58
+ keyboardOptions = KeyboardOptions(useAvoidKeyboard = false)
63
59
  )
64
60
  }
65
61
 
66
- Box(modifier = Modifier.fillMaxWidth().height(getScreenHeight()), contentAlignment = Alignment.BottomCenter) {
62
+ Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
67
63
  Box(modifier = Modifier
68
64
  .fillMaxSize()
69
- .padding(bottom = BOTTOM_TAB_BAR_HEIGHT.dp + AppNavigationBar.current)
70
65
  ) {
71
66
  NavHost(
72
67
  navController = navController,
@@ -133,7 +128,7 @@ fun BottomTab(
133
128
  onTabSelected = {
134
129
  val currentRoute = navController.currentBackStackEntry?.destination?.route
135
130
  val targetRoute = "option$it"
136
- if (currentRoute != targetRoute){
131
+ if (currentRoute != targetRoute) {
137
132
  navController.navigate(targetRoute)
138
133
  }
139
134
  }
@@ -5,19 +5,7 @@ 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.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
8
+ import androidx.compose.foundation.layout.*
21
9
  import androidx.compose.foundation.shape.CircleShape
22
10
  import androidx.compose.foundation.shape.RoundedCornerShape
23
11
  import androidx.compose.runtime.Composable
@@ -32,26 +20,20 @@ import androidx.compose.ui.text.style.TextOverflow
32
20
  import androidx.compose.ui.unit.dp
33
21
  import androidx.navigation.NavController
34
22
  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
42
23
  import vn.momo.kits.application.IsShowBaseLineDebug
43
- import vn.momo.kits.components.BadgeDot
44
- import vn.momo.kits.components.DotSize
45
- import vn.momo.kits.const.Typography
24
+ import vn.momo.kits.components.*
25
+ import vn.momo.kits.const.*
26
+ import vn.momo.kits.modifier.InternalApi
46
27
  import vn.momo.kits.modifier.conditional
47
28
  import vn.momo.kits.modifier.noFeedbackClickable
48
29
  import vn.momo.kits.platform.getScreenDimensions
49
30
 
50
- val floatingButtonWidth = 75.dp
31
+ internal val floatingButtonWidth = 75.dp
32
+ @InternalApi
51
33
  const val BOTTOM_TAB_BAR_HEIGHT = 64
52
34
 
53
35
  @Composable
54
- fun BottomTabBar(
36
+ internal fun BottomTabBar(
55
37
  items: List<BottomTabItem>,
56
38
  floatingButton: BottomTabFloatingButton? = null,
57
39
  navController: NavController,
@@ -91,11 +73,11 @@ fun BottomTabBar(
91
73
  horizontalArrangement = Arrangement.SpaceAround,
92
74
  ) {
93
75
  if (floatingButton == null) {
94
- renderTabBarItem(items, 0, items.size, selectedIndex, onTabSelected)
76
+ RenderTabBarItem(items, 0, items.size, selectedIndex, onTabSelected)
95
77
  } else {
96
- renderTabBarItem(items, 0, mid, selectedIndex, onTabSelected)
78
+ RenderTabBarItem(items, 0, mid, selectedIndex, onTabSelected)
97
79
  Spacer(modifier = Modifier.width(floatingButtonWidth).padding(horizontal = Spacing.XXS))
98
- renderTabBarItem(items, mid, items.size, selectedIndex, onTabSelected)
80
+ RenderTabBarItem(items, mid, items.size, selectedIndex, onTabSelected)
99
81
  }
100
82
  }
101
83
 
@@ -124,7 +106,7 @@ fun BottomTabBar(
124
106
  }
125
107
 
126
108
  @Composable
127
- fun RowScope.renderTabBarItem(
109
+ internal fun RowScope.RenderTabBarItem(
128
110
  items: List<BottomTabItem>,
129
111
  fromIndex: Int,
130
112
  toIndex: Int,
@@ -143,7 +125,7 @@ fun RowScope.renderTabBarItem(
143
125
  }
144
126
 
145
127
  @Composable
146
- fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> Unit) {
128
+ internal fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> Unit) {
147
129
  fun isNumber(label: String): Boolean {
148
130
  val numberRegex = "^\\d+$".toRegex()
149
131
  return numberRegex.matches(label)
@@ -203,7 +185,7 @@ fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> Unit) {
203
185
  }
204
186
 
205
187
  @Composable
206
- fun FloatingButton(data: BottomTabFloatingButton) {
188
+ internal fun FloatingButton(data: BottomTabFloatingButton) {
207
189
  Column(
208
190
  modifier = Modifier
209
191
  .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
- fun CurvedContainer(
20
+ internal fun CurvedContainer(
21
21
  circleSize: Dp = 48.dp,
22
22
  tabBarItemIconSize: Dp = 28.dp,
23
23
  circleOverSize: Dp = 2.dp,
@@ -1,40 +1,21 @@
1
1
  package vn.momo.kits.navigation.component
2
2
 
3
- import androidx.compose.animation.AnimatedVisibility
3
+ import androidx.compose.animation.*
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
12
8
  import androidx.compose.foundation.ScrollState
13
9
  import androidx.compose.foundation.background
14
10
  import androidx.compose.foundation.border
15
11
  import androidx.compose.foundation.interaction.MutableInteractionSource
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
12
+ import androidx.compose.foundation.layout.*
26
13
  import androidx.compose.material3.AlertDialogDefaults
27
14
  import androidx.compose.material3.FloatingActionButton
28
15
  import androidx.compose.material3.FloatingActionButtonDefaults
29
16
  import androidx.compose.material3.FloatingActionButtonElevation
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
17
+ import androidx.compose.runtime.*
36
18
  import androidx.compose.runtime.saveable.rememberSaveable
37
- import androidx.compose.runtime.setValue
38
19
  import androidx.compose.ui.Alignment
39
20
  import androidx.compose.ui.Modifier
40
21
  import androidx.compose.ui.graphics.Color
@@ -50,6 +31,7 @@ import vn.momo.kits.components.Text
50
31
  import vn.momo.kits.const.Colors
51
32
  import vn.momo.kits.const.Typography
52
33
  import vn.momo.kits.modifier.conditional
34
+ import kotlin.time.Duration.Companion.milliseconds
53
35
 
54
36
  enum class FABSize { SMALL, DEFAULT }
55
37
  enum class FABPosition { END, CENTER }
@@ -73,7 +55,7 @@ private val FabIconSizeDefault = 24.dp
73
55
  private val FabPaddingHorizontal = 12.dp
74
56
 
75
57
  @Composable
76
- fun FloatingButton(
58
+ internal fun FloatingButton(
77
59
  scrollPosition: Int,
78
60
  bottom: Dp,
79
61
  onClick: () -> Unit,
@@ -106,7 +88,7 @@ fun FloatingButton(
106
88
  coroutineScope.launch {
107
89
  scrollStateFlow.collectLatest { newScroll ->
108
90
  isExpanded = newScroll <= lastScrollPosition
109
- delay(100)
91
+ delay(100.milliseconds)
110
92
  lastScrollPosition = newScroll
111
93
  }
112
94
  }
@@ -1,5 +1,7 @@
1
1
  package vn.momo.kits.navigation.component
2
2
 
3
+ import vn.momo.kits.modifier.InternalApi
4
+
3
5
  import androidx.compose.animation.core.animateFloatAsState
4
6
  import androidx.compose.foundation.background
5
7
  import androidx.compose.foundation.border
@@ -42,12 +44,14 @@ import vn.momo.kits.navigation.LocalOptions
42
44
  import vn.momo.kits.navigation.LocalScrollState
43
45
  import vn.momo.kits.navigation.getInputSearchType
44
46
 
47
+ @InternalApi
45
48
  const val HEADER_HEIGHT = 52
46
- enum class InputSearchType { None, Header, Animated }
49
+ internal enum class InputSearchType { None, Header, Animated }
47
50
  enum class TitlePosition { LEFT, CENTER }
48
51
 
52
+ @Suppress("FrequentlyChangingValue")
49
53
  @Composable
50
- fun Header(onBackHandler: (() -> Unit)? = null) {
54
+ internal fun Header(onBackHandler: (() -> Unit)? = null) {
51
55
  val options = LocalOptions.current
52
56
  val navigator = LocalNavigator.current
53
57
  val scrollState = LocalScrollState.current
@@ -106,7 +110,7 @@ fun Header(onBackHandler: (() -> Unit)? = null) {
106
110
  backgroundButton = headerColor.backgroundButton,
107
111
  tintIconColor = headerColor.tintIconColor,
108
112
  onBackHandler = {
109
- onBackHandler?.invoke() ?: navigator.onBackSafe { }
113
+ navigator.onBackSafe { }
110
114
  }
111
115
  )
112
116
  Spacer(Modifier.width(Spacing.S))
@@ -168,7 +172,7 @@ private fun BackButton(borderColor: Color, backgroundButton: Color, tintIconColo
168
172
  }
169
173
 
170
174
  @Composable
171
- fun HeaderContent(
175
+ internal fun HeaderContent(
172
176
  headerTitle: HeaderTitle,
173
177
  tintIconColor: Color,
174
178
  titlePosition: TitlePosition = TitlePosition.LEFT,
@@ -199,7 +203,7 @@ fun HeaderContent(
199
203
  }
200
204
 
201
205
  @Composable
202
- fun VerticalShadow(opacity: Float){
206
+ internal fun VerticalShadow(opacity: Float){
203
207
  if(opacity == 1f){
204
208
  Box(modifier = Modifier
205
209
  .fillMaxWidth()
@@ -282,8 +286,9 @@ data class HeaderBackProps(
282
286
  val preventBack: PopupNotifyProps? = null,
283
287
  )
284
288
 
289
+ @InternalApi
285
290
  data class HeaderColor(val tintIconColor: Color, val backgroundButton: Color, val borderColor: Color)
286
- fun getHeaderColor(headerType: HeaderType, opacity: Float, tintColor: Color?, defaultColor: Color): HeaderColor{
291
+ internal fun getHeaderColor(headerType: HeaderType, opacity: Float, tintColor: Color?, defaultColor: Color): HeaderColor{
287
292
  return if(headerType is HeaderType.Animated) {
288
293
  if (opacity == 1f || !headerType.isSurface)
289
294
  HeaderColor(
@@ -27,8 +27,9 @@ enum class AnimatedHeaderRatio(val value: Float){
27
27
  RATIO_3_2(3f / 2f)
28
28
  }
29
29
 
30
+ @Suppress("FrequentlyChangingValue")
30
31
  @Composable
31
- fun HeaderBackground() {
32
+ internal fun HeaderBackground() {
32
33
  val density = LocalDensity.current
33
34
  val theme = AppTheme.current
34
35
  val options = LocalOptions.current
@@ -1,5 +1,7 @@
1
1
  package vn.momo.kits.navigation.component
2
2
 
3
+ import vn.momo.kits.modifier.InternalApi
4
+
3
5
  import androidx.compose.foundation.background
4
6
  import androidx.compose.foundation.border
5
7
  import androidx.compose.foundation.layout.Arrangement
@@ -24,7 +26,7 @@ import androidx.compose.ui.Modifier
24
26
  import androidx.compose.ui.draw.clip
25
27
  import androidx.compose.ui.graphics.Color
26
28
  import androidx.compose.ui.unit.dp
27
- import vn.momo.kits.application.ApplicationContext
29
+ import vn.momo.kits.application.LocalContext
28
30
  import vn.momo.kits.application.MiniAppContext
29
31
  import vn.momo.kits.components.BadgeDot
30
32
  import vn.momo.kits.components.DotSize
@@ -35,6 +37,7 @@ import vn.momo.kits.modifier.activeOpacityClickable
35
37
  import vn.momo.kits.navigation.LocalMaxApi
36
38
 
37
39
  @Composable
40
+ @InternalApi
38
41
  fun HeaderRight(
39
42
  headerRight: HeaderRight,
40
43
  tintColor: Color? = null,
@@ -57,13 +60,13 @@ fun HeaderRight(
57
60
  }
58
61
 
59
62
  @Composable
60
- fun Toolkit(
63
+ internal fun Toolkit(
61
64
  headerRight: HeaderRight.Toolkit,
62
65
  tintColor: Color? = null,
63
66
  headerColor: HeaderColor
64
67
  ) {
65
68
  val api = LocalMaxApi.current
66
- val context = ApplicationContext.current
69
+ val context = LocalContext.current
67
70
 
68
71
  var isFavorite by remember { mutableStateOf(false) }
69
72
  val isLoading by remember { mutableStateOf(false) }
@@ -186,7 +189,7 @@ fun Toolkit(
186
189
 
187
190
 
188
191
  @Composable
189
- fun NavigationButton(
192
+ internal fun NavigationButton(
190
193
  disabled: Boolean,
191
194
  icon: String,
192
195
  showBadge: Boolean? = false,
@@ -300,7 +303,7 @@ data class Tool(
300
303
  }
301
304
  }
302
305
 
303
- data class NavigationButtonConfig(
306
+ private data class NavigationButtonConfig(
304
307
  val icon: String,
305
308
  val onPress: () -> Unit
306
- )
309
+ )