@momo-kits/native-kits 0.162.15 → 0.162.16-debug

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