@momo-kits/native-kits 0.155.1-hello.14 → 0.155.1-hello.14-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 (116) 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 +85 -0
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +35 -0
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +343 -0
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +353 -0
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +98 -0
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +138 -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 +78 -0
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +151 -0
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +190 -0
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +119 -0
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +407 -0
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +175 -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 +233 -0
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +233 -0
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +257 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +240 -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 +59 -0
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +44 -0
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +95 -0
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +43 -0
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +351 -0
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +106 -0
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +72 -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 +99 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +99 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +95 -0
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +132 -0
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +217 -0
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +180 -0
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +208 -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 +15 -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 +241 -0
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +122 -0
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +94 -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 +302 -0
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +495 -0
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +169 -0
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +233 -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 +190 -0
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +258 -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 +135 -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/local.properties +8 -0
  114. package/package.json +1 -1
  115. package/publish_release.sh +153 -0
  116. package/settings.gradle.kts +52 -0
@@ -0,0 +1,233 @@
1
+ package vn.momo.kits.navigation.bottomtab
2
+
3
+ import androidx.compose.animation.core.FastOutSlowInEasing
4
+ import androidx.compose.animation.core.animateDpAsState
5
+ import androidx.compose.animation.core.tween
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.Column
11
+ import androidx.compose.foundation.layout.Row
12
+ import androidx.compose.foundation.layout.RowScope
13
+ import androidx.compose.foundation.layout.Spacer
14
+ import androidx.compose.foundation.layout.fillMaxSize
15
+ import androidx.compose.foundation.layout.fillMaxWidth
16
+ import androidx.compose.foundation.layout.height
17
+ import androidx.compose.foundation.layout.offset
18
+ import androidx.compose.foundation.layout.padding
19
+ import androidx.compose.foundation.layout.size
20
+ import androidx.compose.foundation.layout.width
21
+ import androidx.compose.foundation.shape.CircleShape
22
+ import androidx.compose.foundation.shape.RoundedCornerShape
23
+ import androidx.compose.runtime.Composable
24
+ import androidx.compose.runtime.getValue
25
+ import androidx.compose.ui.Alignment
26
+ import androidx.compose.ui.Modifier
27
+ import androidx.compose.ui.draw.clip
28
+ import androidx.compose.ui.graphics.Brush
29
+ import androidx.compose.ui.graphics.Color
30
+ import androidx.compose.ui.text.style.TextAlign
31
+ import androidx.compose.ui.text.style.TextOverflow
32
+ import androidx.compose.ui.unit.dp
33
+ import androidx.navigation.NavController
34
+ import androidx.navigation.compose.currentBackStackEntryAsState
35
+ import vn.momo.kits.components.Badge
36
+ import vn.momo.kits.components.Icon
37
+ import vn.momo.kits.components.Text
38
+ import vn.momo.kits.const.AppTheme
39
+ import vn.momo.kits.const.Colors
40
+ import vn.momo.kits.const.Radius
41
+ import vn.momo.kits.const.Spacing
42
+ import vn.momo.kits.application.ApplicationContext
43
+ import vn.momo.kits.application.MiniAppContext.Companion.isShowBaselineDebug
44
+ import vn.momo.kits.const.Typography
45
+ import vn.momo.kits.modifier.conditional
46
+ import vn.momo.kits.modifier.noFeedbackClickable
47
+ import vn.momo.kits.platform.getScreenDimensions
48
+
49
+ val floatingButtonWidth = 75.dp
50
+ const val BOTTOM_TAB_BAR_HEIGHT = 56
51
+
52
+ @Composable
53
+ fun BottomTabBar(
54
+ items: List<BottomTabItem>,
55
+ floatingButton: BottomTabFloatingButton? = null,
56
+ navController: NavController,
57
+ onTabSelected: (Int) -> Unit,
58
+ ) {
59
+ val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
60
+ val selectedIndex = items.indexOfFirst { "option${items.indexOf(it)}" == currentRoute }
61
+
62
+ val screenWidth = getScreenDimensions().width.dp
63
+ val mid = items.size / 2
64
+
65
+ val tabWidth = if (floatingButton != null) {
66
+ (screenWidth - floatingButtonWidth) / items.size
67
+ } else {
68
+ screenWidth / items.size
69
+ }
70
+ val adjustSize = if(floatingButton != null && selectedIndex >= mid) floatingButtonWidth else 0.dp
71
+ val targetOffset = tabWidth * selectedIndex + tabWidth / 2 - screenWidth / 2 + adjustSize
72
+
73
+ val indicatorOffsetX by animateDpAsState(
74
+ targetValue = targetOffset,
75
+ animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing),
76
+ label = "IndicatorX"
77
+ )
78
+
79
+ val application = ApplicationContext.current
80
+
81
+ Box(
82
+ contentAlignment = Alignment.BottomCenter
83
+ ) {
84
+ Row(
85
+ modifier = Modifier
86
+ .fillMaxWidth()
87
+ .height(BOTTOM_TAB_BAR_HEIGHT.dp)
88
+ .background(AppTheme.current.colors.background.surface)
89
+ .conditional(application.isShowBaselineDebug()) {
90
+ border(1.dp, Colors.blue_03)
91
+ },
92
+ horizontalArrangement = Arrangement.SpaceAround,
93
+ ) {
94
+ if (floatingButton == null) {
95
+ renderTabBarItem(items, 0, items.size, selectedIndex, onTabSelected)
96
+ } else {
97
+ renderTabBarItem(items, 0, mid, selectedIndex, onTabSelected)
98
+ Spacer(modifier = Modifier.width(floatingButtonWidth).padding(horizontal = Spacing.XXS))
99
+ renderTabBarItem(items, mid, items.size, selectedIndex, onTabSelected)
100
+ }
101
+ }
102
+
103
+ Box(modifier = Modifier
104
+ .offset(x = indicatorOffsetX, y = (-BOTTOM_TAB_BAR_HEIGHT + 2).dp)
105
+ .height(2.dp)
106
+ .width(44.dp)
107
+ .background(
108
+ color = AppTheme.current.colors.primary ,
109
+ shape = RoundedCornerShape(bottomStart = 2.dp, bottomEnd = 2.dp)
110
+ )
111
+ )
112
+
113
+ Box(modifier = Modifier
114
+ .fillMaxWidth()
115
+ .height(6.dp)
116
+ .offset(x = 0.dp, y = (-BOTTOM_TAB_BAR_HEIGHT).dp)
117
+ .background(
118
+ brush = Brush.verticalGradient(
119
+ colors = listOf(Color.Transparent, Color.Black.copy(alpha = 0.05f))
120
+ )
121
+ )
122
+ )
123
+ floatingButton?.let { FloatingButton(it) }
124
+ }
125
+ }
126
+
127
+ @Composable
128
+ fun RowScope.renderTabBarItem(
129
+ items: List<BottomTabItem>,
130
+ fromIndex: Int,
131
+ toIndex: Int,
132
+ selectedIndex: Int,
133
+ onTabSelected: (Int) -> Unit
134
+ ){
135
+ for (index in fromIndex until toIndex) {
136
+ Box(modifier = Modifier.weight(1f)) {
137
+ TabBarItem(
138
+ item = items[index],
139
+ selected = index == selectedIndex,
140
+ onClick = { onTabSelected(index) }
141
+ )
142
+ }
143
+ }
144
+ }
145
+
146
+ @Composable
147
+ fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> Unit) {
148
+ val application = ApplicationContext.current
149
+
150
+ Box(modifier = Modifier
151
+ .fillMaxSize()
152
+ .padding(horizontal = Spacing.XXS)
153
+ .conditional(application.isShowBaselineDebug()) {
154
+ border(1.dp, Colors.blue_03)
155
+ }
156
+ .noFeedbackClickable {
157
+ onClick()
158
+ },
159
+ contentAlignment = Alignment.BottomStart
160
+ ){
161
+ Column(
162
+ modifier = Modifier
163
+ .fillMaxSize()
164
+ .padding(horizontal = Spacing.XXS)
165
+ .noFeedbackClickable {
166
+ onClick()
167
+ },
168
+ horizontalAlignment = Alignment.CenterHorizontally,
169
+ verticalArrangement = Arrangement.Bottom
170
+ ) {
171
+ Icon(
172
+ source = item.icon,
173
+ modifier = Modifier.weight(1f),
174
+ color = if (selected) AppTheme.current.colors.primary else AppTheme.current.colors.text.hint)
175
+ Text(
176
+ text = item.label,
177
+ color = if (selected) AppTheme.current.colors.primary else AppTheme.current.colors.text.hint,
178
+ style = Typography.labelXsMedium,
179
+ maxLines = 1,
180
+ overflow = TextOverflow.Ellipsis
181
+ )
182
+ }
183
+ if(item.badgeLabel != null){
184
+ Box(modifier = Modifier
185
+ .offset(x = 44.dp, y = (-32).dp)
186
+ ){
187
+ Badge(item.badgeLabel)
188
+ }
189
+ }
190
+ }
191
+ }
192
+
193
+ @Composable
194
+ fun FloatingButton(data: BottomTabFloatingButton) {
195
+ val application = ApplicationContext.current
196
+
197
+ Column(
198
+ modifier = Modifier
199
+ .width(floatingButtonWidth)
200
+ .padding(horizontal = Spacing.XXS)
201
+ .conditional(application.isShowBaselineDebug()) {
202
+ border(1.dp, Colors.blue_03)
203
+ }
204
+ .noFeedbackClickable {
205
+ data.onPress()
206
+ },
207
+ verticalArrangement = Arrangement.Bottom,
208
+ horizontalAlignment = Alignment.CenterHorizontally
209
+
210
+ ){
211
+ CurvedContainer()
212
+ Box(
213
+ modifier = Modifier
214
+ .size(48.dp)
215
+ .clip(CircleShape)
216
+ .background(AppTheme.current.colors.primary),
217
+ contentAlignment = Alignment.Center
218
+ ) {
219
+ Icon(source = data.icon, color = Color.White, size = 28.dp)
220
+ }
221
+ Spacer(modifier = Modifier.height(4.dp))
222
+ Text(
223
+ text = data.label,
224
+ style = Typography.labelXsMedium,
225
+ color = Colors.black_01,
226
+ textAlign = TextAlign.Center,
227
+ modifier = Modifier
228
+ .height(14.dp)
229
+ .fillMaxWidth()
230
+ .background(AppTheme.current.colors.primary, shape = RoundedCornerShape(size = Radius.XL))
231
+ )
232
+ }
233
+ }
@@ -0,0 +1,86 @@
1
+ package vn.momo.kits.navigation.bottomtab
2
+
3
+ import androidx.compose.foundation.Canvas
4
+ import androidx.compose.foundation.layout.Box
5
+ import androidx.compose.foundation.layout.height
6
+ import androidx.compose.foundation.layout.offset
7
+ import androidx.compose.foundation.layout.width
8
+ import androidx.compose.runtime.Composable
9
+ import androidx.compose.ui.Alignment
10
+ import androidx.compose.ui.Modifier
11
+ import androidx.compose.ui.graphics.Path
12
+ import androidx.compose.ui.platform.LocalDensity
13
+ import androidx.compose.ui.unit.Dp
14
+ import androidx.compose.ui.unit.IntOffset
15
+ import androidx.compose.ui.unit.dp
16
+ import androidx.compose.ui.zIndex
17
+ import vn.momo.kits.const.AppTheme
18
+
19
+ @Composable
20
+ fun CurvedContainer(
21
+ circleSize: Dp = 48.dp,
22
+ tabBarItemIconSize: Dp = 28.dp,
23
+ circleOverSize: Dp = 2.dp,
24
+ width: Dp = 71.dp,
25
+ height: Dp = 16.dp
26
+ ) {
27
+ val theme = AppTheme.current
28
+ val density = LocalDensity.current
29
+ val widthPx = with(density) { width.toPx()}
30
+
31
+ val circleSizePx = with(density) { circleSize.toPx() }
32
+ val tabBarItemIconSizePx = with(density) { tabBarItemIconSize.toPx() }
33
+ val circleOverSizePx = with(density) { circleOverSize.toPx() }
34
+
35
+ val circlePositionTop = circleSizePx - tabBarItemIconSizePx - 10f
36
+ val circleOverTop = circlePositionTop + circleOverSizePx
37
+ val circleR = circleSizePx / 2
38
+ val circleOverR = circleR + circleOverSizePx
39
+
40
+ val centerX = widthPx / 2
41
+
42
+ val curvedPath = getPath(
43
+ circleOverTop = circleOverTop,
44
+ centerX = centerX,
45
+ circleOverR = circleOverR,
46
+ widthPx = widthPx
47
+ )
48
+
49
+ Box(
50
+ contentAlignment = Alignment.BottomCenter
51
+ ){
52
+ Canvas(
53
+ modifier = Modifier
54
+ .width(width)
55
+ .height(height)
56
+ .offset{ IntOffset(x = 0, y = 24) }
57
+ .zIndex(-1f)
58
+ ) {
59
+ drawPath(path = curvedPath, color = theme.colors.background.surface)
60
+ }
61
+ }
62
+ }
63
+
64
+ private fun getPath(circleOverTop: Float, centerX: Float, circleOverR: Float, widthPx: Float): Path{
65
+ return Path().apply {
66
+ moveTo(0f, circleOverTop)
67
+ lineTo(centerX - circleOverR - 40f, circleOverTop)
68
+
69
+ cubicTo(
70
+ centerX - circleOverR, circleOverTop,
71
+ centerX - circleOverR, 0f,
72
+ centerX, 0f
73
+ )
74
+
75
+ cubicTo(
76
+ centerX + circleOverR, 0f,
77
+ centerX + circleOverR, circleOverTop,
78
+ centerX + circleOverR + 40f, circleOverTop
79
+ )
80
+
81
+ lineTo(widthPx, circleOverTop)
82
+ lineTo(widthPx, 60f)
83
+ lineTo(0f, 60f)
84
+ close()
85
+ }
86
+ }
@@ -0,0 +1,190 @@
1
+ package vn.momo.kits.navigation.component
2
+
3
+ import androidx.compose.animation.AnimatedVisibility
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.animation.expandHorizontally
9
+ import androidx.compose.animation.fadeIn
10
+ import androidx.compose.animation.fadeOut
11
+ import androidx.compose.animation.shrinkHorizontally
12
+ import androidx.compose.foundation.ScrollState
13
+ import androidx.compose.foundation.background
14
+ import androidx.compose.foundation.border
15
+ import androidx.compose.foundation.interaction.MutableInteractionSource
16
+ import androidx.compose.foundation.layout.Arrangement
17
+ import androidx.compose.foundation.layout.Box
18
+ import androidx.compose.foundation.layout.Row
19
+ import androidx.compose.foundation.layout.Spacer
20
+ import androidx.compose.foundation.layout.fillMaxSize
21
+ import androidx.compose.foundation.layout.height
22
+ import androidx.compose.foundation.layout.offset
23
+ import androidx.compose.foundation.layout.padding
24
+ import androidx.compose.foundation.layout.sizeIn
25
+ import androidx.compose.foundation.layout.width
26
+ import androidx.compose.material3.AlertDialogDefaults
27
+ import androidx.compose.material3.FloatingActionButton
28
+ import androidx.compose.material3.FloatingActionButtonDefaults
29
+ import androidx.compose.material3.FloatingActionButtonElevation
30
+ import androidx.compose.runtime.Composable
31
+ import androidx.compose.runtime.LaunchedEffect
32
+ import androidx.compose.runtime.getValue
33
+ import androidx.compose.runtime.mutableStateOf
34
+ import androidx.compose.runtime.remember
35
+ import androidx.compose.runtime.rememberCoroutineScope
36
+ import androidx.compose.runtime.saveable.rememberSaveable
37
+ import androidx.compose.runtime.setValue
38
+ import androidx.compose.ui.Alignment
39
+ import androidx.compose.ui.Modifier
40
+ import androidx.compose.ui.graphics.Color
41
+ import androidx.compose.ui.unit.Dp
42
+ import androidx.compose.ui.unit.dp
43
+ import kotlinx.coroutines.delay
44
+ import kotlinx.coroutines.flow.MutableStateFlow
45
+ import kotlinx.coroutines.flow.collectLatest
46
+ import kotlinx.coroutines.launch
47
+ import vn.momo.kits.application.ApplicationContext
48
+ import vn.momo.kits.application.MiniAppContext.Companion.isShowBaselineDebug
49
+ import vn.momo.kits.components.Icon
50
+ import vn.momo.kits.components.Text
51
+ import vn.momo.kits.const.Colors
52
+ import vn.momo.kits.const.Typography
53
+ import vn.momo.kits.modifier.conditional
54
+
55
+ enum class FABSize { SMALL, DEFAULT }
56
+ enum class FABPosition { END, CENTER }
57
+
58
+ data class FloatingButtonProps(
59
+ val icon: String,
60
+ val iconColor: Color? = null,
61
+ val label: String? = null,
62
+ val onClick: () -> Unit,
63
+ val size: FABSize = FABSize.DEFAULT,
64
+ val bottom: Dp? = null,
65
+ val scrollState: ScrollState? = null,
66
+ val position: FABPosition? = FABPosition.END,
67
+ )
68
+
69
+ private val FabSmallSize = 36.dp
70
+ private val FabDefaultSize = 48.dp
71
+ private val FabExpandedWidth = 80.dp
72
+ private val FabIconSizeSmall = 12.dp
73
+ private val FabIconSizeDefault = 24.dp
74
+ private val FabPaddingHorizontal = 12.dp
75
+
76
+ @Composable
77
+ fun FloatingButton(
78
+ scrollPosition: Int,
79
+ bottom: Dp,
80
+ onClick: () -> Unit,
81
+ containerColor: Color,
82
+ contentColor: Color = containerColor,
83
+ icon: String,
84
+ iconColor: Color? = null,
85
+ text: String? = null,
86
+ elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(4.dp),
87
+ size: FABSize = FABSize.DEFAULT,
88
+ position: FABPosition = FABPosition.END,
89
+ keyboardSize: Dp = 0.dp,
90
+ ) {
91
+ val scrollStateFlow = remember { MutableStateFlow(scrollPosition) }
92
+ var lastScrollPosition by rememberSaveable { mutableStateOf(0) }
93
+ var isExpanded by rememberSaveable { mutableStateOf(false) }
94
+
95
+ val coroutineScope = rememberCoroutineScope()
96
+
97
+ // Animations
98
+ val baseSize = if (size == FABSize.SMALL) FabSmallSize else FabDefaultSize
99
+ val width by animateDpAsState(
100
+ targetValue = if (isExpanded && text != null) FabExpandedWidth else baseSize,
101
+ animationSpec = tween(100, easing = CubicBezierEasing(0.2f, 0.2f, 0.2f, 0.2f))
102
+ )
103
+
104
+ // Scroll listener
105
+ LaunchedEffect(text) {
106
+ if (text != null) {
107
+ coroutineScope.launch {
108
+ scrollStateFlow.collectLatest { newScroll ->
109
+ isExpanded = newScroll <= lastScrollPosition
110
+ delay(100)
111
+ lastScrollPosition = newScroll
112
+ }
113
+ }
114
+ }
115
+ }
116
+
117
+ LaunchedEffect(scrollPosition) {
118
+ scrollStateFlow.value = scrollPosition
119
+ }
120
+
121
+ val application = ApplicationContext.current
122
+
123
+ Box(
124
+ modifier = Modifier
125
+ .fillMaxSize()
126
+ .padding(end = 12.dp, bottom = bottom)
127
+ .offset(y = -keyboardSize)
128
+ .background(Color.Transparent),
129
+ contentAlignment = if (position == FABPosition.END) Alignment.BottomEnd else Alignment.BottomCenter,
130
+ ) {
131
+ FloatingActionButton(
132
+ onClick = onClick,
133
+ shape = AlertDialogDefaults.shape,
134
+ containerColor = containerColor,
135
+ contentColor = contentColor,
136
+ elevation = elevation,
137
+ interactionSource = remember { MutableInteractionSource() },
138
+ modifier = Modifier
139
+ .sizeIn(minWidth = width)
140
+ .height(baseSize)
141
+ .conditional(application.isShowBaselineDebug()) {
142
+ border(1.dp, Colors.blue_03)
143
+ }
144
+ ) {
145
+ Row(
146
+ modifier = Modifier.padding(
147
+ start = if (isExpanded) FabPaddingHorizontal else 0.dp,
148
+ end = if (isExpanded) FabPaddingHorizontal else 0.dp,
149
+ ),
150
+ verticalAlignment = Alignment.CenterVertically,
151
+ horizontalArrangement = if (isExpanded) Arrangement.Start else Arrangement.Center
152
+ ) {
153
+ Icon(
154
+ source = icon,
155
+ size = if (size == FABSize.SMALL) FabIconSizeSmall else FabIconSizeDefault,
156
+ color = iconColor ?: Color.White
157
+ )
158
+
159
+ AnimatedVisibility(
160
+ visible = isExpanded,
161
+ enter = fadeInExpandAnimation,
162
+ exit = fadeOutShrinkAnimation,
163
+ ) {
164
+ Row {
165
+ Spacer(Modifier.width(12.dp))
166
+ Text(
167
+ text = text ?: "",
168
+ color = Color.White,
169
+ style = Typography.actionDefaultBold
170
+ )
171
+ }
172
+ }
173
+ }
174
+ }
175
+ }
176
+ }
177
+
178
+ private val fadeOutShrinkAnimation = fadeOut(
179
+ animationSpec = tween(100, easing = LinearEasing)
180
+ ) + shrinkHorizontally(
181
+ animationSpec = tween(100, easing = LinearEasing),
182
+ shrinkTowards = Alignment.Start
183
+ )
184
+
185
+ private val fadeInExpandAnimation = fadeIn(
186
+ animationSpec = tween(100, easing = LinearEasing)
187
+ ) + expandHorizontally(
188
+ animationSpec = tween(100, easing = LinearEasing),
189
+ expandFrom = Alignment.Start
190
+ )