@momo-kits/native-kits 0.164.1-beta.7-debug → 0.164.1-beta.7

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 (134) 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/OsFontScale.android.kt +0 -7
  9. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -150
  10. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -52
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -106
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -183
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -207
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -39
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -293
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -826
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +0 -269
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -110
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -378
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +0 -56
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +0 -157
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -82
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -32
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -338
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +0 -175
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -356
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -113
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -89
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -128
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +0 -209
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -542
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -23
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -76
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -125
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -231
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -134
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -475
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -114
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -214
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -240
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -216
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -259
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -182
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -343
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +0 -108
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -56
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -41
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -84
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -40
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -328
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -97
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -310
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -59
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +0 -87
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +0 -21
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -90
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +0 -323
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +0 -217
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +0 -473
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +0 -122
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +0 -199
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -91
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -470
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -88
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -131
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -203
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +0 -498
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -175
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +0 -182
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -193
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -234
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -188
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -325
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +0 -16
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -16
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -188
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -263
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -52
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -38
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -39
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -52
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -66
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +0 -104
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -48
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -299
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -129
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -103
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -190
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -406
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +0 -8
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -612
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +0 -164
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -156
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -225
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -169
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -320
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -81
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -309
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -34
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -360
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -132
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +0 -127
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -91
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +0 -9
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -69
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -55
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -105
  121. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +0 -40
  122. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +0 -118
  123. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +0 -32
  124. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +0 -66
  125. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -220
  126. package/gradle/libs.versions.toml +0 -69
  127. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  128. package/gradle/wrapper/gradle-wrapper.properties +0 -8
  129. package/gradle.properties +0 -26
  130. package/gradlew +0 -252
  131. package/gradlew.bat +0 -94
  132. package/ios-demo/README.md +0 -120
  133. package/local.properties +0 -8
  134. package/settings.gradle.kts +0 -64
@@ -1,164 +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.mutableStateOf
22
- import androidx.compose.runtime.remember
23
- import androidx.compose.runtime.setValue
24
- import androidx.compose.runtime.staticCompositionLocalOf
25
- import androidx.compose.ui.Modifier
26
- import androidx.compose.ui.draw.drawBehind
27
- import androidx.compose.ui.geometry.Offset
28
- import androidx.compose.ui.geometry.Size
29
- import androidx.compose.ui.graphics.Brush
30
- import androidx.compose.ui.graphics.Color
31
- import androidx.compose.ui.platform.LocalDensity
32
- import androidx.compose.ui.unit.IntOffset
33
- import androidx.compose.ui.unit.dp
34
- import androidx.navigation.NavBackStackEntry
35
- import androidx.navigation.NavDestination.Companion.hasRoute
36
- import vn.momo.kits.const.Colors
37
- import kotlin.math.roundToInt
38
-
39
- internal const val STACK_TRANSITION_DURATION = 300
40
-
41
- // Ported 1:1 from the RN kit's `forStackTransitionWithOverlay` so both kits look identical.
42
- private const val PARALLAX_RATIO = 0.3f
43
- private const val SCRIM_MAX_ALPHA = 0.6f
44
- private const val EDGE_SHADOW_MAX_ALPHA = 0.3f
45
- private val EDGE_SHADOW_WIDTH = 8.dp
46
-
47
- /**
48
- * Kill switch for the swipe-back gesture. Set to `false` to make kit back handling always win,
49
- * which disables the predictive-back drag on both platforms — on iOS that means the host-driven
50
- * seek stops following the finger, though the host's own gesture still fires a plain back. Screen
51
- * transitions stay on the parallax curve either way.
52
- */
53
- object SwipeBackConfig {
54
- // Snapshot-backed so a host toggle takes effect on the next frame instead of waiting for an
55
- // unrelated recomposition of StackScreen.
56
- var enabled: Boolean by mutableStateOf(true)
57
- }
58
-
59
- // LinearEasing is mandatory, not a style choice: SeekableTransitionState.seekTo() maps the drag
60
- // fraction straight onto the animation timeline, so any curve would decouple the card from the
61
- // finger mid-gesture.
62
- private val stackSpec = tween<IntOffset>(STACK_TRANSITION_DURATION, easing = LinearEasing)
63
-
64
- internal fun AnimatedContentTransitionScope<NavBackStackEntry>.stackEnter(): EnterTransition =
65
- slideInHorizontally(stackSpec) { it }
66
-
67
- internal fun AnimatedContentTransitionScope<NavBackStackEntry>.stackExit(): ExitTransition =
68
- if (targetState.destination.hasRoute<DynamicDialogRoute>()) ExitTransition.None
69
- else slideOutHorizontally(stackSpec) { -(it * PARALLAX_RATIO).roundToInt() }
70
-
71
- internal fun AnimatedContentTransitionScope<NavBackStackEntry>.stackPopEnter(): EnterTransition =
72
- if (initialState.destination.hasRoute<DynamicDialogRoute>()) EnterTransition.None
73
- else slideInHorizontally(stackSpec) { -(it * PARALLAX_RATIO).roundToInt() }
74
-
75
- internal fun AnimatedContentTransitionScope<NavBackStackEntry>.stackPopExit(): ExitTransition =
76
- slideOutHorizontally(stackSpec) { it }
77
-
78
- /**
79
- * Tracks which screen ids are currently composed so a screen can tell whether it is the top card
80
- * or the one underneath.
81
- *
82
- * Screen ids come from a single monotonic process-wide counter and NavHost keeps both entries
83
- * composed for the whole transition segment, so "highest composed stack id" is stable in both
84
- * directions. `currentBackStackEntry` is not usable here: it flips at frame 0 of a pop, which
85
- * would make the scrim vanish instantly instead of fading.
86
- */
87
- @Stable
88
- internal class StackTransitionCoordinator {
89
- private val composedRoutes = mutableStateMapOf<Int, Boolean>()
90
-
91
- val topStackId: Int by derivedStateOf {
92
- composedRoutes.entries.filter { it.value }.maxOfOrNull { it.key } ?: -1
93
- }
94
-
95
- fun attach(id: Int, isStackRoute: Boolean) {
96
- composedRoutes[id] = isStackRoute
97
- }
98
-
99
- fun detach(id: Int) {
100
- composedRoutes.remove(id)
101
- }
102
- }
103
-
104
- internal val LocalStackTransition = staticCompositionLocalOf { StackTransitionCoordinator() }
105
-
106
- /** Draws the covered-screen scrim and the top card's left-edge shadow around a stack destination. */
107
- @Composable
108
- internal fun AnimatedVisibilityScope.StackTransitionDecor(
109
- id: Int,
110
- isStackRoute: Boolean,
111
- content: @Composable () -> Unit,
112
- ) {
113
- val coordinator = LocalStackTransition.current
114
- // attach() writes state that derivedStateOf { topStackId } reads in the same pass, so it has to
115
- // stay out of the composable body.
116
- DisposableEffect(coordinator, id, isStackRoute) {
117
- coordinator.attach(id, isStackRoute)
118
- onDispose { coordinator.detach(id) }
119
- }
120
-
121
- val isTopCard = isStackRoute && id == coordinator.topStackId
122
- val isCovered = isStackRoute && !isTopCard
123
-
124
- val scrimAlpha = transition.animateFloat(
125
- transitionSpec = { tween(STACK_TRANSITION_DURATION, easing = LinearEasing) },
126
- label = "stackScrimAlpha",
127
- ) { state -> if (isCovered && state != EnterExitState.Visible) SCRIM_MAX_ALPHA else 0f }
128
-
129
- val shadowAlpha = transition.animateFloat(
130
- transitionSpec = { tween(STACK_TRANSITION_DURATION, easing = LinearEasing) },
131
- label = "stackEdgeShadowAlpha",
132
- ) { state -> if (isTopCard && state == EnterExitState.Visible) EDGE_SHADOW_MAX_ALPHA else 0f }
133
-
134
- val shadowWidthPx = with(LocalDensity.current) { EDGE_SHADOW_WIDTH.toPx() }
135
- val shadowBrush = remember(shadowWidthPx) {
136
- Brush.horizontalGradient(
137
- colors = listOf(Color.Transparent, Colors.black_20),
138
- startX = -shadowWidthPx,
139
- endX = 0f,
140
- )
141
- }
142
-
143
- Box(Modifier.fillMaxSize()) {
144
- content()
145
-
146
- if (isStackRoute) {
147
- Box(
148
- Modifier
149
- .matchParentSize()
150
- // Both alphas are read inside drawBehind so a drag only re-draws; reading them
151
- // in the composable body would recompose the whole screen every gesture frame.
152
- .drawBehind {
153
- drawRect(
154
- brush = shadowBrush,
155
- topLeft = Offset(-shadowWidthPx, 0f),
156
- size = Size(shadowWidthPx, size.height),
157
- alpha = shadowAlpha.value,
158
- )
159
- drawRect(Colors.pink_09, alpha = scrimAlpha.value)
160
- }
161
- )
162
- }
163
- }
164
- }
@@ -1,156 +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.LaunchedEffect
12
- import androidx.compose.ui.Alignment
13
- import androidx.compose.ui.Modifier
14
- import androidx.compose.ui.unit.dp
15
- import androidx.navigation.compose.NavHost
16
- import androidx.navigation.compose.composable
17
- import androidx.navigation.compose.rememberNavController
18
- import vn.momo.kits.const.AppNavigationBar
19
- import vn.momo.kits.const.AppTheme
20
- import vn.momo.kits.const.Spacing
21
- import vn.momo.kits.navigation.KeyboardOptions
22
- import vn.momo.kits.navigation.LocalNavigation
23
- import vn.momo.kits.navigation.LocalNavigator
24
- import vn.momo.kits.navigation.NavigationOptions
25
- import vn.momo.kits.navigation.StackScreen
26
- import vn.momo.kits.navigation.component.HeaderType
27
-
28
- private var bottomTabOptionItems : MutableList<NavigationOptions?> = mutableListOf()
29
- internal fun setBottomTabOption(index: Int, options: NavigationOptions){
30
- if (index in bottomTabOptionItems.indices) {
31
- bottomTabOptionItems[index] = options
32
- }
33
- }
34
- internal fun getBottomTabOption(index: Int): NavigationOptions? {
35
- return if (index in bottomTabOptionItems.indices) {
36
- bottomTabOptionItems[index]
37
- } else null
38
- }
39
-
40
- @Composable
41
- fun BottomTab(
42
- items: List<BottomTabItem>,
43
- floatingButton: BottomTabFloatingButton? = null
44
- ) {
45
- val navigation = LocalNavigation.current
46
- val navigator = LocalNavigator.current
47
- val navController = rememberNavController()
48
-
49
- bottomTabOptionItems = items.mapIndexed { index, item ->
50
- item.options ?: NavigationOptions()
51
- }.toMutableList()
52
-
53
-
54
- LaunchedEffect(Unit){
55
- navigation.markAsBottomTabRoot()
56
- navigation.setOptions(
57
- headerType = HeaderType.None,
58
- keyboardOptions = KeyboardOptions(useAvoidKeyboard = false)
59
- )
60
- }
61
-
62
- Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
63
- Box(modifier = Modifier
64
- .fillMaxSize()
65
- ) {
66
- NavHost(
67
- navController = navController,
68
- startDestination = "option0"
69
- ) {
70
- items.forEachIndexed { index, item ->
71
- composable(
72
- route = "option$index",
73
- enterTransition = {
74
- fadeIn(animationSpec = tween(200)) +
75
- scaleIn(
76
- initialScale = 0.97f,
77
- animationSpec = tween(200)
78
- )
79
- },
80
- exitTransition = {
81
- fadeOut(animationSpec = tween(200)) +
82
- scaleOut(
83
- targetScale = 0.97f,
84
- animationSpec = tween(200)
85
- )
86
- },
87
- popEnterTransition = {
88
- fadeIn(animationSpec = tween(200)) +
89
- scaleIn(
90
- initialScale = 0.97f,
91
- animationSpec = tween(200)
92
- )
93
- },
94
- popExitTransition = {
95
- fadeOut(animationSpec = tween(200)) +
96
- scaleOut(
97
- targetScale = 0.97f,
98
- animationSpec = tween(200)
99
- )
100
- }
101
- ) {
102
- val option = getBottomTabOption(index)
103
-
104
- StackScreen(
105
- content = item.screen,
106
- navigationOptions = option,
107
- bottomTabIndex = index,
108
- onBackHandler = {
109
- if (index != 0) {
110
- navController.popBackStack(
111
- navController.graph.startDestinationRoute!!,
112
- inclusive = false
113
- )
114
- } else {
115
- navigator.pop()
116
- }
117
- }
118
- )
119
- }
120
- }
121
- }
122
- }
123
- Column {
124
- BottomTabBar(
125
- items = items,
126
- floatingButton = floatingButton,
127
- navController = navController,
128
- onTabSelected = {
129
- val currentRoute = navController.currentBackStackEntry?.destination?.route
130
- val targetRoute = "option$it"
131
- if (currentRoute != targetRoute) {
132
- navController.navigate(targetRoute)
133
- }
134
- }
135
- )
136
- Spacer(modifier = Modifier.fillMaxWidth().height(AppNavigationBar.current + Spacing.S).background(AppTheme.current.colors.background.surface))
137
- }
138
- }
139
- }
140
-
141
- data class BottomTabItem(
142
- val name: String,
143
- val label: String,
144
- val icon: String,
145
- val showDot: Boolean = false,
146
- val badgeLabel: String? = null,
147
- val screen: @Composable () -> Unit,
148
- val options: NavigationOptions? = null,
149
- val initialParams: Any? = null
150
- )
151
-
152
- data class BottomTabFloatingButton(
153
- val icon: String,
154
- val label: String,
155
- val onPress: () -> Unit,
156
- )
@@ -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
- }