@momo-kits/native-kits 0.167.1-sizescale.2-debug → 0.168.1-beta.1-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 (223) hide show
  1. package/CLAUDE.md +45 -3
  2. package/README.md +2 -2
  3. package/compose/build.gradle.kts +9 -1
  4. package/compose/build.gradle.kts.backup +8 -0
  5. package/compose/compose.podspec +1 -1
  6. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +3 -4
  7. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +6 -0
  8. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +353 -113
  9. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +2 -2
  10. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +20 -21
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +2 -1
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +4 -2
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +1 -1
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +24 -14
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +1 -1
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +1 -1
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +9 -6
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +11 -12
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +4 -9
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +2 -2
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +10 -9
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +6 -6
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +55 -22
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +12 -1
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +7 -9
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +1 -1
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +16 -9
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +13 -5
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +3 -2
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +14 -6
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +19 -21
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +2 -2
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +6 -3
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +3 -1
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +7 -15
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +11 -15
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +10 -4
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +7 -23
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +13 -11
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +21 -9
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +309 -0
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +11 -9
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +43 -4
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +8 -0
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +10 -0
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +8 -7
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +6 -0
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +197 -60
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +134 -81
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +54 -62
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +98 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +312 -0
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +126 -29
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +34 -14
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +3 -3
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +1 -2
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +116 -26
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +4 -2
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +2 -3
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +192 -0
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +2 -1
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +3 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +13 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +13 -0
  65. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +1 -3
  66. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +10 -11
  67. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +94 -0
  68. package/gradle/libs.versions.toml +1 -1
  69. package/gradle.properties +1 -1
  70. package/ios/Application/ApplicationEnvironment.swift +17 -0
  71. package/ios/Application/MaxApi.swift +30 -3
  72. package/ios/Avatar/Avatar.swift +4 -3
  73. package/ios/Badge/Badge.swift +6 -4
  74. package/ios/Badge/BadgeDot.swift +1 -1
  75. package/ios/Badge/BadgeRibbon.swift +0 -1
  76. package/ios/Button/Button.swift +15 -12
  77. package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +64 -18
  78. package/ios/Carousel/Carousel.swift +29 -12
  79. package/ios/Checkbox/Checkbox.swift +7 -3
  80. package/ios/Chip/Chip.swift +5 -4
  81. package/ios/Collapse/Collapse.swift +70 -19
  82. package/ios/Colors+Radius+Spacing/Colors.swift +10 -1
  83. package/ios/Colors+Radius+Spacing/Radius.swift +6 -0
  84. package/ios/DateTimePicker/DateTimePicker.swift +54 -20
  85. package/ios/DateTimePicker/WheelPicker.swift +103 -105
  86. package/ios/Divider/Divider.swift +37 -4
  87. package/ios/Extensions/Color++.swift +38 -9
  88. package/ios/Extensions/OptionalEnvironmentObject.swift +12 -0
  89. package/ios/Extensions/SkeletonUICompat.swift +11 -0
  90. package/ios/Icon/Icon.swift +21 -23
  91. package/ios/Icon/Icons.swift +1329 -0
  92. package/ios/IconButton/IconButton.swift +15 -7
  93. package/ios/Image/Image.swift +7 -23
  94. package/ios/Information/Information.swift +27 -9
  95. package/ios/Input/ErrorView.swift +2 -2
  96. package/ios/Input/Input.swift +160 -147
  97. package/ios/Input/InputPhoneNumber.swift +12 -9
  98. package/ios/Input/InputSearch.swift +107 -77
  99. package/ios/Input/InputTextArea.swift +128 -145
  100. package/ios/InputDropDown/InputDropDown.swift +10 -7
  101. package/ios/InputMoney/InputMoney.swift +33 -11
  102. package/ios/InputOTP/InputOTP.swift +22 -12
  103. package/ios/Layout/Card.swift +50 -0
  104. package/ios/Layout/GridContext.swift +187 -3
  105. package/ios/Layout/Item.swift +5 -4
  106. package/ios/Layout/ScreenLayout.swift +380 -0
  107. package/ios/Layout/Section.swift +4 -100
  108. package/ios/Loader/Loader.swift +5 -9
  109. package/ios/Lottie/LottieView.swift +14 -2
  110. package/ios/{Application/Navigation/Overplay → Navigation}/BottomSheet.swift +169 -29
  111. package/ios/Navigation/BottomTab/BottomTab.swift +332 -0
  112. package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTabBar.swift +18 -7
  113. package/ios/Navigation/BottomTab/DuoBarCompression.swift +105 -0
  114. package/ios/Navigation/BottomTab/DuoTabBar.swift +304 -0
  115. package/ios/{Application → Navigation/Component}/FloatingButton.swift +43 -54
  116. package/ios/Navigation/Component/Header.swift +545 -0
  117. package/ios/Navigation/Component/HeaderBackground.swift +133 -0
  118. package/ios/Navigation/Component/HeaderJourney.swift +73 -0
  119. package/ios/Navigation/Component/HeaderLocation.swift +48 -0
  120. package/ios/Navigation/Component/HeaderRight.swift +425 -0
  121. package/ios/Navigation/Component/HeaderTitle.swift +36 -0
  122. package/ios/{Application/Navigation → Navigation/Component}/HeaderUser.swift +21 -24
  123. package/ios/Navigation/Component/SideBar.swift +183 -0
  124. package/ios/{Application/Navigation/Overplay → Navigation/Component}/SnackBar.swift +8 -8
  125. package/ios/Navigation/HostBackGestureBridge.swift +78 -0
  126. package/ios/{Application/Navigation/Overplay → Navigation}/ModalScreen.swift +9 -2
  127. package/ios/Navigation/Navigation.swift +204 -0
  128. package/ios/Navigation/NavigationContainer.swift +195 -0
  129. package/ios/{Application/Navigation → Navigation}/Navigator.swift +207 -107
  130. package/ios/Navigation/StackScreen.swift +934 -0
  131. package/ios/Navigation/StackTransition.swift +400 -0
  132. package/ios/Navigation/SwipeBackHaptic.swift +33 -0
  133. package/ios/{Application/Navigation → Navigation}/Tracking/ScreenTracker.swift +12 -5
  134. package/ios/OTPKeyboard/OTPKeyboard.swift +8 -11
  135. package/ios/Pagination/PaginationScroll.swift +2 -2
  136. package/ios/Popup/PopupDisplay.swift +72 -135
  137. package/ios/Popup/PopupInput.swift +10 -13
  138. package/ios/Popup/PopupLayout.swift +140 -0
  139. package/ios/Popup/PopupNotify.swift +59 -69
  140. package/ios/Popup/PopupPromotion.swift +31 -47
  141. package/ios/PopupView/FullscreenPopup.swift +13 -19
  142. package/ios/PopupView/PopupView.swift +1 -1
  143. package/ios/PopupView/Utils++.swift +0 -8
  144. package/ios/ProgressInfo/ProgressInfo.swift +4 -2
  145. package/ios/Radio/Radio.swift +2 -1
  146. package/ios/Rating/Rating.swift +9 -1
  147. package/ios/Resources/dot_loading.json +558 -0
  148. package/ios/Resources/lottie_circle_loader.json +1 -0
  149. package/ios/ScrollIndicator/ScrollIndicator.swift +11 -10
  150. package/ios/Skeleton/Skeleton.swift +22 -27
  151. package/ios/Slider/Slider.swift +16 -18
  152. package/ios/Stepper/Stepper.swift +6 -1
  153. package/ios/Steps/Steps.swift +8 -6
  154. package/ios/SuggestAction/SuggestAction.swift +5 -0
  155. package/ios/Swipeable/SwipeCell.swift +10 -6
  156. package/ios/Switch/Switch.swift +22 -20
  157. package/ios/TabView/TabView.swift +102 -21
  158. package/ios/Tag/Tag.swift +15 -4
  159. package/ios/Template/Logo/Logo.swift +2 -1
  160. package/ios/Template/TrustBanner/TrustBanner.swift +106 -54
  161. package/ios/Title/Title.swift +6 -17
  162. package/ios/Tooltip/Tooltip.swift +29 -14
  163. package/ios/Typography/FontScale.swift +3 -3
  164. package/ios/Typography/Text.swift +81 -19
  165. package/ios/Uploader/Uploader.swift +3 -1
  166. package/ios/native-kits.podspec +2 -1
  167. package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
  168. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +1 -1
  169. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DuoTabDemo.swift +80 -0
  170. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +2 -0
  171. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IPhoneDuoDemo.swift +173 -0
  172. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +77 -1
  173. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +1 -0
  174. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +2 -2
  175. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +2 -0
  176. package/local.properties +2 -2
  177. package/package.json +1 -1
  178. package/scripts/gen-ios-icons.mjs +36 -0
  179. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -52
  180. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -183
  181. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -207
  182. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -39
  183. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
  184. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
  185. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
  186. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -293
  187. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
  188. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -110
  189. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -378
  190. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
  191. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitSizeScale.kt +0 -56
  192. package/example/ios/Example.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/Example.xcscheme +0 -32
  193. package/example/ios/Example.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  194. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  195. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/WorkspaceSettings.xcsettings +0 -16
  196. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
  197. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -5
  198. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +0 -58
  199. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/Pods-Example.xcscheme +0 -58
  200. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SDWebImage.xcscheme +0 -58
  201. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SDWebImageSwiftUI.xcscheme +0 -58
  202. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SkeletonUI.xcscheme +0 -58
  203. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/lottie-ios-LottiePrivacyInfo.xcscheme +0 -58
  204. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/lottie-ios.xcscheme +0 -58
  205. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -60
  206. package/ios/Application/Components.swift +0 -270
  207. package/ios/Application/ComposeApi.swift +0 -22
  208. package/ios/Application/HeaderRight.swift +0 -306
  209. package/ios/Application/Navigation/BottomTab/BottomTab.swift +0 -104
  210. package/ios/Application/Navigation/HeaderBackProps.swift +0 -66
  211. package/ios/Application/Navigation/HeaderTitle.swift +0 -57
  212. package/ios/Application/Navigation/NavigationContainer.swift +0 -213
  213. package/ios/Application/Navigation/NavigationOptions.swift +0 -109
  214. package/ios/Application/Navigation/NavigatorManager.swift +0 -22
  215. package/ios/Application/Navigation/component/Header.swift +0 -178
  216. package/ios/Application/Navigation/component/HeaderBackground.swift +0 -83
  217. package/ios/Application/Navigation/component/HeaderColor.swift +0 -65
  218. package/ios/Application/Navigation/component/HeaderRightWidthKey.swift +0 -11
  219. package/ios/Application/Navigation/component/NavigationFooter.swift +0 -90
  220. package/ios/Application/Navigation/component/NavigationHeaderRight.swift +0 -196
  221. package/ios/Application/Screen.swift +0 -250
  222. package/ios/Application/StackScreen.swift +0 -492
  223. /package/ios/{Application/Navigation → Navigation}/BottomTab/CurvedContainer.swift +0 -0
@@ -1,32 +1,54 @@
1
1
  package vn.momo.kits.navigation.bottomtab
2
2
 
3
+ import androidx.compose.animation.core.Animatable
4
+ import androidx.compose.animation.core.AnimationVector1D
5
+ import androidx.compose.animation.core.Easing
3
6
  import androidx.compose.animation.core.tween
4
- import androidx.compose.animation.fadeIn
5
- import androidx.compose.animation.fadeOut
6
- import androidx.compose.animation.scaleIn
7
- import androidx.compose.animation.scaleOut
8
7
  import androidx.compose.foundation.background
9
8
  import androidx.compose.foundation.layout.*
10
9
  import androidx.compose.runtime.Composable
10
+ import androidx.compose.runtime.CompositionLocalProvider
11
11
  import androidx.compose.runtime.DisposableEffect
12
12
  import androidx.compose.runtime.LaunchedEffect
13
+ import androidx.compose.runtime.getValue
14
+ import androidx.compose.runtime.key
15
+ import androidx.compose.runtime.mutableIntStateOf
16
+ import androidx.compose.runtime.mutableStateOf
17
+ import androidx.compose.runtime.remember
18
+ import androidx.compose.runtime.saveable.rememberSaveable
19
+ import androidx.compose.runtime.saveable.rememberSaveableStateHolder
20
+ import androidx.compose.runtime.setValue
21
+ import androidx.compose.runtime.staticCompositionLocalOf
13
22
  import androidx.compose.ui.Alignment
14
23
  import androidx.compose.ui.Modifier
24
+ import androidx.compose.ui.graphics.graphicsLayer
25
+ import androidx.compose.ui.layout.layout
15
26
  import androidx.compose.ui.unit.dp
16
- import androidx.navigation.compose.NavHost
17
- import androidx.navigation.compose.composable
18
- import androidx.navigation.compose.currentBackStackEntryAsState
19
- import androidx.navigation.compose.rememberNavController
27
+ import androidx.compose.ui.zIndex
20
28
  import vn.momo.kits.const.AppNavigationBar
21
29
  import vn.momo.kits.const.AppTheme
22
30
  import vn.momo.kits.const.Spacing
23
31
  import vn.momo.kits.navigation.KeyboardOptions
32
+ import vn.momo.kits.navigation.LocalMaxApi
24
33
  import vn.momo.kits.navigation.LocalNavigation
25
34
  import vn.momo.kits.navigation.LocalNavigator
26
35
  import vn.momo.kits.navigation.NavigationOptions
27
36
  import vn.momo.kits.navigation.StackScreen
28
37
  import vn.momo.kits.navigation.component.HeaderType
38
+ import vn.momo.kits.platform.BackHandler
29
39
  import vn.momo.kits.platform.reportFloatingArrowBack
40
+ import vn.momo.kits.layout.getScreenLayout
41
+ import kotlinx.coroutines.launch
42
+
43
+ private val inOutQuad = Easing { t -> if (t < 0.5f) 2 * t * t else 1 - 2 * (1 - t) * (1 - t) }
44
+ internal val tabTimingSpec = tween<Float>(durationMillis = 200, easing = inOutQuad)
45
+
46
+ internal val LocalBottomTabFocused = staticCompositionLocalOf { true }
47
+
48
+ private val Hidden = Modifier.layout { measurable, constraints ->
49
+ val placeable = measurable.measure(constraints)
50
+ layout(placeable.width, placeable.height) {}
51
+ }
30
52
 
31
53
  private var bottomTabOptionItems : MutableList<NavigationOptions?> = mutableListOf()
32
54
  internal fun setBottomTabOption(index: Int, options: NavigationOptions){
@@ -43,16 +65,22 @@ internal fun getBottomTabOption(index: Int): NavigationOptions? {
43
65
  @Composable
44
66
  fun BottomTab(
45
67
  items: List<BottomTabItem>,
46
- floatingButton: BottomTabFloatingButton? = null
68
+ floatingButton: BottomTabFloatingButton? = null,
69
+ // iPhone Duo: when the side bar can't hold both bars, which one starts compressed;
70
+ // tapping the compressed one swaps them
71
+ compressionBehavior: DuoBarCompressionBehavior = DuoBarCompressionBehavior.TOOLBAR,
72
+ initialRouteName: String? = null,
73
+ onTabPress: ((index: Int, switchTab: () -> Unit) -> Unit)? = null,
74
+ backToInitialTab: Boolean = true,
47
75
  ) {
48
76
  val navigation = LocalNavigation.current
49
77
  val navigator = LocalNavigator.current
50
- val navController = rememberNavController()
51
-
52
- bottomTabOptionItems = items.mapIndexed { index, item ->
53
- item.options ?: NavigationOptions()
54
- }.toMutableList()
78
+ val maxApi = LocalMaxApi.current
79
+ val startIndex = remember { items.indexOfFirst { it.name == initialRouteName }.coerceAtLeast(0) }
55
80
 
81
+ remember(items) {
82
+ bottomTabOptionItems = items.map { it.options ?: NavigationOptions() }.toMutableList()
83
+ }
56
84
 
57
85
  LaunchedEffect(Unit){
58
86
  navigation.markAsBottomTabRoot()
@@ -62,97 +90,122 @@ fun BottomTab(
62
90
  )
63
91
  }
64
92
 
65
- // Tab 0 keeps the seeked transition; other tabs go to the host's floating arrow. Matches RN.
66
- val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
67
- val isFirstTab = currentRoute == null || currentRoute == "option0"
93
+ var selectedIndex by rememberSaveable { mutableIntStateOf(startIndex) }
94
+ var loadedTabs by rememberSaveable { mutableStateOf(listOf(startIndex)) }
95
+ val tabStateHolder = rememberSaveableStateHolder()
96
+ val selectTab: (Int) -> Unit = { index ->
97
+ if (index !in loadedTabs) loadedTabs = loadedTabs + index
98
+ selectedIndex = index
99
+ }
100
+
101
+ val onFirstTab = selectedIndex == 0
102
+ val isFirstTab = onFirstTab || !backToInitialTab
103
+ val backToFirstTab = { selectTab(0) }
104
+
105
+ var lastFocusedIndex by rememberSaveable { mutableStateOf<Int?>(null) }
106
+ val tabProgress = remember { mutableMapOf<Int, Animatable<Float, AnimationVector1D>>() }
107
+ fun progressOf(index: Int) = tabProgress.getOrPut(index) {
108
+ Animatable(if (index == lastFocusedIndex) 1f else 0f)
109
+ }
110
+ LaunchedEffect(selectedIndex) {
111
+ lastFocusedIndex = selectedIndex
112
+ tabProgress.forEach { (index, progress) ->
113
+ if (index != selectedIndex) launch { progress.animateTo(0f, tabTimingSpec) }
114
+ }
115
+ progressOf(selectedIndex).animateTo(1f, tabTimingSpec)
116
+ }
68
117
 
69
118
  DisposableEffect(isFirstTab) {
70
119
  reportFloatingArrowBack(!isFirstTab)
71
- navigator.tabBackHandler = if (isFirstTab) null else ({
72
- navController.popBackStack(navController.graph.startDestinationRoute!!, false)
73
- })
74
- // both live outside this composition
120
+ navigator.tabBackHandler = if (isFirstTab) null else ({ backToFirstTab() })
121
+
75
122
  onDispose {
76
123
  reportFloatingArrowBack(false)
77
124
  navigator.tabBackHandler = null
78
125
  }
79
126
  }
80
127
 
128
+ val layout = getScreenLayout()
129
+ val hasQr = floatingButton != null
130
+ val compression = rememberDuoBarCompression(compressionBehavior, items.size, hasQr, layout)
131
+ val railReserve = layout.sideBar?.takeIf { layout.isIPhoneDuo }?.let {
132
+ it.bottom + duoRailHeight(items.size, hasQr, compressed = compression.tabBarCompressed) + DUO_GROUP_GAP
133
+ } ?: 0.dp
134
+ val onTabSelected: (Int) -> Unit = { index ->
135
+ runCatching { maxApi?.triggerEventVibration("light", callback = null) }
136
+ val switchTab = {
137
+ if (index != selectedIndex) selectTab(index)
138
+ }
139
+ onTabPress?.invoke(index, switchTab) ?: switchTab()
140
+ }
141
+
81
142
  Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
82
143
  Box(modifier = Modifier
83
144
  .fillMaxSize()
145
+ .background(AppTheme.current.colors.background.default)
84
146
  ) {
85
- NavHost(
86
- navController = navController,
87
- startDestination = "option0"
147
+ CompositionLocalProvider(
148
+ LocalDuoRailReserve provides railReserve,
149
+ LocalDuoBarCompression provides compression,
88
150
  ) {
89
- items.forEachIndexed { index, item ->
90
- composable(
91
- route = "option$index",
92
- enterTransition = {
93
- fadeIn(animationSpec = tween(200)) +
94
- scaleIn(
95
- initialScale = 0.97f,
96
- animationSpec = tween(200)
97
- )
98
- },
99
- exitTransition = {
100
- fadeOut(animationSpec = tween(200)) +
101
- scaleOut(
102
- targetScale = 0.97f,
103
- animationSpec = tween(200)
151
+ loadedTabs.forEach { index ->
152
+ val item = items.getOrNull(index) ?: return@forEach
153
+ val focused = index == selectedIndex
154
+ val progress = progressOf(index)
155
+ key(index) {
156
+ Box(
157
+ Modifier
158
+ .fillMaxSize()
159
+ .zIndex(if (focused) 0f else -1f)
160
+ .then(if (focused) Modifier.graphicsLayer {
161
+ alpha = 0.3f + 0.7f * progress.value
162
+ scaleX = 0.97f + 0.03f * progress.value
163
+ scaleY = scaleX
164
+ } else Hidden)
165
+ ) {
166
+ CompositionLocalProvider(LocalBottomTabFocused provides focused) {
167
+ tabStateHolder.SaveableStateProvider(index) {
168
+ StackScreen(
169
+ content = item.screen,
170
+ navigationOptions = getBottomTabOption(index),
171
+ bottomTabIndex = index,
172
+ onBackHandler = {
173
+ if (backToInitialTab && index != 0) {
174
+ backToFirstTab()
175
+ } else {
176
+ navigator.pop()
177
+ }
178
+ }
104
179
  )
105
- },
106
- popEnterTransition = {
107
- fadeIn(animationSpec = tween(200)) +
108
- scaleIn(
109
- initialScale = 0.97f,
110
- animationSpec = tween(200)
111
- )
112
- },
113
- popExitTransition = {
114
- fadeOut(animationSpec = tween(200)) +
115
- scaleOut(
116
- targetScale = 0.97f,
117
- animationSpec = tween(200)
118
- )
119
- }
120
- ) {
121
- val option = getBottomTabOption(index)
122
-
123
- StackScreen(
124
- content = item.screen,
125
- navigationOptions = option,
126
- bottomTabIndex = index,
127
- onBackHandler = {
128
- if (index != 0) {
129
- navController.popBackStack(
130
- navController.graph.startDestinationRoute!!,
131
- inclusive = false
132
- )
133
- } else {
134
- navigator.pop()
135
180
  }
136
181
  }
137
- )
182
+ }
138
183
  }
139
184
  }
140
185
  }
141
186
  }
142
- Column {
143
- BottomTabBar(
187
+ BackHandler(enabled = !onFirstTab) {
188
+ if (backToInitialTab) backToFirstTab() else navigator.pop()
189
+ }
190
+ if (layout.isIPhoneDuo) {
191
+ DuoTabBar(
192
+ layout = layout,
144
193
  items = items,
145
194
  floatingButton = floatingButton,
146
- navController = navController,
147
- onTabSelected = {
148
- val currentRoute = navController.currentBackStackEntry?.destination?.route
149
- val targetRoute = "option$it"
150
- if (currentRoute != targetRoute) {
151
- navController.navigate(targetRoute)
152
- }
153
- }
195
+ selectedIndex = selectedIndex,
196
+ onTabSelected = onTabSelected,
197
+ compression = compression,
154
198
  )
155
- Spacer(modifier = Modifier.fillMaxWidth().height(AppNavigationBar.current + Spacing.S).background(AppTheme.current.colors.background.surface))
199
+ } else {
200
+ Column {
201
+ BottomTabBar(
202
+ items = items,
203
+ floatingButton = floatingButton,
204
+ selectedIndex = selectedIndex,
205
+ onTabSelected = onTabSelected,
206
+ )
207
+ Spacer(modifier = Modifier.fillMaxWidth().height(AppNavigationBar.current + Spacing.S).background(AppTheme.current.colors.background.surface))
208
+ }
156
209
  }
157
210
  }
158
211
  }
@@ -1,15 +1,18 @@
1
1
  package vn.momo.kits.navigation.bottomtab
2
2
 
3
- import androidx.compose.animation.core.FastOutSlowInEasing
4
- import androidx.compose.animation.core.animateDpAsState
5
- import androidx.compose.animation.core.tween
3
+ import androidx.compose.animation.core.Animatable
6
4
  import androidx.compose.foundation.background
7
5
  import androidx.compose.foundation.border
8
6
  import androidx.compose.foundation.layout.*
9
7
  import androidx.compose.foundation.shape.CircleShape
10
8
  import androidx.compose.foundation.shape.RoundedCornerShape
11
9
  import androidx.compose.runtime.Composable
10
+ import androidx.compose.runtime.LaunchedEffect
12
11
  import androidx.compose.runtime.getValue
12
+ import androidx.compose.runtime.mutableIntStateOf
13
+ import androidx.compose.runtime.remember
14
+ import androidx.compose.runtime.saveable.rememberSaveable
15
+ import androidx.compose.runtime.setValue
13
16
  import androidx.compose.ui.Alignment
14
17
  import androidx.compose.ui.Modifier
15
18
  import androidx.compose.ui.draw.clip
@@ -17,18 +20,18 @@ import androidx.compose.ui.graphics.Brush
17
20
  import androidx.compose.ui.graphics.Color
18
21
  import androidx.compose.ui.text.style.TextAlign
19
22
  import androidx.compose.ui.text.style.TextOverflow
23
+ import androidx.compose.ui.unit.Dp
24
+ import androidx.compose.ui.unit.IntOffset
20
25
  import androidx.compose.ui.unit.dp
21
- import androidx.navigation.NavController
22
- import androidx.navigation.compose.currentBackStackEntryAsState
23
26
  import vn.momo.kits.application.IsShowBaseLineDebug
24
27
  import vn.momo.kits.components.*
25
28
  import vn.momo.kits.const.*
26
29
  import vn.momo.kits.modifier.InternalApi
27
30
  import vn.momo.kits.modifier.conditional
28
- import vn.momo.kits.modifier.noFeedbackClickable
29
- import vn.momo.kits.platform.getScreenDimensions
31
+ import vn.momo.kits.modifier.activeOpacityClickable
32
+ import vn.momo.kits.layout.getScreenLayout
33
+ import vn.momo.kits.utils.horizontalSafeAreaPadding
30
34
 
31
- internal val floatingButtonWidth = 75.dp
32
35
  @InternalApi
33
36
  const val BOTTOM_TAB_BAR_HEIGHT = 64
34
37
 
@@ -36,28 +39,24 @@ const val BOTTOM_TAB_BAR_HEIGHT = 64
36
39
  internal fun BottomTabBar(
37
40
  items: List<BottomTabItem>,
38
41
  floatingButton: BottomTabFloatingButton? = null,
39
- navController: NavController,
42
+ selectedIndex: Int,
40
43
  onTabSelected: (Int) -> Unit,
41
44
  ) {
42
- val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
43
- val selectedIndex = items.indexOfFirst { "option${items.indexOf(it)}" == currentRoute }
44
45
 
45
- val screenWidth = getScreenDimensions().width.dp
46
+ val layout = getScreenLayout()
46
47
  val mid = items.size / 2
47
48
 
48
- val tabWidth = if (floatingButton != null) {
49
- (screenWidth - floatingButtonWidth) / items.size
50
- } else {
51
- screenWidth / items.size
52
- }
53
- val adjustSize = if(floatingButton != null && selectedIndex >= mid) floatingButtonWidth else 0.dp
54
- val targetOffset = tabWidth * selectedIndex + tabWidth / 2 - screenWidth / 2 + adjustSize
49
+ val floating = floatingButton?.takeIf { items.size % 2 == 0 }
50
+ val slotWidth = layout.contentWidth / (items.size + if (floating != null) 1 else 0)
51
+ fun slotOf(index: Int) = if (floating != null && index >= mid) index + 1 else index
55
52
 
56
- val indicatorOffsetX by animateDpAsState(
57
- targetValue = targetOffset,
58
- animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing),
59
- label = "IndicatorX"
60
- )
53
+ var indicatorSlot by rememberSaveable { mutableIntStateOf(0) }
54
+ val indicatorX = remember { Animatable(indicatorSlot.toFloat()) }
55
+ LaunchedEffect(selectedIndex) {
56
+ val target = slotOf(selectedIndex)
57
+ indicatorSlot = target
58
+ indicatorX.animateTo(target.toFloat(), tabTimingSpec)
59
+ }
61
60
 
62
61
  Box(
63
62
  contentAlignment = Alignment.BottomCenter
@@ -69,20 +68,21 @@ internal fun BottomTabBar(
69
68
  .background(AppTheme.current.colors.background.surface)
70
69
  .conditional(IsShowBaseLineDebug) {
71
70
  border(1.dp, Colors.blue_03)
72
- },
71
+ }
72
+ .horizontalSafeAreaPadding(),
73
73
  horizontalArrangement = Arrangement.SpaceAround,
74
74
  ) {
75
- if (floatingButton == null) {
75
+ if (floating == null) {
76
76
  RenderTabBarItem(items, 0, items.size, selectedIndex, onTabSelected)
77
77
  } else {
78
78
  RenderTabBarItem(items, 0, mid, selectedIndex, onTabSelected)
79
- Spacer(modifier = Modifier.width(floatingButtonWidth).padding(horizontal = Spacing.XXS))
79
+ Spacer(modifier = Modifier.width(slotWidth))
80
80
  RenderTabBarItem(items, mid, items.size, selectedIndex, onTabSelected)
81
81
  }
82
82
  }
83
83
 
84
84
  Box(modifier = Modifier
85
- .offset(x = indicatorOffsetX, y = (-BOTTOM_TAB_BAR_HEIGHT + 2).dp)
85
+ .offset { IntOffset((layout.insetLeft + slotWidth * (indicatorX.value + 0.5f) - layout.width / 2).roundToPx(), (-BOTTOM_TAB_BAR_HEIGHT + 2).dp.roundToPx()) }
86
86
  .height(2.dp)
87
87
  .width(44.dp)
88
88
  .background(
@@ -101,7 +101,11 @@ internal fun BottomTabBar(
101
101
  )
102
102
  )
103
103
  )
104
- floatingButton?.let { FloatingButton(it) }
104
+ floating?.let {
105
+ Box(Modifier.offset(x = (layout.insetLeft - layout.insetRight) / 2)) {
106
+ FloatingButton(it, slotWidth)
107
+ }
108
+ }
105
109
  }
106
110
  }
107
111
 
@@ -126,17 +130,10 @@ internal fun RowScope.RenderTabBarItem(
126
130
 
127
131
  @Composable
128
132
  internal fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> Unit) {
129
- fun isNumber(label: String): Boolean {
130
- val numberRegex = "^\\d+$".toRegex()
131
- return numberRegex.matches(label)
132
- }
133
- fun formatLabel(label: String): String? {
134
- if (isNumber(label) && label.toInt() == 0) {
135
- return null
136
- }
137
- return label
138
- }
139
- val badgeLabel = item.badgeLabel?.let { formatLabel(it) }
133
+ // RN parity: "0" hides the badge, "" renders a dot.
134
+ val badgeLabel = item.badgeLabel?.takeUnless { it.toIntOrNull() == 0 }
135
+ val isNumber = badgeLabel?.toIntOrNull() != null
136
+ val showDot = badgeLabel?.isEmpty() ?: item.showDot
140
137
 
141
138
  Box(modifier = Modifier
142
139
  .fillMaxSize()
@@ -144,7 +141,7 @@ internal fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> U
144
141
  .conditional(IsShowBaseLineDebug) {
145
142
  border(1.dp, Colors.blue_03)
146
143
  }
147
- .noFeedbackClickable {
144
+ .activeOpacityClickable {
148
145
  onClick()
149
146
  },
150
147
  contentAlignment = Alignment.BottomStart
@@ -152,18 +149,22 @@ internal fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> U
152
149
  Column(
153
150
  modifier = Modifier
154
151
  .fillMaxSize()
155
- .padding(horizontal = Spacing.S, vertical = Spacing.S)
156
- .noFeedbackClickable {
157
- onClick()
158
- },
152
+ .padding(horizontal = Spacing.S, vertical = Spacing.S),
159
153
  horizontalAlignment = Alignment.CenterHorizontally,
160
154
  verticalArrangement = Arrangement.Center
161
155
  ) {
162
- Icon(
163
- source = item.icon,
164
- size = 28.dp,
165
- modifier = Modifier.padding(Spacing.XS),
166
- color = if (selected) AppTheme.current.colors.primary else AppTheme.current.colors.text.hint)
156
+ Box(modifier = Modifier.padding(Spacing.XS)) {
157
+ Icon(
158
+ source = item.icon,
159
+ size = 28.dp,
160
+ color = if (selected) AppTheme.current.colors.primary else AppTheme.current.colors.text.hint)
161
+ val badgeAnchor = Modifier.matchParentSize().wrapContentSize(Alignment.TopEnd, unbounded = true)
162
+ if (showDot) {
163
+ BadgeDot(size = DotSize.Small, modifier = badgeAnchor.offset(x = 4.dp, y = (-4).dp))
164
+ } else if (!badgeLabel.isNullOrEmpty()) {
165
+ Badge(badgeLabel, modifier = badgeAnchor.offset(x = if (isNumber) 8.dp else 12.dp, y = (-8).dp))
166
+ }
167
+ }
167
168
  Text(
168
169
  text = item.label,
169
170
  color = if (selected) AppTheme.current.colors.primary else AppTheme.current.colors.text.hint,
@@ -172,28 +173,19 @@ internal fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> U
172
173
  overflow = TextOverflow.Ellipsis
173
174
  )
174
175
  }
175
- if (badgeLabel != null) {
176
- if (badgeLabel.isEmpty())
177
- BadgeDot(
178
- size = DotSize.Small,
179
- modifier = Modifier.offset(x = 50.dp, y = (-48).dp)
180
- )
181
- else
182
- Badge(badgeLabel, modifier = Modifier.offset(x = 44.dp, y = (-42).dp))
183
- }
184
176
  }
185
177
  }
186
178
 
187
179
  @Composable
188
- internal fun FloatingButton(data: BottomTabFloatingButton) {
180
+ internal fun FloatingButton(data: BottomTabFloatingButton, width: Dp) {
189
181
  Column(
190
182
  modifier = Modifier
191
- .width(floatingButtonWidth)
183
+ .width(width)
192
184
  .padding(horizontal = Spacing.XXS, vertical = Spacing.S)
193
185
  .conditional(IsShowBaseLineDebug) {
194
186
  border(1.dp, Colors.blue_03)
195
187
  }
196
- .noFeedbackClickable {
188
+ .activeOpacityClickable {
197
189
  data.onPress()
198
190
  },
199
191
  verticalArrangement = Arrangement.Bottom,
@@ -0,0 +1,98 @@
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
+ }