@momo-kits/native-kits 0.167.1-duo.18-debug → 0.167.1-duo.19

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 (163) hide show
  1. package/CLAUDE.md +5 -3
  2. package/ios/Application/MaxApi.swift +12 -3
  3. package/ios/Colors+Radius+Spacing/Radius.swift +6 -0
  4. package/ios/Layout/GridContext.swift +67 -3
  5. package/ios/Navigation/BottomSheet.swift +30 -4
  6. package/ios/Navigation/BottomTab/BottomTab.swift +0 -2
  7. package/ios/Navigation/Component/SnackBar.swift +0 -1
  8. package/ios/Navigation/HostBackGestureBridge.swift +27 -6
  9. package/ios/Navigation/ModalScreen.swift +0 -1
  10. package/ios/Navigation/Navigation.swift +14 -0
  11. package/ios/Navigation/NavigationContainer.swift +26 -56
  12. package/ios/Navigation/Navigator.swift +102 -165
  13. package/ios/Navigation/StackScreen.swift +19 -2
  14. package/ios/Navigation/StackTransition.swift +400 -0
  15. package/ios/Navigation/SwipeBackHaptic.swift +33 -0
  16. package/package.json +1 -1
  17. package/build.gradle.kts +0 -11
  18. package/compose/build.gradle.kts +0 -204
  19. package/compose/build.gradle.kts.backup +0 -204
  20. package/compose/compose.podspec +0 -47
  21. package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +0 -19
  22. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +0 -6
  23. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +0 -12
  24. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +0 -7
  25. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -153
  26. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +0 -6
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -52
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -124
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -183
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -207
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -39
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -293
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -916
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +0 -269
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -110
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -378
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +0 -56
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +0 -157
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -81
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -32
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -338
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +0 -175
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -357
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -115
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -89
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -128
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +0 -209
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -542
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -49
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -86
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -125
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -231
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -189
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -478
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -114
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -265
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -235
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -223
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -265
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -185
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -343
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +0 -108
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -56
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -41
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -84
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -40
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -361
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -108
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -308
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -59
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +0 -87
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +0 -21
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -90
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +0 -330
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +0 -220
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +0 -473
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +0 -122
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +0 -207
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -92
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -560
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -86
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -125
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -207
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +0 -501
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -176
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +0 -182
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -193
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -236
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -180
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -325
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +0 -20
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +0 -18
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -16
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -194
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -283
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -54
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -50
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +0 -309
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -41
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -52
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -66
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +0 -104
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -48
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -338
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +0 -58
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -137
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -106
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -205
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -393
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +0 -8
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -713
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +0 -148
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +0 -35
  124. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -218
  125. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -231
  126. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
  127. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +0 -98
  128. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +0 -312
  129. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -169
  130. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -447
  131. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -103
  132. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +0 -106
  133. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +0 -74
  134. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -379
  135. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -39
  136. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -363
  137. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +0 -192
  138. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -133
  139. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +0 -127
  140. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +0 -12
  141. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -91
  142. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +0 -9
  143. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -74
  144. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +0 -13
  145. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
  146. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -55
  147. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
  148. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -118
  149. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +0 -78
  150. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +0 -45
  151. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +0 -32
  152. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +0 -38
  153. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +0 -66
  154. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -217
  155. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +0 -94
  156. package/gradle/libs.versions.toml +0 -69
  157. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  158. package/gradle/wrapper/gradle-wrapper.properties +0 -8
  159. package/gradle.properties +0 -26
  160. package/gradlew +0 -252
  161. package/gradlew.bat +0 -94
  162. package/ios-demo/README.md +0 -120
  163. package/settings.gradle.kts +0 -64
@@ -1,86 +0,0 @@
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
- }
@@ -1,98 +0,0 @@
1
- package vn.momo.kits.navigation.bottomtab
2
-
3
- import androidx.compose.runtime.Composable
4
- import androidx.compose.runtime.Stable
5
- import androidx.compose.runtime.compositionLocalOf
6
- import androidx.compose.runtime.getValue
7
- import androidx.compose.runtime.mutableStateOf
8
- import androidx.compose.runtime.remember
9
- import androidx.compose.runtime.setValue
10
- import androidx.compose.ui.unit.Dp
11
- import vn.momo.kits.const.Spacing
12
- import vn.momo.kits.layout.DuoMetrics
13
- import vn.momo.kits.layout.ScreenLayout
14
-
15
- // iPhone Duo: which side bar bar starts compressed when the strip cannot hold both whole.
16
- enum class DuoBarCompressionBehavior { TOOLBAR, TAB_BAR }
17
-
18
- private enum class DuoBar { TOOLBAR, TAB_BAR }
19
-
20
- @Stable
21
- internal class DuoBarCompression(
22
- val toolbarCompressed: Boolean = false,
23
- val tabBarCompressed: Boolean = false,
24
- // the open rail needs the toolbar button slot too: only Back stays
25
- val toolbarHidden: Boolean = false,
26
- val expandToolbar: () -> Unit = {},
27
- val expandTabBar: () -> Unit = {},
28
- // a rail opened from the compressed state closes once a tab is picked
29
- val onTabSelected: () -> Unit = {},
30
- // side bar columns report their full height so the tab bar knows what is left
31
- val setToolbarHeight: (Dp) -> Unit = {},
32
- )
33
-
34
- internal val LocalDuoBarCompression = compositionLocalOf { DuoBarCompression() }
35
-
36
- // space kept between the top and the bottom groups (HIG Vertical controls)
37
- internal val DUO_GROUP_GAP = Spacing.M
38
- // Back, then the right toolkit: help + home capsule
39
- private val DEFAULT_TOOLBAR_HEIGHT = DuoMetrics.controlSize + Spacing.M + DuoMetrics.itemPitch * 2
40
- // Back, then the single button the toolbar folds into
41
- private val COMPRESSED_TOOLBAR_HEIGHT = DuoMetrics.controlSize * 2 + Spacing.M
42
- // Back alone, while a rail opened from the compressed state takes the toolbar slot
43
- private val BACK_ONLY_HEIGHT = DuoMetrics.controlSize
44
-
45
- // Port of RN DuoBarCompression (HIG, Designing for iPhone Duo: Vertical controls):
46
- // - both fit: both stay whole
47
- // - one fits whole beside the other compressed: one is compressed, tapping it swaps them
48
- // - otherwise both rest compressed; tapping one opens it for a moment (the rail closes
49
- // again once a tab is picked)
50
- @Composable
51
- internal fun rememberDuoBarCompression(
52
- behavior: DuoBarCompressionBehavior,
53
- tabCount: Int,
54
- hasQr: Boolean,
55
- layout: ScreenLayout,
56
- ): DuoBarCompression {
57
- var toolbarHeight by remember { mutableStateOf(DEFAULT_TOOLBAR_HEIGHT) }
58
- var expanded by remember(behavior) {
59
- mutableStateOf(if (behavior == DuoBarCompressionBehavior.TOOLBAR) DuoBar.TAB_BAR else DuoBar.TOOLBAR)
60
- }
61
- var opened by remember { mutableStateOf<DuoBar?>(null) }
62
- val setToolbarHeight = remember { { height: Dp -> toolbarHeight = height } }
63
- val sideBar = layout.sideBar
64
-
65
- return remember(sideBar, layout.height, tabCount, hasQr, toolbarHeight, expanded, opened) {
66
- if (sideBar == null) return@remember DuoBarCompression()
67
- val room = layout.height - sideBar.bottom - sideBar.top - DUO_GROUP_GAP
68
- val tabBar = duoRailHeight(tabCount, hasQr)
69
- val compressedTabBar = duoRailHeight(1, hasQr, compressed = true)
70
- fun fits(toolbar: Dp, tabs: Dp) = toolbar + tabs <= room
71
-
72
- when {
73
- fits(toolbarHeight, tabBar) -> DuoBarCompression(setToolbarHeight = setToolbarHeight)
74
- fits(COMPRESSED_TOOLBAR_HEIGHT, tabBar) && fits(toolbarHeight, compressedTabBar) -> DuoBarCompression(
75
- toolbarCompressed = expanded == DuoBar.TAB_BAR,
76
- tabBarCompressed = expanded == DuoBar.TOOLBAR,
77
- expandToolbar = { expanded = DuoBar.TOOLBAR },
78
- expandTabBar = { expanded = DuoBar.TAB_BAR },
79
- setToolbarHeight = setToolbarHeight,
80
- )
81
- opened == DuoBar.TAB_BAR && fits(BACK_ONLY_HEIGHT, tabBar) -> DuoBarCompression(
82
- toolbarCompressed = true,
83
- toolbarHidden = true,
84
- expandToolbar = { opened = DuoBar.TOOLBAR },
85
- expandTabBar = { opened = DuoBar.TAB_BAR },
86
- onTabSelected = { opened = null },
87
- setToolbarHeight = setToolbarHeight,
88
- )
89
- else -> DuoBarCompression(
90
- toolbarCompressed = opened != DuoBar.TOOLBAR,
91
- tabBarCompressed = true,
92
- expandToolbar = { opened = DuoBar.TOOLBAR },
93
- expandTabBar = { opened = DuoBar.TAB_BAR },
94
- setToolbarHeight = setToolbarHeight,
95
- )
96
- }
97
- }
98
- }
@@ -1,312 +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.XXS
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 and its gap take, so the header side bar stays above it.
72
- internal val LocalDuoRailReserve = compositionLocalOf { 0.dp }
73
-
74
- // Rail plus the QR button under it; compressed, the rail is a single button.
75
- internal fun duoRailHeight(itemCount: Int, hasQr: Boolean, compressed: Boolean = false): Dp {
76
- val rail = if (compressed) ITEM_SIZE
77
- else RAIL_PADDING * 2 + ITEM_SIZE * itemCount + RAIL_GAP * (itemCount - 1).coerceAtLeast(0)
78
- return rail + if (hasQr) Spacing.M + ITEM_SIZE else 0.dp
79
- }
80
-
81
- // Space tab content leaves under the floating bar; 0 when the rail sits in the side strip or off Duo.
82
- internal fun duoFloatingTabBarInset(layout: ScreenLayout): Dp =
83
- if (layout.isIPhoneDuo && layout.sideBar == null) FLOATING_HEIGHT + Spacing.M + layout.insetBottom else 0.dp
84
-
85
- // iPhone Duo tab bar: glass rail in the side bar, or a floating glass bar when unfolded portrait.
86
- @Composable
87
- internal fun BoxScope.DuoTabBar(
88
- layout: ScreenLayout,
89
- items: List<BottomTabItem>,
90
- floatingButton: BottomTabFloatingButton?,
91
- selectedIndex: Int,
92
- onTabSelected: (Int) -> Unit,
93
- compression: DuoBarCompression = DuoBarCompression(),
94
- ) {
95
- if (layout.sideBar != null) {
96
- DuoRailTabBar(layout, items, floatingButton, selectedIndex, onTabSelected, compression)
97
- } else {
98
- DuoFloatingTabBar(layout, items, floatingButton, selectedIndex, onTabSelected)
99
- }
100
- }
101
-
102
- // Vertical bar anchored to the bottom of the side bar, QR button below it (Figma MT1-C/E). When the
103
- // side bar is short it can shrink to one button showing the current tab (HIG "Tab bar compressed");
104
- // tapping it expands the rail and compresses the toolbar.
105
- @Composable
106
- private fun BoxScope.DuoRailTabBar(
107
- layout: ScreenLayout,
108
- items: List<BottomTabItem>,
109
- floatingButton: BottomTabFloatingButton?,
110
- selectedIndex: Int,
111
- onTabSelected: (Int) -> Unit,
112
- compression: DuoBarCompression,
113
- ) {
114
- val sideBar = layout.sideBar ?: return
115
- val isRight = sideBar.edge == SideBarEdge.RIGHT
116
-
117
- Column(
118
- modifier = Modifier
119
- .align(if (isRight) AbsoluteAlignment.BottomRight else AbsoluteAlignment.BottomLeft)
120
- .width(sideBar.width)
121
- .padding(bottom = sideBar.bottom)
122
- .absolutePadding(left = if (isRight) Spacing.M else 0.dp, right = if (isRight) 0.dp else Spacing.M),
123
- horizontalAlignment = if (isRight) AbsoluteAlignment.Left else AbsoluteAlignment.Right,
124
- verticalArrangement = Arrangement.spacedBy(Spacing.M),
125
- ) {
126
- val current = items.getOrNull(selectedIndex)
127
- if (compression.tabBarCompressed && current != null) {
128
- Box(
129
- modifier = Modifier
130
- .size(ITEM_SIZE)
131
- .tabBarMaterial(CircleShape)
132
- .noFeedbackClickable(onClick = compression.expandTabBar)
133
- .setAutomationId("bottom_tab_bar"),
134
- contentAlignment = Alignment.Center,
135
- ) {
136
- DuoTabIcon(current, selected = true)
137
- }
138
- } else {
139
- Rail(items, selectedIndex) {
140
- onTabSelected(it)
141
- compression.onTabSelected()
142
- }
143
- }
144
- if (floatingButton != null) {
145
- QrButton(floatingButton, size = ITEM_SIZE)
146
- }
147
- }
148
- }
149
-
150
- // Glass column of tab items with the sliding selection bubble.
151
- @Composable
152
- private fun Rail(items: List<BottomTabItem>, selectedIndex: Int, onItemClick: (Int) -> Unit) {
153
- val bubbleOffset by animateDpAsState(
154
- targetValue = (ITEM_SIZE + RAIL_GAP) * selectedIndex.coerceAtLeast(0),
155
- animationSpec = BUBBLE_SPRING,
156
- label = "DuoRailBubble",
157
- )
158
- Box(
159
- Modifier
160
- .width(ITEM_SIZE)
161
- .tabBarMaterial(RoundedCornerShape(Radius.XL))
162
- .setAutomationId("bottom_tab_bar")
163
- ) {
164
- Box(
165
- Modifier
166
- .offset(
167
- x = (ITEM_SIZE - RAIL_BUBBLE_WIDTH) / 2,
168
- y = RAIL_PADDING - (RAIL_BUBBLE_HEIGHT - ITEM_SIZE) / 2 + bubbleOffset,
169
- )
170
- .size(RAIL_BUBBLE_WIDTH, RAIL_BUBBLE_HEIGHT)
171
- .background(AppTheme.current.colors.background.selected, CircleShape)
172
- )
173
- Column(
174
- modifier = Modifier.padding(vertical = RAIL_PADDING),
175
- verticalArrangement = Arrangement.spacedBy(RAIL_GAP),
176
- ) {
177
- items.forEachIndexed { index, item ->
178
- Box(
179
- modifier = Modifier
180
- .size(ITEM_SIZE)
181
- .noFeedbackClickable { onItemClick(index) },
182
- contentAlignment = Alignment.Center,
183
- ) {
184
- DuoTabIcon(item, selected = index == selectedIndex)
185
- }
186
- }
187
- }
188
- }
189
- }
190
-
191
- @Composable
192
- private fun BoxScope.DuoFloatingTabBar(
193
- layout: ScreenLayout,
194
- items: List<BottomTabItem>,
195
- floatingButton: BottomTabFloatingButton?,
196
- selectedIndex: Int,
197
- onTabSelected: (Int) -> Unit,
198
- ) {
199
- val columnWidth = (layout.width - Spacing.M * 2 - Spacing.M * (GRID_COLUMNS - 1)) / GRID_COLUMNS
200
- val barWidth = columnWidth * GRID_SPAN + Spacing.M * (GRID_SPAN - 1)
201
- val useQr = floatingButton != null && items.size % 2 == 0
202
- val middle = items.size / 2
203
- val cellCount = items.size + if (useQr) 1 else 0
204
- val visualIndex = if (useQr && selectedIndex >= middle) selectedIndex + 1 else selectedIndex
205
-
206
- Box(
207
- modifier = Modifier
208
- .align(Alignment.BottomCenter)
209
- .padding(bottom = layout.insetBottom + Spacing.M)
210
- .width(barWidth)
211
- .height(FLOATING_HEIGHT)
212
- .tabBarMaterial(RoundedCornerShape(FLOATING_HEIGHT / 2))
213
- .padding(FLOATING_PADDING)
214
- .setAutomationId("bottom_tab_bar")
215
- ) {
216
- BoxWithConstraints(Modifier.fillMaxSize()) {
217
- val cellWidth = maxWidth / cellCount
218
- val bubbleOffset by animateDpAsState(
219
- targetValue = cellWidth * visualIndex.coerceAtLeast(0),
220
- animationSpec = BUBBLE_SPRING,
221
- label = "DuoFloatingBubble",
222
- )
223
- Box(
224
- Modifier
225
- .offset(x = bubbleOffset)
226
- .width(cellWidth)
227
- .fillMaxHeight()
228
- .background(AppTheme.current.colors.background.selected, CircleShape)
229
- )
230
- Row(Modifier.fillMaxSize()) {
231
- items.forEachIndexed { index, item ->
232
- if (useQr && index == middle) {
233
- Box(Modifier.weight(1f).fillMaxHeight(), contentAlignment = Alignment.Center) {
234
- QrButton(floatingButton, size = FLOATING_QR_SIZE)
235
- }
236
- }
237
- FloatingCell(item, selected = index == selectedIndex) { onTabSelected(index) }
238
- }
239
- }
240
- }
241
- }
242
- }
243
-
244
- @Composable
245
- private fun RowScope.FloatingCell(item: BottomTabItem, selected: Boolean, onClick: () -> Unit) {
246
- val colors = AppTheme.current.colors
247
- Column(
248
- modifier = Modifier
249
- .weight(1f)
250
- .fillMaxHeight()
251
- .noFeedbackClickable(onClick = onClick),
252
- horizontalAlignment = Alignment.CenterHorizontally,
253
- verticalArrangement = Arrangement.spacedBy(Spacing.XXS, Alignment.CenterVertically),
254
- ) {
255
- DuoTabIcon(item, selected)
256
- Text(
257
- text = item.label,
258
- style = Typography.labelXsMedium,
259
- color = if (selected) colors.primary else colors.text.default,
260
- maxLines = 1,
261
- overflow = TextOverflow.Ellipsis,
262
- )
263
- }
264
- }
265
-
266
- @Composable
267
- private fun DuoTabIcon(item: BottomTabItem, selected: Boolean) {
268
- val colors = AppTheme.current.colors
269
- val badgeLabel = item.badgeLabel?.takeUnless { it.toIntOrNull() == 0 }
270
- Box {
271
- Icon(
272
- source = item.icon,
273
- size = ICON_SIZE,
274
- color = if (selected) colors.primary else colors.text.default,
275
- )
276
- when {
277
- badgeLabel == null && !item.showDot -> Unit
278
- badgeLabel.isNullOrEmpty() -> BadgeDot(
279
- size = DotSize.Small,
280
- modifier = Modifier.align(Alignment.TopEnd),
281
- )
282
- else -> Badge(
283
- badgeLabel,
284
- modifier = Modifier.align(Alignment.TopStart).offset(x = ICON_SIZE / 2, y = -Spacing.XS),
285
- )
286
- }
287
- }
288
- }
289
-
290
- @Composable
291
- private fun QrButton(data: BottomTabFloatingButton, size: Dp) {
292
- Box(
293
- modifier = Modifier
294
- .size(size)
295
- .clip(CircleShape)
296
- .background(AppTheme.current.colors.primary)
297
- .noFeedbackClickable(onClick = data.onPress)
298
- .setAutomationId(data.label),
299
- contentAlignment = Alignment.Center,
300
- ) {
301
- Icon(source = data.icon, color = Color.White, size = QR_ICON_SIZE)
302
- }
303
- }
304
-
305
- // RN falls back to this when native glass is missing; Compose has no backdrop blur, so it always does.
306
- @Composable
307
- private fun Modifier.tabBarMaterial(shape: Shape): Modifier {
308
- val colors = AppTheme.current.colors
309
- return shadow(3.dp, shape)
310
- .background(colors.background.surface)
311
- .border(1.dp, colors.border.default, shape)
312
- }
@@ -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
- )