@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,612 +0,0 @@
1
- package vn.momo.kits.navigation
2
-
3
- import androidx.compose.animation.animateColorAsState
4
- import androidx.compose.animation.core.animateDpAsState
5
- import androidx.compose.foundation.ScrollState
6
- import androidx.compose.foundation.background
7
- import androidx.compose.foundation.border
8
- import androidx.compose.foundation.gestures.ScrollableState
9
- import androidx.compose.foundation.layout.Box
10
- import androidx.compose.foundation.layout.Column
11
- import androidx.compose.foundation.layout.ColumnScope
12
- import androidx.compose.foundation.layout.Spacer
13
- import androidx.compose.foundation.layout.WindowInsets
14
- import androidx.compose.foundation.layout.asPaddingValues
15
- import androidx.compose.foundation.layout.aspectRatio
16
- import androidx.compose.foundation.layout.fillMaxSize
17
- import androidx.compose.foundation.layout.fillMaxWidth
18
- import androidx.compose.foundation.layout.height
19
- import androidx.compose.foundation.layout.ime
20
- import androidx.compose.foundation.layout.imePadding
21
- import androidx.compose.foundation.layout.offset
22
- import androidx.compose.foundation.layout.padding
23
- import androidx.compose.foundation.lazy.LazyListState
24
- import androidx.compose.foundation.lazy.rememberLazyListState
25
- import androidx.compose.foundation.rememberScrollState
26
- import androidx.compose.foundation.verticalScroll
27
- import androidx.compose.material.ExperimentalMaterialApi
28
- import androidx.compose.runtime.Composable
29
- import androidx.compose.runtime.CompositionLocalProvider
30
- import androidx.compose.runtime.LaunchedEffect
31
- import androidx.compose.runtime.State
32
- import androidx.compose.runtime.getValue
33
- import androidx.compose.runtime.mutableIntStateOf
34
- import androidx.compose.runtime.mutableStateOf
35
- import androidx.compose.runtime.remember
36
- import androidx.compose.runtime.rememberUpdatedState
37
- import androidx.compose.runtime.snapshotFlow
38
- import androidx.compose.runtime.staticCompositionLocalOf
39
- import androidx.compose.ui.Alignment
40
- import androidx.compose.ui.Modifier
41
- import androidx.compose.ui.graphics.Brush
42
- import androidx.compose.ui.graphics.Color
43
- import androidx.compose.ui.graphics.lerp
44
- import androidx.compose.ui.layout.onGloballyPositioned
45
- import androidx.compose.ui.platform.LocalDensity
46
- import androidx.compose.ui.unit.Dp
47
- import androidx.compose.ui.unit.dp
48
- import androidx.compose.ui.zIndex
49
- import kotlinx.coroutines.flow.Flow
50
- import vn.momo.kits.components.InputSearch
51
- import vn.momo.kits.const.AppNavigationBar
52
- import vn.momo.kits.const.AppStatusBar
53
- import vn.momo.kits.const.AppTheme
54
- import vn.momo.kits.application.IsShowBaseLineDebug
55
- import vn.momo.kits.const.Colors
56
- import vn.momo.kits.const.Spacing
57
- import vn.momo.kits.modifier.conditional
58
- import vn.momo.kits.modifier.hideKeyboardOnTap
59
- import vn.momo.kits.navigation.bottomtab.BOTTOM_TAB_BAR_HEIGHT
60
- import vn.momo.kits.navigation.component.FABPosition
61
- import vn.momo.kits.navigation.component.FloatingButton
62
- import vn.momo.kits.navigation.component.HEADER_HEIGHT
63
- import vn.momo.kits.navigation.component.Header
64
- import vn.momo.kits.navigation.component.HeaderBackground
65
- import vn.momo.kits.navigation.component.HeaderRight
66
- import vn.momo.kits.navigation.component.HeaderType
67
- import vn.momo.kits.navigation.component.InputSearchType
68
- import vn.momo.kits.platform.BackHandler
69
- import vn.momo.kits.platform.supportsImePadding
70
- import vn.momo.kits.navigation.tracking.ScreenTracker
71
- import vn.momo.kits.navigation.tracking.ScreenTrackingState
72
- import kotlinx.coroutines.delay
73
- import vn.momo.kits.application.LocalContext
74
- import kotlin.time.Clock
75
- import kotlin.time.Duration.Companion.milliseconds
76
- import kotlin.time.ExperimentalTime
77
-
78
- internal val LocalFooterHeightPx = staticCompositionLocalOf { mutableIntStateOf(0) }
79
- internal val LocalHeaderRightWidthPx = staticCompositionLocalOf { mutableIntStateOf(0) }
80
-
81
- @Composable
82
- private fun bottomTabBarInset(): Dp {
83
- if (LocalNavigation.current.bottomTabIndex == -1) return 0.dp
84
- val keyboard = keyboardSizeState().value
85
- return (BOTTOM_TAB_BAR_HEIGHT.dp + Spacing.S - keyboard).coerceAtLeast(0.dp)
86
- }
87
-
88
- @OptIn(ExperimentalMaterialApi::class, ExperimentalTime::class)
89
- @Composable
90
- internal fun StackScreen(
91
- content: @Composable () -> Unit,
92
- navigationOptions: NavigationOptions? = null,
93
- id: Int = -1,
94
- name: String = "",
95
- bottomTabIndex: Int = -1,
96
- isDialogRoute: Boolean = false,
97
- onBackHandler: (() -> Unit)? = null,
98
- ) {
99
- val navigator = LocalNavigator.current
100
- val maxApi = LocalMaxApi.current
101
- val context = LocalContext.current
102
- val statusBar = AppStatusBar.current
103
- val density = LocalDensity.current
104
- val screenRegistry = LocalDynamicScreenRegistry.current
105
- val navigation = remember { Navigation(id = id, bottomTabIndex = bottomTabIndex, initOptions = navigationOptions, registry = screenRegistry) }
106
-
107
- val options by navigation.currentOptions
108
- val isBottomTabChild = bottomTabIndex != -1
109
- val isBottomTabRoot = navigation.isBottomTabRoot
110
-
111
- // Auto tracking state
112
- val trackingState = remember {
113
- ScreenTrackingState().apply {
114
- time = Clock.System.now().toEpochMilliseconds()
115
- }
116
- }
117
-
118
- // Determine action: push or back
119
- val action = remember {
120
- if (ScreenTracker.getLastScreenName() != null) "push" else "back"
121
- }
122
-
123
- // Auto tracking effects
124
- LaunchedEffect(name) {
125
- // Track screen navigated immediately
126
- ScreenTracker.trackScreenNavigated(
127
- maxApi = maxApi,
128
- context = context,
129
- screenName = name,
130
- action = action,
131
- state = trackingState
132
- )
133
-
134
- // Track screen displayed after 2 seconds (debounce)
135
- delay(2000.milliseconds)
136
- val loadTime = Clock.System.now().toEpochMilliseconds() - trackingState.time
137
- ScreenTracker.trackScreenDisplayed(
138
- maxApi = maxApi,
139
- context = context,
140
- screenName = name,
141
- duration = loadTime,
142
- state = trackingState
143
- )
144
-
145
- // Track screen interacted after displayed
146
- val interactionTime = Clock.System.now().toEpochMilliseconds() - trackingState.time
147
- ScreenTracker.trackScreenInteracted(
148
- maxApi = maxApi,
149
- context = context,
150
- screenName = name,
151
- duration = interactionTime - loadTime,
152
- totalDuration = interactionTime,
153
- state = trackingState
154
- )
155
- }
156
-
157
- val limit = with(density) {
158
- (statusBar).toPx() + HEADER_HEIGHT
159
- }.toInt()
160
-
161
- val (scrollState, scrollInProcess) = if (options.scrollData.scrollState is LazyListState)
162
- (options.scrollData.scrollState as? LazyListState ?: rememberLazyListState()).proxyScrollState(limit, 15)
163
- else
164
- (options.scrollData.scrollState as? ScrollState ?: rememberScrollState()).proxyLimitedScrollState(limit, 15)
165
-
166
- val footerHeightPx = remember { mutableIntStateOf(0) }
167
- val headerRightWidthPx = remember { mutableIntStateOf(0) }
168
-
169
- if (navigation.options.scrollData.scrollToTopCallback != null) {
170
- RegisterScrollToTop(navigation.options.scrollData.scrollToTopCallback)
171
- }
172
-
173
- // Hoisted out of the `||` chain below: short-circuiting would drop the back-stack subscription
174
- // whenever an earlier term is true.
175
- val canPop = navigator.rememberCanPop()
176
-
177
- // Port of RN's `hasCustomBackHandling`: this handler is registered inside the NavHost
178
- // destination, so while it is enabled it beats NavHost's own predictive-back handler and
179
- // swallows the swipe. Enable it only when a plain pop is the wrong answer.
180
- //
181
- // A presented route is one of those cases: NavHost arms predictive back on back-stack size
182
- // alone, so a horizontal edge drag over a dialog would seek its *vertical* slide.
183
- val hasCustomBackHandling = !SwipeBackConfig.enabled ||
184
- isBottomTabChild ||
185
- isDialogRoute ||
186
- options.onBackHandler != null ||
187
- options.headerBackProps.preventBack != null ||
188
- OverplayComponentRegistry.isOverplayShowing() ||
189
- !canPop
190
-
191
- // BottomTab supplies a tab-aware handler; falling through to onBackSafe() there would dismiss
192
- // the whole mini-app instead of returning to tab 0.
193
- val onBack = onBackHandler ?: { navigator.onBackSafe() }
194
-
195
- BackHandler(hasCustomBackHandling, onBack)
196
-
197
- CompositionLocalProvider(
198
- StackScreenScrollableState provides options.scrollData.scrollState,
199
- LocalNavigation provides navigation,
200
- LocalScrollState provides scrollState,
201
- LocalOptions provides options,
202
- LocalFooterHeightPx provides footerHeightPx,
203
- LocalHeaderRightWidthPx provides headerRightWidthPx
204
- ) {
205
- Box(
206
- Modifier
207
- .fillMaxSize()
208
- .background(options.backgroundColor ?: AppTheme.current.colors.background.default)
209
- .conditional(options.keyboardOptions.keyboardShouldPersistTaps) {
210
- hideKeyboardOnTap()
211
- }
212
- .conditional(options.keyboardOptions.useAvoidKeyboard && supportsImePadding()) {
213
- imePadding()
214
- }
215
- ) {
216
- Box(Modifier.zIndex(1f)) {
217
- HeaderBackground()
218
- }
219
-
220
- Box(Modifier.zIndex(4f)) {
221
- Header(onBackHandler)
222
- }
223
-
224
- Column(Modifier.zIndex(5f)) {
225
- SearchAnimated(isScrollInProgress = scrollInProcess)
226
- }
227
-
228
- Column(Modifier.zIndex(2f).fillMaxSize()) {
229
- MainContent(content = content, isBottomTabRoot = isBottomTabRoot, isBottomTabChild = isBottomTabChild)
230
- FooterContent()
231
- }
232
-
233
- Box(Modifier.zIndex(6f)) {
234
- FloatingContent(isBottomTabChild = isBottomTabChild)
235
- }
236
-
237
- OverplayView(isBottomTabChild = isBottomTabChild, id = id)
238
- }
239
- }
240
- }
241
-
242
- @Suppress("FrequentlyChangingValue")
243
- @Composable
244
- internal fun FloatingContent(isBottomTabChild: Boolean) {
245
- val options = LocalOptions.current
246
- val density = LocalDensity.current
247
- val footerHeightPx = LocalFooterHeightPx.current
248
- val scrollState = LocalScrollState.current
249
- val navigationBar = AppNavigationBar.current
250
-
251
- val fabProps = options.floatingButtonProps ?: return
252
- val bottomPadding = fabProps.bottom
253
- ?: (Spacing.M + when {
254
- options.hasFooter() -> with(density) { footerHeightPx.intValue.toDp() }
255
- isBottomTabChild -> bottomTabBarInset() + (navigationBar - keyboardSizeState().value).coerceAtLeast(0.dp)
256
- else -> navigationBar
257
- })
258
-
259
- FloatingButton(
260
- scrollPosition = fabProps.scrollState?.value ?: scrollState.value,
261
- onClick = fabProps.onClick,
262
- containerColor = AppTheme.current.colors.primary,
263
- bottom = bottomPadding,
264
- icon = fabProps.icon,
265
- iconColor = fabProps.iconColor,
266
- text = fabProps.label,
267
- size = fabProps.size,
268
- position = fabProps.position ?: FABPosition.END,
269
- )
270
- }
271
-
272
- @Composable
273
- internal fun ColumnScope.MainContent(content: @Composable () -> Unit, isBottomTabRoot: Boolean, isBottomTabChild: Boolean) {
274
- val options = LocalOptions.current
275
- val inputSearchType = getInputSearchType(options)
276
- val density = LocalDensity.current
277
- val scrollState = LocalScrollState.current
278
- val navigationBar = AppNavigationBar.current
279
-
280
- Spacer(
281
- Modifier.height(
282
- if (options.headerType is HeaderType.DefaultOrExtended || (options.headerType is HeaderType.Transparent && !options.headerType.isFullScreenContent))
283
- AppStatusBar.current + HEADER_HEIGHT.dp else 0.dp
284
- )
285
- )
286
- if (inputSearchType == InputSearchType.Animated) {
287
- val scrollDp = with(density) { scrollState.value.toDp() }
288
-
289
- val animatedTopPadding by animateDpAsState(
290
- targetValue = (HEADER_HEIGHT.dp - scrollDp).coerceIn(0.dp, HEADER_HEIGHT.dp),
291
- label = "AnimatedTopPadding"
292
- )
293
- Spacer(Modifier.height(animatedTopPadding))
294
- }
295
- Column(
296
- Modifier
297
- .fillMaxWidth()
298
- .weight(1f)
299
- .conditional(options.scrollData.scrollable && options.scrollData.scrollState is ScrollState) {
300
- verticalScroll(scrollState)
301
- }
302
- .padding(
303
- bottom = when {
304
- options.hasFooter() || isBottomTabRoot -> 0.dp
305
- isBottomTabChild -> bottomTabBarInset() + (navigationBar - keyboardSizeState().value).coerceAtLeast(0.dp)
306
- !options.scrollData.scrollable && options.scrollData.scrollState == null -> 0.dp
307
- else -> navigationBar
308
- }
309
- )
310
- ) {
311
- ScreenContent(content = content)
312
- }
313
-
314
- }
315
-
316
- @Composable
317
- internal fun ScreenContent(content: @Composable () -> Unit) {
318
- val scrollState = LocalScrollState.current
319
- val options = LocalOptions.current
320
-
321
- if (options.headerType is HeaderType.Animated) {
322
- val animatedHeader = options.headerType
323
- Box {
324
- Box(Modifier.fillMaxWidth().aspectRatio(animatedHeader.aspectRatio.value)) {
325
- animatedHeader.composable.invoke(scrollState.value)
326
- }
327
- Box(Modifier.padding(top = AppStatusBar.current + HEADER_HEIGHT.dp + animatedHeader.layoutOffSet)) {
328
- content()
329
- }
330
- }
331
- } else {
332
- content()
333
- }
334
- }
335
-
336
- @Composable
337
- internal fun FooterContent() {
338
- val options = LocalOptions.current
339
- if (options.hasFooter()) {
340
- val keyboardSize = keyboardSizeState()
341
- val bottomPadding = (AppNavigationBar.current - keyboardSize.value).coerceAtLeast(0.dp)
342
- Footer(
343
- footerComponent = options.footerComponent,
344
- bottomPadding = bottomPadding,
345
- transparent = options.footerTransparent
346
- )
347
- }
348
- }
349
-
350
- @Composable
351
- internal fun keyboardSizeState(): State<Dp> {
352
- val bottom = WindowInsets.ime.asPaddingValues()
353
- return rememberUpdatedState(bottom.calculateBottomPadding())
354
- }
355
-
356
- @Composable
357
- internal fun OverplayView(isBottomTabChild: Boolean, id: Int) {
358
- val overplayType = OverplayComponentRegistry.getOverplayType()
359
-
360
- if (overplayType != null) {
361
- Box(Modifier.zIndex(if (overplayType == OverplayComponentType.SNACK_BAR) 3f else 7f).fillMaxSize()) {
362
- if (isBottomTabChild) return@Box
363
- if (OverplayComponentRegistry.currentRootId() != id) return@Box
364
- OverplayComponentRegistry.OverlayComponent()
365
- }
366
- }
367
- }
368
-
369
- @Composable
370
- fun Footer(footerComponent: @Composable (() -> Unit)?, bottomPadding: Dp, transparent: Boolean = false) {
371
- if (footerComponent == null) return
372
-
373
- val footerHeightPx = LocalFooterHeightPx.current
374
-
375
- val shadowBrush = remember {
376
- Brush.verticalGradient(
377
- colors = listOf(Color.Transparent, Color.Black.copy(alpha = 0.05f))
378
- )
379
- }
380
-
381
- val backgroundColor = if (transparent) Color.Transparent else AppTheme.current.colors.background.surface
382
-
383
- Box(Modifier.onGloballyPositioned {
384
- if (footerHeightPx.intValue != it.size.height) footerHeightPx.intValue = it.size.height
385
- }) {
386
- Box(
387
- Modifier
388
- .fillMaxWidth()
389
- .background(backgroundColor)
390
- .conditional(IsShowBaseLineDebug) {
391
- border(1.dp, Colors.blue_03)
392
- }
393
- .padding(top = Spacing.S, start = Spacing.M, end = Spacing.M, bottom = bottomPadding + Spacing.S)
394
- ) {
395
- footerComponent.invoke()
396
- }
397
-
398
- if (!transparent) {
399
- Box(
400
- modifier = Modifier
401
- .fillMaxWidth()
402
- .height(6.dp)
403
- .offset(x = 0.dp, y = (-6).dp)
404
- .background(shadowBrush)
405
- )
406
- }
407
- }
408
- }
409
-
410
- data class InputSearchLayoutParams(
411
- val topPadding: Dp,
412
- val startPadding: Dp,
413
- val endPadding: Dp
414
- )
415
-
416
- @Suppress("FrequentlyChangingValue")
417
- @Composable
418
- internal fun SearchAnimated(
419
- isScrollInProgress: Boolean
420
- ) {
421
- val scrollState = LocalScrollState.current
422
- val options = LocalOptions.current
423
- val navigator = LocalNavigator.current
424
- val density = LocalDensity.current
425
-
426
- val scrollDp = with(density) { scrollState.value.toDp() }
427
-
428
- val inputSearchType = getInputSearchType(options)
429
- val headerRightWidthPx = LocalHeaderRightWidthPx.current
430
- val headerRightWidthDp = with(density) { headerRightWidthPx.intValue.toDp() }
431
-
432
- if (inputSearchType == InputSearchType.None) return
433
- val inputSearchProps = (options.headerType as? HeaderType.DefaultOrExtended)?.inputSearchProps ?: return
434
-
435
- val minTopPadding = AppStatusBar.current
436
- val maxTopPadding = AppStatusBar.current + HEADER_HEIGHT.dp
437
- val minStartPadding = Spacing.M
438
- val maxStartPadding = if (options.hiddenBack) Spacing.M else 48.dp // button back: size + left +right = 28 + 12 + 8 = 48
439
- val minEndPadding = Spacing.M
440
- val maxEndPadding = headerRightWidthDp + if (options.headerRight is HeaderRight.None) Spacing.M else Spacing.M * 2
441
-
442
- val targetColor = lerp(
443
- AppTheme.current.colors.background.surface,
444
- AppTheme.current.colors.background.default,
445
- (scrollDp / minTopPadding).coerceIn(0f, 1f)
446
- )
447
- val animatedColor by animateColorAsState(targetValue = targetColor, label = "BackgroundColor")
448
-
449
- val layoutParams = when (inputSearchType) {
450
- InputSearchType.Header -> {
451
- InputSearchLayoutParams(
452
- topPadding = minTopPadding,
453
- startPadding = maxStartPadding,
454
- endPadding = maxEndPadding
455
- )
456
- }
457
-
458
- InputSearchType.Animated -> {
459
- val animatedTopPadding by animateDpAsState(
460
- targetValue = (maxTopPadding - scrollDp).coerceIn(minTopPadding, maxTopPadding),
461
- label = "AnimatedTopPadding"
462
- )
463
-
464
- val animatedStartPadding by animateDpAsState(
465
- targetValue = (minStartPadding + scrollDp).coerceIn(minStartPadding, maxStartPadding),
466
- label = "AnimatedStartPadding"
467
- )
468
-
469
- val animatedEndPadding by animateDpAsState(
470
- targetValue = (minEndPadding + scrollDp).coerceIn(minEndPadding, maxEndPadding),
471
- label = "AnimatedEndPadding"
472
- )
473
-
474
- val maxPadding = remember(maxTopPadding, maxStartPadding, maxEndPadding) {
475
- maxOf(maxEndPadding, maxStartPadding, maxTopPadding)
476
- }
477
- val snapScrollValue = with(density) { maxPadding.toPx().toInt() }
478
-
479
- LaunchedEffect(isScrollInProgress && scrollState.value != 0 && scrollState.value != snapScrollValue) {
480
- if (scrollDp < maxPadding) {
481
- if (!isScrollInProgress) {
482
- val midpoint = (maxTopPadding - minTopPadding) / 2
483
-
484
- if (scrollDp < midpoint) {
485
- scrollState.animateScrollTo(0)
486
- } else {
487
- scrollState.animateScrollTo(snapScrollValue)
488
- }
489
- }
490
- }
491
- }
492
-
493
- InputSearchLayoutParams(
494
- topPadding = animatedTopPadding,
495
- startPadding = animatedStartPadding,
496
- endPadding = animatedEndPadding
497
- )
498
- }
499
-
500
- InputSearchType.None -> return
501
- }
502
-
503
- Spacer(Modifier.height(layoutParams.topPadding))
504
- Box(
505
- modifier = Modifier
506
- .height(HEADER_HEIGHT.dp)
507
- .fillMaxWidth()
508
- .padding(
509
- start = layoutParams.startPadding,
510
- end = layoutParams.endPadding
511
- ),
512
- contentAlignment = Alignment.Center
513
- ) {
514
- InputSearch(
515
- inputSearchProps = inputSearchProps.copy(
516
- backgroundColor = animatedColor,
517
- onPressButtonText = {
518
- navigator.pop()
519
- }
520
- )
521
- )
522
- }
523
- }
524
-
525
- private fun quantize(value: Int, stepPx: Int): Int {
526
- if (stepPx <= 1) return value
527
- return (value / stepPx) * stepPx
528
- }
529
-
530
- @Composable
531
- internal fun LazyListState.proxyScrollState(
532
- thresholdPx: Int = 200,
533
- stepPx: Int = 4
534
- ): Pair<ScrollState, Boolean> = proxyScroll(
535
- source = this,
536
- thresholdPx = thresholdPx,
537
- stepPx = stepPx,
538
- isScrollInProgress = { isScrollInProgress },
539
- rawTargetFlow = {
540
- snapshotFlow {
541
- if (firstVisibleItemIndex == 0) firstVisibleItemScrollOffset
542
- else SCROLLED_PAST_FIRST_ITEM_THRESHOLD
543
- }
544
- },
545
- )
546
-
547
- @Composable
548
- internal fun ScrollState.proxyLimitedScrollState(
549
- thresholdPx: Int = 200,
550
- stepPx: Int = 4
551
- ): Pair<ScrollState, Boolean> = proxyScroll(
552
- source = this,
553
- thresholdPx = thresholdPx,
554
- stepPx = stepPx,
555
- isScrollInProgress = { isScrollInProgress },
556
- rawTargetFlow = { snapshotFlow { value } },
557
- )
558
-
559
- @Composable
560
- private fun proxyScroll(
561
- source: Any,
562
- thresholdPx: Int,
563
- stepPx: Int,
564
- isScrollInProgress: () -> Boolean,
565
- rawTargetFlow: () -> Flow<Int>,
566
- ): Pair<ScrollState, Boolean> {
567
- val proxy = rememberScrollState()
568
- val locked = remember { mutableStateOf(false) }
569
-
570
- LaunchedEffect(source, proxy, thresholdPx, stepPx) {
571
- rawTargetFlow().collect { rawTarget ->
572
- val shouldLock = rawTarget > thresholdPx
573
- if (locked.value != shouldLock) locked.value = shouldLock
574
-
575
- val desired = if (shouldLock) thresholdPx else quantize(rawTarget, stepPx)
576
-
577
- if (proxy.value != desired) {
578
- proxy.scrollTo(desired.coerceAtLeast(0))
579
- }
580
- }
581
- }
582
-
583
- val inProgress = remember { mutableStateOf(false) }
584
- LaunchedEffect(source, thresholdPx) {
585
- snapshotFlow { isScrollInProgress() }
586
- .collect { progressing ->
587
- inProgress.value = if (locked.value) false else progressing
588
- }
589
- }
590
-
591
- return proxy to inProgress.value
592
- }
593
-
594
- private const val SCROLLED_PAST_FIRST_ITEM_THRESHOLD = 10_000
595
-
596
- internal val LocalScrollState = staticCompositionLocalOf<ScrollState> { error("No Scroll State provided") }
597
- internal val LocalOptions = staticCompositionLocalOf<NavigationOptions> { error("No NavigationOptions provided") }
598
-
599
- val StackScreenScrollableState = staticCompositionLocalOf<ScrollableState?> { null }
600
-
601
- internal fun getInputSearchType(options: NavigationOptions): InputSearchType {
602
- return when (val headerType = options.headerType) {
603
- is HeaderType.DefaultOrExtended -> when {
604
- headerType.inputSearchProps == null -> InputSearchType.None
605
- headerType.useAnimated -> InputSearchType.Animated
606
- else -> InputSearchType.Header
607
- }
608
-
609
- else -> InputSearchType.None
610
- }
611
- }
612
-