@momo-kits/native-kits 0.156.4 → 0.156.5-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 (117) hide show
  1. package/build.gradle.kts +11 -0
  2. package/compose/build.gradle.kts +180 -0
  3. package/compose/build.gradle.kts.backup +180 -0
  4. package/compose/compose.podspec +54 -0
  5. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +113 -0
  6. package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
  7. package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
  8. package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
  9. package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
  10. package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
  11. package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
  12. package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
  13. package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
  14. package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
  15. package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
  16. package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
  17. package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
  18. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
  19. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +57 -0
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +95 -0
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +201 -0
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +222 -0
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +48 -0
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +86 -0
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +76 -0
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +76 -0
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +305 -0
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +715 -0
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +121 -0
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +405 -0
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +83 -0
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +32 -0
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +340 -0
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +348 -0
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +94 -0
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +136 -0
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +543 -0
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +23 -0
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +76 -0
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +148 -0
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +188 -0
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +116 -0
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +447 -0
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +172 -0
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +244 -0
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +231 -0
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +234 -0
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +254 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +241 -0
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +364 -0
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +56 -0
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +41 -0
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +92 -0
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +40 -0
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +352 -0
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +103 -0
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +70 -0
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +17 -0
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +96 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +96 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +92 -0
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +130 -0
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +214 -0
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +177 -0
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +205 -0
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +239 -0
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +191 -0
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +306 -0
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +13 -0
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +189 -0
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +285 -0
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +2 -0
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +35 -0
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +2 -0
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +59 -0
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +68 -0
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +14 -0
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +50 -0
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +239 -0
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +119 -0
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +98 -0
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +163 -0
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +331 -0
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +497 -0
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +162 -0
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +226 -0
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +187 -0
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +274 -0
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +80 -0
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +306 -0
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +31 -0
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +370 -0
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +132 -0
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +42 -0
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +62 -0
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +88 -0
  106. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +152 -0
  107. package/gradle/libs.versions.toml +57 -0
  108. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  109. package/gradle/wrapper/gradle-wrapper.properties +8 -0
  110. package/gradle.properties +26 -0
  111. package/gradlew +252 -0
  112. package/gradlew.bat +94 -0
  113. package/ios/Button/Button.swift +6 -7
  114. package/ios/Input/Input.swift +73 -34
  115. package/ios/Input/InputPhoneNumber.swift +16 -15
  116. package/package.json +1 -1
  117. package/settings.gradle.kts +52 -0
@@ -0,0 +1,274 @@
1
+ package vn.momo.kits.navigation.component
2
+
3
+ import androidx.compose.animation.core.animateFloatAsState
4
+ import androidx.compose.foundation.background
5
+ import androidx.compose.foundation.border
6
+ import androidx.compose.foundation.layout.Arrangement
7
+ import androidx.compose.foundation.layout.Box
8
+ import androidx.compose.foundation.layout.Row
9
+ import androidx.compose.foundation.layout.RowScope
10
+ import androidx.compose.foundation.layout.fillMaxWidth
11
+ import androidx.compose.foundation.layout.height
12
+ import androidx.compose.foundation.layout.offset
13
+ import androidx.compose.foundation.layout.padding
14
+ import androidx.compose.foundation.layout.size
15
+ import androidx.compose.foundation.shape.RoundedCornerShape
16
+ import androidx.compose.runtime.Composable
17
+ import androidx.compose.runtime.getValue
18
+ import androidx.compose.ui.Alignment
19
+ import androidx.compose.ui.Modifier
20
+ import androidx.compose.ui.graphics.Brush
21
+ import androidx.compose.ui.graphics.Color
22
+ import androidx.compose.ui.layout.onGloballyPositioned
23
+ import androidx.compose.ui.platform.LocalDensity
24
+ import androidx.compose.ui.unit.Dp
25
+ import androidx.compose.ui.unit.dp
26
+ import vn.momo.kits.components.Icon
27
+ import vn.momo.kits.components.InputSearchProps
28
+ import vn.momo.kits.const.AppStatusBar
29
+ import vn.momo.kits.const.AppTheme
30
+ import vn.momo.kits.const.Colors
31
+ import vn.momo.kits.components.PopupNotifyProps
32
+ import vn.momo.kits.application.IsShowBaseLineDebug
33
+ import vn.momo.kits.const.Spacing
34
+ import vn.momo.kits.modifier.activeOpacityClickable
35
+ import vn.momo.kits.modifier.conditional
36
+ import vn.momo.kits.navigation.LocalHeaderRightWidthPx
37
+ import vn.momo.kits.navigation.LocalNavigator
38
+ import vn.momo.kits.navigation.LocalOptions
39
+ import vn.momo.kits.navigation.LocalScrollState
40
+ import vn.momo.kits.navigation.getInputSearchType
41
+
42
+ const val HEADER_HEIGHT = 52
43
+ enum class InputSearchType { None, Header, Animated }
44
+
45
+ @Composable
46
+ fun Header(onBackHandler: (() -> Unit)? = null) {
47
+ val options = LocalOptions.current
48
+ val navigator = LocalNavigator.current
49
+ val scrollState = LocalScrollState.current
50
+ val headerRightWidthPx = LocalHeaderRightWidthPx.current
51
+ val inputSearchType = getInputSearchType(options)
52
+
53
+ val opacityHeight = when (val header = options.headerType) {
54
+ is HeaderType.Animated -> with(LocalDensity.current) { header.layoutOffSet.roundToPx() }
55
+ else -> HEADER_HEIGHT
56
+ }
57
+ val opacity by animateFloatAsState(
58
+ targetValue = if (options.headerType is HeaderType.Transparent) {
59
+ 0f
60
+ } else {
61
+ (scrollState.value / opacityHeight.toFloat())
62
+ .coerceIn(0f, 1f)
63
+ }
64
+ )
65
+
66
+ val headerColor = getHeaderColor(options.headerType, opacity, options.tintColor, Colors.black_17)
67
+
68
+ val animatedAlpha by animateFloatAsState(targetValue = opacity, label = "BackgroundAlpha")
69
+
70
+ val background = if (options.headerType is HeaderType.Transparent)
71
+ options.backgroundColor ?: AppTheme.current.colors.background.surface
72
+ else
73
+ AppTheme.current.colors.background.surface
74
+
75
+ if (options.headerType == HeaderType.None) return
76
+ Box(
77
+ Modifier.height(AppStatusBar.current + HEADER_HEIGHT.dp)
78
+ .fillMaxWidth()
79
+ .background(background.copy(alpha = animatedAlpha))
80
+ .conditional(IsShowBaseLineDebug) {
81
+ border(1.dp, Colors.blue_03)
82
+ },
83
+ contentAlignment = Alignment.BottomCenter
84
+ ) {
85
+ Row(
86
+ modifier = Modifier.height(HEADER_HEIGHT.dp)
87
+ .fillMaxWidth()
88
+ .padding(horizontal = Spacing.M),
89
+ verticalAlignment = Alignment.CenterVertically,
90
+ horizontalArrangement = Arrangement.SpaceBetween
91
+ ) {
92
+ if(!options.hiddenBack) {
93
+ BackButton(
94
+ borderColor = headerColor.borderColor,
95
+ backgroundButton = headerColor.backgroundButton,
96
+ tintIconColor = headerColor.tintIconColor,
97
+ onBackHandler = {
98
+ onBackHandler?.invoke() ?: navigator.onBackSafe { }
99
+ }
100
+ )
101
+ }
102
+
103
+ HeaderContent(
104
+ options.headerTitle,
105
+ headerColor.tintIconColor
106
+ .copy(alpha = if (inputSearchType == InputSearchType.Animated) 1f - animatedAlpha else 1f)
107
+ )
108
+ Box(Modifier.onGloballyPositioned {
109
+ if(headerRightWidthPx.intValue != it.size.width) headerRightWidthPx.intValue = it.size.width
110
+ }){
111
+ HeaderRight(options.headerRight, options.tintColor, headerColor)
112
+ }
113
+ }
114
+ VerticalShadow(opacity)
115
+ }
116
+ }
117
+
118
+ @Composable
119
+ fun BackButton(borderColor: Color, backgroundButton: Color, tintIconColor: Color, onBackHandler: () -> Unit){
120
+ Box(
121
+ modifier = Modifier
122
+ .size(28.dp)
123
+ .background(backgroundButton, RoundedCornerShape(100))
124
+ .border(width = 0.2.dp, color = borderColor, shape = RoundedCornerShape(100))
125
+ .activeOpacityClickable(onClick = onBackHandler)
126
+ .padding(Spacing.XS),
127
+ contentAlignment = Alignment.Center
128
+ ) {
129
+ Icon(
130
+ source = "arrow-back",
131
+ color = tintIconColor,
132
+ size = 20.dp,
133
+ )
134
+ }
135
+ }
136
+
137
+ @Composable
138
+ fun RowScope.HeaderContent(headerTitle: HeaderTitle, tintIconColor: Color){
139
+ Box(
140
+ Modifier.weight(1f).padding(horizontal = Spacing.M)
141
+ ) {
142
+ when (headerTitle){
143
+ is HeaderTitle.Default -> {
144
+ HeaderTitle(
145
+ title = headerTitle.title,
146
+ color = tintIconColor
147
+ )
148
+ }
149
+ is HeaderTitle.Journey -> {}
150
+ is HeaderTitle.Location -> {}
151
+ is HeaderTitle.User -> {
152
+ HeaderUser(
153
+ data = headerTitle
154
+ )
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ @Composable
161
+ fun VerticalShadow(opacity: Float){
162
+ if(opacity == 1f){
163
+ Box(modifier = Modifier
164
+ .fillMaxWidth()
165
+ .height(6.dp)
166
+ .offset(x = 0.dp, y = 6.dp)
167
+ .background(
168
+ brush = Brush.verticalGradient(
169
+ colors = listOf(Color.Black.copy(alpha = 0.05f), Color.Transparent)
170
+ )
171
+ )
172
+ )
173
+ }
174
+ }
175
+
176
+ sealed class HeaderTitle {
177
+ class Default(val title: String) : HeaderTitle()
178
+
179
+ class User(
180
+ val title: String,
181
+ val subTitle: String? = null,
182
+ val image: List<String>? = null,
183
+ val dotColor: Color? = null,
184
+ val tintColor: Color? = null,
185
+ val onPress: (() -> Unit)? = null,
186
+ val icons: List<String> = emptyList(),
187
+ val isLoading: Boolean = false
188
+ ) : HeaderTitle()
189
+
190
+ class Location(
191
+ val description: String? = null,
192
+ val location: String,
193
+ val tintColor: String? = null,
194
+ val onPress: (() -> Unit)? = null,
195
+ val isLoading: Boolean = false
196
+ ) : HeaderTitle()
197
+
198
+ class Journey(
199
+ val start: String,
200
+ val end: String? = null,
201
+ val description: String? = null,
202
+ val icon: String,
203
+ val iconColor: String? = null,
204
+ val tintColor: String? = null,
205
+ val onPress: (() -> Unit)? = null,
206
+ val isLoading: Boolean = false
207
+ ) : HeaderTitle()
208
+ }
209
+
210
+ sealed class HeaderType {
211
+ interface DefaultOrExtended {
212
+ val useAnimated: Boolean
213
+ val inputSearchProps: InputSearchProps?
214
+ }
215
+
216
+ data class Default(
217
+ override val useAnimated: Boolean = false,
218
+ override val inputSearchProps: InputSearchProps? = null
219
+ ) : HeaderType(), DefaultOrExtended
220
+
221
+ data class Extended(
222
+ override val useAnimated: Boolean = false,
223
+ override val inputSearchProps: InputSearchProps? = null
224
+ ) : HeaderType(), DefaultOrExtended
225
+
226
+ data object None : HeaderType()
227
+
228
+ data class Animated(
229
+ val aspectRatio: AnimatedHeaderRatio = AnimatedHeaderRatio.RATIO_16_9,
230
+ val isSurface: Boolean = true,
231
+ val layoutOffSet: Dp = 56.dp,
232
+ val composable: @Composable (scrollState: Int) -> Unit = {}
233
+ ) : HeaderType()
234
+
235
+ data class Transparent(
236
+ val isFullScreenContent: Boolean = false
237
+ ) : HeaderType()
238
+ }
239
+
240
+ data class HeaderBackProps(
241
+ val preventBack: PopupNotifyProps? = null,
242
+ )
243
+
244
+ data class HeaderColor(val tintIconColor: Color, val backgroundButton: Color, val borderColor: Color)
245
+ fun getHeaderColor(headerType: HeaderType, opacity: Float, tintColor: Color?, defaultColor: Color): HeaderColor{
246
+ return if(headerType is HeaderType.Animated) {
247
+ if (opacity == 1f || !headerType.isSurface)
248
+ HeaderColor(
249
+ tintIconColor = Colors.black_17,
250
+ backgroundButton = Colors.black_01.copy(alpha = 0.6f),
251
+ borderColor = Colors.black_20.copy(alpha = 0.2f)
252
+ )
253
+ else
254
+ HeaderColor(
255
+ tintIconColor = Colors.black_01,
256
+ backgroundButton = Colors.black_20.copy(alpha = 0.6f),
257
+ borderColor = Color.Transparent
258
+ )
259
+ }
260
+ else {
261
+ if (tintColor == Colors.black_01)
262
+ HeaderColor(
263
+ tintIconColor = tintColor,
264
+ backgroundButton = Colors.black_20.copy(alpha = 0.6f),
265
+ borderColor = Colors.black_01.copy(alpha = 0.2f)
266
+ )
267
+ else
268
+ HeaderColor(
269
+ tintIconColor = tintColor ?: defaultColor,
270
+ backgroundButton = Colors.black_01.copy(alpha = 0.6f),
271
+ borderColor = Colors.black_20.copy(alpha = 0.2f)
272
+ )
273
+ }
274
+ }
@@ -0,0 +1,80 @@
1
+ package vn.momo.kits.navigation.component
2
+
3
+ import androidx.compose.animation.core.animateFloatAsState
4
+ import androidx.compose.foundation.background
5
+ import androidx.compose.foundation.layout.Box
6
+ import androidx.compose.foundation.layout.fillMaxSize
7
+ import androidx.compose.foundation.layout.fillMaxWidth
8
+ import androidx.compose.foundation.layout.height
9
+ import androidx.compose.runtime.Composable
10
+ import androidx.compose.runtime.getValue
11
+ import androidx.compose.ui.Modifier
12
+ import androidx.compose.ui.geometry.Offset
13
+ import androidx.compose.ui.graphics.Brush
14
+ import androidx.compose.ui.graphics.Color
15
+ import androidx.compose.ui.platform.LocalDensity
16
+ import androidx.compose.ui.unit.dp
17
+ import vn.momo.kits.components.Image
18
+ import vn.momo.kits.const.AppStatusBar
19
+ import vn.momo.kits.const.AppTheme
20
+ import vn.momo.kits.modifier.conditional
21
+ import vn.momo.kits.navigation.LocalOptions
22
+ import vn.momo.kits.navigation.LocalScrollState
23
+
24
+ enum class AnimatedHeaderRatio(val value: Float){
25
+ RATIO_16_9(16f / 9f),
26
+ RATIO_1_1(1f),
27
+ RATIO_3_2(3f / 2f)
28
+ }
29
+
30
+ @Composable
31
+ fun HeaderBackground() {
32
+ val density = LocalDensity.current
33
+ val theme = AppTheme.current
34
+ val options = LocalOptions.current
35
+ val scrollState = LocalScrollState.current
36
+ val backgroundColor = options.backgroundColor ?: AppTheme.current.colors.background.default
37
+
38
+ val minHeight = AppStatusBar.current + HEADER_HEIGHT.dp
39
+ val maxHeight = 154.dp
40
+ val opacity by animateFloatAsState(
41
+ targetValue = (1 - (scrollState.value * 1f / HEADER_HEIGHT * 1f)).coerceIn(0f, 1f),
42
+ )
43
+
44
+ val color = if (opacity == 0f) backgroundColor else Color(0xFFFDCADE)
45
+ val height = when (options.headerType) {
46
+ is HeaderType.Default -> minHeight
47
+ is HeaderType.Extended -> {
48
+ if (AppTheme.current.isHeaderImage()){
49
+ if (opacity == 0f) minHeight else maxHeight
50
+ } else {
51
+ maxHeight
52
+ }
53
+ }
54
+ else -> 0.dp
55
+ }
56
+
57
+ Box(Modifier.fillMaxWidth().height(height).background(backgroundColor)) {
58
+ Box(Modifier
59
+ .height(maxHeight)
60
+ .fillMaxWidth()
61
+ .conditional(!theme.isHeaderImage()) {
62
+ background(Brush.linearGradient(
63
+ colors = listOf(
64
+ color,
65
+ backgroundColor
66
+ ),
67
+ start = Offset(0f, 0f),
68
+ end = Offset(0f, with(density) { maxHeight.toPx() })
69
+ )
70
+ )
71
+ }
72
+ ){
73
+ if (!theme.isHeaderImage()) return@Box
74
+ Image(
75
+ source = theme.assets.headerBackground!!,
76
+ modifier = Modifier.fillMaxSize(),
77
+ )
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,306 @@
1
+ package vn.momo.kits.navigation.component
2
+
3
+ import androidx.compose.foundation.background
4
+ import androidx.compose.foundation.border
5
+ import androidx.compose.foundation.layout.Arrangement
6
+ import androidx.compose.foundation.layout.Box
7
+ import androidx.compose.foundation.layout.Row
8
+ import androidx.compose.foundation.layout.height
9
+ import androidx.compose.foundation.layout.offset
10
+ import androidx.compose.foundation.layout.padding
11
+ import androidx.compose.foundation.layout.size
12
+ import androidx.compose.foundation.layout.width
13
+ import androidx.compose.foundation.shape.CircleShape
14
+ import androidx.compose.foundation.shape.RoundedCornerShape
15
+ import androidx.compose.runtime.Composable
16
+ import androidx.compose.runtime.LaunchedEffect
17
+ import androidx.compose.runtime.derivedStateOf
18
+ import androidx.compose.runtime.getValue
19
+ import androidx.compose.runtime.mutableStateOf
20
+ import androidx.compose.runtime.remember
21
+ import androidx.compose.runtime.setValue
22
+ import androidx.compose.ui.Alignment
23
+ import androidx.compose.ui.Modifier
24
+ import androidx.compose.ui.draw.clip
25
+ import androidx.compose.ui.graphics.Color
26
+ import androidx.compose.ui.unit.dp
27
+ import vn.momo.kits.application.ApplicationContext
28
+ import vn.momo.kits.application.MiniAppContext
29
+ import vn.momo.kits.components.BadgeDot
30
+ import vn.momo.kits.components.DotSize
31
+ import vn.momo.kits.components.Icon
32
+ import vn.momo.kits.const.Colors
33
+ import vn.momo.kits.const.Spacing
34
+ import vn.momo.kits.modifier.activeOpacityClickable
35
+ import vn.momo.kits.navigation.LocalMaxApi
36
+
37
+ @Composable
38
+ fun HeaderRight(
39
+ headerRight: HeaderRight,
40
+ tintColor: Color? = null,
41
+ headerColor: HeaderColor
42
+ ) {
43
+ when (headerRight) {
44
+ is HeaderRight.None -> {}
45
+ is HeaderRight.Custom -> {
46
+ headerRight.content()
47
+ }
48
+ is HeaderRight.OnBoarding -> {}
49
+ is HeaderRight.Toolkit -> {
50
+ Toolkit(
51
+ headerRight = headerRight,
52
+ tintColor = tintColor,
53
+ headerColor = headerColor
54
+ )
55
+ }
56
+ }
57
+ }
58
+
59
+ @Composable
60
+ fun Toolkit(
61
+ headerRight: HeaderRight.Toolkit,
62
+ tintColor: Color? = null,
63
+ headerColor: HeaderColor
64
+ ) {
65
+ val api = LocalMaxApi.current
66
+ val context = ApplicationContext.current
67
+
68
+ var isFavorite by remember { mutableStateOf(false) }
69
+ val isLoading by remember { mutableStateOf(false) }
70
+
71
+ LaunchedEffect(context?.appCode) {
72
+ api?.isFavoriteApp(mapOf("code" to context?.appCode)) { callback ->
73
+ val response = callback?.get("response") as? Boolean
74
+ isFavorite = response == true
75
+ }
76
+ }
77
+
78
+ fun onPressShortcut() {
79
+ api?.onToolAction(mapOf("item" to mapOf("key" to "onFavorite"), "context" to MiniAppContext.toMap(context))) { callback ->
80
+ val response = callback?.get("response") as? Map<*, *>?
81
+ val success = response?.get("success") as? Boolean
82
+ if (success == true){
83
+ isFavorite = !isFavorite
84
+ }
85
+ }
86
+ }
87
+
88
+ fun onPressMore() {
89
+ api?.showTools(
90
+ mapOf(
91
+ "useSystemTools" to headerRight.useSystemTools,
92
+ "tools" to headerRight.tools.map { it.toMap() },
93
+ "context" to MiniAppContext.toMap(context)
94
+ )
95
+ ) { callback ->
96
+ val response = callback?.get("response") as? String
97
+ if (response != null) {
98
+ headerRight.toolCallback?.invoke(response)
99
+ }
100
+ }
101
+ }
102
+
103
+ val navButtonConfig = getNavigationButtonConfig(
104
+ headerRight,
105
+ ::onPressShortcut,
106
+ ::onPressMore
107
+ )
108
+
109
+ val showBadge = headerRight.tools.any { group ->
110
+ group.items.any { it.showBadge }
111
+ }
112
+
113
+ val isShowShortcut by remember(headerRight.useShortcut, headerRight.useMore, context) {
114
+ derivedStateOf {
115
+ (headerRight.useShortcut == true) &&
116
+ !(headerRight.useMore == true && context == null)
117
+ }
118
+ }
119
+
120
+ val icon by remember(navButtonConfig.icon, isFavorite) {
121
+ derivedStateOf {
122
+ navButtonConfig.icon.takeUnless { it == "star" }
123
+ ?: if (isFavorite) "pin_star_checked" else "pin_star"
124
+ }
125
+ }
126
+
127
+ Row(
128
+ verticalAlignment = Alignment.CenterVertically
129
+ ) {
130
+ if (isShowShortcut) {
131
+ NavigationButton(
132
+ disabled = isLoading,
133
+ icon = icon,
134
+ showBadge = showBadge,
135
+ onClick = navButtonConfig.onPress,
136
+ headerColor = headerColor
137
+ )
138
+ }
139
+
140
+ Row(
141
+ verticalAlignment = Alignment.CenterVertically,
142
+ horizontalArrangement = Arrangement.Center,
143
+ modifier = Modifier
144
+ .padding(start = Spacing.S)
145
+ .border(0.2.dp, headerColor.borderColor, shape = RoundedCornerShape(14.dp))
146
+ .height(28.dp)
147
+ .clip(shape = RoundedCornerShape(14.dp))
148
+ .background(headerColor.backgroundButton)
149
+ ) {
150
+ if (context != null) {
151
+ Icon(
152
+ source = "help_center",
153
+ size = 20.dp,
154
+ color = headerColor.tintIconColor,
155
+ modifier = Modifier.padding(4.dp).activeOpacityClickable {
156
+ api?.showHelpCenter(
157
+ mapOf(
158
+ "appId" to context.appId,
159
+ "code" to context.appCode,
160
+ "name" to context.appName,
161
+ "icon" to context.appIcon,
162
+ "description" to context.description
163
+ )
164
+ ) {}
165
+ }
166
+ )
167
+ Box(
168
+ modifier = Modifier
169
+ .width(0.5.dp)
170
+ .height(12.dp)
171
+ .background(tintColor ?: Colors.black_20)
172
+ )
173
+ }
174
+ Icon(
175
+ source = "16_basic_home",
176
+ size = 20.dp,
177
+ color = headerColor.tintIconColor,
178
+ modifier = Modifier.padding(4.dp).activeOpacityClickable {
179
+ api?.dismissAll { }
180
+ }
181
+ )
182
+ }
183
+ }
184
+ }
185
+
186
+
187
+
188
+ @Composable
189
+ fun NavigationButton(
190
+ disabled: Boolean,
191
+ icon: String,
192
+ showBadge: Boolean? = false,
193
+ onClick: () -> Unit,
194
+ headerColor: HeaderColor
195
+ ) {
196
+ Box(
197
+ modifier = Modifier
198
+ .size(28.dp)
199
+ .activeOpacityClickable(enabled = !disabled, onClick = onClick)
200
+ ) {
201
+ Box(
202
+ modifier = Modifier
203
+ .matchParentSize()
204
+ .clip(CircleShape)
205
+ .background(headerColor.backgroundButton)
206
+ .border(0.2.dp, headerColor.borderColor, CircleShape)
207
+ )
208
+ Box(
209
+ modifier = Modifier.matchParentSize(),
210
+ contentAlignment = Alignment.Center
211
+ ) {
212
+ Icon(
213
+ source = icon,
214
+ size = 20.dp,
215
+ color = headerColor.tintIconColor
216
+ )
217
+ }
218
+
219
+ if (showBadge == true) {
220
+ BadgeDot(
221
+ size = DotSize.Small,
222
+ modifier = Modifier
223
+ .align(Alignment.TopEnd)
224
+ .offset(x = -Spacing.XXS, y = -Spacing.XXS)
225
+ )
226
+ }
227
+ }
228
+ }
229
+
230
+ private fun getNavigationButtonConfig(
231
+ headerRight: HeaderRight.Toolkit,
232
+ onPressShortcut: () -> Unit,
233
+ onPressMore: () -> Unit
234
+ ): NavigationButtonConfig {
235
+ val totalTools = headerRight.tools.sumOf { it.items.size }
236
+ val config = NavigationButtonConfig(icon = "star", onPress = onPressShortcut)
237
+ return if (totalTools > 1 || headerRight.useMore == true) {
238
+ NavigationButtonConfig(
239
+ icon = "navigation_more_icon",
240
+ onPress = onPressMore
241
+ )
242
+ } else if (totalTools == 1 && headerRight.tools.isNotEmpty()) {
243
+ val singleTool = headerRight.tools.first().items.firstOrNull()
244
+ return if (singleTool != null){
245
+ NavigationButtonConfig(
246
+ icon = singleTool.icon,
247
+ onPress = {
248
+ headerRight.toolCallback?.invoke(singleTool.key)
249
+ }
250
+ )
251
+ } else {
252
+ config
253
+ }
254
+ } else {
255
+ config
256
+ }
257
+ }
258
+
259
+ sealed interface HeaderRight {
260
+ data object None : HeaderRight
261
+ data class Custom(
262
+ val content: @Composable () -> Unit
263
+ ) : HeaderRight
264
+ data object OnBoarding : HeaderRight
265
+ data class Toolkit(
266
+ val useShortcut: Boolean = false,
267
+ val useMore: Boolean = false,
268
+ val useSystemTools: Boolean = true,
269
+ val tools: List<ToolGroup> = emptyList(),
270
+ val toolCallback: ((String) -> Unit)? = { _ -> }
271
+ ) : HeaderRight
272
+ }
273
+
274
+ data class ToolGroup(
275
+ val title: Map<String, String> = emptyMap(),
276
+ val items: List<Tool>
277
+ ) {
278
+ fun toMap(): Map<String, Any> {
279
+ return mapOf(
280
+ "title" to title,
281
+ "items" to items.map { it.toMap() }
282
+ )
283
+ }
284
+ }
285
+ data class Tool(
286
+ val key: String,
287
+ val icon: String,
288
+ val showBadge: Boolean = false,
289
+ val name: Map<String, String> = emptyMap(),
290
+ val showRightIcon: Boolean = true,
291
+ ) {
292
+ fun toMap(): Map<String, Any> {
293
+ return mapOf(
294
+ "key" to key,
295
+ "icon" to icon,
296
+ "showBadge" to showBadge,
297
+ "name" to name,
298
+ "showRightIcon" to showRightIcon
299
+ )
300
+ }
301
+ }
302
+
303
+ data class NavigationButtonConfig(
304
+ val icon: String,
305
+ val onPress: () -> Unit
306
+ )
@@ -0,0 +1,31 @@
1
+ package vn.momo.kits.navigation.component
2
+
3
+ import androidx.compose.foundation.layout.fillMaxWidth
4
+ import androidx.compose.runtime.Composable
5
+ import androidx.compose.ui.Modifier
6
+ import androidx.compose.ui.graphics.Color
7
+ import androidx.compose.ui.text.style.TextAlign
8
+ import androidx.compose.ui.text.style.TextOverflow
9
+ import androidx.compose.ui.unit.sp
10
+ import androidx.compose.ui.zIndex
11
+ import vn.momo.kits.components.Text
12
+ import vn.momo.kits.const.Typography
13
+
14
+ @Composable
15
+ fun HeaderTitle(
16
+ title: String = "",
17
+ color: Color? = null,
18
+ ) {
19
+ Text(
20
+ modifier = Modifier.fillMaxWidth().zIndex(1f),
21
+ text = title,
22
+ textAlign = TextAlign.Start,
23
+ style = Typography.actionSBold.copy(
24
+ fontSize = 15.sp,
25
+ lineHeight = 22.sp,
26
+ ),
27
+ color = color,
28
+ maxLines = 1,
29
+ overflow = TextOverflow.Ellipsis
30
+ )
31
+ }