@momo-kits/native-kits 0.162.2-test.2-debug → 0.162.2-test.21-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 (132) 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 +15 -11
  5. package/compose/compose.podspec +1 -1
  6. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +2 -7
  7. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +6 -10
  8. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +6 -24
  9. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +4 -19
  10. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +1 -10
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +2 -2
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +1 -1
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +1 -1
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +6 -17
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +1 -1
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +6 -101
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +5 -19
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +8 -33
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +1 -1
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +22 -25
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +27 -29
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +3 -26
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +30 -31
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +3 -13
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -5
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +3 -11
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +3 -18
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +11 -11
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +3 -11
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +69 -26
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +1 -5
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +84 -59
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +13 -71
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +10 -51
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +6 -1
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +4 -21
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +1 -1
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +16 -75
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +5 -26
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +1 -1
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +3 -11
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +16 -40
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +3 -9
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -28
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +2 -13
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +17 -0
  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 +7 -27
  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 -27
  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 +91 -63
  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 +0 -2
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -8
  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 +0 -2
  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 +2 -2
  117. package/package.json +1 -1
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FontScaleStore.kt +0 -77
  119. package/example/ios/Example.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/Example.xcscheme +0 -32
  120. package/example/ios/Example.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  121. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  122. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/WorkspaceSettings.xcsettings +0 -16
  123. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
  124. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -5
  125. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +0 -58
  126. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/Pods-Example.xcscheme +0 -58
  127. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SDWebImage.xcscheme +0 -58
  128. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SDWebImageSwiftUI.xcscheme +0 -58
  129. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SkeletonUI.xcscheme +0 -58
  130. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/lottie-ios-LottiePrivacyInfo.xcscheme +0 -58
  131. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/lottie-ios.xcscheme +0 -58
  132. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -60
@@ -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,7 +203,7 @@ 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
 
@@ -199,21 +211,29 @@ internal fun StackScreen(
199
211
  FloatingContent()
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() {
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
227
+
228
+ val isBottomTabChild = LocalNavigation.current.bottomTabIndex != -1
213
229
 
214
230
  val fabProps = options.floatingButtonProps ?: return
215
231
  val bottomPadding = fabProps.bottom
216
- ?: (Spacing.M + if (options.footerComponent != null) with(density) { footerHeightPx.intValue.toDp() } else 0.dp)
232
+ ?: (Spacing.M + when {
233
+ options.hasFooter() -> with(density) { footerHeightPx.intValue.toDp() }
234
+ isBottomTabChild -> bottomTabBarInset() + (navigationBar - keyboardSizeState().value).coerceAtLeast(0.dp)
235
+ else -> navigationBar
236
+ })
217
237
 
218
238
  FloatingButton(
219
239
  scrollPosition = fabProps.scrollState?.value ?: scrollState.value,
@@ -229,11 +249,12 @@ fun FloatingContent() {
229
249
  }
230
250
 
231
251
  @Composable
232
- fun ColumnScope.MainContent(content: @Composable () -> Unit) {
252
+ internal fun ColumnScope.MainContent(content: @Composable () -> Unit, isBottomTabRoot: Boolean, isBottomTabChild: Boolean) {
233
253
  val options = LocalOptions.current
234
254
  val inputSearchType = getInputSearchType(options)
235
255
  val density = LocalDensity.current
236
256
  val scrollState = LocalScrollState.current
257
+ val navigationBar = AppNavigationBar.current
237
258
 
238
259
  Spacer(
239
260
  Modifier.height(
@@ -257,6 +278,15 @@ fun ColumnScope.MainContent(content: @Composable () -> Unit) {
257
278
  .conditional(options.scrollData.scrollable && options.scrollData.scrollState is ScrollState) {
258
279
  verticalScroll(scrollState)
259
280
  }
281
+ .padding(
282
+ bottom = when {
283
+ options.hasFooter() || isBottomTabRoot -> 0.dp
284
+ // navigationBar must collapse with the IME too, else it leaves a gap
285
+ // between the content and the keyboard (imePadding already lifted it).
286
+ isBottomTabChild -> bottomTabBarInset() + (navigationBar - keyboardSizeState().value).coerceAtLeast(0.dp)
287
+ else -> navigationBar
288
+ }
289
+ )
260
290
  ) {
261
291
  ScreenContent(content = content)
262
292
  }
@@ -264,7 +294,7 @@ fun ColumnScope.MainContent(content: @Composable () -> Unit) {
264
294
  }
265
295
 
266
296
  @Composable
267
- fun ScreenContent(content: @Composable () -> Unit) {
297
+ internal fun ScreenContent(content: @Composable () -> Unit) {
268
298
  val scrollState = LocalScrollState.current
269
299
  val options = LocalOptions.current
270
300
 
@@ -284,28 +314,28 @@ fun ScreenContent(content: @Composable () -> Unit) {
284
314
  }
285
315
 
286
316
  @Composable
287
- fun FooterContent() {
317
+ internal fun FooterContent() {
288
318
  val options = LocalOptions.current
289
- if (options.footerComponent != null) {
319
+ if (options.hasFooter()) {
290
320
  val keyboardSize = keyboardSizeState()
291
- val bottomPadding = (AppNavigationBar.current - keyboardSize.value).coerceAtLeast(0.dp)
321
+ val bottomPadding = (AppNavigationBar.current + bottomTabBarInset() - keyboardSize.value).coerceAtLeast(0.dp)
292
322
  Footer(footerComponent = options.footerComponent, bottomPadding = bottomPadding)
293
323
  }
294
324
  }
295
325
 
296
326
  @Composable
297
- fun keyboardSizeState(): State<Dp> {
327
+ internal fun keyboardSizeState(): State<Dp> {
298
328
  val bottom = WindowInsets.ime.asPaddingValues()
299
329
  return rememberUpdatedState(bottom.calculateBottomPadding())
300
330
  }
301
331
 
302
332
  @Composable
303
- fun OverplayView(bottomTabIndex: Int, id: Int) {
333
+ internal fun OverplayView(isBottomTabChild: Boolean, id: Int) {
304
334
  val overplayType = OverplayComponentRegistry.getOverplayType()
305
335
 
306
336
  if (overplayType != null) {
307
337
  Box(Modifier.zIndex(if (overplayType == OverplayComponentType.SNACK_BAR) 3f else 7f).fillMaxSize()) {
308
- if (bottomTabIndex != -1) return@Box
338
+ if (isBottomTabChild) return@Box
309
339
  if (OverplayComponentRegistry.currentRootId() != id) return@Box
310
340
  OverplayComponentRegistry.OverlayComponent()
311
341
  }
@@ -355,8 +385,9 @@ data class InputSearchLayoutParams(
355
385
  val endPadding: Dp
356
386
  )
357
387
 
388
+ @Suppress("FrequentlyChangingValue")
358
389
  @Composable
359
- fun SearchAnimated(
390
+ internal fun SearchAnimated(
360
391
  isScrollInProgress: Boolean
361
392
  ) {
362
393
  val scrollState = LocalScrollState.current
@@ -469,64 +500,61 @@ private fun quantize(value: Int, stepPx: Int): Int {
469
500
  }
470
501
 
471
502
  @Composable
472
- fun LazyListState.proxyScrollState(
503
+ internal fun LazyListState.proxyScrollState(
473
504
  thresholdPx: Int = 200,
474
505
  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
- }
506
+ ): Pair<ScrollState, Boolean> = proxyScroll(
507
+ source = this,
508
+ thresholdPx = thresholdPx,
509
+ stepPx = stepPx,
510
+ isScrollInProgress = { isScrollInProgress },
511
+ rawTargetFlow = {
512
+ snapshotFlow {
513
+ if (firstVisibleItemIndex == 0) firstVisibleItemScrollOffset
514
+ else SCROLLED_PAST_FIRST_ITEM_THRESHOLD
515
+ }
516
+ },
517
+ )
504
518
 
505
519
  @Composable
506
- fun ScrollState.proxyLimitedScrollState(
520
+ internal fun ScrollState.proxyLimitedScrollState(
507
521
  thresholdPx: Int = 200,
508
522
  stepPx: Int = 4
523
+ ): Pair<ScrollState, Boolean> = proxyScroll(
524
+ source = this,
525
+ thresholdPx = thresholdPx,
526
+ stepPx = stepPx,
527
+ isScrollInProgress = { isScrollInProgress },
528
+ rawTargetFlow = { snapshotFlow { value } },
529
+ )
530
+
531
+ @Composable
532
+ private fun proxyScroll(
533
+ source: Any,
534
+ thresholdPx: Int,
535
+ stepPx: Int,
536
+ isScrollInProgress: () -> Boolean,
537
+ rawTargetFlow: () -> Flow<Int>,
509
538
  ): Pair<ScrollState, Boolean> {
510
539
  val proxy = rememberScrollState()
511
540
  val locked = remember { mutableStateOf(false) }
512
541
 
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
542
+ LaunchedEffect(source, proxy, thresholdPx, stepPx) {
543
+ rawTargetFlow().collect { rawTarget ->
544
+ val shouldLock = rawTarget > thresholdPx
545
+ if (locked.value != shouldLock) locked.value = shouldLock
518
546
 
519
- val desired = if (shouldLock) thresholdPx else quantize(rawTarget, stepPx)
547
+ val desired = if (shouldLock) thresholdPx else quantize(rawTarget, stepPx)
520
548
 
521
- if (proxy.value != desired) {
522
- proxy.scrollTo(desired.coerceAtLeast(0))
523
- }
549
+ if (proxy.value != desired) {
550
+ proxy.scrollTo(desired.coerceAtLeast(0))
524
551
  }
552
+ }
525
553
  }
526
554
 
527
555
  val inProgress = remember { mutableStateOf(false) }
528
- LaunchedEffect(this, thresholdPx) {
529
- snapshotFlow { isScrollInProgress }
556
+ LaunchedEffect(source, thresholdPx) {
557
+ snapshotFlow { isScrollInProgress() }
530
558
  .collect { progressing ->
531
559
  inProgress.value = if (locked.value) false else progressing
532
560
  }
@@ -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