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