@momo-kits/native-kits 0.163.1-beta.1 → 0.163.1-beta.2-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 (155) 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 +180 -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 +223 -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 +319 -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/ios/Application/ApplicationEnvironment.swift +1 -0
  135. package/ios/Application/Components.swift +1 -0
  136. package/ios/Application/FloatingButton.swift +1 -0
  137. package/ios/Application/FontScaleStore.swift +59 -0
  138. package/ios/Badge/Badge.swift +1 -0
  139. package/ios/Badge/BadgeRibbon.swift +2 -0
  140. package/ios/Button/Button.swift +1 -1
  141. package/ios/Checkbox/Checkbox.swift +1 -0
  142. package/ios/Information/Information.swift +151 -0
  143. package/ios/Input/Input.swift +1 -0
  144. package/ios/Input/InputPhoneNumber.swift +1 -0
  145. package/ios/Input/InputSearch.swift +3 -0
  146. package/ios/Input/InputTextArea.swift +1 -0
  147. package/ios/OTPKeyboard/KeyboardButton.swift +1 -1
  148. package/ios/Popup/PopupDisplay.swift +6 -0
  149. package/ios/Popup/PopupInput.swift +2 -0
  150. package/ios/Template/TrustBanner/TrustBanner.swift +2 -0
  151. package/ios/Typography/Text.swift +13 -7
  152. package/ios/Typography/Typography.swift +22 -8
  153. package/local.properties +8 -0
  154. package/package.json +1 -1
  155. package/settings.gradle.kts +64 -0
@@ -0,0 +1,223 @@
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.*
9
+ import androidx.compose.foundation.shape.CircleShape
10
+ import androidx.compose.foundation.shape.RoundedCornerShape
11
+ import androidx.compose.runtime.Composable
12
+ import androidx.compose.runtime.getValue
13
+ import androidx.compose.ui.Alignment
14
+ import androidx.compose.ui.Modifier
15
+ import androidx.compose.ui.draw.clip
16
+ import androidx.compose.ui.graphics.Brush
17
+ import androidx.compose.ui.graphics.Color
18
+ import androidx.compose.ui.text.style.TextAlign
19
+ import androidx.compose.ui.text.style.TextOverflow
20
+ import androidx.compose.ui.unit.dp
21
+ import androidx.navigation.NavController
22
+ import androidx.navigation.compose.currentBackStackEntryAsState
23
+ import vn.momo.kits.application.IsShowBaseLineDebug
24
+ import vn.momo.kits.components.*
25
+ import vn.momo.kits.const.*
26
+ import vn.momo.kits.modifier.conditional
27
+ import vn.momo.kits.modifier.noFeedbackClickable
28
+ import vn.momo.kits.platform.getScreenDimensions
29
+
30
+ internal val floatingButtonWidth = 75.dp
31
+ internal const val BOTTOM_TAB_BAR_HEIGHT = 64
32
+
33
+ @Composable
34
+ internal fun BottomTabBar(
35
+ items: List<BottomTabItem>,
36
+ floatingButton: BottomTabFloatingButton? = null,
37
+ navController: NavController,
38
+ onTabSelected: (Int) -> Unit,
39
+ ) {
40
+ val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
41
+ val selectedIndex = items.indexOfFirst { "option${items.indexOf(it)}" == currentRoute }
42
+
43
+ val screenWidth = getScreenDimensions().width.dp
44
+ val mid = items.size / 2
45
+
46
+ val tabWidth = if (floatingButton != null) {
47
+ (screenWidth - floatingButtonWidth) / items.size
48
+ } else {
49
+ screenWidth / items.size
50
+ }
51
+ val adjustSize = if(floatingButton != null && selectedIndex >= mid) floatingButtonWidth else 0.dp
52
+ val targetOffset = tabWidth * selectedIndex + tabWidth / 2 - screenWidth / 2 + adjustSize
53
+
54
+ val indicatorOffsetX by animateDpAsState(
55
+ targetValue = targetOffset,
56
+ animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing),
57
+ label = "IndicatorX"
58
+ )
59
+
60
+ Box(
61
+ contentAlignment = Alignment.BottomCenter
62
+ ) {
63
+ Row(
64
+ modifier = Modifier
65
+ .fillMaxWidth()
66
+ .height(BOTTOM_TAB_BAR_HEIGHT.dp)
67
+ .background(AppTheme.current.colors.background.surface)
68
+ .conditional(IsShowBaseLineDebug) {
69
+ border(1.dp, Colors.blue_03)
70
+ },
71
+ horizontalArrangement = Arrangement.SpaceAround,
72
+ ) {
73
+ if (floatingButton == null) {
74
+ RenderTabBarItem(items, 0, items.size, selectedIndex, onTabSelected)
75
+ } else {
76
+ RenderTabBarItem(items, 0, mid, selectedIndex, onTabSelected)
77
+ Spacer(modifier = Modifier.width(floatingButtonWidth).padding(horizontal = Spacing.XXS))
78
+ RenderTabBarItem(items, mid, items.size, selectedIndex, onTabSelected)
79
+ }
80
+ }
81
+
82
+ Box(modifier = Modifier
83
+ .offset(x = indicatorOffsetX, y = (-BOTTOM_TAB_BAR_HEIGHT + 2).dp)
84
+ .height(2.dp)
85
+ .width(44.dp)
86
+ .background(
87
+ color = AppTheme.current.colors.primary ,
88
+ shape = RoundedCornerShape(bottomStart = 2.dp, bottomEnd = 2.dp)
89
+ )
90
+ )
91
+
92
+ Box(modifier = Modifier
93
+ .fillMaxWidth()
94
+ .height(6.dp)
95
+ .offset(x = 0.dp, y = (-BOTTOM_TAB_BAR_HEIGHT).dp)
96
+ .background(
97
+ brush = Brush.verticalGradient(
98
+ colors = listOf(Color.Transparent, Color.Black.copy(alpha = 0.05f))
99
+ )
100
+ )
101
+ )
102
+ floatingButton?.let { FloatingButton(it) }
103
+ }
104
+ }
105
+
106
+ @Composable
107
+ internal fun RowScope.RenderTabBarItem(
108
+ items: List<BottomTabItem>,
109
+ fromIndex: Int,
110
+ toIndex: Int,
111
+ selectedIndex: Int,
112
+ onTabSelected: (Int) -> Unit
113
+ ){
114
+ for (index in fromIndex until toIndex) {
115
+ Box(modifier = Modifier.weight(1f)) {
116
+ TabBarItem(
117
+ item = items[index],
118
+ selected = index == selectedIndex,
119
+ onClick = { onTabSelected(index) }
120
+ )
121
+ }
122
+ }
123
+ }
124
+
125
+ @Composable
126
+ internal fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> Unit) {
127
+ fun isNumber(label: String): Boolean {
128
+ val numberRegex = "^\\d+$".toRegex()
129
+ return numberRegex.matches(label)
130
+ }
131
+ fun formatLabel(label: String): String? {
132
+ if (isNumber(label) && label.toInt() == 0) {
133
+ return null
134
+ }
135
+ return label
136
+ }
137
+ val badgeLabel = item.badgeLabel?.let { formatLabel(it) }
138
+
139
+ Box(modifier = Modifier
140
+ .fillMaxSize()
141
+ .padding(horizontal = Spacing.XXS)
142
+ .conditional(IsShowBaseLineDebug) {
143
+ border(1.dp, Colors.blue_03)
144
+ }
145
+ .noFeedbackClickable {
146
+ onClick()
147
+ },
148
+ contentAlignment = Alignment.BottomStart
149
+ ){
150
+ Column(
151
+ modifier = Modifier
152
+ .fillMaxSize()
153
+ .padding(horizontal = Spacing.S, vertical = Spacing.S)
154
+ .noFeedbackClickable {
155
+ onClick()
156
+ },
157
+ horizontalAlignment = Alignment.CenterHorizontally,
158
+ verticalArrangement = Arrangement.Center
159
+ ) {
160
+ Icon(
161
+ source = item.icon,
162
+ size = 28.dp,
163
+ modifier = Modifier.padding(Spacing.XS),
164
+ color = if (selected) AppTheme.current.colors.primary else AppTheme.current.colors.text.hint)
165
+ Text(
166
+ text = item.label,
167
+ color = if (selected) AppTheme.current.colors.primary else AppTheme.current.colors.text.hint,
168
+ style = if (selected) Typography.labelXsMedium else Typography.descriptionXsRegular,
169
+ maxLines = 1,
170
+ overflow = TextOverflow.Ellipsis
171
+ )
172
+ }
173
+ if (badgeLabel != null) {
174
+ if (badgeLabel.isEmpty())
175
+ BadgeDot(
176
+ size = DotSize.Small,
177
+ modifier = Modifier.offset(x = 50.dp, y = (-48).dp)
178
+ )
179
+ else
180
+ Badge(badgeLabel, modifier = Modifier.offset(x = 44.dp, y = (-42).dp))
181
+ }
182
+ }
183
+ }
184
+
185
+ @Composable
186
+ internal fun FloatingButton(data: BottomTabFloatingButton) {
187
+ Column(
188
+ modifier = Modifier
189
+ .width(floatingButtonWidth)
190
+ .padding(horizontal = Spacing.XXS, vertical = Spacing.S)
191
+ .conditional(IsShowBaseLineDebug) {
192
+ border(1.dp, Colors.blue_03)
193
+ }
194
+ .noFeedbackClickable {
195
+ data.onPress()
196
+ },
197
+ verticalArrangement = Arrangement.Bottom,
198
+ horizontalAlignment = Alignment.CenterHorizontally
199
+
200
+ ){
201
+ CurvedContainer()
202
+ Box(
203
+ modifier = Modifier
204
+ .size(48.dp)
205
+ .clip(CircleShape)
206
+ .background(AppTheme.current.colors.primary),
207
+ contentAlignment = Alignment.Center
208
+ ) {
209
+ Icon(source = data.icon, color = Color.White, size = 28.dp)
210
+ }
211
+ Spacer(modifier = Modifier.height(4.dp))
212
+ Text(
213
+ text = data.label,
214
+ style = Typography.labelXsMedium,
215
+ color = Colors.black_01,
216
+ textAlign = TextAlign.Center,
217
+ modifier = Modifier
218
+ .height(14.dp)
219
+ .fillMaxWidth()
220
+ .background(AppTheme.current.colors.primary, shape = RoundedCornerShape(size = Radius.XL))
221
+ )
222
+ }
223
+ }
@@ -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
+ internal 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,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
+ )