@momo-kits/native-kits 0.167.1-beta.4-debug → 0.167.1-beta.4

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 (142) hide show
  1. package/package.json +1 -1
  2. package/build.gradle.kts +0 -11
  3. package/compose/build.gradle.kts +0 -190
  4. package/compose/build.gradle.kts.backup +0 -190
  5. package/compose/compose.podspec +0 -47
  6. package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +0 -19
  7. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +0 -6
  8. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +0 -12
  9. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +0 -7
  10. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -151
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -52
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -124
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -183
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -207
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -39
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -293
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -831
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +0 -269
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -110
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -378
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +0 -56
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +0 -157
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -82
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -32
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -338
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +0 -175
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -356
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -113
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -89
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -128
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +0 -209
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -542
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -49
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -76
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -125
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -231
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -189
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -475
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -114
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -266
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -240
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -223
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -264
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -185
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -343
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +0 -108
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -56
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -41
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -84
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -40
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -328
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -97
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -310
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -59
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +0 -87
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +0 -21
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -90
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +0 -323
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +0 -220
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +0 -473
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +0 -122
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +0 -199
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -91
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -552
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -88
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -125
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -207
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +0 -498
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -176
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +0 -182
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -193
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -234
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -188
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -325
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +0 -16
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +0 -18
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -16
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -188
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -296
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -52
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -38
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -39
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -52
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -66
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +0 -104
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -48
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -299
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +0 -58
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -129
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -106
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -201
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -393
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +0 -8
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -595
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +0 -148
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +0 -35
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -175
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -225
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -169
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -358
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -83
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +0 -106
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +0 -75
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -309
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -37
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -364
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -132
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +0 -127
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +0 -12
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -91
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +0 -9
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -71
  124. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
  125. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -55
  126. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
  127. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -105
  128. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +0 -40
  129. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +0 -45
  130. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +0 -32
  131. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +0 -38
  132. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +0 -66
  133. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -213
  134. package/gradle/libs.versions.toml +0 -69
  135. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  136. package/gradle/wrapper/gradle-wrapper.properties +0 -8
  137. package/gradle.properties +0 -26
  138. package/gradlew +0 -252
  139. package/gradlew.bat +0 -94
  140. package/ios-demo/README.md +0 -120
  141. package/local.properties +0 -8
  142. package/settings.gradle.kts +0 -64
@@ -1,148 +0,0 @@
1
- package vn.momo.kits.navigation
2
-
3
- import androidx.compose.animation.AnimatedContentTransitionScope
4
- import androidx.compose.animation.AnimatedVisibilityScope
5
- import androidx.compose.animation.EnterExitState
6
- import androidx.compose.animation.EnterTransition
7
- import androidx.compose.animation.ExitTransition
8
- import androidx.compose.animation.core.LinearEasing
9
- import androidx.compose.animation.core.animateFloat
10
- import androidx.compose.animation.core.tween
11
- import androidx.compose.animation.slideInHorizontally
12
- import androidx.compose.animation.slideOutHorizontally
13
- import androidx.compose.foundation.layout.Box
14
- import androidx.compose.foundation.layout.fillMaxSize
15
- import androidx.compose.runtime.Composable
16
- import androidx.compose.runtime.DisposableEffect
17
- import androidx.compose.runtime.Stable
18
- import androidx.compose.runtime.derivedStateOf
19
- import androidx.compose.runtime.getValue
20
- import androidx.compose.runtime.mutableStateMapOf
21
- import androidx.compose.runtime.remember
22
- import androidx.compose.runtime.staticCompositionLocalOf
23
- import androidx.compose.ui.Modifier
24
- import androidx.compose.ui.draw.drawBehind
25
- import androidx.compose.ui.geometry.Offset
26
- import androidx.compose.ui.geometry.Size
27
- import androidx.compose.ui.graphics.Brush
28
- import androidx.compose.ui.graphics.Color
29
- import androidx.compose.ui.platform.LocalDensity
30
- import androidx.compose.ui.unit.IntOffset
31
- import androidx.compose.ui.unit.dp
32
- import androidx.navigation.NavBackStackEntry
33
- import androidx.navigation.NavDestination.Companion.hasRoute
34
- import vn.momo.kits.const.Colors
35
- import kotlin.math.roundToInt
36
-
37
- internal const val STACK_TRANSITION_DURATION = 300
38
-
39
- // Ported 1:1 from the RN kit's `forStackTransitionWithOverlay` so both kits look identical.
40
- private const val PARALLAX_RATIO = 0.3f
41
- private const val SCRIM_MAX_ALPHA = 0.6f
42
- private const val EDGE_SHADOW_MAX_ALPHA = 0.3f
43
- private val EDGE_SHADOW_WIDTH = 8.dp
44
-
45
- /** Kill switch: `false` restores pre-DS-760 back handling without a kit release. Transitions
46
- * stay on the new curve either way. */
47
- object SwipeBackConfig {
48
- var enabled: Boolean = true
49
- }
50
-
51
- // LinearEasing is mandatory: seekTo() maps drag fraction onto the timeline, so a curve would
52
- // decouple the card from the finger.
53
- private val stackSpec = tween<IntOffset>(STACK_TRANSITION_DURATION, easing = LinearEasing)
54
-
55
- internal fun AnimatedContentTransitionScope<NavBackStackEntry>.stackEnter(): EnterTransition =
56
- slideInHorizontally(stackSpec) { it }
57
-
58
- internal fun AnimatedContentTransitionScope<NavBackStackEntry>.stackExit(): ExitTransition =
59
- if (targetState.destination.hasRoute<DynamicDialogRoute>()) ExitTransition.None
60
- else slideOutHorizontally(stackSpec) { -(it * PARALLAX_RATIO).roundToInt() }
61
-
62
- internal fun AnimatedContentTransitionScope<NavBackStackEntry>.stackPopEnter(): EnterTransition =
63
- if (initialState.destination.hasRoute<DynamicDialogRoute>()) EnterTransition.None
64
- else slideInHorizontally(stackSpec) { -(it * PARALLAX_RATIO).roundToInt() }
65
-
66
- internal fun AnimatedContentTransitionScope<NavBackStackEntry>.stackPopExit(): ExitTransition =
67
- slideOutHorizontally(stackSpec) { it }
68
-
69
- /**
70
- * Which composed screen is the top card. Ids are monotonic and NavHost keeps both entries composed
71
- * for the whole segment, so "highest id" is stable both ways. `currentBackStackEntry` is not: it
72
- * flips at frame 0 of a pop and the scrim would vanish instead of fading.
73
- */
74
- @Stable
75
- internal class StackTransitionCoordinator {
76
- private val composedRoutes = mutableStateMapOf<Int, Boolean>()
77
-
78
- val topStackId: Int by derivedStateOf {
79
- composedRoutes.entries.filter { it.value }.maxOfOrNull { it.key } ?: -1
80
- }
81
-
82
- fun attach(id: Int, isStackRoute: Boolean) {
83
- composedRoutes[id] = isStackRoute
84
- }
85
-
86
- fun detach(id: Int) {
87
- composedRoutes.remove(id)
88
- }
89
- }
90
-
91
- internal val LocalStackTransition = staticCompositionLocalOf { StackTransitionCoordinator() }
92
-
93
- /** Draws the covered-screen scrim and the top card's left-edge shadow around a stack destination. */
94
- @Composable
95
- internal fun AnimatedVisibilityScope.StackTransitionDecor(
96
- id: Int,
97
- isStackRoute: Boolean,
98
- content: @Composable () -> Unit,
99
- ) {
100
- val coordinator = LocalStackTransition.current
101
- coordinator.attach(id, isStackRoute)
102
- DisposableEffect(coordinator, id) {
103
- onDispose { coordinator.detach(id) }
104
- }
105
-
106
- val isTopCard = isStackRoute && id == coordinator.topStackId
107
- val isCovered = isStackRoute && !isTopCard
108
-
109
- val scrimAlpha = transition.animateFloat(
110
- transitionSpec = { tween(STACK_TRANSITION_DURATION, easing = LinearEasing) },
111
- label = "stackScrimAlpha",
112
- ) { state -> if (isCovered && state != EnterExitState.Visible) SCRIM_MAX_ALPHA else 0f }
113
-
114
- val shadowAlpha = transition.animateFloat(
115
- transitionSpec = { tween(STACK_TRANSITION_DURATION, easing = LinearEasing) },
116
- label = "stackEdgeShadowAlpha",
117
- ) { state -> if (isTopCard && state == EnterExitState.Visible) EDGE_SHADOW_MAX_ALPHA else 0f }
118
-
119
- val shadowWidthPx = with(LocalDensity.current) { EDGE_SHADOW_WIDTH.toPx() }
120
- val shadowBrush = remember(shadowWidthPx) {
121
- Brush.horizontalGradient(
122
- colors = listOf(Color.Transparent, Colors.black_20),
123
- startX = -shadowWidthPx,
124
- endX = 0f,
125
- )
126
- }
127
-
128
- Box(Modifier.fillMaxSize()) {
129
- content()
130
-
131
- if (isStackRoute) {
132
- Box(
133
- Modifier
134
- .matchParentSize()
135
- // read inside drawBehind: in the body this would recompose every gesture frame
136
- .drawBehind {
137
- drawRect(
138
- brush = shadowBrush,
139
- topLeft = Offset(-shadowWidthPx, 0f),
140
- size = Size(shadowWidthPx, size.height),
141
- alpha = shadowAlpha.value,
142
- )
143
- drawRect(Colors.pink_09, alpha = scrimAlpha.value)
144
- }
145
- )
146
- }
147
- }
148
- }
@@ -1,35 +0,0 @@
1
- package vn.momo.kits.navigation
2
-
3
- import androidx.compose.runtime.Composable
4
- import androidx.compose.runtime.LaunchedEffect
5
- import androidx.compose.runtime.snapshotFlow
6
- import vn.momo.kits.platform.getScreenDimensions
7
- import vn.momo.kits.platform.rememberBackGestureProgress
8
- import vn.momo.maxapi.IMaxApi
9
-
10
- // Unscaled, so the tick lands at the same physical distance on every device.
11
- private const val SWIPE_BACK_HAPTIC_THRESHOLD_DP = 50f
12
-
13
- /** One light tick per crossing, re-armed below the threshold. iOS only. */
14
- @Composable
15
- internal fun SwipeBackHaptic(maxApi: IMaxApi?) {
16
- if (maxApi == null) return
17
-
18
- val progress = rememberBackGestureProgress()
19
- val screenWidth = getScreenDimensions().width.toFloat()
20
-
21
- LaunchedEffect(maxApi, screenWidth) {
22
- if (screenWidth <= 0f) return@LaunchedEffect
23
- val threshold = SWIPE_BACK_HAPTIC_THRESHOLD_DP / screenWidth
24
- var isSendHaptic = false
25
- snapshotFlow { progress.value }.collect { fraction ->
26
- val travelled = fraction ?: 0f
27
- if (travelled >= threshold && !isSendHaptic) {
28
- runCatching { maxApi.triggerEventVibration("light", callback = null) }
29
- isSendHaptic = true
30
- } else if (travelled < threshold && isSendHaptic) {
31
- isSendHaptic = false
32
- }
33
- }
34
- }
35
- }
@@ -1,175 +0,0 @@
1
- package vn.momo.kits.navigation.bottomtab
2
-
3
- import androidx.compose.animation.core.tween
4
- import androidx.compose.animation.fadeIn
5
- import androidx.compose.animation.fadeOut
6
- import androidx.compose.animation.scaleIn
7
- import androidx.compose.animation.scaleOut
8
- import androidx.compose.foundation.background
9
- import androidx.compose.foundation.layout.*
10
- import androidx.compose.runtime.Composable
11
- import androidx.compose.runtime.DisposableEffect
12
- import androidx.compose.runtime.LaunchedEffect
13
- import androidx.compose.ui.Alignment
14
- import androidx.compose.ui.Modifier
15
- import androidx.compose.ui.unit.dp
16
- import androidx.navigation.compose.NavHost
17
- import androidx.navigation.compose.composable
18
- import androidx.navigation.compose.currentBackStackEntryAsState
19
- import androidx.navigation.compose.rememberNavController
20
- import vn.momo.kits.const.AppNavigationBar
21
- import vn.momo.kits.const.AppTheme
22
- import vn.momo.kits.const.Spacing
23
- import vn.momo.kits.navigation.KeyboardOptions
24
- import vn.momo.kits.navigation.LocalNavigation
25
- import vn.momo.kits.navigation.LocalNavigator
26
- import vn.momo.kits.navigation.NavigationOptions
27
- import vn.momo.kits.navigation.StackScreen
28
- import vn.momo.kits.navigation.component.HeaderType
29
- import vn.momo.kits.platform.reportFloatingArrowBack
30
-
31
- private var bottomTabOptionItems : MutableList<NavigationOptions?> = mutableListOf()
32
- internal fun setBottomTabOption(index: Int, options: NavigationOptions){
33
- if (index in bottomTabOptionItems.indices) {
34
- bottomTabOptionItems[index] = options
35
- }
36
- }
37
- internal fun getBottomTabOption(index: Int): NavigationOptions? {
38
- return if (index in bottomTabOptionItems.indices) {
39
- bottomTabOptionItems[index]
40
- } else null
41
- }
42
-
43
- @Composable
44
- fun BottomTab(
45
- items: List<BottomTabItem>,
46
- floatingButton: BottomTabFloatingButton? = null
47
- ) {
48
- val navigation = LocalNavigation.current
49
- val navigator = LocalNavigator.current
50
- val navController = rememberNavController()
51
-
52
- bottomTabOptionItems = items.mapIndexed { index, item ->
53
- item.options ?: NavigationOptions()
54
- }.toMutableList()
55
-
56
-
57
- LaunchedEffect(Unit){
58
- navigation.markAsBottomTabRoot()
59
- navigation.setOptions(
60
- headerType = HeaderType.None,
61
- keyboardOptions = KeyboardOptions(useAvoidKeyboard = false)
62
- )
63
- }
64
-
65
- // Tab 0 keeps the seeked transition; other tabs go to the host's floating arrow. Matches RN.
66
- val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
67
- val isFirstTab = currentRoute == null || currentRoute == "option0"
68
-
69
- DisposableEffect(isFirstTab) {
70
- reportFloatingArrowBack(!isFirstTab)
71
- navigator.tabBackHandler = if (isFirstTab) null else ({
72
- navController.popBackStack(navController.graph.startDestinationRoute!!, false)
73
- })
74
- // both live outside this composition
75
- onDispose {
76
- reportFloatingArrowBack(false)
77
- navigator.tabBackHandler = null
78
- }
79
- }
80
-
81
- Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
82
- Box(modifier = Modifier
83
- .fillMaxSize()
84
- ) {
85
- NavHost(
86
- navController = navController,
87
- startDestination = "option0"
88
- ) {
89
- items.forEachIndexed { index, item ->
90
- composable(
91
- route = "option$index",
92
- enterTransition = {
93
- fadeIn(animationSpec = tween(200)) +
94
- scaleIn(
95
- initialScale = 0.97f,
96
- animationSpec = tween(200)
97
- )
98
- },
99
- exitTransition = {
100
- fadeOut(animationSpec = tween(200)) +
101
- scaleOut(
102
- targetScale = 0.97f,
103
- animationSpec = tween(200)
104
- )
105
- },
106
- popEnterTransition = {
107
- fadeIn(animationSpec = tween(200)) +
108
- scaleIn(
109
- initialScale = 0.97f,
110
- animationSpec = tween(200)
111
- )
112
- },
113
- popExitTransition = {
114
- fadeOut(animationSpec = tween(200)) +
115
- scaleOut(
116
- targetScale = 0.97f,
117
- animationSpec = tween(200)
118
- )
119
- }
120
- ) {
121
- val option = getBottomTabOption(index)
122
-
123
- StackScreen(
124
- content = item.screen,
125
- navigationOptions = option,
126
- bottomTabIndex = index,
127
- onBackHandler = {
128
- if (index != 0) {
129
- navController.popBackStack(
130
- navController.graph.startDestinationRoute!!,
131
- inclusive = false
132
- )
133
- } else {
134
- navigator.pop()
135
- }
136
- }
137
- )
138
- }
139
- }
140
- }
141
- }
142
- Column {
143
- BottomTabBar(
144
- items = items,
145
- floatingButton = floatingButton,
146
- navController = navController,
147
- onTabSelected = {
148
- val currentRoute = navController.currentBackStackEntry?.destination?.route
149
- val targetRoute = "option$it"
150
- if (currentRoute != targetRoute) {
151
- navController.navigate(targetRoute)
152
- }
153
- }
154
- )
155
- Spacer(modifier = Modifier.fillMaxWidth().height(AppNavigationBar.current + Spacing.S).background(AppTheme.current.colors.background.surface))
156
- }
157
- }
158
- }
159
-
160
- data class BottomTabItem(
161
- val name: String,
162
- val label: String,
163
- val icon: String,
164
- val showDot: Boolean = false,
165
- val badgeLabel: String? = null,
166
- val screen: @Composable () -> Unit,
167
- val options: NavigationOptions? = null,
168
- val initialParams: Any? = null
169
- )
170
-
171
- data class BottomTabFloatingButton(
172
- val icon: String,
173
- val label: String,
174
- val onPress: () -> Unit,
175
- )
@@ -1,225 +0,0 @@
1
- package vn.momo.kits.navigation.bottomtab
2
-
3
- import androidx.compose.animation.core.FastOutSlowInEasing
4
- import androidx.compose.animation.core.animateDpAsState
5
- import androidx.compose.animation.core.tween
6
- import androidx.compose.foundation.background
7
- import androidx.compose.foundation.border
8
- import androidx.compose.foundation.layout.*
9
- import androidx.compose.foundation.shape.CircleShape
10
- import androidx.compose.foundation.shape.RoundedCornerShape
11
- import androidx.compose.runtime.Composable
12
- import androidx.compose.runtime.getValue
13
- import androidx.compose.ui.Alignment
14
- import androidx.compose.ui.Modifier
15
- import androidx.compose.ui.draw.clip
16
- import androidx.compose.ui.graphics.Brush
17
- import androidx.compose.ui.graphics.Color
18
- import androidx.compose.ui.text.style.TextAlign
19
- import androidx.compose.ui.text.style.TextOverflow
20
- import androidx.compose.ui.unit.dp
21
- import androidx.navigation.NavController
22
- import androidx.navigation.compose.currentBackStackEntryAsState
23
- 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
27
- import vn.momo.kits.modifier.conditional
28
- import vn.momo.kits.modifier.noFeedbackClickable
29
- import vn.momo.kits.platform.getScreenDimensions
30
-
31
- internal val floatingButtonWidth = 75.dp
32
- @InternalApi
33
- const val BOTTOM_TAB_BAR_HEIGHT = 64
34
-
35
- @Composable
36
- internal fun BottomTabBar(
37
- items: List<BottomTabItem>,
38
- floatingButton: BottomTabFloatingButton? = null,
39
- navController: NavController,
40
- onTabSelected: (Int) -> Unit,
41
- ) {
42
- val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
43
- val selectedIndex = items.indexOfFirst { "option${items.indexOf(it)}" == currentRoute }
44
-
45
- val screenWidth = getScreenDimensions().width.dp
46
- val mid = items.size / 2
47
-
48
- val tabWidth = if (floatingButton != null) {
49
- (screenWidth - floatingButtonWidth) / items.size
50
- } else {
51
- screenWidth / items.size
52
- }
53
- val adjustSize = if(floatingButton != null && selectedIndex >= mid) floatingButtonWidth else 0.dp
54
- val targetOffset = tabWidth * selectedIndex + tabWidth / 2 - screenWidth / 2 + adjustSize
55
-
56
- val indicatorOffsetX by animateDpAsState(
57
- targetValue = targetOffset,
58
- animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing),
59
- label = "IndicatorX"
60
- )
61
-
62
- Box(
63
- contentAlignment = Alignment.BottomCenter
64
- ) {
65
- Row(
66
- modifier = Modifier
67
- .fillMaxWidth()
68
- .height(BOTTOM_TAB_BAR_HEIGHT.dp)
69
- .background(AppTheme.current.colors.background.surface)
70
- .conditional(IsShowBaseLineDebug) {
71
- border(1.dp, Colors.blue_03)
72
- },
73
- horizontalArrangement = Arrangement.SpaceAround,
74
- ) {
75
- if (floatingButton == null) {
76
- RenderTabBarItem(items, 0, items.size, selectedIndex, onTabSelected)
77
- } else {
78
- RenderTabBarItem(items, 0, mid, selectedIndex, onTabSelected)
79
- Spacer(modifier = Modifier.width(floatingButtonWidth).padding(horizontal = Spacing.XXS))
80
- RenderTabBarItem(items, mid, items.size, selectedIndex, onTabSelected)
81
- }
82
- }
83
-
84
- Box(modifier = Modifier
85
- .offset(x = indicatorOffsetX, y = (-BOTTOM_TAB_BAR_HEIGHT + 2).dp)
86
- .height(2.dp)
87
- .width(44.dp)
88
- .background(
89
- color = AppTheme.current.colors.primary ,
90
- shape = RoundedCornerShape(bottomStart = 2.dp, bottomEnd = 2.dp)
91
- )
92
- )
93
-
94
- Box(modifier = Modifier
95
- .fillMaxWidth()
96
- .height(6.dp)
97
- .offset(x = 0.dp, y = (-BOTTOM_TAB_BAR_HEIGHT).dp)
98
- .background(
99
- brush = Brush.verticalGradient(
100
- colors = listOf(Color.Transparent, Color.Black.copy(alpha = 0.05f))
101
- )
102
- )
103
- )
104
- floatingButton?.let { FloatingButton(it) }
105
- }
106
- }
107
-
108
- @Composable
109
- internal fun RowScope.RenderTabBarItem(
110
- items: List<BottomTabItem>,
111
- fromIndex: Int,
112
- toIndex: Int,
113
- selectedIndex: Int,
114
- onTabSelected: (Int) -> Unit
115
- ){
116
- for (index in fromIndex until toIndex) {
117
- Box(modifier = Modifier.weight(1f)) {
118
- TabBarItem(
119
- item = items[index],
120
- selected = index == selectedIndex,
121
- onClick = { onTabSelected(index) }
122
- )
123
- }
124
- }
125
- }
126
-
127
- @Composable
128
- internal fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> Unit) {
129
- fun isNumber(label: String): Boolean {
130
- val numberRegex = "^\\d+$".toRegex()
131
- return numberRegex.matches(label)
132
- }
133
- fun formatLabel(label: String): String? {
134
- if (isNumber(label) && label.toInt() == 0) {
135
- return null
136
- }
137
- return label
138
- }
139
- val badgeLabel = item.badgeLabel?.let { formatLabel(it) }
140
-
141
- Box(modifier = Modifier
142
- .fillMaxSize()
143
- .padding(horizontal = Spacing.XXS)
144
- .conditional(IsShowBaseLineDebug) {
145
- border(1.dp, Colors.blue_03)
146
- }
147
- .noFeedbackClickable {
148
- onClick()
149
- },
150
- contentAlignment = Alignment.BottomStart
151
- ){
152
- Column(
153
- modifier = Modifier
154
- .fillMaxSize()
155
- .padding(horizontal = Spacing.S, vertical = Spacing.S)
156
- .noFeedbackClickable {
157
- onClick()
158
- },
159
- horizontalAlignment = Alignment.CenterHorizontally,
160
- verticalArrangement = Arrangement.Center
161
- ) {
162
- Icon(
163
- source = item.icon,
164
- size = 28.dp,
165
- modifier = Modifier.padding(Spacing.XS),
166
- color = if (selected) AppTheme.current.colors.primary else AppTheme.current.colors.text.hint)
167
- Text(
168
- text = item.label,
169
- color = if (selected) AppTheme.current.colors.primary else AppTheme.current.colors.text.hint,
170
- style = if (selected) Typography.labelXsMedium else Typography.descriptionXsRegular,
171
- maxLines = 1,
172
- overflow = TextOverflow.Ellipsis
173
- )
174
- }
175
- if (badgeLabel != null) {
176
- if (badgeLabel.isEmpty())
177
- BadgeDot(
178
- size = DotSize.Small,
179
- modifier = Modifier.offset(x = 50.dp, y = (-48).dp)
180
- )
181
- else
182
- Badge(badgeLabel, modifier = Modifier.offset(x = 44.dp, y = (-42).dp))
183
- }
184
- }
185
- }
186
-
187
- @Composable
188
- internal fun FloatingButton(data: BottomTabFloatingButton) {
189
- Column(
190
- modifier = Modifier
191
- .width(floatingButtonWidth)
192
- .padding(horizontal = Spacing.XXS, vertical = Spacing.S)
193
- .conditional(IsShowBaseLineDebug) {
194
- border(1.dp, Colors.blue_03)
195
- }
196
- .noFeedbackClickable {
197
- data.onPress()
198
- },
199
- verticalArrangement = Arrangement.Bottom,
200
- horizontalAlignment = Alignment.CenterHorizontally
201
-
202
- ){
203
- CurvedContainer()
204
- Box(
205
- modifier = Modifier
206
- .size(48.dp)
207
- .clip(CircleShape)
208
- .background(AppTheme.current.colors.primary),
209
- contentAlignment = Alignment.Center
210
- ) {
211
- Icon(source = data.icon, color = Color.White, size = 28.dp)
212
- }
213
- Spacer(modifier = Modifier.height(4.dp))
214
- Text(
215
- text = data.label,
216
- style = Typography.labelXsMedium,
217
- color = Colors.black_01,
218
- textAlign = TextAlign.Center,
219
- modifier = Modifier
220
- .height(14.dp)
221
- .fillMaxWidth()
222
- .background(AppTheme.current.colors.primary, shape = RoundedCornerShape(size = Radius.XL))
223
- )
224
- }
225
- }
@@ -1,86 +0,0 @@
1
- package vn.momo.kits.navigation.bottomtab
2
-
3
- import androidx.compose.foundation.Canvas
4
- import androidx.compose.foundation.layout.Box
5
- import androidx.compose.foundation.layout.height
6
- import androidx.compose.foundation.layout.offset
7
- import androidx.compose.foundation.layout.width
8
- import androidx.compose.runtime.Composable
9
- import androidx.compose.ui.Alignment
10
- import androidx.compose.ui.Modifier
11
- import androidx.compose.ui.graphics.Path
12
- import androidx.compose.ui.platform.LocalDensity
13
- import androidx.compose.ui.unit.Dp
14
- import androidx.compose.ui.unit.IntOffset
15
- import androidx.compose.ui.unit.dp
16
- import androidx.compose.ui.zIndex
17
- import vn.momo.kits.const.AppTheme
18
-
19
- @Composable
20
- internal fun CurvedContainer(
21
- circleSize: Dp = 48.dp,
22
- tabBarItemIconSize: Dp = 28.dp,
23
- circleOverSize: Dp = 2.dp,
24
- width: Dp = 71.dp,
25
- height: Dp = 16.dp
26
- ) {
27
- val theme = AppTheme.current
28
- val density = LocalDensity.current
29
- val widthPx = with(density) { width.toPx()}
30
-
31
- val circleSizePx = with(density) { circleSize.toPx() }
32
- val tabBarItemIconSizePx = with(density) { tabBarItemIconSize.toPx() }
33
- val circleOverSizePx = with(density) { circleOverSize.toPx() }
34
-
35
- val circlePositionTop = circleSizePx - tabBarItemIconSizePx - 10f
36
- val circleOverTop = circlePositionTop + circleOverSizePx
37
- val circleR = circleSizePx / 2
38
- val circleOverR = circleR + circleOverSizePx
39
-
40
- val centerX = widthPx / 2
41
-
42
- val curvedPath = getPath(
43
- circleOverTop = circleOverTop,
44
- centerX = centerX,
45
- circleOverR = circleOverR,
46
- widthPx = widthPx
47
- )
48
-
49
- Box(
50
- contentAlignment = Alignment.BottomCenter
51
- ){
52
- Canvas(
53
- modifier = Modifier
54
- .width(width)
55
- .height(height)
56
- .offset{ IntOffset(x = 0, y = 24) }
57
- .zIndex(-1f)
58
- ) {
59
- drawPath(path = curvedPath, color = theme.colors.background.surface)
60
- }
61
- }
62
- }
63
-
64
- private fun getPath(circleOverTop: Float, centerX: Float, circleOverR: Float, widthPx: Float): Path{
65
- return Path().apply {
66
- moveTo(0f, circleOverTop)
67
- lineTo(centerX - circleOverR - 40f, circleOverTop)
68
-
69
- cubicTo(
70
- centerX - circleOverR, circleOverTop,
71
- centerX - circleOverR, 0f,
72
- centerX, 0f
73
- )
74
-
75
- cubicTo(
76
- centerX + circleOverR, 0f,
77
- centerX + circleOverR, circleOverTop,
78
- centerX + circleOverR + 40f, circleOverTop
79
- )
80
-
81
- lineTo(widthPx, circleOverTop)
82
- lineTo(widthPx, 60f)
83
- lineTo(0f, 60f)
84
- close()
85
- }
86
- }