@momo-kits/native-kits 0.152.4-scale.4 → 0.152.5-klib.1
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.
- package/README.md +175 -5
- package/ios/native-kits.podspec +18 -16
- package/package.json +2 -4
- package/CODE_OF_CONDUCT.md +0 -133
- package/CONTRIBUTING.md +0 -114
- package/LICENSE +0 -20
- package/build.gradle.kts +0 -32
- package/compose/MoMoComposeKits.podspec +0 -54
- package/compose/build.gradle.kts +0 -149
- package/compose/src/androidMain/AndroidManifest.xml +0 -2
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -105
- package/compose/src/commonMain/composeResources/files/lottie_circle_loader.json +0 -1
- package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
- package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -57
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -201
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -222
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -48
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -308
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -715
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -214
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -404
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -78
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -27
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -334
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -345
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -90
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -133
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -543
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -23
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -69
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -143
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -179
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -111
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -395
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -164
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -234
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -226
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -227
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -241
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -235
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -364
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -50
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -34
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -85
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -33
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -338
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -95
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -64
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -89
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -91
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -86
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -91
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -208
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -172
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -199
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -237
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -191
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -306
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -13
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -189
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -293
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -35
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -59
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -68
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -49
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -232
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -111
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -94
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -159
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -302
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScaleSizeScope.kt +0 -22
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -483
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -169
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -217
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -180
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -251
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -80
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -306
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -31
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -385
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -125
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -38
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -62
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -88
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -144
- package/gradle.properties +0 -19
- package/gradlew +0 -240
- package/gradlew.bat +0 -91
- package/ios/Theme.md +0 -18
- package/local.properties +0 -8
- package/settings.gradle.kts +0 -25
|
@@ -1,483 +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.gestures.ScrollableState
|
|
8
|
-
import androidx.compose.foundation.layout.Box
|
|
9
|
-
import androidx.compose.foundation.layout.Column
|
|
10
|
-
import androidx.compose.foundation.layout.ColumnScope
|
|
11
|
-
import androidx.compose.foundation.layout.Spacer
|
|
12
|
-
import androidx.compose.foundation.layout.WindowInsets
|
|
13
|
-
import androidx.compose.foundation.layout.aspectRatio
|
|
14
|
-
import androidx.compose.foundation.layout.fillMaxSize
|
|
15
|
-
import androidx.compose.foundation.layout.fillMaxWidth
|
|
16
|
-
import androidx.compose.foundation.layout.height
|
|
17
|
-
import androidx.compose.foundation.layout.ime
|
|
18
|
-
import androidx.compose.foundation.layout.imePadding
|
|
19
|
-
import androidx.compose.foundation.layout.offset
|
|
20
|
-
import androidx.compose.foundation.layout.padding
|
|
21
|
-
import androidx.compose.foundation.lazy.LazyListState
|
|
22
|
-
import androidx.compose.foundation.lazy.rememberLazyListState
|
|
23
|
-
import androidx.compose.foundation.rememberScrollState
|
|
24
|
-
import androidx.compose.foundation.verticalScroll
|
|
25
|
-
import androidx.compose.material.ExperimentalMaterialApi
|
|
26
|
-
import androidx.compose.runtime.Composable
|
|
27
|
-
import androidx.compose.runtime.CompositionLocalProvider
|
|
28
|
-
import androidx.compose.runtime.LaunchedEffect
|
|
29
|
-
import androidx.compose.runtime.getValue
|
|
30
|
-
import androidx.compose.runtime.mutableIntStateOf
|
|
31
|
-
import androidx.compose.runtime.mutableStateOf
|
|
32
|
-
import androidx.compose.runtime.remember
|
|
33
|
-
import androidx.compose.runtime.snapshotFlow
|
|
34
|
-
import androidx.compose.runtime.staticCompositionLocalOf
|
|
35
|
-
import androidx.compose.ui.Alignment
|
|
36
|
-
import androidx.compose.ui.Modifier
|
|
37
|
-
import androidx.compose.ui.graphics.Brush
|
|
38
|
-
import androidx.compose.ui.graphics.Color
|
|
39
|
-
import androidx.compose.ui.graphics.lerp
|
|
40
|
-
import androidx.compose.ui.layout.onGloballyPositioned
|
|
41
|
-
import androidx.compose.ui.platform.LocalDensity
|
|
42
|
-
import androidx.compose.ui.unit.Dp
|
|
43
|
-
import androidx.compose.ui.unit.dp
|
|
44
|
-
import androidx.compose.ui.unit.min
|
|
45
|
-
import androidx.compose.ui.zIndex
|
|
46
|
-
import vn.momo.kits.components.InputSearch
|
|
47
|
-
import vn.momo.kits.const.AppNavigationBar
|
|
48
|
-
import vn.momo.kits.const.AppStatusBar
|
|
49
|
-
import vn.momo.kits.const.AppTheme
|
|
50
|
-
import vn.momo.kits.const.Spacing
|
|
51
|
-
import vn.momo.kits.modifier.conditional
|
|
52
|
-
import vn.momo.kits.modifier.hideKeyboardOnTap
|
|
53
|
-
import vn.momo.kits.navigation.component.FABPosition
|
|
54
|
-
import vn.momo.kits.navigation.component.FloatingButton
|
|
55
|
-
import vn.momo.kits.navigation.component.HEADER_HEIGHT
|
|
56
|
-
import vn.momo.kits.navigation.component.Header
|
|
57
|
-
import vn.momo.kits.navigation.component.HeaderBackground
|
|
58
|
-
import vn.momo.kits.navigation.component.HeaderRight
|
|
59
|
-
import vn.momo.kits.navigation.component.HeaderType
|
|
60
|
-
import vn.momo.kits.navigation.component.InputSearchType
|
|
61
|
-
import vn.momo.kits.platform.BackHandler
|
|
62
|
-
import vn.momo.kits.platform.getAndroidBuildVersion
|
|
63
|
-
|
|
64
|
-
internal val LocalFooterHeightPx = staticCompositionLocalOf { mutableIntStateOf(0) }
|
|
65
|
-
internal val LocalHeaderRightWidthPx = staticCompositionLocalOf { mutableIntStateOf(0) }
|
|
66
|
-
|
|
67
|
-
@OptIn(ExperimentalMaterialApi::class)
|
|
68
|
-
@Composable
|
|
69
|
-
internal fun StackScreen(
|
|
70
|
-
content: @Composable () -> Unit,
|
|
71
|
-
navigationOptions: NavigationOptions? = null,
|
|
72
|
-
id: Int = -1,
|
|
73
|
-
bottomTabIndex: Int = -1,
|
|
74
|
-
) {
|
|
75
|
-
val navigator = LocalNavigator.current
|
|
76
|
-
val statusBar = AppStatusBar.current
|
|
77
|
-
val density = LocalDensity.current
|
|
78
|
-
val navigation = remember { Navigation(id = id, bottomTabIndex = bottomTabIndex, initOptions = navigationOptions) }
|
|
79
|
-
|
|
80
|
-
val options by navigation.currentOptions
|
|
81
|
-
|
|
82
|
-
val limit = with(density) {
|
|
83
|
-
(statusBar).toPx() + HEADER_HEIGHT
|
|
84
|
-
}.toInt()
|
|
85
|
-
|
|
86
|
-
val (scrollState, scrollInProcess) = if (options.scrollData.scrollState is LazyListState)
|
|
87
|
-
(options.scrollData.scrollState as? LazyListState ?: rememberLazyListState()).proxyScrollState(limit, 15)
|
|
88
|
-
else
|
|
89
|
-
(options.scrollData.scrollState as? ScrollState ?: rememberScrollState()).proxyLimitedScrollState(limit, 15)
|
|
90
|
-
|
|
91
|
-
val footerHeightPx = remember { mutableIntStateOf(0) }
|
|
92
|
-
val headerRightWidthPx = remember { mutableIntStateOf(0) }
|
|
93
|
-
|
|
94
|
-
BackHandler(true) { navigator.pop() }
|
|
95
|
-
|
|
96
|
-
CompositionLocalProvider(
|
|
97
|
-
StackScreenScrollableState provides options.scrollData.scrollState,
|
|
98
|
-
LocalNavigation provides navigation,
|
|
99
|
-
LocalScrollState provides scrollState,
|
|
100
|
-
LocalOptions provides options,
|
|
101
|
-
LocalFooterHeightPx provides footerHeightPx,
|
|
102
|
-
LocalHeaderRightWidthPx provides headerRightWidthPx
|
|
103
|
-
) {
|
|
104
|
-
Box(Modifier
|
|
105
|
-
.fillMaxSize()
|
|
106
|
-
.background(options.backgroundColor ?: AppTheme.current.colors.background.default)
|
|
107
|
-
.conditional(options.keyboardOptions.keyboardShouldPersistTaps) {
|
|
108
|
-
hideKeyboardOnTap()
|
|
109
|
-
}
|
|
110
|
-
.conditional(options.keyboardOptions.useAvoidKeyboard && getAndroidBuildVersion() > 29) {
|
|
111
|
-
imePadding()
|
|
112
|
-
}
|
|
113
|
-
) {
|
|
114
|
-
Box(Modifier.zIndex(1f)) {
|
|
115
|
-
HeaderBackground()
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
Box(Modifier.zIndex(5f)) {
|
|
119
|
-
Header()
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
Column (Modifier.zIndex(6f)) {
|
|
123
|
-
SearchAnimated(isScrollInProgress = scrollInProcess)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
Column(Modifier.zIndex(2f).fillMaxSize()) {
|
|
127
|
-
MainContent(content = content)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
Box(Modifier.zIndex(4f).align(Alignment.BottomCenter)) {
|
|
131
|
-
FooterContent()
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
Box(Modifier.zIndex(7f)){
|
|
135
|
-
FloatingContent()
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
OverplayView(bottomTabIndex = bottomTabIndex, id = id)
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
@Composable
|
|
144
|
-
fun FloatingContent(){
|
|
145
|
-
val options = LocalOptions.current
|
|
146
|
-
val density = LocalDensity.current
|
|
147
|
-
val footerHeightPx = LocalFooterHeightPx.current
|
|
148
|
-
val scrollState = LocalScrollState.current
|
|
149
|
-
|
|
150
|
-
val fabProps = options.floatingButtonProps ?: return
|
|
151
|
-
val bottomPadding = fabProps.bottom ?:
|
|
152
|
-
(Spacing.M + if (options.footerComponent != null) with(density){ footerHeightPx.intValue.toDp() } else 0.dp)
|
|
153
|
-
|
|
154
|
-
FloatingButton(
|
|
155
|
-
scrollPosition = fabProps.scrollState?.value ?: scrollState.value,
|
|
156
|
-
onClick = fabProps.onClick,
|
|
157
|
-
containerColor = AppTheme.current.colors.primary,
|
|
158
|
-
bottom = bottomPadding,
|
|
159
|
-
icon = fabProps.icon,
|
|
160
|
-
iconColor = fabProps.iconColor,
|
|
161
|
-
text = fabProps.label,
|
|
162
|
-
size = fabProps.size,
|
|
163
|
-
position = fabProps.position ?: FABPosition.END,
|
|
164
|
-
)
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
@Composable
|
|
168
|
-
fun ColumnScope.MainContent(content: @Composable ()-> Unit){
|
|
169
|
-
val options = LocalOptions.current
|
|
170
|
-
val inputSearchType = getInputSearchType(options)
|
|
171
|
-
val density = LocalDensity.current
|
|
172
|
-
val scrollState = LocalScrollState.current
|
|
173
|
-
|
|
174
|
-
Spacer(Modifier.height(if (options.headerType is HeaderType.DefaultOrExtended) AppStatusBar.current + HEADER_HEIGHT.dp else 0.dp))
|
|
175
|
-
if (inputSearchType == InputSearchType.Animated){
|
|
176
|
-
val scrollDp = with(density) { scrollState.value.toDp() }
|
|
177
|
-
|
|
178
|
-
val animatedTopPadding by animateDpAsState(
|
|
179
|
-
targetValue = (HEADER_HEIGHT.dp - scrollDp).coerceIn(0.dp, HEADER_HEIGHT.dp),
|
|
180
|
-
label = "AnimatedTopPadding"
|
|
181
|
-
)
|
|
182
|
-
Spacer(Modifier.height(animatedTopPadding))
|
|
183
|
-
}
|
|
184
|
-
Column (Modifier
|
|
185
|
-
.fillMaxWidth()
|
|
186
|
-
.weight(1f)
|
|
187
|
-
.conditional(options.scrollData.scrollable && options.scrollData.scrollState is ScrollState) {
|
|
188
|
-
verticalScroll(scrollState)
|
|
189
|
-
}
|
|
190
|
-
) {
|
|
191
|
-
ScreenContent(content = content)
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (options.footerComponent != null){
|
|
195
|
-
val footerHeight = LocalFooterHeightPx.current
|
|
196
|
-
val footerHeightDp = with(density) { footerHeight.value.toDp() }
|
|
197
|
-
Spacer(Modifier.height(footerHeightDp))
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
@Composable
|
|
202
|
-
fun ScreenContent(content: @Composable () -> Unit){
|
|
203
|
-
val scrollState = LocalScrollState.current
|
|
204
|
-
val options = LocalOptions.current
|
|
205
|
-
|
|
206
|
-
if (options.headerType is HeaderType.Animated){
|
|
207
|
-
val animatedHeader = options.headerType
|
|
208
|
-
Box {
|
|
209
|
-
Box(Modifier.fillMaxWidth().aspectRatio(animatedHeader.aspectRatio.value)){
|
|
210
|
-
animatedHeader.composable.invoke(scrollState.value)
|
|
211
|
-
}
|
|
212
|
-
Box(Modifier.offset(x = 0.dp, y = AppStatusBar.current + HEADER_HEIGHT.dp + animatedHeader.layoutOffSet)){
|
|
213
|
-
content()
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
} else {
|
|
217
|
-
content()
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
@Composable
|
|
222
|
-
fun FooterContent(){
|
|
223
|
-
val options = LocalOptions.current
|
|
224
|
-
if (options.footerComponent != null){
|
|
225
|
-
val isKeyboardVisible = isKeyboardVisible()
|
|
226
|
-
val bottomPadding = min(AppNavigationBar.current, if (isKeyboardVisible) 0.dp else 21.dp)
|
|
227
|
-
Footer(footerComponent = options.footerComponent, bottomPadding = bottomPadding)
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
@Composable
|
|
232
|
-
fun OverplayView(bottomTabIndex: Int, id: Int){
|
|
233
|
-
val overplayType = OverplayComponentRegistry.getOverplayType()
|
|
234
|
-
|
|
235
|
-
if (overplayType != null) {
|
|
236
|
-
Box(Modifier.zIndex(if (overplayType == OverplayComponentType.SNACK_BAR) 3f else 8f).fillMaxSize()){
|
|
237
|
-
if (bottomTabIndex != -1) return@Box
|
|
238
|
-
if (OverplayComponentRegistry.currentRootId() != id) return@Box
|
|
239
|
-
OverplayComponentRegistry.OverlayComponent()
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
@Composable
|
|
245
|
-
fun Footer(footerComponent: @Composable (() -> Unit)?, bottomPadding: Dp) {
|
|
246
|
-
if (footerComponent == null) return
|
|
247
|
-
|
|
248
|
-
val footerHeightPx = LocalFooterHeightPx.current
|
|
249
|
-
|
|
250
|
-
val shadowBrush = remember {
|
|
251
|
-
Brush.verticalGradient(
|
|
252
|
-
colors = listOf(Color.Transparent, Color.Black.copy(alpha = 0.05f))
|
|
253
|
-
)
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
Box(Modifier.onGloballyPositioned {
|
|
257
|
-
if (footerHeightPx.intValue != it.size.height) footerHeightPx.intValue = it.size.height
|
|
258
|
-
}) {
|
|
259
|
-
Box(Modifier
|
|
260
|
-
.fillMaxWidth()
|
|
261
|
-
.background(AppTheme.current.colors.background.surface)
|
|
262
|
-
.padding(top = Spacing.S, start = Spacing.M, end = Spacing.M, bottom = bottomPadding + Spacing.S)
|
|
263
|
-
){
|
|
264
|
-
footerComponent.invoke()
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
Box(modifier = Modifier
|
|
268
|
-
.fillMaxWidth()
|
|
269
|
-
.height(6.dp)
|
|
270
|
-
.offset(x = 0.dp, y = (-6).dp)
|
|
271
|
-
.background(shadowBrush)
|
|
272
|
-
)
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
data class InputSearchLayoutParams(
|
|
277
|
-
val topPadding: Dp,
|
|
278
|
-
val startPadding: Dp,
|
|
279
|
-
val endPadding: Dp
|
|
280
|
-
)
|
|
281
|
-
@Composable
|
|
282
|
-
fun SearchAnimated(
|
|
283
|
-
isScrollInProgress: Boolean
|
|
284
|
-
) {
|
|
285
|
-
val scrollState = LocalScrollState.current
|
|
286
|
-
val options = LocalOptions.current
|
|
287
|
-
val navigator = LocalNavigator.current
|
|
288
|
-
val density = LocalDensity.current
|
|
289
|
-
|
|
290
|
-
val scrollDp = with(density) { scrollState.value.toDp() }
|
|
291
|
-
|
|
292
|
-
val inputSearchType = getInputSearchType(options)
|
|
293
|
-
val headerRightWidthPx = LocalHeaderRightWidthPx.current
|
|
294
|
-
val headerRightWidthDp = with(density) { headerRightWidthPx.intValue.toDp() }
|
|
295
|
-
|
|
296
|
-
if (inputSearchType == InputSearchType.None) return
|
|
297
|
-
val inputSearchProps = (options.headerType as? HeaderType.DefaultOrExtended)?.inputSearchProps ?: return
|
|
298
|
-
|
|
299
|
-
val minTopPadding = AppStatusBar.current
|
|
300
|
-
val maxTopPadding = AppStatusBar.current + HEADER_HEIGHT.dp
|
|
301
|
-
val minStartPadding = Spacing.M
|
|
302
|
-
val maxStartPadding = if (options.hiddenBack) Spacing.M else 52.dp
|
|
303
|
-
val minEndPadding = Spacing.M
|
|
304
|
-
val maxEndPadding = headerRightWidthDp + if (options.headerRight is HeaderRight.None) Spacing.M else Spacing.M * 2
|
|
305
|
-
|
|
306
|
-
val targetColor = lerp(
|
|
307
|
-
AppTheme.current.colors.background.surface,
|
|
308
|
-
AppTheme.current.colors.background.default,
|
|
309
|
-
(scrollDp / minTopPadding).coerceIn(0f, 1f)
|
|
310
|
-
)
|
|
311
|
-
val animatedColor by animateColorAsState(targetValue = targetColor, label = "BackgroundColor")
|
|
312
|
-
|
|
313
|
-
val layoutParams = when (inputSearchType) {
|
|
314
|
-
InputSearchType.Header -> {
|
|
315
|
-
InputSearchLayoutParams(
|
|
316
|
-
topPadding = minTopPadding,
|
|
317
|
-
startPadding = maxStartPadding,
|
|
318
|
-
endPadding = maxEndPadding
|
|
319
|
-
)
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
InputSearchType.Animated -> {
|
|
323
|
-
val animatedTopPadding by animateDpAsState(
|
|
324
|
-
targetValue = (maxTopPadding - scrollDp).coerceIn(minTopPadding, maxTopPadding),
|
|
325
|
-
label = "AnimatedTopPadding"
|
|
326
|
-
)
|
|
327
|
-
|
|
328
|
-
val animatedStartPadding by animateDpAsState(
|
|
329
|
-
targetValue = (minStartPadding + scrollDp).coerceIn(minStartPadding, maxStartPadding),
|
|
330
|
-
label = "AnimatedStartPadding"
|
|
331
|
-
)
|
|
332
|
-
|
|
333
|
-
val animatedEndPadding by animateDpAsState(
|
|
334
|
-
targetValue = (minEndPadding + scrollDp).coerceIn(minEndPadding, maxEndPadding),
|
|
335
|
-
label = "AnimatedEndPadding"
|
|
336
|
-
)
|
|
337
|
-
|
|
338
|
-
val maxPadding = remember(maxTopPadding, maxStartPadding, maxEndPadding) {
|
|
339
|
-
maxOf(maxEndPadding, maxStartPadding, maxTopPadding)
|
|
340
|
-
}
|
|
341
|
-
val snapScrollValue = with(density) { maxPadding.toPx().toInt() }
|
|
342
|
-
|
|
343
|
-
LaunchedEffect(isScrollInProgress && scrollState.value != 0 && scrollState.value != snapScrollValue) {
|
|
344
|
-
if (scrollDp < maxPadding) {
|
|
345
|
-
if (!isScrollInProgress) {
|
|
346
|
-
val midpoint = (maxTopPadding - minTopPadding) / 2
|
|
347
|
-
|
|
348
|
-
if (scrollDp < midpoint) {
|
|
349
|
-
scrollState.animateScrollTo(0)
|
|
350
|
-
} else {
|
|
351
|
-
scrollState.animateScrollTo(snapScrollValue)
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
InputSearchLayoutParams(
|
|
358
|
-
topPadding = animatedTopPadding,
|
|
359
|
-
startPadding = animatedStartPadding,
|
|
360
|
-
endPadding = animatedEndPadding
|
|
361
|
-
)
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
InputSearchType.None -> return
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
Spacer(Modifier.height(layoutParams.topPadding))
|
|
368
|
-
Box(
|
|
369
|
-
modifier = Modifier
|
|
370
|
-
.height(HEADER_HEIGHT.dp)
|
|
371
|
-
.fillMaxWidth()
|
|
372
|
-
.padding(
|
|
373
|
-
start = layoutParams.startPadding,
|
|
374
|
-
end = layoutParams.endPadding
|
|
375
|
-
),
|
|
376
|
-
contentAlignment = Alignment.Center
|
|
377
|
-
) {
|
|
378
|
-
InputSearch(
|
|
379
|
-
inputSearchProps = inputSearchProps.copy(
|
|
380
|
-
backgroundColor = animatedColor,
|
|
381
|
-
onPressButtonText = {
|
|
382
|
-
navigator.pop()
|
|
383
|
-
}
|
|
384
|
-
)
|
|
385
|
-
)
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
@Composable
|
|
390
|
-
internal fun isKeyboardVisible(): Boolean {
|
|
391
|
-
val ime = WindowInsets.ime
|
|
392
|
-
val density = LocalDensity.current
|
|
393
|
-
return ime.getBottom(density) > 0
|
|
394
|
-
}
|
|
395
|
-
private fun quantize(value: Int, stepPx: Int): Int {
|
|
396
|
-
if (stepPx <= 1) return value
|
|
397
|
-
return (value / stepPx) * stepPx
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
@Composable
|
|
401
|
-
fun LazyListState.proxyScrollState(
|
|
402
|
-
thresholdPx: Int = 200,
|
|
403
|
-
stepPx: Int = 10
|
|
404
|
-
): Pair<ScrollState, Boolean> {
|
|
405
|
-
val scrollState = rememberScrollState()
|
|
406
|
-
val locked = remember { mutableStateOf(false) }
|
|
407
|
-
|
|
408
|
-
LaunchedEffect(this, scrollState, thresholdPx, stepPx) {
|
|
409
|
-
snapshotFlow { firstVisibleItemIndex to firstVisibleItemScrollOffset }
|
|
410
|
-
.collect { (index, offset) ->
|
|
411
|
-
val rawTarget = if (index == 0) offset else SCROLLED_PAST_FIRST_ITEM_THRESHOLD
|
|
412
|
-
val shouldLock = rawTarget > thresholdPx
|
|
413
|
-
if (locked.value != shouldLock) locked.value = shouldLock
|
|
414
|
-
|
|
415
|
-
val desired = if (shouldLock) thresholdPx else quantize(rawTarget, stepPx)
|
|
416
|
-
|
|
417
|
-
if (scrollState.value != desired) {
|
|
418
|
-
scrollState.scrollTo(desired.coerceAtLeast(0))
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
val inProgress = remember { mutableStateOf(false) }
|
|
424
|
-
LaunchedEffect(this, thresholdPx) {
|
|
425
|
-
snapshotFlow { isScrollInProgress }
|
|
426
|
-
.collect { progressing ->
|
|
427
|
-
inProgress.value = if (locked.value) false else progressing
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
return scrollState to inProgress.value
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
@Composable
|
|
435
|
-
fun ScrollState.proxyLimitedScrollState(
|
|
436
|
-
thresholdPx: Int = 200,
|
|
437
|
-
stepPx: Int = 10
|
|
438
|
-
): Pair<ScrollState, Boolean> {
|
|
439
|
-
val proxy = rememberScrollState()
|
|
440
|
-
val locked = remember { mutableStateOf(false) }
|
|
441
|
-
|
|
442
|
-
LaunchedEffect(this, proxy, thresholdPx, stepPx) {
|
|
443
|
-
snapshotFlow { value }
|
|
444
|
-
.collect { rawTarget ->
|
|
445
|
-
val shouldLock = rawTarget > thresholdPx
|
|
446
|
-
if (locked.value != shouldLock) locked.value = shouldLock
|
|
447
|
-
|
|
448
|
-
val desired = if (shouldLock) thresholdPx else quantize(rawTarget, stepPx)
|
|
449
|
-
|
|
450
|
-
if (proxy.value != desired) {
|
|
451
|
-
proxy.scrollTo(desired.coerceAtLeast(0))
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
val inProgress = remember { mutableStateOf(false) }
|
|
457
|
-
LaunchedEffect(this, thresholdPx) {
|
|
458
|
-
snapshotFlow { isScrollInProgress }
|
|
459
|
-
.collect { progressing ->
|
|
460
|
-
inProgress.value = if (locked.value) false else progressing
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
return proxy to inProgress.value
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
private const val SCROLLED_PAST_FIRST_ITEM_THRESHOLD = 10_000
|
|
468
|
-
|
|
469
|
-
internal val LocalScrollState = staticCompositionLocalOf<ScrollState> { error("No Scroll State provided") }
|
|
470
|
-
internal val LocalOptions = staticCompositionLocalOf<NavigationOptions> { error("No NavigationOptions provided") }
|
|
471
|
-
|
|
472
|
-
val StackScreenScrollableState = staticCompositionLocalOf<ScrollableState?> { null }
|
|
473
|
-
|
|
474
|
-
internal fun getInputSearchType(options: NavigationOptions): InputSearchType{
|
|
475
|
-
return when (val headerType = options.headerType) {
|
|
476
|
-
is HeaderType.DefaultOrExtended -> when {
|
|
477
|
-
headerType.inputSearchProps == null -> InputSearchType.None
|
|
478
|
-
headerType.useAnimated -> InputSearchType.Animated
|
|
479
|
-
else -> InputSearchType.Header
|
|
480
|
-
}
|
|
481
|
-
else -> InputSearchType.None
|
|
482
|
-
}
|
|
483
|
-
}
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
package vn.momo.kits.navigation.bottomtab
|
|
2
|
-
|
|
3
|
-
import androidx.compose.animation.core.tween
|
|
4
|
-
import androidx.compose.animation.fadeIn
|
|
5
|
-
import androidx.compose.animation.fadeOut
|
|
6
|
-
import androidx.compose.animation.scaleIn
|
|
7
|
-
import androidx.compose.animation.scaleOut
|
|
8
|
-
import androidx.compose.foundation.background
|
|
9
|
-
import androidx.compose.foundation.layout.Box
|
|
10
|
-
import androidx.compose.foundation.layout.Column
|
|
11
|
-
import androidx.compose.foundation.layout.Spacer
|
|
12
|
-
import androidx.compose.foundation.layout.fillMaxSize
|
|
13
|
-
import androidx.compose.foundation.layout.fillMaxWidth
|
|
14
|
-
import androidx.compose.foundation.layout.height
|
|
15
|
-
import androidx.compose.foundation.layout.padding
|
|
16
|
-
import androidx.compose.runtime.Composable
|
|
17
|
-
import androidx.compose.runtime.LaunchedEffect
|
|
18
|
-
import androidx.compose.ui.Alignment
|
|
19
|
-
import androidx.compose.ui.Modifier
|
|
20
|
-
import androidx.compose.ui.unit.dp
|
|
21
|
-
import androidx.compose.ui.unit.min
|
|
22
|
-
import androidx.navigation.compose.NavHost
|
|
23
|
-
import androidx.navigation.compose.composable
|
|
24
|
-
import androidx.navigation.compose.rememberNavController
|
|
25
|
-
import vn.momo.kits.const.AppNavigationBar
|
|
26
|
-
import vn.momo.kits.const.AppTheme
|
|
27
|
-
import vn.momo.kits.const.Spacing
|
|
28
|
-
import vn.momo.kits.navigation.LocalNavigation
|
|
29
|
-
import vn.momo.kits.navigation.LocalNavigator
|
|
30
|
-
import vn.momo.kits.navigation.NavigationOptions
|
|
31
|
-
import vn.momo.kits.navigation.StackScreen
|
|
32
|
-
import vn.momo.kits.navigation.component.HeaderType
|
|
33
|
-
import vn.momo.kits.platform.getScreenHeight
|
|
34
|
-
|
|
35
|
-
private var bottomTabOptionItems : MutableList<NavigationOptions?> = mutableListOf()
|
|
36
|
-
fun setBottomTabOption(index: Int, options: NavigationOptions){
|
|
37
|
-
if (index in bottomTabOptionItems.indices) {
|
|
38
|
-
bottomTabOptionItems[index] = options
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
fun getBottomTabOption(index: Int): NavigationOptions? {
|
|
42
|
-
return if (index in bottomTabOptionItems.indices) {
|
|
43
|
-
bottomTabOptionItems[index]
|
|
44
|
-
} else null
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
@Composable
|
|
48
|
-
fun BottomTab(
|
|
49
|
-
items: List<BottomTabItem>,
|
|
50
|
-
floatingButton: BottomTabFloatingButton? = null
|
|
51
|
-
) {
|
|
52
|
-
val navigation = LocalNavigation.current
|
|
53
|
-
val navigator = LocalNavigator.current
|
|
54
|
-
val navController = rememberNavController()
|
|
55
|
-
|
|
56
|
-
bottomTabOptionItems = items.mapIndexed { index, item ->
|
|
57
|
-
val options = item.options ?: NavigationOptions()
|
|
58
|
-
options.copy(
|
|
59
|
-
onBackHandler = {
|
|
60
|
-
if (index != 0) {
|
|
61
|
-
navController.popBackStack()
|
|
62
|
-
} else {
|
|
63
|
-
navigator.pop()
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
)
|
|
67
|
-
}.toMutableList()
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
LaunchedEffect(Unit){
|
|
71
|
-
navigation.setOptions(
|
|
72
|
-
headerType = HeaderType.None
|
|
73
|
-
)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
Box(modifier = Modifier.fillMaxWidth().height(getScreenHeight()), contentAlignment = Alignment.BottomCenter) {
|
|
77
|
-
Box(modifier = Modifier
|
|
78
|
-
.fillMaxSize()
|
|
79
|
-
.padding(bottom = BOTTOM_TAB_BAR_HEIGHT.dp + AppNavigationBar.current)
|
|
80
|
-
) {
|
|
81
|
-
NavHost(
|
|
82
|
-
navController = navController,
|
|
83
|
-
startDestination = "option0"
|
|
84
|
-
) {
|
|
85
|
-
items.forEachIndexed { index, item ->
|
|
86
|
-
composable(
|
|
87
|
-
route = "option$index",
|
|
88
|
-
enterTransition = {
|
|
89
|
-
fadeIn(animationSpec = tween(200)) +
|
|
90
|
-
scaleIn(
|
|
91
|
-
initialScale = 0.97f,
|
|
92
|
-
animationSpec = tween(200)
|
|
93
|
-
)
|
|
94
|
-
},
|
|
95
|
-
exitTransition = {
|
|
96
|
-
fadeOut(animationSpec = tween(200)) +
|
|
97
|
-
scaleOut(
|
|
98
|
-
targetScale = 0.97f,
|
|
99
|
-
animationSpec = tween(200)
|
|
100
|
-
)
|
|
101
|
-
},
|
|
102
|
-
popEnterTransition = {
|
|
103
|
-
fadeIn(animationSpec = tween(200)) +
|
|
104
|
-
scaleIn(
|
|
105
|
-
initialScale = 0.97f,
|
|
106
|
-
animationSpec = tween(200)
|
|
107
|
-
)
|
|
108
|
-
},
|
|
109
|
-
popExitTransition = {
|
|
110
|
-
fadeOut(animationSpec = tween(200)) +
|
|
111
|
-
scaleOut(
|
|
112
|
-
targetScale = 0.97f,
|
|
113
|
-
animationSpec = tween(200)
|
|
114
|
-
)
|
|
115
|
-
}
|
|
116
|
-
) {
|
|
117
|
-
val option = getBottomTabOption(index)?.copy(
|
|
118
|
-
onBackHandler = {
|
|
119
|
-
if (index != 0) {
|
|
120
|
-
navController.popBackStack()
|
|
121
|
-
} else {
|
|
122
|
-
navigator.pop()
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
StackScreen(
|
|
128
|
-
content = item.screen,
|
|
129
|
-
navigationOptions = option,
|
|
130
|
-
bottomTabIndex = index
|
|
131
|
-
)
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
Column {
|
|
137
|
-
BottomTabBar(
|
|
138
|
-
items = items,
|
|
139
|
-
floatingButton = floatingButton,
|
|
140
|
-
navController = navController,
|
|
141
|
-
onTabSelected = {
|
|
142
|
-
val currentRoute = navController.currentBackStackEntry?.destination?.route
|
|
143
|
-
val targetRoute = "option$it"
|
|
144
|
-
if (currentRoute != targetRoute){
|
|
145
|
-
navController.navigate(targetRoute)
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
)
|
|
149
|
-
Spacer(modifier = Modifier.fillMaxWidth().height(min(AppNavigationBar.current, 21.dp) + Spacing.S).background(AppTheme.current.colors.background.surface))
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
data class BottomTabItem(
|
|
155
|
-
val name: String,
|
|
156
|
-
val label: String,
|
|
157
|
-
val icon: String,
|
|
158
|
-
val showDot: Boolean = false,
|
|
159
|
-
val badgeLabel: String? = null,
|
|
160
|
-
val screen: @Composable () -> Unit,
|
|
161
|
-
val options: NavigationOptions? = null,
|
|
162
|
-
val initialParams: Any? = null
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
data class BottomTabFloatingButton(
|
|
166
|
-
val icon: String,
|
|
167
|
-
val label: String,
|
|
168
|
-
val onPress: () -> Unit,
|
|
169
|
-
)
|