@momo-kits/native-kits 0.167.1-duo.3-debug → 0.167.1-duo.4

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 (189) hide show
  1. package/CLAUDE.md +17 -8
  2. package/ios/Application/ApplicationEnvironment.swift +6 -0
  3. package/ios/Application/Components.swift +1 -3
  4. package/ios/Application/HeaderRight.swift +19 -27
  5. package/ios/Application/Navigation/BottomTab/BottomTab.swift +9 -30
  6. package/ios/Application/Navigation/BottomTab/BottomTabBar.swift +2 -5
  7. package/ios/Application/Navigation/HeaderUser.swift +1 -7
  8. package/ios/Application/Navigation/NavigationContainer.swift +45 -45
  9. package/ios/Application/Navigation/Navigator.swift +122 -29
  10. package/ios/Application/Navigation/NavigatorManager.swift +13 -6
  11. package/ios/Application/Navigation/Overplay/BottomSheet.swift +4 -41
  12. package/ios/Application/Navigation/Overplay/ModalScreen.swift +1 -5
  13. package/ios/Application/Navigation/component/Header.swift +4 -12
  14. package/ios/Application/Navigation/component/HeaderBackground.swift +2 -3
  15. package/ios/Application/Navigation/component/NavigationFooter.swift +1 -1
  16. package/ios/Application/Navigation/component/NavigationHeaderRight.swift +9 -61
  17. package/ios/Application/StackScreen.swift +18 -135
  18. package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +59 -5
  19. package/ios/Colors+Radius+Spacing/Colors.swift +1 -1
  20. package/ios/Extensions/Color++.swift +20 -9
  21. package/ios/Extensions/OptionalEnvironmentObject.swift +12 -0
  22. package/ios/Icon/Icon.swift +14 -22
  23. package/ios/Icon/Icons.swift +1329 -0
  24. package/ios/Image/Image.swift +16 -5
  25. package/ios/Lottie/LottieView.swift +4 -2
  26. package/ios/Popup/PopupDisplay.swift +68 -84
  27. package/ios/Popup/PopupNotify.swift +32 -43
  28. package/ios/Popup/PopupPromotion.swift +6 -18
  29. package/ios/Resources/dot_loading.json +558 -0
  30. package/ios/Resources/lottie_circle_loader.json +1 -0
  31. package/ios/Template/TrustBanner/TrustBanner.swift +69 -31
  32. package/ios/native-kits.podspec +1 -1
  33. package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
  34. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +0 -1
  35. package/package.json +1 -1
  36. package/scripts/gen-ios-icons.mjs +36 -0
  37. package/.claude/scheduled_tasks.lock +0 -1
  38. package/.claude/settings.local.json +0 -185
  39. package/build.gradle.kts +0 -11
  40. package/compose/build.gradle.kts +0 -204
  41. package/compose/build.gradle.kts.backup +0 -204
  42. package/compose/compose.podspec +0 -47
  43. package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +0 -19
  44. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +0 -6
  45. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +0 -12
  46. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +0 -7
  47. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -153
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -52
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -124
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -183
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -207
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -39
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -293
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -831
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +0 -269
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -110
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -378
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +0 -56
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +0 -157
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -82
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -32
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -338
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +0 -175
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -356
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -113
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -89
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -128
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +0 -209
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -542
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -49
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -76
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -125
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -231
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -189
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -475
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -114
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -266
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -240
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -223
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -264
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -185
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -343
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +0 -108
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -56
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -41
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -84
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -40
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -361
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -108
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -310
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -59
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +0 -87
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +0 -21
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -90
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +0 -323
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +0 -220
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +0 -473
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +0 -122
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +0 -199
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -91
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -552
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -88
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -125
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -207
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +0 -498
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -176
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +0 -182
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -193
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -234
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -188
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -325
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +0 -16
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +0 -18
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -16
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -188
  124. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -297
  125. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -54
  126. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -50
  127. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +0 -187
  128. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -41
  129. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -52
  130. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -66
  131. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
  132. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +0 -104
  133. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -48
  134. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
  135. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -338
  136. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +0 -58
  137. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -137
  138. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -106
  139. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -205
  140. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -393
  141. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +0 -8
  142. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -648
  143. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +0 -148
  144. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +0 -35
  145. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -194
  146. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -232
  147. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
  148. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +0 -281
  149. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -169
  150. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -408
  151. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -108
  152. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +0 -105
  153. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +0 -74
  154. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -354
  155. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -37
  156. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -363
  157. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +0 -169
  158. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -132
  159. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +0 -127
  160. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +0 -12
  161. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -91
  162. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +0 -9
  163. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -74
  164. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
  165. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -55
  166. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
  167. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -118
  168. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +0 -40
  169. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +0 -45
  170. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +0 -32
  171. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +0 -38
  172. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +0 -66
  173. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -217
  174. package/gradle/libs.versions.toml +0 -69
  175. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  176. package/gradle/wrapper/gradle-wrapper.properties +0 -8
  177. package/gradle.properties +0 -26
  178. package/gradlew +0 -252
  179. package/gradlew.bat +0 -94
  180. package/ios/Application/Layout/ScreenLayout.swift +0 -196
  181. package/ios/Application/Navigation/BottomTab/DuoTabBar.swift +0 -275
  182. package/ios/Application/Navigation/component/DuoScrollGradient.swift +0 -55
  183. package/ios/Application/Navigation/component/SideBar.swift +0 -155
  184. package/ios/Popup/PopupLayout.swift +0 -45
  185. package/ios-demo/README.md +0 -120
  186. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DuoTabDemo.swift +0 -76
  187. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IPhoneDuoDemo.swift +0 -170
  188. package/local.properties +0 -8
  189. package/settings.gradle.kts +0 -64
@@ -1,281 +0,0 @@
1
- package vn.momo.kits.navigation.bottomtab
2
-
3
- import androidx.compose.animation.core.Spring
4
- import androidx.compose.animation.core.animateDpAsState
5
- import androidx.compose.animation.core.spring
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.BoxScope
11
- import androidx.compose.foundation.layout.BoxWithConstraints
12
- import androidx.compose.foundation.layout.Column
13
- import androidx.compose.foundation.layout.Row
14
- import androidx.compose.foundation.layout.RowScope
15
- import androidx.compose.foundation.layout.absolutePadding
16
- import androidx.compose.foundation.layout.fillMaxHeight
17
- import androidx.compose.foundation.layout.fillMaxSize
18
- import androidx.compose.foundation.layout.height
19
- import androidx.compose.foundation.layout.offset
20
- import androidx.compose.foundation.layout.padding
21
- import androidx.compose.foundation.layout.size
22
- import androidx.compose.foundation.layout.width
23
- import androidx.compose.foundation.shape.CircleShape
24
- import androidx.compose.foundation.shape.RoundedCornerShape
25
- import androidx.compose.runtime.Composable
26
- import androidx.compose.runtime.compositionLocalOf
27
- import androidx.compose.runtime.getValue
28
- import androidx.compose.ui.AbsoluteAlignment
29
- import androidx.compose.ui.Alignment
30
- import androidx.compose.ui.Modifier
31
- import androidx.compose.ui.draw.clip
32
- import androidx.compose.ui.draw.shadow
33
- import androidx.compose.ui.graphics.Color
34
- import androidx.compose.ui.graphics.Shape
35
- import androidx.compose.ui.text.style.TextOverflow
36
- import androidx.compose.ui.unit.Dp
37
- import androidx.compose.ui.unit.dp
38
- import vn.momo.kits.components.Badge
39
- import vn.momo.kits.components.BadgeDot
40
- import vn.momo.kits.components.DotSize
41
- import vn.momo.kits.components.Icon
42
- import vn.momo.kits.components.Text
43
- import vn.momo.kits.const.AppTheme
44
- import vn.momo.kits.const.Radius
45
- import vn.momo.kits.const.Spacing
46
- import vn.momo.kits.const.Typography
47
- import vn.momo.kits.layout.DuoMetrics
48
- import vn.momo.kits.layout.ScreenLayout
49
- import vn.momo.kits.layout.SideBarEdge
50
- import vn.momo.kits.modifier.noFeedbackClickable
51
- import vn.momo.kits.modifier.setAutomationId
52
-
53
- // Figma Fold-Design-System MT1/MT2, mirrored from RN DuoTabBar.tsx.
54
- private val ITEM_SIZE = DuoMetrics.controlSize
55
- private val ICON_SIZE = 28.dp
56
- private val QR_ICON_SIZE = 24.dp
57
- private val RAIL_PADDING = 6.dp
58
- private val RAIL_GAP = Spacing.XS
59
- // selection bubble overflows the 48 rail cell by 5 above and below
60
- private val RAIL_BUBBLE_WIDTH = 44.dp
61
- private val RAIL_BUBBLE_HEIGHT = 58.dp
62
- private val FLOATING_HEIGHT = 62.dp
63
- private val FLOATING_PADDING = Spacing.XS
64
- private val FLOATING_QR_SIZE = 44.dp
65
- // floating bar spans 6 of the 8 portrait grid columns, like the content
66
- private const val GRID_COLUMNS = 8
67
- private const val GRID_SPAN = 6
68
- // RN reanimated spring { damping: 18, stiffness: 220, mass: 0.8 }
69
- private val BUBBLE_SPRING = spring<Dp>(dampingRatio = 0.68f, stiffness = 275f, visibilityThreshold = Dp(0.5f))
70
-
71
- // Space from the window bottom the rail stack takes, so the header side bar stays above it.
72
- internal val LocalDuoRailHeight = compositionLocalOf { 0.dp }
73
-
74
- internal fun duoRailHeight(itemCount: Int, hasQr: Boolean): Dp =
75
- Spacing.XL + RAIL_PADDING * 2 + ITEM_SIZE * itemCount + RAIL_GAP * (itemCount - 1).coerceAtLeast(0) +
76
- if (hasQr) Spacing.M + ITEM_SIZE else 0.dp
77
-
78
- // Space tab content leaves under the floating bar; 0 when the rail sits in the side strip or off Duo.
79
- internal fun duoFloatingTabBarInset(layout: ScreenLayout): Dp =
80
- if (layout.isIPhoneDuo && layout.sideBar == null) FLOATING_HEIGHT + Spacing.M + layout.insetBottom else 0.dp
81
-
82
- // iPhone Duo tab bar: glass rail in the side bar, or a floating glass bar when unfolded portrait.
83
- @Composable
84
- internal fun BoxScope.DuoTabBar(
85
- layout: ScreenLayout,
86
- items: List<BottomTabItem>,
87
- floatingButton: BottomTabFloatingButton?,
88
- selectedIndex: Int,
89
- onTabSelected: (Int) -> Unit,
90
- ) {
91
- if (layout.sideBar != null) {
92
- DuoRailTabBar(layout, items, floatingButton, selectedIndex, onTabSelected)
93
- } else {
94
- DuoFloatingTabBar(layout, items, floatingButton, selectedIndex, onTabSelected)
95
- }
96
- }
97
-
98
- @Composable
99
- private fun BoxScope.DuoRailTabBar(
100
- layout: ScreenLayout,
101
- items: List<BottomTabItem>,
102
- floatingButton: BottomTabFloatingButton?,
103
- selectedIndex: Int,
104
- onTabSelected: (Int) -> Unit,
105
- ) {
106
- val sideBar = layout.sideBar ?: return
107
- val isRight = sideBar.edge == SideBarEdge.RIGHT
108
- val bubbleOffset by animateDpAsState(
109
- targetValue = (ITEM_SIZE + RAIL_GAP) * selectedIndex.coerceAtLeast(0),
110
- animationSpec = BUBBLE_SPRING,
111
- label = "DuoRailBubble",
112
- )
113
-
114
- Column(
115
- modifier = Modifier
116
- .align(if (isRight) AbsoluteAlignment.BottomRight else AbsoluteAlignment.BottomLeft)
117
- .width(sideBar.width)
118
- .padding(bottom = Spacing.XL)
119
- .absolutePadding(left = if (isRight) Spacing.M else 0.dp, right = if (isRight) 0.dp else Spacing.M),
120
- horizontalAlignment = if (isRight) AbsoluteAlignment.Left else AbsoluteAlignment.Right,
121
- verticalArrangement = Arrangement.spacedBy(Spacing.M),
122
- ) {
123
- Box(
124
- Modifier
125
- .width(ITEM_SIZE)
126
- .tabBarMaterial(RoundedCornerShape(Radius.XL))
127
- .setAutomationId("bottom_tab_bar")
128
- ) {
129
- Box(
130
- Modifier
131
- .offset(
132
- x = (ITEM_SIZE - RAIL_BUBBLE_WIDTH) / 2,
133
- y = RAIL_PADDING - (RAIL_BUBBLE_HEIGHT - ITEM_SIZE) / 2 + bubbleOffset,
134
- )
135
- .size(RAIL_BUBBLE_WIDTH, RAIL_BUBBLE_HEIGHT)
136
- .background(AppTheme.current.colors.background.selected, CircleShape)
137
- )
138
- Column(
139
- modifier = Modifier.padding(vertical = RAIL_PADDING),
140
- verticalArrangement = Arrangement.spacedBy(RAIL_GAP),
141
- ) {
142
- items.forEachIndexed { index, item ->
143
- Box(
144
- modifier = Modifier
145
- .size(ITEM_SIZE)
146
- .noFeedbackClickable { onTabSelected(index) },
147
- contentAlignment = Alignment.Center,
148
- ) {
149
- DuoTabIcon(item, selected = index == selectedIndex)
150
- }
151
- }
152
- }
153
- }
154
- if (floatingButton != null) {
155
- QrButton(floatingButton, size = ITEM_SIZE)
156
- }
157
- }
158
- }
159
-
160
- @Composable
161
- private fun BoxScope.DuoFloatingTabBar(
162
- layout: ScreenLayout,
163
- items: List<BottomTabItem>,
164
- floatingButton: BottomTabFloatingButton?,
165
- selectedIndex: Int,
166
- onTabSelected: (Int) -> Unit,
167
- ) {
168
- val columnWidth = (layout.width - Spacing.M * 2 - Spacing.M * (GRID_COLUMNS - 1)) / GRID_COLUMNS
169
- val barWidth = columnWidth * GRID_SPAN + Spacing.M * (GRID_SPAN - 1)
170
- val useQr = floatingButton != null && items.size % 2 == 0
171
- val middle = items.size / 2
172
- val cellCount = items.size + if (useQr) 1 else 0
173
- val visualIndex = if (useQr && selectedIndex >= middle) selectedIndex + 1 else selectedIndex
174
-
175
- Box(
176
- modifier = Modifier
177
- .align(Alignment.BottomCenter)
178
- .padding(bottom = layout.insetBottom + Spacing.M)
179
- .width(barWidth)
180
- .height(FLOATING_HEIGHT)
181
- .tabBarMaterial(RoundedCornerShape(FLOATING_HEIGHT / 2))
182
- .padding(FLOATING_PADDING)
183
- .setAutomationId("bottom_tab_bar")
184
- ) {
185
- BoxWithConstraints(Modifier.fillMaxSize()) {
186
- val cellWidth = maxWidth / cellCount
187
- val bubbleOffset by animateDpAsState(
188
- targetValue = cellWidth * visualIndex.coerceAtLeast(0),
189
- animationSpec = BUBBLE_SPRING,
190
- label = "DuoFloatingBubble",
191
- )
192
- Box(
193
- Modifier
194
- .offset(x = bubbleOffset)
195
- .width(cellWidth)
196
- .fillMaxHeight()
197
- .background(AppTheme.current.colors.background.selected, CircleShape)
198
- )
199
- Row(Modifier.fillMaxSize()) {
200
- items.forEachIndexed { index, item ->
201
- if (useQr && index == middle) {
202
- Box(Modifier.weight(1f).fillMaxHeight(), contentAlignment = Alignment.Center) {
203
- QrButton(floatingButton, size = FLOATING_QR_SIZE)
204
- }
205
- }
206
- FloatingCell(item, selected = index == selectedIndex) { onTabSelected(index) }
207
- }
208
- }
209
- }
210
- }
211
- }
212
-
213
- @Composable
214
- private fun RowScope.FloatingCell(item: BottomTabItem, selected: Boolean, onClick: () -> Unit) {
215
- val colors = AppTheme.current.colors
216
- Column(
217
- modifier = Modifier
218
- .weight(1f)
219
- .fillMaxHeight()
220
- .noFeedbackClickable(onClick = onClick),
221
- horizontalAlignment = Alignment.CenterHorizontally,
222
- verticalArrangement = Arrangement.spacedBy(Spacing.XXS, Alignment.CenterVertically),
223
- ) {
224
- DuoTabIcon(item, selected)
225
- Text(
226
- text = item.label,
227
- style = Typography.labelXsMedium,
228
- color = if (selected) colors.primary else colors.text.default,
229
- maxLines = 1,
230
- overflow = TextOverflow.Ellipsis,
231
- )
232
- }
233
- }
234
-
235
- @Composable
236
- private fun DuoTabIcon(item: BottomTabItem, selected: Boolean) {
237
- val colors = AppTheme.current.colors
238
- val badgeLabel = item.badgeLabel?.takeUnless { it.toIntOrNull() == 0 }
239
- Box {
240
- Icon(
241
- source = item.icon,
242
- size = ICON_SIZE,
243
- color = if (selected) colors.primary else colors.text.default,
244
- )
245
- when {
246
- badgeLabel == null && !item.showDot -> Unit
247
- badgeLabel.isNullOrEmpty() -> BadgeDot(
248
- size = DotSize.Small,
249
- modifier = Modifier.align(Alignment.TopEnd),
250
- )
251
- else -> Badge(
252
- badgeLabel,
253
- modifier = Modifier.align(Alignment.TopStart).offset(x = ICON_SIZE / 2, y = -Spacing.XS),
254
- )
255
- }
256
- }
257
- }
258
-
259
- @Composable
260
- private fun QrButton(data: BottomTabFloatingButton, size: Dp) {
261
- Box(
262
- modifier = Modifier
263
- .size(size)
264
- .clip(CircleShape)
265
- .background(AppTheme.current.colors.primary)
266
- .noFeedbackClickable(onClick = data.onPress)
267
- .setAutomationId(data.label),
268
- contentAlignment = Alignment.Center,
269
- ) {
270
- Icon(source = data.icon, color = Color.White, size = QR_ICON_SIZE)
271
- }
272
- }
273
-
274
- // RN falls back to this when native glass is missing; Compose has no backdrop blur, so it always does.
275
- @Composable
276
- private fun Modifier.tabBarMaterial(shape: Shape): Modifier {
277
- val colors = AppTheme.current.colors
278
- return shadow(3.dp, shape)
279
- .background(colors.background.surface)
280
- .border(1.dp, colors.border.default, shape)
281
- }
@@ -1,169 +0,0 @@
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
- )