@momo-kits/native-kits 0.168.1-beta.2-debug → 0.168.1-beta.23

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 (169) hide show
  1. package/CLAUDE.md +10 -6
  2. package/ios/Application/ApplicationEnvironment.swift +7 -6
  3. package/ios/Application/DesignSystemKits.swift +163 -0
  4. package/ios/Application/DesignSystemKitsPayload.swift +68 -0
  5. package/ios/Badge/BadgeRibbon.swift +8 -1
  6. package/ios/Extensions/Color++.swift +2 -4
  7. package/ios/Input/Input.swift +13 -3
  8. package/ios/Input/InputPhoneNumber.swift +1 -3
  9. package/ios/Input/InputSearch.swift +1 -2
  10. package/ios/Input/InputTextArea.swift +5 -3
  11. package/ios/InputMoney/InputMoney.swift +6 -7
  12. package/ios/Layout/ScreenLayout.swift +65 -10
  13. package/ios/Navigation/BottomSheet.swift +153 -23
  14. package/ios/Navigation/BottomTab/DuoBarCompression.swift +2 -3
  15. package/ios/Navigation/BottomTab/DuoTabBar.swift +94 -69
  16. package/ios/Navigation/Component/HeaderBackground.swift +22 -1
  17. package/ios/Navigation/Component/SnackBar.swift +4 -0
  18. package/ios/Navigation/ModalScreen.swift +7 -3
  19. package/ios/Navigation/StackScreen.swift +3 -7
  20. package/ios/Popup/PopupDisplay.swift +2 -2
  21. package/ios/Popup/PopupInput.swift +2 -2
  22. package/ios/Popup/PopupLayout.swift +15 -10
  23. package/ios/Popup/PopupNotify.swift +2 -2
  24. package/ios/Stepper/Stepper.swift +1 -2
  25. package/ios/Template/TrustBanner/TrustBanner.swift +4 -2
  26. package/ios/Typography/FontScale.swift +29 -15
  27. package/ios/Typography/Text.swift +86 -12
  28. package/ios/Typography/Typography.swift +2 -2
  29. package/ios/native-kits.podspec +1 -1
  30. package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
  31. package/package.json +1 -1
  32. package/.claude/settings.local.json +0 -9
  33. package/build.gradle.kts +0 -11
  34. package/compose/build.gradle.kts +0 -198
  35. package/compose/build.gradle.kts.backup +0 -198
  36. package/compose/compose.podspec +0 -47
  37. package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +0 -19
  38. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +0 -6
  39. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +0 -12
  40. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +0 -7
  41. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -153
  42. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +0 -6
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -124
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -1071
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +0 -269
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +0 -56
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +0 -157
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -81
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -32
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -338
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +0 -175
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -357
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -115
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -89
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -128
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +0 -209
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -542
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -49
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -86
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -125
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -231
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -189
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -478
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -114
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -265
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -235
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -223
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -265
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -185
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -343
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +0 -108
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -56
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -41
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -84
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -40
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -361
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -108
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -308
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -59
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +0 -87
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +0 -21
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -90
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +0 -330
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +0 -220
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +0 -473
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +0 -122
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +0 -207
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -92
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -560
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -86
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -125
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -207
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +0 -501
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -176
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +0 -182
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -193
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -236
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -180
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -325
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +0 -16
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +0 -18
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -16
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -194
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -283
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -54
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -50
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +0 -309
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -41
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -52
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -66
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +0 -104
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -48
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -338
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +0 -58
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -137
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -116
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -205
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -399
  124. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +0 -8
  125. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -732
  126. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +0 -148
  127. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +0 -35
  128. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -228
  129. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -217
  130. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
  131. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +0 -98
  132. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +0 -312
  133. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -169
  134. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -455
  135. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -103
  136. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +0 -106
  137. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +0 -74
  138. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -399
  139. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -39
  140. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -363
  141. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +0 -192
  142. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -133
  143. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +0 -127
  144. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +0 -12
  145. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -91
  146. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +0 -9
  147. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -74
  148. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +0 -13
  149. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
  150. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -55
  151. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
  152. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -118
  153. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +0 -40
  154. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +0 -45
  155. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +0 -32
  156. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +0 -38
  157. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +0 -66
  158. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -217
  159. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +0 -94
  160. package/gradle/libs.versions.toml +0 -69
  161. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  162. package/gradle/wrapper/gradle-wrapper.properties +0 -8
  163. package/gradle.properties +0 -26
  164. package/gradlew +0 -252
  165. package/gradlew.bat +0 -94
  166. package/ios/Typography/FontScaleStore.swift +0 -58
  167. package/ios/Typography/KitDesignSystem.swift +0 -17
  168. package/ios-demo/README.md +0 -120
  169. package/settings.gradle.kts +0 -64
@@ -1,205 +0,0 @@
1
- package vn.momo.kits.navigation
2
-
3
- import androidx.compose.animation.core.tween
4
- import androidx.compose.animation.fadeIn
5
- import androidx.compose.animation.slideInVertically
6
- import androidx.compose.animation.slideOutVertically
7
- import androidx.compose.runtime.Composable
8
- import androidx.compose.runtime.CompositionLocalProvider
9
- import androidx.compose.runtime.DisposableEffect
10
- import androidx.compose.runtime.LaunchedEffect
11
- import androidx.compose.runtime.mutableStateOf
12
- import androidx.compose.runtime.remember
13
- import androidx.compose.runtime.saveable.rememberSaveable
14
- import androidx.compose.runtime.staticCompositionLocalOf
15
- import androidx.compose.ui.unit.Dp
16
- import androidx.navigation.compose.NavHost
17
- import androidx.navigation.compose.composable
18
- import androidx.navigation.compose.rememberNavController
19
- import androidx.navigation.toRoute
20
- import kotlinx.coroutines.delay
21
- import kotlinx.coroutines.flow.collectLatest
22
- import vn.momo.kits.application.DesignSystemConfig
23
- import vn.momo.kits.application.KitDesignSystemConfig
24
- import vn.momo.kits.application.LocalContext
25
- import vn.momo.kits.application.LocalDesignSystemConfig
26
- import vn.momo.kits.application.LocalLocalize
27
- import vn.momo.kits.application.Localize
28
- import vn.momo.kits.application.MiniAppContext
29
- import vn.momo.kits.const.AppNavigationBar
30
- import vn.momo.kits.const.AppStatusBar
31
- import vn.momo.kits.const.AppTheme
32
- import vn.momo.kits.const.Theme
33
- import vn.momo.kits.const.defaultTheme
34
- import vn.momo.kits.layout.LocalScreenLayout
35
- import vn.momo.kits.layout.calculateScreenLayout
36
- import vn.momo.kits.platform.ProvideNavigationEventDispatcherOwner
37
- import vn.momo.kits.platform.ProvideOsFontScale
38
- import vn.momo.kits.utils.getAppStatusBarHeight
39
- import vn.momo.kits.utils.getNavigationBarHeight
40
- import vn.momo.maxapi.IMaxApi
41
- import kotlin.time.Duration.Companion.milliseconds
42
-
43
- @Composable
44
- fun NavigationContainer(
45
- initialScreenName: String,
46
- initialScreen: @Composable () -> Unit,
47
- maxApi: IMaxApi,
48
- options: NavigationOptions? = null,
49
- initialTheme: Theme = defaultTheme,
50
- miniAppContext: MiniAppContext? = null,
51
- setNavigator: ((Navigator) -> Unit)? = null,
52
- statusBarHeight: Dp? = null,
53
- localize: Localize? = null,
54
- designSystemConfig: DesignSystemConfig? = null,
55
- ) {
56
- val navController = rememberNavController()
57
- val registry = remember { DynamicScreenRegistry() }
58
- val navigator = remember { Navigator(navController = navController, maxApi = maxApi, registry = registry) }
59
- val statusBarHeight = statusBarHeight ?: getAppStatusBarHeight()
60
- val navigationBarHeight = getNavigationBarHeight()
61
- val screenLayout = calculateScreenLayout(statusBarHeight, parent = LocalScreenLayout.current)
62
-
63
- val parentContext = LocalContext.current
64
- val mergedContext = MiniAppContext.merge(parentContext, miniAppContext)
65
-
66
- val stackTransition = remember { StackTransitionCoordinator() }
67
- val theme = remember { mutableStateOf(initialTheme) }
68
- val resolvedDesignSystemConfig =
69
- designSystemConfig ?: LocalDesignSystemConfig.current ?: KitDesignSystemConfig.config
70
- val headerBar = resolvedDesignSystemConfig?.headerBar
71
- // RN parity: the theme's own header image wins; config headerBar is only the fallback.
72
- val resolvedTheme = remember(theme.value, headerBar) {
73
- if (theme.value.isHeaderImage() || headerBar.isNullOrEmpty()) {
74
- theme.value
75
- } else {
76
- theme.value.copy(assets = theme.value.assets.copy(headerBackground = headerBar))
77
- }
78
- }
79
- val fallbackLocalize = remember { Localize() }
80
-
81
- val resolvedLocalize = localize ?: fallbackLocalize
82
-
83
- LaunchedEffect(maxApi, resolvedLocalize) {
84
- val api = maxApi ?: return@LaunchedEffect
85
- runCatching {
86
- api.getLanguage(callback = { data ->
87
- if (parseLanguage(data) == Localize.EN) {
88
- resolvedLocalize.changeLanguage(Localize.EN)
89
- }
90
- })
91
- }
92
- }
93
-
94
- val screenId = rememberSaveable { registry.nextId() }
95
- registry.bind(screenId, initialScreenName, initialScreen, options)
96
- val startDestination = remember(screenId) { DynamicScreenRoute(screenId) }
97
-
98
- ProvideOsFontScale {
99
- ProvideNavigationEventDispatcherOwner(navigator) {
100
- CompositionLocalProvider(
101
- LocalNavigator provides navigator,
102
- LocalDynamicScreenRegistry provides registry,
103
- LocalMaxApi provides maxApi,
104
- AppTheme provides resolvedTheme,
105
- AppStatusBar provides statusBarHeight,
106
- AppNavigationBar provides navigationBarHeight,
107
- LocalScreenLayout provides screenLayout,
108
- LocalContext provides mergedContext,
109
- LocalDesignSystemConfig provides resolvedDesignSystemConfig,
110
- LocalLocalize provides resolvedLocalize,
111
- LocalStackTransition provides stackTransition,
112
- ) {
113
- LaunchedEffect(Unit) {
114
- setNavigator?.invoke(navigator)
115
- }
116
-
117
- SwipeBackHaptic(maxApi)
118
-
119
- LaunchedEffect(navController, registry) {
120
- navController.currentBackStack.collectLatest { entries ->
121
- delay(STACK_TRANSITION_DURATION.milliseconds)
122
- val aliveIds = entries.mapNotNullTo(mutableSetOf()) { entry ->
123
- runCatching { entry.toRoute<DynamicScreenRoute>().id }.getOrNull()
124
- ?: runCatching { entry.toRoute<DynamicDialogRoute>().id }.getOrNull()
125
- }
126
- aliveIds.add(screenId)
127
- registry.retainOnly(aliveIds)
128
- }
129
- }
130
-
131
- NavHost(
132
- navController,
133
- startDestination = startDestination,
134
- enterTransition = { stackEnter() },
135
- exitTransition = { stackExit() },
136
- popEnterTransition = { stackPopEnter() },
137
- popExitTransition = { stackPopExit() },
138
- ) {
139
- composable<DynamicScreenRoute> { backStackEntry ->
140
- val route = backStackEntry.toRoute<DynamicScreenRoute>()
141
- val screen = registry.getScreen(route.id)
142
-
143
- if (screen != null) {
144
- StackTransitionDecor(id = route.id, isStackRoute = true) {
145
- StackScreen(
146
- id = route.id,
147
- name = screen.name,
148
- content = screen.content,
149
- navigationOptions = screen.options
150
- )
151
- }
152
- }
153
- }
154
-
155
- composable<DynamicDialogRoute>(
156
- enterTransition = {
157
- slideInVertically(
158
- animationSpec = tween(300),
159
- initialOffsetY = { it }
160
- )
161
- },
162
- exitTransition = null,
163
- popEnterTransition = { fadeIn(animationSpec = tween(0)) },
164
- popExitTransition = {
165
- slideOutVertically(
166
- animationSpec = tween(300),
167
- targetOffsetY = { it }
168
- )
169
- }
170
- ) { backStackEntry ->
171
- val route = backStackEntry.toRoute<DynamicDialogRoute>()
172
- val screen = registry.getScreen(route.id)
173
-
174
- if (screen != null) {
175
- StackTransitionDecor(id = route.id, isStackRoute = false) {
176
- StackScreen(
177
- id = route.id,
178
- name = screen.name,
179
- content = screen.content,
180
- navigationOptions = screen.options
181
- )
182
- }
183
- }
184
- }
185
- }
186
- }
187
- }
188
- }
189
-
190
- DisposableEffect(Unit) {
191
- onDispose {
192
- navigator.dispose()
193
- registry.unregisterScreen(screenId)
194
- }
195
- }
196
- }
197
-
198
- val LocalMaxApi = staticCompositionLocalOf<IMaxApi?> { null }
199
-
200
- private fun parseLanguage(raw: Map<String, Any?>?): String? {
201
- val code = (raw?.get("response") as? String)?.trim()?.lowercase()
202
- if (code.isNullOrEmpty()) return null
203
- return if (code.startsWith(Localize.EN)) Localize.EN else Localize.VI
204
- }
205
-
@@ -1,399 +0,0 @@
1
- package vn.momo.kits.navigation
2
-
3
- import androidx.compose.foundation.layout.Box
4
- import androidx.compose.foundation.layout.fillMaxSize
5
- import androidx.compose.runtime.Composable
6
- import androidx.compose.runtime.MutableState
7
- import androidx.compose.runtime.mutableStateOf
8
- import androidx.compose.runtime.staticCompositionLocalOf
9
- import androidx.compose.ui.Alignment
10
- import androidx.compose.ui.Modifier
11
- import androidx.navigation.NavController
12
- import androidx.navigation.toRoute
13
- import kotlinx.coroutines.CoroutineScope
14
- import kotlinx.coroutines.Dispatchers
15
- import kotlinx.coroutines.SupervisorJob
16
- import kotlinx.coroutines.cancel
17
- import kotlinx.coroutines.delay
18
- import kotlinx.coroutines.launch
19
- import kotlinx.serialization.Serializable
20
- import vn.momo.kits.components.PopupAction
21
- import vn.momo.kits.components.PopupNotify
22
- import vn.momo.kits.navigation.BottomHeader.*
23
- import vn.momo.kits.navigation.component.SnackBar
24
- import vn.momo.maxapi.IMaxApi
25
- import kotlin.time.Duration.Companion.milliseconds
26
-
27
- class Navigator(
28
- private val navController: NavController,
29
- private val maxApi: IMaxApi?,
30
- private val registry: DynamicScreenRegistry
31
- ) {
32
- private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
33
-
34
- // currentScreen() reads the root back stack, so it never sees the nested tab NavHost.
35
- internal var tabBackHandler: (() -> Unit)? = null
36
-
37
- private fun currentScreen(): DynamicScreen? {
38
- val entry = navController.currentBackStackEntry ?: return null
39
- val id = runCatching { entry.toRoute<DynamicScreenRoute>().id }.getOrNull()
40
- ?: runCatching { entry.toRoute<DynamicDialogRoute>().id }.getOrNull()
41
- ?: return null
42
- return registry.getScreen(id)
43
- }
44
-
45
- fun push(screenName: String, content: @Composable () -> Unit, options: NavigationOptions? = null) {
46
- val route = registry.register(screenName, content, options)
47
- navController.navigate(DynamicScreenRoute(route.id))
48
- }
49
-
50
- fun replace(screenName: String, content: @Composable () -> Unit, options: NavigationOptions? = null) {
51
- val replacedScreen = currentScreen()
52
- replacedScreen?.let { registry.unregisterScreen(it.id) }
53
- navController.popBackStack()
54
- push(screenName, content, options)
55
- }
56
-
57
- fun onBackSafe(callBack: (() -> Unit)? = null) {
58
- // An open modal or bottom sheet takes the back first, as its BackHandler does for gestures.
59
- val overplayType = OverplayComponentRegistry.getOverplayType()
60
- if (overplayType != null && overplayType != OverplayComponentType.SNACK_BAR) {
61
- pop(1, callBack)
62
- return
63
- }
64
- tabBackHandler?.let {
65
- it()
66
- return
67
- }
68
- val currentScreen = currentScreen()
69
- val options = currentScreen?.options
70
- if (options?.onBackHandler != null) {
71
- options.onBackHandler.invoke()
72
- return
73
- }
74
- val preventBack = options?.headerBackProps?.preventBack
75
- if (preventBack != null) {
76
- showModal(
77
- content = {
78
- PopupNotify(preventBack.copy(
79
- primary = PopupAction(
80
- title = preventBack.primary?.title ?: "",
81
- onPress = {
82
- preventBack.primary?.onPress?.invoke()
83
- pop(2, callBack) // close popup notify, then close the screen
84
- }
85
- )
86
- ))
87
- }
88
- )
89
- } else {
90
- pop(1, callBack)
91
- }
92
- }
93
-
94
- // The swipe-back opt-out predicate in StackScreen needs this: the root screen must keep custom
95
- // back handling so it still reaches maxApi.dismiss.
96
- // Public: the iOS host asks this to decide whether the kit owns the swipe on this screen.
97
- fun canPop(): Boolean = navController.previousBackStackEntry != null
98
-
99
- fun pop(count: Int = 1, callBack: (() -> Unit)? = null) {
100
- scope.launch {
101
- repeat(count) {
102
- if (OverplayComponentRegistry.getOverplayType() == OverplayComponentType.SNACK_BAR){
103
- dismissScreen()
104
- OverplayComponentRegistry.hardClearAfterDismiss()
105
- }
106
- else if (OverplayComponentRegistry.isOverplayShowing()){
107
- dismissOverplay()
108
- } else {
109
- dismissScreen()
110
- }
111
- }
112
- callBack?.invoke()
113
- }
114
- }
115
- private suspend fun dismissOverplay(isDelay: Boolean = true) {
116
- OverplayComponentRegistry.clear()
117
- if (isDelay) delay(300L.milliseconds)
118
- OverplayComponentRegistry.hardClearAfterDismiss()
119
- }
120
-
121
- private suspend fun dismissScreen() {
122
- if (navController.previousBackStackEntry != null){
123
- val dismissing = currentScreen()
124
- navController.popBackStack()
125
- delay(STACK_TRANSITION_DURATION.milliseconds)
126
- dismissing?.let { registry.unregisterScreen(it.id) }
127
- } else {
128
- maxApi?.dismiss { }
129
- }
130
- }
131
-
132
- fun present(screenName: String, content: @Composable () -> Unit, options: NavigationOptions? = null) {
133
- val route = registry.register(screenName, content, options)
134
- navController.navigate(DynamicDialogRoute(route.id))
135
- }
136
-
137
- fun reset(screenName: String, content: @Composable () -> Unit, options: NavigationOptions? = null) {
138
- registry.unregisterAll()
139
-
140
- val route = registry.register(screenName, content, options)
141
- navController.navigate(DynamicScreenRoute(route.id)) {
142
- popUpTo(0) { inclusive = true }
143
- }
144
- }
145
-
146
- fun showModal(
147
- content: @Composable () -> Unit,
148
- barrierDismissible: Boolean = true,
149
- onDismiss: (() -> Unit)? = null
150
- ){
151
- val id = currentScreen()?.id ?: -1
152
- OverplayComponentRegistry.registerOverplay(id, content, OverplayComponentType.MODAL, false, barrierDismissible, onDismiss)
153
- }
154
-
155
- fun showBottomSheet(
156
- content: @Composable () -> Unit,
157
- isSurface: Boolean = false,
158
- barrierDismissible: Boolean = true,
159
- onDismiss: (() -> Unit)? = null,
160
- bottomSheetHeader: BottomHeader? = null,
161
- draggable: Boolean = true,
162
- useBottomInset: Boolean = true,
163
- footerComponent: (@Composable () -> Unit)? = null
164
- ){
165
- val id = currentScreen()?.id ?: -1
166
- OverplayComponentRegistry.registerOverplay(id, content, OverplayComponentType.BOTTOM_SHEET, isSurface, barrierDismissible, onDismiss, bottomSheetHeader, draggable, useBottomInset, footerComponent)
167
- }
168
-
169
- fun showSnackBar(snackBar: SnackBar, onDismiss: (() -> Unit)? = null) {
170
- val id = currentScreen()?.id ?: -1
171
- scope.launch {
172
- OverplayComponentRegistry.registerOverplay(
173
- id = id,
174
- content = {
175
- SnackBar(snackBar, onDismiss)
176
- },
177
- type = OverplayComponentType.SNACK_BAR,
178
- onDismiss = onDismiss
179
- )
180
- }
181
- }
182
-
183
- fun hideSnackBar() {
184
- scope.launch {
185
- OverplayComponentRegistry.clear()
186
- delay(250L.milliseconds)
187
- OverplayComponentRegistry.hardClearAfterDismiss()
188
- }
189
- }
190
-
191
- fun dispose(){
192
- scope.cancel()
193
- }
194
- }
195
-
196
- val LocalNavigator = staticCompositionLocalOf<Navigator> {
197
- error("No NavigationStack provided")
198
- }
199
-
200
- @Serializable
201
- data class DynamicScreenRoute(val id: Int)
202
-
203
- @Serializable
204
- data class DynamicDialogRoute(val id: Int)
205
-
206
- data class DynamicScreen(
207
- val id: Int,
208
- val name: String,
209
- val content: @Composable () -> Unit,
210
- var options: NavigationOptions? = null
211
- )
212
-
213
- class DynamicScreenRegistry {
214
- private val screens = mutableMapOf<Int, DynamicScreen>()
215
-
216
- fun register(screenName: String, content: @Composable () -> Unit, options: NavigationOptions?): DynamicScreenRoute {
217
- val id = nextGlobalId()
218
- screens[id] = DynamicScreen(
219
- id = id,
220
- name = screenName,
221
- content = content,
222
- options = options
223
- )
224
- return DynamicScreenRoute(id)
225
- }
226
-
227
- fun nextId(): Int = nextGlobalId()
228
-
229
- fun bind(id: Int, screenName: String, content: @Composable () -> Unit, options: NavigationOptions?) {
230
- screens[id] = DynamicScreen(
231
- id = id,
232
- name = screenName,
233
- content = content,
234
- options = options
235
- )
236
- advanceGlobalIdPast(id) // keep counter ahead after process-death restore
237
- }
238
-
239
- fun unregisterScreen(id: Int) {
240
- screens.remove(id)
241
- }
242
-
243
- // Gesture pops go through composeNavigator.popBackStack() inside NavHost and never reach
244
- // Navigator.dismissScreen(), so screens would leak without a stack-driven prune.
245
- fun retainOnly(ids: Set<Int>) {
246
- val stale = screens.keys.filter { it !in ids }
247
- for (id in stale) {
248
- screens.remove(id)
249
- }
250
- }
251
-
252
- fun unregisterAll(){
253
- val ids = screens.keys.toList()
254
- for (id in ids) {
255
- unregisterScreen(id)
256
- }
257
- }
258
-
259
- fun getScreen(id: Int): DynamicScreen? = screens[id]
260
-
261
- fun setOptions(id: Int, options: NavigationOptions){
262
- screens[id]?.options = options
263
- }
264
-
265
- companion object {
266
- private var globalIdCounter = 1
267
-
268
- private fun nextGlobalId(): Int = globalIdCounter++
269
-
270
- private fun advanceGlobalIdPast(id: Int) {
271
- if (id >= globalIdCounter) globalIdCounter = id + 1
272
- }
273
- }
274
- }
275
-
276
- val LocalDynamicScreenRegistry = staticCompositionLocalOf<DynamicScreenRegistry?> { null }
277
-
278
-
279
- sealed class OverplayComponentParams {
280
- class Modal(
281
- val onDismiss: (() -> Unit)? = null,
282
- val barrierDismissible: Boolean = true
283
- ) : OverplayComponentParams()
284
-
285
- class BottomSheet(
286
- val isSurface: Boolean = false,
287
- val onDismiss: (() -> Unit)? = null,
288
- val barrierDismissible: Boolean = true,
289
- val bottomSheetHeader: BottomHeader? = null,
290
- val draggable: Boolean = true,
291
- val useBottomInset: Boolean = true,
292
- val footerComponent: (@Composable () -> Unit)? = null,
293
- ) : OverplayComponentParams()
294
-
295
- class SnackBar : OverplayComponentParams()
296
- }
297
-
298
- data class OverplayComponent(
299
- val id: Int,
300
- val type: OverplayComponentType? = null,
301
- val content: @Composable () -> Unit,
302
- val params: OverplayComponentParams? = null
303
- )
304
-
305
- enum class OverplayComponentType {
306
- MODAL, BOTTOM_SHEET, SNACK_BAR
307
- }
308
-
309
- object OverplayComponentRegistry {
310
- private var currentOverlayComponent : MutableState<OverplayComponent?> = mutableStateOf(null)
311
- private var requestClose: (() -> Unit)? = null
312
- internal fun bindClose(handler: (() -> Unit)?) {
313
- requestClose = handler
314
- }
315
-
316
- fun registerOverplay(
317
- id: Int,
318
- content: @Composable () -> Unit,
319
- type: OverplayComponentType,
320
- isSurface: Boolean = false,
321
- barrierDismissible: Boolean = true,
322
- onDismiss: (() -> Unit)?,
323
- bottomSheetHeader: BottomHeader? = null,
324
- draggable: Boolean = true,
325
- useBottomInset: Boolean = true,
326
- footerComponent: (@Composable () -> Unit)? = null,
327
- ){
328
- val params = when(type){
329
- OverplayComponentType.MODAL -> OverplayComponentParams.Modal(onDismiss, barrierDismissible)
330
- OverplayComponentType.BOTTOM_SHEET -> OverplayComponentParams.BottomSheet(isSurface, onDismiss, barrierDismissible, bottomSheetHeader, draggable, useBottomInset, footerComponent)
331
- OverplayComponentType.SNACK_BAR -> OverplayComponentParams.SnackBar()
332
- }
333
-
334
- currentOverlayComponent.value = OverplayComponent(
335
- id = id,
336
- type = type,
337
- content = content,
338
- params = params
339
- )
340
- }
341
-
342
- fun isOverplayShowing(): Boolean = currentOverlayComponent.value != null
343
-
344
- fun getOverplayType(): OverplayComponentType? = currentOverlayComponent.value?.type
345
-
346
- fun currentRootId(): Int? = currentOverlayComponent.value?.id
347
-
348
- fun clear(){
349
- if (requestClose != null) {
350
- requestClose?.invoke()
351
- } else {
352
- currentOverlayComponent.value = null
353
- }
354
- }
355
-
356
- internal fun hardClearAfterDismiss() {
357
- currentOverlayComponent.value = null
358
- requestClose = null
359
- }
360
-
361
- @Composable
362
- internal fun OverlayComponent(){
363
- val overplay = currentOverlayComponent.value ?: return
364
-
365
- when (val params = overplay.params) {
366
- is OverplayComponentParams.BottomSheet -> {
367
- BottomSheet(
368
- content = overplay.content,
369
- header = params.bottomSheetHeader ?: Title(),
370
- isSurface = params.isSurface,
371
- barrierDismissible = params.barrierDismissible,
372
- draggable = params.draggable,
373
- useBottomInset = params.useBottomInset,
374
- footerComponent = params.footerComponent,
375
- onDismiss = params.onDismiss
376
- )
377
- }
378
-
379
- is OverplayComponentParams.Modal -> {
380
- ModalScreen(
381
- content = overplay.content,
382
- barrierDismissible = params.barrierDismissible,
383
- onDismiss = params.onDismiss
384
- )
385
- }
386
-
387
- is OverplayComponentParams.SnackBar -> {
388
- Box(
389
- modifier = Modifier.fillMaxSize(),
390
- contentAlignment = Alignment.BottomCenter
391
- ) {
392
- overplay.content()
393
- }
394
- }
395
-
396
- null -> {}
397
- }
398
- }
399
- }
@@ -1,8 +0,0 @@
1
- package vn.momo.kits.navigation
2
-
3
- import androidx.compose.runtime.Composable
4
-
5
- @Composable
6
- internal expect fun RegisterScrollToTop(callback: (() -> Unit)?)
7
-
8
-