@momo-kits/native-kits 0.164.1-beta.16-debug → 0.164.1-beta.17

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 (140) hide show
  1. package/ios/native-kits.podspec +1 -1
  2. package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
  3. package/package.json +2 -2
  4. package/build.gradle.kts +0 -11
  5. package/compose/build.gradle.kts +0 -190
  6. package/compose/build.gradle.kts.backup +0 -190
  7. package/compose/compose.podspec +0 -47
  8. package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +0 -19
  9. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +0 -6
  10. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +0 -12
  11. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +0 -7
  12. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -150
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -52
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -106
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -183
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -207
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -39
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -293
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -826
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +0 -269
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -110
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -378
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +0 -56
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +0 -157
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -82
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -32
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -338
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +0 -175
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -356
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -113
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -89
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -128
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +0 -209
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -542
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -23
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -76
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -125
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -231
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -134
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -475
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -114
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -214
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -240
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -216
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -259
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -182
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -343
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +0 -108
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -56
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -41
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -84
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -40
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -328
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -97
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -310
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -59
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +0 -87
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +0 -21
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -90
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +0 -323
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +0 -217
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +0 -473
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +0 -122
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +0 -199
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -91
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -470
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -88
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -131
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -203
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +0 -498
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -175
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +0 -182
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -193
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -234
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -188
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -325
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +0 -16
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -16
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -188
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -263
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -52
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -38
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -39
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -52
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -66
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +0 -104
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -48
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -299
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -129
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -103
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -191
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -386
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +0 -8
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -610
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +0 -157
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +0 -39
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -156
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -225
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -169
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -320
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -81
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -309
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -34
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -360
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -132
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +0 -127
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +0 -15
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -91
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +0 -9
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -69
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -55
  124. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
  125. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -105
  126. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +0 -40
  127. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +0 -125
  128. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +0 -32
  129. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +0 -35
  130. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +0 -66
  131. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -220
  132. package/gradle/libs.versions.toml +0 -69
  133. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  134. package/gradle/wrapper/gradle-wrapper.properties +0 -8
  135. package/gradle.properties +0 -26
  136. package/gradlew +0 -252
  137. package/gradlew.bat +0 -94
  138. package/ios-demo/README.md +0 -120
  139. package/local.properties +0 -8
  140. package/settings.gradle.kts +0 -64
@@ -1,610 +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
- onBackHandler: (() -> Unit)? = null,
97
- ) {
98
- val navigator = LocalNavigator.current
99
- val maxApi = LocalMaxApi.current
100
- val context = LocalContext.current
101
- val statusBar = AppStatusBar.current
102
- val density = LocalDensity.current
103
- val screenRegistry = LocalDynamicScreenRegistry.current
104
- val navigation = remember { Navigation(id = id, bottomTabIndex = bottomTabIndex, initOptions = navigationOptions, registry = screenRegistry) }
105
-
106
- val options by navigation.currentOptions
107
- val isBottomTabChild = bottomTabIndex != -1
108
- val isBottomTabRoot = navigation.isBottomTabRoot
109
-
110
- // Auto tracking state
111
- val trackingState = remember {
112
- ScreenTrackingState().apply {
113
- time = Clock.System.now().toEpochMilliseconds()
114
- }
115
- }
116
-
117
- // Determine action: push or back
118
- val action = remember {
119
- if (ScreenTracker.getLastScreenName() != null) "push" else "back"
120
- }
121
-
122
- // Auto tracking effects
123
- LaunchedEffect(name) {
124
- // Track screen navigated immediately
125
- ScreenTracker.trackScreenNavigated(
126
- maxApi = maxApi,
127
- context = context,
128
- screenName = name,
129
- action = action,
130
- state = trackingState
131
- )
132
-
133
- // Track screen displayed after 2 seconds (debounce)
134
- delay(2000.milliseconds)
135
- val loadTime = Clock.System.now().toEpochMilliseconds() - trackingState.time
136
- ScreenTracker.trackScreenDisplayed(
137
- maxApi = maxApi,
138
- context = context,
139
- screenName = name,
140
- duration = loadTime,
141
- state = trackingState
142
- )
143
-
144
- // Track screen interacted after displayed
145
- val interactionTime = Clock.System.now().toEpochMilliseconds() - trackingState.time
146
- ScreenTracker.trackScreenInteracted(
147
- maxApi = maxApi,
148
- context = context,
149
- screenName = name,
150
- duration = interactionTime - loadTime,
151
- totalDuration = interactionTime,
152
- state = trackingState
153
- )
154
- }
155
-
156
- val limit = with(density) {
157
- (statusBar).toPx() + HEADER_HEIGHT
158
- }.toInt()
159
-
160
- val (scrollState, scrollInProcess) = if (options.scrollData.scrollState is LazyListState)
161
- (options.scrollData.scrollState as? LazyListState ?: rememberLazyListState()).proxyScrollState(limit, 15)
162
- else
163
- (options.scrollData.scrollState as? ScrollState ?: rememberScrollState()).proxyLimitedScrollState(limit, 15)
164
-
165
- val footerHeightPx = remember { mutableIntStateOf(0) }
166
- val headerRightWidthPx = remember { mutableIntStateOf(0) }
167
-
168
- if (navigation.options.scrollData.scrollToTopCallback != null) {
169
- RegisterScrollToTop(navigation.options.scrollData.scrollToTopCallback)
170
- }
171
-
172
- // Port of RN's `hasCustomBackHandling`: this handler is registered inside the NavHost
173
- // destination, so while it is enabled it beats NavHost's own predictive-back handler and
174
- // swallows the swipe. Enable it only when a plain pop is the wrong answer.
175
- val canPop = navigator.canPop()
176
-
177
- // `isBottomTabChild` used to be a term here, which swallowed the swipe on every tab screen:
178
- // this handler out-ranks NavHost's predictive-back one and BackEventHandler implements only
179
- // onBackCompleted, so back still landed but nothing seeked. The root case it was guarding is
180
- // already covered by `!canPop` below, and letting a tab screen through means the nested tab
181
- // NavHost seeks its own transition back to the previous tab.
182
- //
183
- // `options.onBackHandler` was dropped for the same reason: it is set widely (tracking, custom
184
- // close flows) and every one of those screens lost the seek. The trade is that NavHost pops
185
- // them straight through `composeNavigator.popBackStack()`, so `onBackSafe` — and with it the
186
- // handler — does not run on a gesture or hardware back. Screens that must intercept back
187
- // should use `preventBack`, which is still a term below.
188
- val hasCustomBackHandling = !SwipeBackConfig.enabled ||
189
- options.headerBackProps.preventBack != null ||
190
- OverplayComponentRegistry.isOverplayShowing() ||
191
- !canPop
192
-
193
- BackHandler(hasCustomBackHandling) { navigator.onBackSafe() }
194
-
195
- CompositionLocalProvider(
196
- StackScreenScrollableState provides options.scrollData.scrollState,
197
- LocalNavigation provides navigation,
198
- LocalScrollState provides scrollState,
199
- LocalOptions provides options,
200
- LocalFooterHeightPx provides footerHeightPx,
201
- LocalHeaderRightWidthPx provides headerRightWidthPx
202
- ) {
203
- Box(
204
- Modifier
205
- .fillMaxSize()
206
- .background(options.backgroundColor ?: AppTheme.current.colors.background.default)
207
- .conditional(options.keyboardOptions.keyboardShouldPersistTaps) {
208
- hideKeyboardOnTap()
209
- }
210
- .conditional(options.keyboardOptions.useAvoidKeyboard && supportsImePadding()) {
211
- imePadding()
212
- }
213
- ) {
214
- Box(Modifier.zIndex(1f)) {
215
- HeaderBackground()
216
- }
217
-
218
- Box(Modifier.zIndex(4f)) {
219
- Header(onBackHandler)
220
- }
221
-
222
- Column(Modifier.zIndex(5f)) {
223
- SearchAnimated(isScrollInProgress = scrollInProcess)
224
- }
225
-
226
- Column(Modifier.zIndex(2f).fillMaxSize()) {
227
- MainContent(content = content, isBottomTabRoot = isBottomTabRoot, isBottomTabChild = isBottomTabChild)
228
- FooterContent()
229
- }
230
-
231
- Box(Modifier.zIndex(6f)) {
232
- FloatingContent(isBottomTabChild = isBottomTabChild)
233
- }
234
-
235
- OverplayView(isBottomTabChild = isBottomTabChild, id = id)
236
- }
237
- }
238
- }
239
-
240
- @Suppress("FrequentlyChangingValue")
241
- @Composable
242
- internal fun FloatingContent(isBottomTabChild: Boolean) {
243
- val options = LocalOptions.current
244
- val density = LocalDensity.current
245
- val footerHeightPx = LocalFooterHeightPx.current
246
- val scrollState = LocalScrollState.current
247
- val navigationBar = AppNavigationBar.current
248
-
249
- val fabProps = options.floatingButtonProps ?: return
250
- val bottomPadding = fabProps.bottom
251
- ?: (Spacing.M + when {
252
- options.hasFooter() -> with(density) { footerHeightPx.intValue.toDp() }
253
- isBottomTabChild -> bottomTabBarInset() + (navigationBar - keyboardSizeState().value).coerceAtLeast(0.dp)
254
- else -> navigationBar
255
- })
256
-
257
- FloatingButton(
258
- scrollPosition = fabProps.scrollState?.value ?: scrollState.value,
259
- onClick = fabProps.onClick,
260
- containerColor = AppTheme.current.colors.primary,
261
- bottom = bottomPadding,
262
- icon = fabProps.icon,
263
- iconColor = fabProps.iconColor,
264
- text = fabProps.label,
265
- size = fabProps.size,
266
- position = fabProps.position ?: FABPosition.END,
267
- )
268
- }
269
-
270
- @Composable
271
- internal fun ColumnScope.MainContent(content: @Composable () -> Unit, isBottomTabRoot: Boolean, isBottomTabChild: Boolean) {
272
- val options = LocalOptions.current
273
- val inputSearchType = getInputSearchType(options)
274
- val density = LocalDensity.current
275
- val scrollState = LocalScrollState.current
276
- val navigationBar = AppNavigationBar.current
277
-
278
- Spacer(
279
- Modifier.height(
280
- if (options.headerType is HeaderType.DefaultOrExtended || (options.headerType is HeaderType.Transparent && !options.headerType.isFullScreenContent))
281
- AppStatusBar.current + HEADER_HEIGHT.dp else 0.dp
282
- )
283
- )
284
- if (inputSearchType == InputSearchType.Animated) {
285
- val scrollDp = with(density) { scrollState.value.toDp() }
286
-
287
- val animatedTopPadding by animateDpAsState(
288
- targetValue = (HEADER_HEIGHT.dp - scrollDp).coerceIn(0.dp, HEADER_HEIGHT.dp),
289
- label = "AnimatedTopPadding"
290
- )
291
- Spacer(Modifier.height(animatedTopPadding))
292
- }
293
- Column(
294
- Modifier
295
- .fillMaxWidth()
296
- .weight(1f)
297
- .conditional(options.scrollData.scrollable && options.scrollData.scrollState is ScrollState) {
298
- verticalScroll(scrollState)
299
- }
300
- .padding(
301
- bottom = when {
302
- options.hasFooter() || isBottomTabRoot -> 0.dp
303
- isBottomTabChild -> bottomTabBarInset() + (navigationBar - keyboardSizeState().value).coerceAtLeast(0.dp)
304
- !options.scrollData.scrollable && options.scrollData.scrollState == null -> 0.dp
305
- else -> navigationBar
306
- }
307
- )
308
- ) {
309
- ScreenContent(content = content)
310
- }
311
-
312
- }
313
-
314
- @Composable
315
- internal fun ScreenContent(content: @Composable () -> Unit) {
316
- val scrollState = LocalScrollState.current
317
- val options = LocalOptions.current
318
-
319
- if (options.headerType is HeaderType.Animated) {
320
- val animatedHeader = options.headerType
321
- Box {
322
- Box(Modifier.fillMaxWidth().aspectRatio(animatedHeader.aspectRatio.value)) {
323
- animatedHeader.composable.invoke(scrollState.value)
324
- }
325
- Box(Modifier.padding(top = AppStatusBar.current + HEADER_HEIGHT.dp + animatedHeader.layoutOffSet)) {
326
- content()
327
- }
328
- }
329
- } else {
330
- content()
331
- }
332
- }
333
-
334
- @Composable
335
- internal fun FooterContent() {
336
- val options = LocalOptions.current
337
- if (options.hasFooter()) {
338
- val keyboardSize = keyboardSizeState()
339
- val bottomPadding = (AppNavigationBar.current - keyboardSize.value).coerceAtLeast(0.dp)
340
- Footer(
341
- footerComponent = options.footerComponent,
342
- bottomPadding = bottomPadding,
343
- transparent = options.footerTransparent
344
- )
345
- }
346
- }
347
-
348
- @Composable
349
- internal fun keyboardSizeState(): State<Dp> {
350
- val bottom = WindowInsets.ime.asPaddingValues()
351
- return rememberUpdatedState(bottom.calculateBottomPadding())
352
- }
353
-
354
- @Composable
355
- internal fun OverplayView(isBottomTabChild: Boolean, id: Int) {
356
- val overplayType = OverplayComponentRegistry.getOverplayType()
357
-
358
- if (overplayType != null) {
359
- Box(Modifier.zIndex(if (overplayType == OverplayComponentType.SNACK_BAR) 3f else 7f).fillMaxSize()) {
360
- if (isBottomTabChild) return@Box
361
- if (OverplayComponentRegistry.currentRootId() != id) return@Box
362
- OverplayComponentRegistry.OverlayComponent()
363
- }
364
- }
365
- }
366
-
367
- @Composable
368
- fun Footer(footerComponent: @Composable (() -> Unit)?, bottomPadding: Dp, transparent: Boolean = false) {
369
- if (footerComponent == null) return
370
-
371
- val footerHeightPx = LocalFooterHeightPx.current
372
-
373
- val shadowBrush = remember {
374
- Brush.verticalGradient(
375
- colors = listOf(Color.Transparent, Color.Black.copy(alpha = 0.05f))
376
- )
377
- }
378
-
379
- val backgroundColor = if (transparent) Color.Transparent else AppTheme.current.colors.background.surface
380
-
381
- Box(Modifier.onGloballyPositioned {
382
- if (footerHeightPx.intValue != it.size.height) footerHeightPx.intValue = it.size.height
383
- }) {
384
- Box(
385
- Modifier
386
- .fillMaxWidth()
387
- .background(backgroundColor)
388
- .conditional(IsShowBaseLineDebug) {
389
- border(1.dp, Colors.blue_03)
390
- }
391
- .padding(top = Spacing.S, start = Spacing.M, end = Spacing.M, bottom = bottomPadding + Spacing.S)
392
- ) {
393
- footerComponent.invoke()
394
- }
395
-
396
- if (!transparent) {
397
- Box(
398
- modifier = Modifier
399
- .fillMaxWidth()
400
- .height(6.dp)
401
- .offset(x = 0.dp, y = (-6).dp)
402
- .background(shadowBrush)
403
- )
404
- }
405
- }
406
- }
407
-
408
- data class InputSearchLayoutParams(
409
- val topPadding: Dp,
410
- val startPadding: Dp,
411
- val endPadding: Dp
412
- )
413
-
414
- @Suppress("FrequentlyChangingValue")
415
- @Composable
416
- internal fun SearchAnimated(
417
- isScrollInProgress: Boolean
418
- ) {
419
- val scrollState = LocalScrollState.current
420
- val options = LocalOptions.current
421
- val navigator = LocalNavigator.current
422
- val density = LocalDensity.current
423
-
424
- val scrollDp = with(density) { scrollState.value.toDp() }
425
-
426
- val inputSearchType = getInputSearchType(options)
427
- val headerRightWidthPx = LocalHeaderRightWidthPx.current
428
- val headerRightWidthDp = with(density) { headerRightWidthPx.intValue.toDp() }
429
-
430
- if (inputSearchType == InputSearchType.None) return
431
- val inputSearchProps = (options.headerType as? HeaderType.DefaultOrExtended)?.inputSearchProps ?: return
432
-
433
- val minTopPadding = AppStatusBar.current
434
- val maxTopPadding = AppStatusBar.current + HEADER_HEIGHT.dp
435
- val minStartPadding = Spacing.M
436
- val maxStartPadding = if (options.hiddenBack) Spacing.M else 48.dp // button back: size + left +right = 28 + 12 + 8 = 48
437
- val minEndPadding = Spacing.M
438
- val maxEndPadding = headerRightWidthDp + if (options.headerRight is HeaderRight.None) Spacing.M else Spacing.M * 2
439
-
440
- val targetColor = lerp(
441
- AppTheme.current.colors.background.surface,
442
- AppTheme.current.colors.background.default,
443
- (scrollDp / minTopPadding).coerceIn(0f, 1f)
444
- )
445
- val animatedColor by animateColorAsState(targetValue = targetColor, label = "BackgroundColor")
446
-
447
- val layoutParams = when (inputSearchType) {
448
- InputSearchType.Header -> {
449
- InputSearchLayoutParams(
450
- topPadding = minTopPadding,
451
- startPadding = maxStartPadding,
452
- endPadding = maxEndPadding
453
- )
454
- }
455
-
456
- InputSearchType.Animated -> {
457
- val animatedTopPadding by animateDpAsState(
458
- targetValue = (maxTopPadding - scrollDp).coerceIn(minTopPadding, maxTopPadding),
459
- label = "AnimatedTopPadding"
460
- )
461
-
462
- val animatedStartPadding by animateDpAsState(
463
- targetValue = (minStartPadding + scrollDp).coerceIn(minStartPadding, maxStartPadding),
464
- label = "AnimatedStartPadding"
465
- )
466
-
467
- val animatedEndPadding by animateDpAsState(
468
- targetValue = (minEndPadding + scrollDp).coerceIn(minEndPadding, maxEndPadding),
469
- label = "AnimatedEndPadding"
470
- )
471
-
472
- val maxPadding = remember(maxTopPadding, maxStartPadding, maxEndPadding) {
473
- maxOf(maxEndPadding, maxStartPadding, maxTopPadding)
474
- }
475
- val snapScrollValue = with(density) { maxPadding.toPx().toInt() }
476
-
477
- LaunchedEffect(isScrollInProgress && scrollState.value != 0 && scrollState.value != snapScrollValue) {
478
- if (scrollDp < maxPadding) {
479
- if (!isScrollInProgress) {
480
- val midpoint = (maxTopPadding - minTopPadding) / 2
481
-
482
- if (scrollDp < midpoint) {
483
- scrollState.animateScrollTo(0)
484
- } else {
485
- scrollState.animateScrollTo(snapScrollValue)
486
- }
487
- }
488
- }
489
- }
490
-
491
- InputSearchLayoutParams(
492
- topPadding = animatedTopPadding,
493
- startPadding = animatedStartPadding,
494
- endPadding = animatedEndPadding
495
- )
496
- }
497
-
498
- InputSearchType.None -> return
499
- }
500
-
501
- Spacer(Modifier.height(layoutParams.topPadding))
502
- Box(
503
- modifier = Modifier
504
- .height(HEADER_HEIGHT.dp)
505
- .fillMaxWidth()
506
- .padding(
507
- start = layoutParams.startPadding,
508
- end = layoutParams.endPadding
509
- ),
510
- contentAlignment = Alignment.Center
511
- ) {
512
- InputSearch(
513
- inputSearchProps = inputSearchProps.copy(
514
- backgroundColor = animatedColor,
515
- onPressButtonText = {
516
- navigator.pop()
517
- }
518
- )
519
- )
520
- }
521
- }
522
-
523
- private fun quantize(value: Int, stepPx: Int): Int {
524
- if (stepPx <= 1) return value
525
- return (value / stepPx) * stepPx
526
- }
527
-
528
- @Composable
529
- internal fun LazyListState.proxyScrollState(
530
- thresholdPx: Int = 200,
531
- stepPx: Int = 4
532
- ): Pair<ScrollState, Boolean> = proxyScroll(
533
- source = this,
534
- thresholdPx = thresholdPx,
535
- stepPx = stepPx,
536
- isScrollInProgress = { isScrollInProgress },
537
- rawTargetFlow = {
538
- snapshotFlow {
539
- if (firstVisibleItemIndex == 0) firstVisibleItemScrollOffset
540
- else SCROLLED_PAST_FIRST_ITEM_THRESHOLD
541
- }
542
- },
543
- )
544
-
545
- @Composable
546
- internal fun ScrollState.proxyLimitedScrollState(
547
- thresholdPx: Int = 200,
548
- stepPx: Int = 4
549
- ): Pair<ScrollState, Boolean> = proxyScroll(
550
- source = this,
551
- thresholdPx = thresholdPx,
552
- stepPx = stepPx,
553
- isScrollInProgress = { isScrollInProgress },
554
- rawTargetFlow = { snapshotFlow { value } },
555
- )
556
-
557
- @Composable
558
- private fun proxyScroll(
559
- source: Any,
560
- thresholdPx: Int,
561
- stepPx: Int,
562
- isScrollInProgress: () -> Boolean,
563
- rawTargetFlow: () -> Flow<Int>,
564
- ): Pair<ScrollState, Boolean> {
565
- val proxy = rememberScrollState()
566
- val locked = remember { mutableStateOf(false) }
567
-
568
- LaunchedEffect(source, proxy, thresholdPx, stepPx) {
569
- rawTargetFlow().collect { rawTarget ->
570
- val shouldLock = rawTarget > thresholdPx
571
- if (locked.value != shouldLock) locked.value = shouldLock
572
-
573
- val desired = if (shouldLock) thresholdPx else quantize(rawTarget, stepPx)
574
-
575
- if (proxy.value != desired) {
576
- proxy.scrollTo(desired.coerceAtLeast(0))
577
- }
578
- }
579
- }
580
-
581
- val inProgress = remember { mutableStateOf(false) }
582
- LaunchedEffect(source, thresholdPx) {
583
- snapshotFlow { isScrollInProgress() }
584
- .collect { progressing ->
585
- inProgress.value = if (locked.value) false else progressing
586
- }
587
- }
588
-
589
- return proxy to inProgress.value
590
- }
591
-
592
- private const val SCROLLED_PAST_FIRST_ITEM_THRESHOLD = 10_000
593
-
594
- internal val LocalScrollState = staticCompositionLocalOf<ScrollState> { error("No Scroll State provided") }
595
- internal val LocalOptions = staticCompositionLocalOf<NavigationOptions> { error("No NavigationOptions provided") }
596
-
597
- val StackScreenScrollableState = staticCompositionLocalOf<ScrollableState?> { null }
598
-
599
- internal fun getInputSearchType(options: NavigationOptions): InputSearchType {
600
- return when (val headerType = options.headerType) {
601
- is HeaderType.DefaultOrExtended -> when {
602
- headerType.inputSearchProps == null -> InputSearchType.None
603
- headerType.useAnimated -> InputSearchType.Animated
604
- else -> InputSearchType.Header
605
- }
606
-
607
- else -> InputSearchType.None
608
- }
609
- }
610
-