@momo-kits/native-kits 0.164.1-beta.4 → 0.164.1-beta.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 (140) hide show
  1. package/.claude/settings.local.json +185 -0
  2. package/build.gradle.kts +11 -0
  3. package/compose/build.gradle.kts +190 -0
  4. package/compose/build.gradle.kts.backup +190 -0
  5. package/compose/compose.podspec +47 -0
  6. package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +19 -0
  7. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +6 -0
  8. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +12 -0
  9. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +7 -0
  10. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +150 -0
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +52 -0
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +106 -0
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +183 -0
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +207 -0
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +39 -0
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +86 -0
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +76 -0
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +76 -0
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +293 -0
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +826 -0
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +110 -0
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +378 -0
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +56 -0
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +157 -0
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +82 -0
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +32 -0
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +338 -0
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +175 -0
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +356 -0
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +113 -0
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +89 -0
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +128 -0
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +209 -0
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +542 -0
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +23 -0
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +76 -0
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +125 -0
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +231 -0
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +134 -0
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +475 -0
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +114 -0
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +214 -0
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +240 -0
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +216 -0
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +259 -0
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +182 -0
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +343 -0
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +108 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +56 -0
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +41 -0
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +84 -0
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +40 -0
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +328 -0
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +97 -0
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +310 -0
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +59 -0
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +87 -0
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +21 -0
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +90 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +323 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +217 -0
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +473 -0
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +122 -0
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +199 -0
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +91 -0
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +470 -0
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +88 -0
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +131 -0
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +203 -0
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +498 -0
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +175 -0
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +182 -0
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +193 -0
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +234 -0
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +188 -0
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +325 -0
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +16 -0
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +22 -0
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +16 -0
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +188 -0
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +263 -0
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +52 -0
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +38 -0
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +39 -0
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +52 -0
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +66 -0
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +104 -0
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +48 -0
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +299 -0
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +129 -0
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +103 -0
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +191 -0
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +385 -0
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +8 -0
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +599 -0
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +157 -0
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +39 -0
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +156 -0
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +225 -0
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +169 -0
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +320 -0
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +81 -0
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +309 -0
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +34 -0
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +360 -0
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +132 -0
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +127 -0
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +15 -0
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +91 -0
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +9 -0
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +69 -0
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +55 -0
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +15 -0
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +105 -0
  124. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +40 -0
  125. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +32 -0
  126. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +35 -0
  127. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +66 -0
  128. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +225 -0
  129. package/gradle/libs.versions.toml +69 -0
  130. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  131. package/gradle/wrapper/gradle-wrapper.properties +8 -0
  132. package/gradle.properties +26 -0
  133. package/gradlew +252 -0
  134. package/gradlew.bat +94 -0
  135. package/ios/native-kits.podspec +1 -1
  136. package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
  137. package/ios-demo/README.md +120 -0
  138. package/local.properties +8 -0
  139. package/package.json +1 -1
  140. package/settings.gradle.kts +64 -0
@@ -0,0 +1,169 @@
1
+ package vn.momo.kits.navigation.component
2
+
3
+ import androidx.compose.animation.*
4
+ import androidx.compose.animation.core.CubicBezierEasing
5
+ import androidx.compose.animation.core.LinearEasing
6
+ import androidx.compose.animation.core.animateDpAsState
7
+ import androidx.compose.animation.core.tween
8
+ import androidx.compose.foundation.ScrollState
9
+ import androidx.compose.foundation.background
10
+ import androidx.compose.foundation.border
11
+ import androidx.compose.foundation.interaction.MutableInteractionSource
12
+ import androidx.compose.foundation.layout.*
13
+ import androidx.compose.material3.AlertDialogDefaults
14
+ import androidx.compose.material3.FloatingActionButton
15
+ import androidx.compose.material3.FloatingActionButtonDefaults
16
+ import androidx.compose.material3.FloatingActionButtonElevation
17
+ import androidx.compose.runtime.*
18
+ import androidx.compose.runtime.saveable.rememberSaveable
19
+ import androidx.compose.ui.Alignment
20
+ import androidx.compose.ui.Modifier
21
+ import androidx.compose.ui.graphics.Color
22
+ import androidx.compose.ui.unit.Dp
23
+ import androidx.compose.ui.unit.dp
24
+ import kotlinx.coroutines.delay
25
+ import kotlinx.coroutines.flow.MutableStateFlow
26
+ import kotlinx.coroutines.flow.collectLatest
27
+ import kotlinx.coroutines.launch
28
+ import vn.momo.kits.application.IsShowBaseLineDebug
29
+ import vn.momo.kits.components.Icon
30
+ import vn.momo.kits.components.Text
31
+ import vn.momo.kits.const.Colors
32
+ import vn.momo.kits.const.Typography
33
+ import vn.momo.kits.modifier.conditional
34
+ import kotlin.time.Duration.Companion.milliseconds
35
+
36
+ enum class FABSize { SMALL, DEFAULT }
37
+ enum class FABPosition { END, CENTER }
38
+
39
+ data class FloatingButtonProps(
40
+ val icon: String,
41
+ val iconColor: Color? = null,
42
+ val label: String? = null,
43
+ val onClick: () -> Unit,
44
+ val size: FABSize = FABSize.DEFAULT,
45
+ val bottom: Dp? = null,
46
+ val scrollState: ScrollState? = null,
47
+ val position: FABPosition? = FABPosition.END,
48
+ )
49
+
50
+ private val FabSmallSize = 36.dp
51
+ private val FabDefaultSize = 48.dp
52
+ private val FabExpandedWidth = 80.dp
53
+ private val FabIconSizeSmall = 12.dp
54
+ private val FabIconSizeDefault = 24.dp
55
+ private val FabPaddingHorizontal = 12.dp
56
+
57
+ @Composable
58
+ internal fun FloatingButton(
59
+ scrollPosition: Int,
60
+ bottom: Dp,
61
+ onClick: () -> Unit,
62
+ containerColor: Color,
63
+ contentColor: Color = containerColor,
64
+ icon: String,
65
+ iconColor: Color? = null,
66
+ text: String? = null,
67
+ elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(4.dp),
68
+ size: FABSize = FABSize.DEFAULT,
69
+ position: FABPosition = FABPosition.END,
70
+ keyboardSize: Dp = 0.dp,
71
+ ) {
72
+ val scrollStateFlow = remember { MutableStateFlow(scrollPosition) }
73
+ var lastScrollPosition by rememberSaveable { mutableStateOf(0) }
74
+ var isExpanded by rememberSaveable { mutableStateOf(false) }
75
+
76
+ val coroutineScope = rememberCoroutineScope()
77
+
78
+ // Animations
79
+ val baseSize = if (size == FABSize.SMALL) FabSmallSize else FabDefaultSize
80
+ val width by animateDpAsState(
81
+ targetValue = if (isExpanded && text != null) FabExpandedWidth else baseSize,
82
+ animationSpec = tween(100, easing = CubicBezierEasing(0.2f, 0.2f, 0.2f, 0.2f))
83
+ )
84
+
85
+ // Scroll listener
86
+ LaunchedEffect(text) {
87
+ if (text != null) {
88
+ coroutineScope.launch {
89
+ scrollStateFlow.collectLatest { newScroll ->
90
+ isExpanded = newScroll <= lastScrollPosition
91
+ delay(100.milliseconds)
92
+ lastScrollPosition = newScroll
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ LaunchedEffect(scrollPosition) {
99
+ scrollStateFlow.value = scrollPosition
100
+ }
101
+
102
+ Box(
103
+ modifier = Modifier
104
+ .fillMaxSize()
105
+ .padding(end = 12.dp, bottom = bottom)
106
+ .offset(y = -keyboardSize)
107
+ .background(Color.Transparent),
108
+ contentAlignment = if (position == FABPosition.END) Alignment.BottomEnd else Alignment.BottomCenter,
109
+ ) {
110
+ FloatingActionButton(
111
+ onClick = onClick,
112
+ shape = AlertDialogDefaults.shape,
113
+ containerColor = containerColor,
114
+ contentColor = contentColor,
115
+ elevation = elevation,
116
+ interactionSource = remember { MutableInteractionSource() },
117
+ modifier = Modifier
118
+ .sizeIn(minWidth = width)
119
+ .height(baseSize)
120
+ .conditional(IsShowBaseLineDebug) {
121
+ border(1.dp, Colors.blue_03)
122
+ }
123
+ ) {
124
+ Row(
125
+ modifier = Modifier.padding(
126
+ start = if (isExpanded) FabPaddingHorizontal else 0.dp,
127
+ end = if (isExpanded) FabPaddingHorizontal else 0.dp,
128
+ ),
129
+ verticalAlignment = Alignment.CenterVertically,
130
+ horizontalArrangement = if (isExpanded) Arrangement.Start else Arrangement.Center
131
+ ) {
132
+ Icon(
133
+ source = icon,
134
+ size = if (size == FABSize.SMALL) FabIconSizeSmall else FabIconSizeDefault,
135
+ color = iconColor ?: Color.White
136
+ )
137
+
138
+ AnimatedVisibility(
139
+ visible = isExpanded,
140
+ enter = fadeInExpandAnimation,
141
+ exit = fadeOutShrinkAnimation,
142
+ ) {
143
+ Row {
144
+ Spacer(Modifier.width(12.dp))
145
+ Text(
146
+ text = text ?: "",
147
+ color = Color.White,
148
+ style = Typography.actionDefaultBold
149
+ )
150
+ }
151
+ }
152
+ }
153
+ }
154
+ }
155
+ }
156
+
157
+ private val fadeOutShrinkAnimation = fadeOut(
158
+ animationSpec = tween(100, easing = LinearEasing)
159
+ ) + shrinkHorizontally(
160
+ animationSpec = tween(100, easing = LinearEasing),
161
+ shrinkTowards = Alignment.Start
162
+ )
163
+
164
+ private val fadeInExpandAnimation = fadeIn(
165
+ animationSpec = tween(100, easing = LinearEasing)
166
+ ) + expandHorizontally(
167
+ animationSpec = tween(100, easing = LinearEasing),
168
+ expandFrom = Alignment.Start
169
+ )
@@ -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
+ }