@momo-kits/native-kits 0.156.3 → 0.156.4-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 (114) hide show
  1. package/build.gradle.kts +11 -0
  2. package/compose/build.gradle.kts +180 -0
  3. package/compose/build.gradle.kts.backup +180 -0
  4. package/compose/compose.podspec +54 -0
  5. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +113 -0
  6. package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
  7. package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
  8. package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
  9. package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
  10. package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
  11. package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
  12. package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
  13. package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
  14. package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
  15. package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
  16. package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
  17. package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
  18. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
  19. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +57 -0
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +95 -0
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +201 -0
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +222 -0
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +48 -0
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +86 -0
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +76 -0
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +76 -0
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +305 -0
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +715 -0
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +121 -0
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +405 -0
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +83 -0
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +32 -0
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +340 -0
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +348 -0
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +94 -0
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +136 -0
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +543 -0
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +23 -0
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +76 -0
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +148 -0
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +188 -0
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +116 -0
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +447 -0
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +172 -0
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +244 -0
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +231 -0
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +234 -0
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +254 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +241 -0
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +364 -0
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +56 -0
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +41 -0
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +92 -0
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +40 -0
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +352 -0
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +103 -0
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +70 -0
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +17 -0
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +96 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +96 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +92 -0
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +130 -0
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +214 -0
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +177 -0
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +205 -0
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +239 -0
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +191 -0
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +306 -0
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +13 -0
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +189 -0
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +285 -0
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +2 -0
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +35 -0
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +2 -0
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +59 -0
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +68 -0
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +14 -0
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +50 -0
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +239 -0
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +119 -0
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +98 -0
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +163 -0
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +331 -0
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +497 -0
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +162 -0
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +226 -0
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +187 -0
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +274 -0
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +80 -0
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +306 -0
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +31 -0
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +370 -0
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +132 -0
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +42 -0
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +62 -0
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +88 -0
  106. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +152 -0
  107. package/gradle/libs.versions.toml +57 -0
  108. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  109. package/gradle/wrapper/gradle-wrapper.properties +8 -0
  110. package/gradle.properties +26 -0
  111. package/gradlew +252 -0
  112. package/gradlew.bat +94 -0
  113. package/package.json +1 -1
  114. package/settings.gradle.kts +52 -0
@@ -0,0 +1,226 @@
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.IsShowBaseLineDebug
43
+ import vn.momo.kits.const.Typography
44
+ import vn.momo.kits.modifier.conditional
45
+ import vn.momo.kits.modifier.noFeedbackClickable
46
+ import vn.momo.kits.platform.getScreenDimensions
47
+
48
+ val floatingButtonWidth = 75.dp
49
+ const val BOTTOM_TAB_BAR_HEIGHT = 56
50
+
51
+ @Composable
52
+ fun BottomTabBar(
53
+ items: List<BottomTabItem>,
54
+ floatingButton: BottomTabFloatingButton? = null,
55
+ navController: NavController,
56
+ onTabSelected: (Int) -> Unit,
57
+ ) {
58
+ val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
59
+ val selectedIndex = items.indexOfFirst { "option${items.indexOf(it)}" == currentRoute }
60
+
61
+ val screenWidth = getScreenDimensions().width.dp
62
+ val mid = items.size / 2
63
+
64
+ val tabWidth = if (floatingButton != null) {
65
+ (screenWidth - floatingButtonWidth) / items.size
66
+ } else {
67
+ screenWidth / items.size
68
+ }
69
+ val adjustSize = if(floatingButton != null && selectedIndex >= mid) floatingButtonWidth else 0.dp
70
+ val targetOffset = tabWidth * selectedIndex + tabWidth / 2 - screenWidth / 2 + adjustSize
71
+
72
+ val indicatorOffsetX by animateDpAsState(
73
+ targetValue = targetOffset,
74
+ animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing),
75
+ label = "IndicatorX"
76
+ )
77
+
78
+ Box(
79
+ contentAlignment = Alignment.BottomCenter
80
+ ) {
81
+ Row(
82
+ modifier = Modifier
83
+ .fillMaxWidth()
84
+ .height(BOTTOM_TAB_BAR_HEIGHT.dp)
85
+ .background(AppTheme.current.colors.background.surface)
86
+ .conditional(IsShowBaseLineDebug) {
87
+ border(1.dp, Colors.blue_03)
88
+ },
89
+ horizontalArrangement = Arrangement.SpaceAround,
90
+ ) {
91
+ if (floatingButton == null) {
92
+ renderTabBarItem(items, 0, items.size, selectedIndex, onTabSelected)
93
+ } else {
94
+ renderTabBarItem(items, 0, mid, selectedIndex, onTabSelected)
95
+ Spacer(modifier = Modifier.width(floatingButtonWidth).padding(horizontal = Spacing.XXS))
96
+ renderTabBarItem(items, mid, items.size, selectedIndex, onTabSelected)
97
+ }
98
+ }
99
+
100
+ Box(modifier = Modifier
101
+ .offset(x = indicatorOffsetX, y = (-BOTTOM_TAB_BAR_HEIGHT + 2).dp)
102
+ .height(2.dp)
103
+ .width(44.dp)
104
+ .background(
105
+ color = AppTheme.current.colors.primary ,
106
+ shape = RoundedCornerShape(bottomStart = 2.dp, bottomEnd = 2.dp)
107
+ )
108
+ )
109
+
110
+ Box(modifier = Modifier
111
+ .fillMaxWidth()
112
+ .height(6.dp)
113
+ .offset(x = 0.dp, y = (-BOTTOM_TAB_BAR_HEIGHT).dp)
114
+ .background(
115
+ brush = Brush.verticalGradient(
116
+ colors = listOf(Color.Transparent, Color.Black.copy(alpha = 0.05f))
117
+ )
118
+ )
119
+ )
120
+ floatingButton?.let { FloatingButton(it) }
121
+ }
122
+ }
123
+
124
+ @Composable
125
+ fun RowScope.renderTabBarItem(
126
+ items: List<BottomTabItem>,
127
+ fromIndex: Int,
128
+ toIndex: Int,
129
+ selectedIndex: Int,
130
+ onTabSelected: (Int) -> Unit
131
+ ){
132
+ for (index in fromIndex until toIndex) {
133
+ Box(modifier = Modifier.weight(1f)) {
134
+ TabBarItem(
135
+ item = items[index],
136
+ selected = index == selectedIndex,
137
+ onClick = { onTabSelected(index) }
138
+ )
139
+ }
140
+ }
141
+ }
142
+
143
+ @Composable
144
+ fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> Unit) {
145
+ Box(modifier = Modifier
146
+ .fillMaxSize()
147
+ .padding(horizontal = Spacing.XXS)
148
+ .conditional(IsShowBaseLineDebug) {
149
+ border(1.dp, Colors.blue_03)
150
+ }
151
+ .noFeedbackClickable {
152
+ onClick()
153
+ },
154
+ contentAlignment = Alignment.BottomStart
155
+ ){
156
+ Column(
157
+ modifier = Modifier
158
+ .fillMaxSize()
159
+ .padding(horizontal = Spacing.XXS)
160
+ .noFeedbackClickable {
161
+ onClick()
162
+ },
163
+ horizontalAlignment = Alignment.CenterHorizontally,
164
+ verticalArrangement = Arrangement.Bottom
165
+ ) {
166
+ Icon(
167
+ source = item.icon,
168
+ modifier = Modifier.weight(1f),
169
+ color = if (selected) AppTheme.current.colors.primary else AppTheme.current.colors.text.hint)
170
+ Text(
171
+ text = item.label,
172
+ color = if (selected) AppTheme.current.colors.primary else AppTheme.current.colors.text.hint,
173
+ style = Typography.labelXsMedium,
174
+ maxLines = 1,
175
+ overflow = TextOverflow.Ellipsis
176
+ )
177
+ }
178
+ if(item.badgeLabel != null){
179
+ Box(modifier = Modifier
180
+ .offset(x = 44.dp, y = (-32).dp)
181
+ ){
182
+ Badge(item.badgeLabel)
183
+ }
184
+ }
185
+ }
186
+ }
187
+
188
+ @Composable
189
+ fun FloatingButton(data: BottomTabFloatingButton) {
190
+ Column(
191
+ modifier = Modifier
192
+ .width(floatingButtonWidth)
193
+ .padding(horizontal = Spacing.XXS)
194
+ .conditional(IsShowBaseLineDebug) {
195
+ border(1.dp, Colors.blue_03)
196
+ }
197
+ .noFeedbackClickable {
198
+ data.onPress()
199
+ },
200
+ verticalArrangement = Arrangement.Bottom,
201
+ horizontalAlignment = Alignment.CenterHorizontally
202
+
203
+ ){
204
+ CurvedContainer()
205
+ Box(
206
+ modifier = Modifier
207
+ .size(48.dp)
208
+ .clip(CircleShape)
209
+ .background(AppTheme.current.colors.primary),
210
+ contentAlignment = Alignment.Center
211
+ ) {
212
+ Icon(source = data.icon, color = Color.White, size = 28.dp)
213
+ }
214
+ Spacer(modifier = Modifier.height(4.dp))
215
+ Text(
216
+ text = data.label,
217
+ style = Typography.labelXsMedium,
218
+ color = Colors.black_01,
219
+ textAlign = TextAlign.Center,
220
+ modifier = Modifier
221
+ .height(14.dp)
222
+ .fillMaxWidth()
223
+ .background(AppTheme.current.colors.primary, shape = RoundedCornerShape(size = Radius.XL))
224
+ )
225
+ }
226
+ }
@@ -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,187 @@
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.IsShowBaseLineDebug
48
+ import vn.momo.kits.components.Icon
49
+ import vn.momo.kits.components.Text
50
+ import vn.momo.kits.const.Colors
51
+ import vn.momo.kits.const.Typography
52
+ import vn.momo.kits.modifier.conditional
53
+
54
+ enum class FABSize { SMALL, DEFAULT }
55
+ enum class FABPosition { END, CENTER }
56
+
57
+ data class FloatingButtonProps(
58
+ val icon: String,
59
+ val iconColor: Color? = null,
60
+ val label: String? = null,
61
+ val onClick: () -> Unit,
62
+ val size: FABSize = FABSize.DEFAULT,
63
+ val bottom: Dp? = null,
64
+ val scrollState: ScrollState? = null,
65
+ val position: FABPosition? = FABPosition.END,
66
+ )
67
+
68
+ private val FabSmallSize = 36.dp
69
+ private val FabDefaultSize = 48.dp
70
+ private val FabExpandedWidth = 80.dp
71
+ private val FabIconSizeSmall = 12.dp
72
+ private val FabIconSizeDefault = 24.dp
73
+ private val FabPaddingHorizontal = 12.dp
74
+
75
+ @Composable
76
+ fun FloatingButton(
77
+ scrollPosition: Int,
78
+ bottom: Dp,
79
+ onClick: () -> Unit,
80
+ containerColor: Color,
81
+ contentColor: Color = containerColor,
82
+ icon: String,
83
+ iconColor: Color? = null,
84
+ text: String? = null,
85
+ elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(4.dp),
86
+ size: FABSize = FABSize.DEFAULT,
87
+ position: FABPosition = FABPosition.END,
88
+ keyboardSize: Dp = 0.dp,
89
+ ) {
90
+ val scrollStateFlow = remember { MutableStateFlow(scrollPosition) }
91
+ var lastScrollPosition by rememberSaveable { mutableStateOf(0) }
92
+ var isExpanded by rememberSaveable { mutableStateOf(false) }
93
+
94
+ val coroutineScope = rememberCoroutineScope()
95
+
96
+ // Animations
97
+ val baseSize = if (size == FABSize.SMALL) FabSmallSize else FabDefaultSize
98
+ val width by animateDpAsState(
99
+ targetValue = if (isExpanded && text != null) FabExpandedWidth else baseSize,
100
+ animationSpec = tween(100, easing = CubicBezierEasing(0.2f, 0.2f, 0.2f, 0.2f))
101
+ )
102
+
103
+ // Scroll listener
104
+ LaunchedEffect(text) {
105
+ if (text != null) {
106
+ coroutineScope.launch {
107
+ scrollStateFlow.collectLatest { newScroll ->
108
+ isExpanded = newScroll <= lastScrollPosition
109
+ delay(100)
110
+ lastScrollPosition = newScroll
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ LaunchedEffect(scrollPosition) {
117
+ scrollStateFlow.value = scrollPosition
118
+ }
119
+
120
+ Box(
121
+ modifier = Modifier
122
+ .fillMaxSize()
123
+ .padding(end = 12.dp, bottom = bottom)
124
+ .offset(y = -keyboardSize)
125
+ .background(Color.Transparent),
126
+ contentAlignment = if (position == FABPosition.END) Alignment.BottomEnd else Alignment.BottomCenter,
127
+ ) {
128
+ FloatingActionButton(
129
+ onClick = onClick,
130
+ shape = AlertDialogDefaults.shape,
131
+ containerColor = containerColor,
132
+ contentColor = contentColor,
133
+ elevation = elevation,
134
+ interactionSource = remember { MutableInteractionSource() },
135
+ modifier = Modifier
136
+ .sizeIn(minWidth = width)
137
+ .height(baseSize)
138
+ .conditional(IsShowBaseLineDebug) {
139
+ border(1.dp, Colors.blue_03)
140
+ }
141
+ ) {
142
+ Row(
143
+ modifier = Modifier.padding(
144
+ start = if (isExpanded) FabPaddingHorizontal else 0.dp,
145
+ end = if (isExpanded) FabPaddingHorizontal else 0.dp,
146
+ ),
147
+ verticalAlignment = Alignment.CenterVertically,
148
+ horizontalArrangement = if (isExpanded) Arrangement.Start else Arrangement.Center
149
+ ) {
150
+ Icon(
151
+ source = icon,
152
+ size = if (size == FABSize.SMALL) FabIconSizeSmall else FabIconSizeDefault,
153
+ color = iconColor ?: Color.White
154
+ )
155
+
156
+ AnimatedVisibility(
157
+ visible = isExpanded,
158
+ enter = fadeInExpandAnimation,
159
+ exit = fadeOutShrinkAnimation,
160
+ ) {
161
+ Row {
162
+ Spacer(Modifier.width(12.dp))
163
+ Text(
164
+ text = text ?: "",
165
+ color = Color.White,
166
+ style = Typography.actionDefaultBold
167
+ )
168
+ }
169
+ }
170
+ }
171
+ }
172
+ }
173
+ }
174
+
175
+ private val fadeOutShrinkAnimation = fadeOut(
176
+ animationSpec = tween(100, easing = LinearEasing)
177
+ ) + shrinkHorizontally(
178
+ animationSpec = tween(100, easing = LinearEasing),
179
+ shrinkTowards = Alignment.Start
180
+ )
181
+
182
+ private val fadeInExpandAnimation = fadeIn(
183
+ animationSpec = tween(100, easing = LinearEasing)
184
+ ) + expandHorizontally(
185
+ animationSpec = tween(100, easing = LinearEasing),
186
+ expandFrom = Alignment.Start
187
+ )