@momo-kits/native-kits 0.167.1-duo.1-debug → 0.167.1-duo.10-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.
- package/CLAUDE.md +32 -0
- package/compose/build.gradle.kts +15 -1
- package/compose/build.gradle.kts.backup +14 -0
- package/compose/compose.podspec +1 -1
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +2 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +6 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +20 -21
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +2 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +4 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +9 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +11 -12
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +4 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +10 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +6 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +53 -26
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +12 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +7 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +13 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +13 -5
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +3 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +14 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +19 -21
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +5 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +3 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +7 -15
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +13 -11
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +21 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +232 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +11 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +43 -4
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +8 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +4 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +143 -31
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +100 -66
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +22 -16
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +98 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +312 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +66 -18
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +23 -14
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +3 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +1 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +81 -24
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +1 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +185 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +2 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +3 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +13 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +4 -1
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +4 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +86 -0
- package/gradle.properties +1 -1
- package/ios/Application/ApplicationEnvironment.swift +17 -0
- package/ios/Application/ComposeApi.swift +2 -1
- package/ios/Application/{Components.swift → LegacyHeader.swift} +7 -65
- package/ios/Application/Screen.swift +3 -2
- package/ios/Avatar/Avatar.swift +4 -3
- package/ios/Badge/Badge.swift +6 -4
- package/ios/Badge/BadgeDot.swift +1 -1
- package/ios/Badge/BadgeRibbon.swift +0 -1
- package/ios/Button/Button.swift +15 -12
- package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +64 -18
- package/ios/Carousel/Carousel.swift +29 -12
- package/ios/Checkbox/Checkbox.swift +7 -3
- package/ios/Chip/Chip.swift +5 -4
- package/ios/Collapse/Collapse.swift +70 -19
- package/ios/Colors+Radius+Spacing/Colors.swift +10 -1
- package/ios/DateTimePicker/DateTimePicker.swift +54 -20
- package/ios/DateTimePicker/WheelPicker.swift +103 -105
- package/ios/Divider/Divider.swift +37 -4
- package/ios/Extensions/Color++.swift +38 -9
- package/ios/Extensions/OptionalEnvironmentObject.swift +12 -0
- package/ios/Extensions/SkeletonUICompat.swift +11 -0
- package/ios/Icon/Icon.swift +21 -23
- package/ios/Icon/Icons.swift +1329 -0
- package/ios/IconButton/IconButton.swift +15 -7
- package/ios/Image/Image.swift +7 -23
- package/ios/Information/Information.swift +27 -9
- package/ios/Input/ErrorView.swift +2 -2
- package/ios/Input/Input.swift +160 -147
- package/ios/Input/InputPhoneNumber.swift +12 -9
- package/ios/Input/InputSearch.swift +102 -75
- package/ios/Input/InputTextArea.swift +128 -145
- package/ios/InputDropDown/InputDropDown.swift +10 -7
- package/ios/InputMoney/InputMoney.swift +33 -11
- package/ios/InputOTP/InputOTP.swift +22 -12
- package/ios/Layout/Card.swift +50 -0
- package/ios/Layout/GridContext.swift +123 -3
- package/ios/Layout/Item.swift +5 -4
- package/ios/Layout/ScreenLayout.swift +312 -0
- package/ios/Layout/Section.swift +4 -100
- package/ios/Loader/Loader.swift +5 -9
- package/ios/Lottie/LottieView.swift +14 -2
- package/ios/{Application/Navigation/Overplay → Navigation}/BottomSheet.swift +97 -8
- package/ios/Navigation/BottomTab/BottomTab.swift +187 -0
- package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTabBar.swift +15 -7
- package/ios/Navigation/BottomTab/DuoBarCompression.swift +105 -0
- package/ios/Navigation/BottomTab/DuoTabBar.swift +304 -0
- package/ios/{Application → Navigation/Component}/FloatingButton.swift +14 -3
- package/ios/Navigation/Component/Header.swift +493 -0
- package/ios/Navigation/Component/HeaderBackground.swift +134 -0
- package/ios/Navigation/Component/HeaderJourney.swift +73 -0
- package/ios/Navigation/Component/HeaderLocation.swift +48 -0
- package/ios/Navigation/Component/HeaderRight.swift +386 -0
- package/ios/Navigation/Component/HeaderTitle.swift +36 -0
- package/ios/{Application/Navigation → Navigation/Component}/HeaderUser.swift +21 -24
- package/ios/Navigation/Component/SideBar.swift +176 -0
- package/ios/{Application/Navigation/Overplay → Navigation/Component}/SnackBar.swift +5 -5
- package/ios/Navigation/HostBackGestureBridge.swift +57 -0
- package/ios/{Application/Navigation/Overplay → Navigation}/ModalScreen.swift +5 -1
- package/ios/Navigation/Navigation.swift +190 -0
- package/ios/{Application/Navigation → Navigation}/NavigationContainer.swift +92 -81
- package/ios/{Application/Navigation → Navigation}/Navigator.swift +185 -36
- package/ios/Navigation/StackScreen.swift +782 -0
- package/ios/{Application/Navigation → Navigation}/Tracking/ScreenTracker.swift +12 -5
- package/ios/OTPKeyboard/OTPKeyboard.swift +8 -11
- package/ios/Pagination/PaginationScroll.swift +2 -2
- package/ios/Popup/PopupDisplay.swift +72 -136
- package/ios/Popup/PopupInput.swift +10 -13
- package/ios/Popup/PopupLayout.swift +140 -0
- package/ios/Popup/PopupNotify.swift +56 -73
- package/ios/Popup/PopupPromotion.swift +31 -47
- package/ios/PopupView/FullscreenPopup.swift +13 -19
- package/ios/PopupView/PopupView.swift +1 -1
- package/ios/PopupView/Utils++.swift +0 -8
- package/ios/ProgressInfo/ProgressInfo.swift +4 -2
- package/ios/Radio/Radio.swift +2 -1
- package/ios/Rating/Rating.swift +9 -1
- package/ios/Resources/dot_loading.json +558 -0
- package/ios/Resources/lottie_circle_loader.json +1 -0
- package/ios/ScrollIndicator/ScrollIndicator.swift +11 -10
- package/ios/Skeleton/Skeleton.swift +22 -27
- package/ios/Slider/Slider.swift +16 -18
- package/ios/Stepper/Stepper.swift +6 -1
- package/ios/Steps/Steps.swift +8 -6
- package/ios/SuggestAction/SuggestAction.swift +5 -0
- package/ios/Swipeable/SwipeCell.swift +10 -6
- package/ios/Switch/Switch.swift +22 -20
- package/ios/TabView/TabView.swift +102 -21
- package/ios/Tag/Tag.swift +15 -4
- package/ios/Template/Logo/Logo.swift +2 -1
- package/ios/Template/TrustBanner/TrustBanner.swift +106 -54
- package/ios/Title/Title.swift +6 -17
- package/ios/Tooltip/Tooltip.swift +29 -14
- package/ios/Typography/Text.swift +61 -19
- package/ios/Uploader/Uploader.swift +3 -1
- package/ios/native-kits.podspec +2 -1
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +1 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DuoTabDemo.swift +80 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +1 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IPhoneDuoDemo.swift +173 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +77 -1
- package/package.json +1 -1
- package/scripts/gen-ios-icons.mjs +36 -0
- package/.claude/settings.local.json +0 -185
- package/ios/Application/HeaderRight.swift +0 -306
- package/ios/Application/Navigation/BottomTab/BottomTab.swift +0 -104
- package/ios/Application/Navigation/HeaderBackProps.swift +0 -66
- package/ios/Application/Navigation/HeaderTitle.swift +0 -57
- package/ios/Application/Navigation/NavigationOptions.swift +0 -109
- package/ios/Application/Navigation/NavigatorManager.swift +0 -22
- package/ios/Application/Navigation/component/Header.swift +0 -178
- package/ios/Application/Navigation/component/HeaderBackground.swift +0 -83
- package/ios/Application/Navigation/component/HeaderColor.swift +0 -65
- package/ios/Application/Navigation/component/HeaderRightWidthKey.swift +0 -11
- package/ios/Application/Navigation/component/NavigationFooter.swift +0 -90
- package/ios/Application/Navigation/component/NavigationHeaderRight.swift +0 -196
- package/ios/Application/StackScreen.swift +0 -492
- package/local.properties +0 -8
- /package/ios/{Application/Navigation → Navigation}/BottomTab/CurvedContainer.swift +0 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
package vn.momo.kits.navigation
|
|
2
2
|
|
|
3
3
|
import androidx.compose.animation.animateColorAsState
|
|
4
|
+
import androidx.compose.animation.core.Animatable
|
|
5
|
+
import androidx.compose.animation.core.AnimationVector1D
|
|
6
|
+
import androidx.compose.animation.core.tween
|
|
4
7
|
import androidx.compose.foundation.ScrollState
|
|
5
8
|
import androidx.compose.foundation.background
|
|
6
9
|
import androidx.compose.foundation.border
|
|
@@ -39,6 +42,7 @@ import androidx.compose.ui.Alignment
|
|
|
39
42
|
import androidx.compose.ui.Modifier
|
|
40
43
|
import androidx.compose.ui.graphics.Brush
|
|
41
44
|
import androidx.compose.ui.graphics.Color
|
|
45
|
+
import androidx.compose.ui.graphics.graphicsLayer
|
|
42
46
|
import androidx.compose.ui.graphics.lerp
|
|
43
47
|
import androidx.compose.ui.layout.onGloballyPositioned
|
|
44
48
|
import androidx.compose.ui.platform.LocalDensity
|
|
@@ -51,7 +55,6 @@ import kotlinx.coroutines.flow.filter
|
|
|
51
55
|
import kotlinx.coroutines.launch
|
|
52
56
|
import vn.momo.kits.components.InputSearch
|
|
53
57
|
import vn.momo.kits.const.AppNavigationBar
|
|
54
|
-
import vn.momo.kits.const.AppStatusBar
|
|
55
58
|
import vn.momo.kits.const.AppTheme
|
|
56
59
|
import vn.momo.kits.application.IsShowBaseLineDebug
|
|
57
60
|
import vn.momo.kits.const.Colors
|
|
@@ -65,6 +68,9 @@ import vn.momo.kits.navigation.component.HEADER_HEIGHT
|
|
|
65
68
|
import vn.momo.kits.navigation.component.Header
|
|
66
69
|
import vn.momo.kits.navigation.component.HeaderBackground
|
|
67
70
|
import vn.momo.kits.navigation.component.HeaderRight
|
|
71
|
+
import vn.momo.kits.navigation.component.HeaderSideBar
|
|
72
|
+
import vn.momo.kits.navigation.bottomtab.LocalDuoRailReserve
|
|
73
|
+
import vn.momo.kits.navigation.bottomtab.duoFloatingTabBarInset
|
|
68
74
|
import vn.momo.kits.navigation.component.HeaderType
|
|
69
75
|
import vn.momo.kits.navigation.component.InputSearchType
|
|
70
76
|
import vn.momo.kits.platform.BackHandler
|
|
@@ -74,6 +80,10 @@ import vn.momo.kits.navigation.tracking.ScreenTrackingState
|
|
|
74
80
|
import kotlinx.coroutines.delay
|
|
75
81
|
import vn.momo.kits.application.LocalContext
|
|
76
82
|
import vn.momo.kits.utils.horizontalSafeAreaPadding
|
|
83
|
+
import vn.momo.kits.layout.LocalScreenLayout
|
|
84
|
+
import vn.momo.kits.layout.ScreenLayout
|
|
85
|
+
import vn.momo.kits.layout.consumeHorizontalInsets
|
|
86
|
+
import vn.momo.kits.layout.getScreenLayout
|
|
77
87
|
import kotlin.time.Clock
|
|
78
88
|
import kotlin.time.Duration.Companion.milliseconds
|
|
79
89
|
import kotlin.time.ExperimentalTime
|
|
@@ -81,11 +91,19 @@ import kotlin.time.ExperimentalTime
|
|
|
81
91
|
internal val LocalFooterHeightPx = staticCompositionLocalOf { mutableIntStateOf(0) }
|
|
82
92
|
internal val LocalHeaderRightWidthPx = staticCompositionLocalOf { mutableIntStateOf(0) }
|
|
83
93
|
|
|
94
|
+
// Space a tab's content leaves for the tab bar and home indicator. On iPhone Duo the floating bar
|
|
95
|
+
// overlays the content and the rail lives in the side strip, so only their own inset applies.
|
|
84
96
|
@Composable
|
|
85
|
-
private fun
|
|
86
|
-
if (LocalNavigation.current.bottomTabIndex == -1) return 0.dp
|
|
97
|
+
private fun bottomTabChildInset(): Dp {
|
|
87
98
|
val keyboard = keyboardSizeState().value
|
|
88
|
-
|
|
99
|
+
val navigationBar = AppNavigationBar.current
|
|
100
|
+
val layout = getScreenLayout()
|
|
101
|
+
return when {
|
|
102
|
+
layout.isIPhoneDuo && layout.sideBar == null -> (duoFloatingTabBarInset(layout) - keyboard).coerceAtLeast(0.dp)
|
|
103
|
+
layout.isIPhoneDuo -> (navigationBar - keyboard).coerceAtLeast(0.dp)
|
|
104
|
+
else -> (BOTTOM_TAB_BAR_HEIGHT.dp + Spacing.S - keyboard).coerceAtLeast(0.dp) +
|
|
105
|
+
(navigationBar - keyboard).coerceAtLeast(0.dp)
|
|
106
|
+
}
|
|
89
107
|
}
|
|
90
108
|
|
|
91
109
|
@OptIn(ExperimentalMaterialApi::class, ExperimentalTime::class)
|
|
@@ -101,7 +119,7 @@ internal fun StackScreen(
|
|
|
101
119
|
val navigator = LocalNavigator.current
|
|
102
120
|
val maxApi = LocalMaxApi.current
|
|
103
121
|
val context = LocalContext.current
|
|
104
|
-
val
|
|
122
|
+
val screenLayout = getScreenLayout()
|
|
105
123
|
val density = LocalDensity.current
|
|
106
124
|
val screenRegistry = LocalDynamicScreenRegistry.current
|
|
107
125
|
val navigation = remember { Navigation(id = id, bottomTabIndex = bottomTabIndex, initOptions = navigationOptions, registry = screenRegistry) }
|
|
@@ -157,7 +175,7 @@ internal fun StackScreen(
|
|
|
157
175
|
}
|
|
158
176
|
|
|
159
177
|
val limit = with(density) {
|
|
160
|
-
|
|
178
|
+
screenLayout.headerHeight.toPx()
|
|
161
179
|
}.toInt()
|
|
162
180
|
|
|
163
181
|
val lazyListState = options.scrollData.scrollState as? LazyListState
|
|
@@ -166,6 +184,11 @@ internal fun StackScreen(
|
|
|
166
184
|
else
|
|
167
185
|
(options.scrollData.scrollState as? ScrollState ?: rememberScrollState()).proxyLimitedScrollState(limit, 1)
|
|
168
186
|
val snapState: ScrollableState = lazyListState ?: scrollState
|
|
187
|
+
val headerHide = rememberQuickReturnHide(
|
|
188
|
+
enabled = options.usesDuoQuickReturnHeader(screenLayout),
|
|
189
|
+
scrollState = scrollState,
|
|
190
|
+
heightPx = with(density) { screenLayout.headerHeight.toPx() },
|
|
191
|
+
)
|
|
169
192
|
|
|
170
193
|
val footerHeightPx = remember { mutableIntStateOf(0) }
|
|
171
194
|
val headerRightWidthPx = remember { mutableIntStateOf(0) }
|
|
@@ -205,15 +228,23 @@ internal fun StackScreen(
|
|
|
205
228
|
HeaderBackground()
|
|
206
229
|
}
|
|
207
230
|
|
|
208
|
-
Box(Modifier.zIndex(4f)) {
|
|
231
|
+
Box(Modifier.zIndex(4f).graphicsLayer { translationY = -headerHide.value }) {
|
|
209
232
|
Header(onBackHandler)
|
|
210
233
|
}
|
|
211
234
|
|
|
235
|
+
HeaderSideBar(
|
|
236
|
+
bottomInset = maxOf(
|
|
237
|
+
bottomBarsHeight(isBottomTabChild) + Spacing.XL,
|
|
238
|
+
if (isBottomTabChild) LocalDuoRailReserve.current else 0.dp,
|
|
239
|
+
),
|
|
240
|
+
modifier = Modifier.zIndex(4f),
|
|
241
|
+
)
|
|
242
|
+
|
|
212
243
|
Column(Modifier.zIndex(5f).horizontalSafeAreaPadding()) {
|
|
213
244
|
SearchAnimated(snapState = snapState)
|
|
214
245
|
}
|
|
215
246
|
|
|
216
|
-
Column(Modifier.zIndex(2f).fillMaxSize()
|
|
247
|
+
Column(Modifier.zIndex(2f).fillMaxSize()) {
|
|
217
248
|
MainContent(content = content, isBottomTabRoot = isBottomTabRoot, isBottomTabChild = isBottomTabChild)
|
|
218
249
|
FooterContent()
|
|
219
250
|
}
|
|
@@ -227,22 +258,26 @@ internal fun StackScreen(
|
|
|
227
258
|
}
|
|
228
259
|
}
|
|
229
260
|
|
|
261
|
+
// Height the footer, tab bar or home indicator takes from the bottom of the screen.
|
|
262
|
+
@Composable
|
|
263
|
+
private fun bottomBarsHeight(isBottomTabChild: Boolean): Dp {
|
|
264
|
+
val options = LocalOptions.current
|
|
265
|
+
val navigationBar = AppNavigationBar.current
|
|
266
|
+
return when {
|
|
267
|
+
options.hasFooter() -> with(LocalDensity.current) { LocalFooterHeightPx.current.intValue.toDp() }
|
|
268
|
+
isBottomTabChild -> bottomTabChildInset()
|
|
269
|
+
else -> navigationBar
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
230
273
|
@Suppress("FrequentlyChangingValue")
|
|
231
274
|
@Composable
|
|
232
275
|
internal fun FloatingContent(isBottomTabChild: Boolean) {
|
|
233
276
|
val options = LocalOptions.current
|
|
234
|
-
val density = LocalDensity.current
|
|
235
|
-
val footerHeightPx = LocalFooterHeightPx.current
|
|
236
277
|
val scrollState = LocalScrollState.current
|
|
237
|
-
val navigationBar = AppNavigationBar.current
|
|
238
278
|
|
|
239
279
|
val fabProps = options.floatingButtonProps ?: return
|
|
240
|
-
val bottomPadding = fabProps.bottom
|
|
241
|
-
?: (Spacing.M + when {
|
|
242
|
-
options.hasFooter() -> with(density) { footerHeightPx.intValue.toDp() }
|
|
243
|
-
isBottomTabChild -> bottomTabBarInset() + (navigationBar - keyboardSizeState().value).coerceAtLeast(0.dp)
|
|
244
|
-
else -> navigationBar
|
|
245
|
-
})
|
|
280
|
+
val bottomPadding = fabProps.bottom ?: (Spacing.M + bottomBarsHeight(isBottomTabChild))
|
|
246
281
|
|
|
247
282
|
FloatingButton(
|
|
248
283
|
scrollPosition = fabProps.scrollState?.value ?: scrollState.value,
|
|
@@ -264,11 +299,13 @@ internal fun ColumnScope.MainContent(content: @Composable () -> Unit, isBottomTa
|
|
|
264
299
|
val density = LocalDensity.current
|
|
265
300
|
val scrollState = LocalScrollState.current
|
|
266
301
|
val navigationBar = AppNavigationBar.current
|
|
302
|
+
val screenLayout = getScreenLayout()
|
|
303
|
+
val useDuoGlass = options.usesDuoGlassHeader(screenLayout)
|
|
267
304
|
|
|
268
305
|
Spacer(
|
|
269
306
|
Modifier.height(
|
|
270
|
-
if (options.headerType is HeaderType.DefaultOrExtended || (options.headerType is HeaderType.Transparent && !options.headerType.isFullScreenContent))
|
|
271
|
-
|
|
307
|
+
if (!useDuoGlass && (options.headerType is HeaderType.DefaultOrExtended || (options.headerType is HeaderType.Transparent && !options.headerType.isFullScreenContent)))
|
|
308
|
+
screenLayout.headerHeight else 0.dp
|
|
272
309
|
)
|
|
273
310
|
)
|
|
274
311
|
if (inputSearchType == InputSearchType.Animated) {
|
|
@@ -276,31 +313,46 @@ internal fun ColumnScope.MainContent(content: @Composable () -> Unit, isBottomTa
|
|
|
276
313
|
|
|
277
314
|
Spacer(Modifier.height((HEADER_HEIGHT.dp - scrollDp).coerceIn(0.dp, HEADER_HEIGHT.dp)))
|
|
278
315
|
}
|
|
316
|
+
// a bottom tab root stays full width: the tab bar and each tab's own StackScreen handle the insets
|
|
317
|
+
val insetContent = !isBottomTabRoot
|
|
279
318
|
Column(
|
|
280
319
|
Modifier
|
|
281
320
|
.fillMaxWidth()
|
|
282
321
|
.weight(1f)
|
|
322
|
+
.conditional(insetContent && options.headerType !is HeaderType.Animated) {
|
|
323
|
+
horizontalSafeAreaPadding()
|
|
324
|
+
}
|
|
283
325
|
.conditional(options.scrollData.scrollable && options.scrollData.scrollState is ScrollState) {
|
|
284
326
|
verticalScroll(scrollState)
|
|
285
327
|
}
|
|
328
|
+
.conditional(useDuoGlass) {
|
|
329
|
+
padding(top = screenLayout.headerHeight)
|
|
330
|
+
}
|
|
286
331
|
.padding(
|
|
287
332
|
bottom = when {
|
|
288
333
|
options.hasFooter() || isBottomTabRoot -> 0.dp
|
|
289
|
-
isBottomTabChild ->
|
|
334
|
+
isBottomTabChild -> bottomTabChildInset()
|
|
290
335
|
!options.scrollData.scrollable && options.scrollData.scrollState == null -> 0.dp
|
|
291
336
|
else -> navigationBar
|
|
292
337
|
}
|
|
293
338
|
)
|
|
294
339
|
) {
|
|
295
|
-
ScreenContent(content = content)
|
|
340
|
+
ScreenContent(content = content, insetContent = insetContent)
|
|
296
341
|
}
|
|
297
342
|
|
|
298
343
|
}
|
|
299
344
|
|
|
300
345
|
@Composable
|
|
301
|
-
internal fun ScreenContent(content: @Composable () -> Unit) {
|
|
346
|
+
internal fun ScreenContent(content: @Composable () -> Unit, insetContent: Boolean) {
|
|
302
347
|
val scrollState = LocalScrollState.current
|
|
303
348
|
val options = LocalOptions.current
|
|
349
|
+
val screenLayout = getScreenLayout()
|
|
350
|
+
val insetBody: @Composable () -> Unit = {
|
|
351
|
+
CompositionLocalProvider(
|
|
352
|
+
LocalScreenLayout provides if (insetContent) screenLayout.consumeHorizontalInsets() else screenLayout,
|
|
353
|
+
content = content,
|
|
354
|
+
)
|
|
355
|
+
}
|
|
304
356
|
|
|
305
357
|
if (options.headerType is HeaderType.Animated) {
|
|
306
358
|
val animatedHeader = options.headerType
|
|
@@ -311,12 +363,16 @@ internal fun ScreenContent(content: @Composable () -> Unit) {
|
|
|
311
363
|
Box(bannerModifier) {
|
|
312
364
|
animatedHeader.composable.invoke(scrollState.value)
|
|
313
365
|
}
|
|
314
|
-
Box(
|
|
315
|
-
|
|
366
|
+
Box(
|
|
367
|
+
Modifier
|
|
368
|
+
.padding(top = screenLayout.headerHeight + animatedHeader.layoutOffSet)
|
|
369
|
+
.conditional(insetContent) { horizontalSafeAreaPadding() }
|
|
370
|
+
) {
|
|
371
|
+
insetBody()
|
|
316
372
|
}
|
|
317
373
|
}
|
|
318
374
|
} else {
|
|
319
|
-
|
|
375
|
+
insetBody()
|
|
320
376
|
}
|
|
321
377
|
}
|
|
322
378
|
|
|
@@ -325,7 +381,9 @@ internal fun FooterContent() {
|
|
|
325
381
|
val options = LocalOptions.current
|
|
326
382
|
if (options.hasFooter()) {
|
|
327
383
|
val keyboardSize = keyboardSizeState()
|
|
328
|
-
|
|
384
|
+
// the Duo floating tab bar overlays tab content, so a tab's footer sits above it
|
|
385
|
+
val duoTabInset = if (LocalNavigation.current.bottomTabIndex != -1) duoFloatingTabBarInset(getScreenLayout()) else 0.dp
|
|
386
|
+
val bottomPadding = (maxOf(AppNavigationBar.current, duoTabInset) - keyboardSize.value).coerceAtLeast(0.dp)
|
|
329
387
|
Footer(
|
|
330
388
|
footerComponent = options.footerComponent,
|
|
331
389
|
bottomPadding = bottomPadding,
|
|
@@ -383,6 +441,7 @@ fun Footer(
|
|
|
383
441
|
.conditional(IsShowBaseLineDebug) {
|
|
384
442
|
border(1.dp, Colors.blue_03)
|
|
385
443
|
}
|
|
444
|
+
.horizontalSafeAreaPadding()
|
|
386
445
|
.padding(
|
|
387
446
|
top = if (useMargin) Spacing.S else 0.dp,
|
|
388
447
|
start = if (useMargin) Spacing.M else 0.dp,
|
|
@@ -430,17 +489,24 @@ internal fun SearchAnimated(
|
|
|
430
489
|
if (inputSearchType == InputSearchType.None) return
|
|
431
490
|
val inputSearchProps = (options.headerType as? HeaderType.DefaultOrExtended)?.inputSearchProps ?: return
|
|
432
491
|
|
|
433
|
-
val
|
|
434
|
-
val
|
|
492
|
+
val screenLayout = getScreenLayout()
|
|
493
|
+
val useSideBar = screenLayout.sideBar != null
|
|
494
|
+
// collapsed, the search is centered in the bar row
|
|
495
|
+
val minTopPadding = screenLayout.headerStatusBarHeight + (screenLayout.headerBarHeight - HEADER_HEIGHT.dp) / 2
|
|
496
|
+
val maxTopPadding = screenLayout.headerHeight
|
|
435
497
|
val minStartPadding = Spacing.M
|
|
436
|
-
val maxStartPadding = if (options.hiddenBack) Spacing.M else 48.dp // button back: size + left +right = 28 + 12 + 8 = 48
|
|
498
|
+
val maxStartPadding = if (options.hiddenBack || useSideBar) Spacing.M else 48.dp // button back: size + left +right = 28 + 12 + 8 = 48
|
|
437
499
|
val minEndPadding = Spacing.M
|
|
438
|
-
val maxEndPadding =
|
|
500
|
+
val maxEndPadding = when {
|
|
501
|
+
options.headerRight is HeaderRight.None -> Spacing.M
|
|
502
|
+
useSideBar && options.headerRight is HeaderRight.Toolkit -> Spacing.M
|
|
503
|
+
else -> headerRightWidthDp + Spacing.M * 2
|
|
504
|
+
}
|
|
439
505
|
|
|
440
506
|
val targetColor = lerp(
|
|
441
507
|
AppTheme.current.colors.background.surface,
|
|
442
508
|
AppTheme.current.colors.background.default,
|
|
443
|
-
(scrollDp / minTopPadding).coerceIn(0f, 1f)
|
|
509
|
+
(scrollDp / minTopPadding.coerceAtLeast(1.dp)).coerceIn(0f, 1f)
|
|
444
510
|
)
|
|
445
511
|
val animatedColor by animateColorAsState(targetValue = targetColor, label = "BackgroundColor")
|
|
446
512
|
|
|
@@ -582,6 +648,52 @@ internal val LocalOptions = staticCompositionLocalOf<NavigationOptions> { error(
|
|
|
582
648
|
|
|
583
649
|
val StackScreenScrollableState = staticCompositionLocalOf<ScrollableState?> { null }
|
|
584
650
|
|
|
651
|
+
// iPhone Duo side bar: the header leaves with the content and slides back in on a reverse scroll
|
|
652
|
+
// (RN DuoQuickReturnHeader). The side bar is drawn apart from the header, so it stays put.
|
|
653
|
+
@Composable
|
|
654
|
+
private fun rememberQuickReturnHide(
|
|
655
|
+
enabled: Boolean,
|
|
656
|
+
scrollState: ScrollState,
|
|
657
|
+
heightPx: Float,
|
|
658
|
+
): Animatable<Float, AnimationVector1D> {
|
|
659
|
+
val hide = remember { Animatable(0f) }
|
|
660
|
+
LaunchedEffect(enabled, scrollState, heightPx) {
|
|
661
|
+
if (!enabled) {
|
|
662
|
+
hide.snapTo(0f)
|
|
663
|
+
return@LaunchedEffect
|
|
664
|
+
}
|
|
665
|
+
var last = scrollState.value
|
|
666
|
+
snapshotFlow { scrollState.value }.collect { y ->
|
|
667
|
+
val delta = y - last
|
|
668
|
+
last = y
|
|
669
|
+
hide.snapTo(if (y <= 0) 0f else (hide.value + delta).coerceIn(0f, heightPx))
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
// a half-shown header settles fully in or out once the scroll stops
|
|
673
|
+
LaunchedEffect(enabled, scrollState, heightPx) {
|
|
674
|
+
if (!enabled) return@LaunchedEffect
|
|
675
|
+
snapshotFlow { scrollState.isScrollInProgress }
|
|
676
|
+
.filter { !it }
|
|
677
|
+
.collect {
|
|
678
|
+
val value = hide.value
|
|
679
|
+
// within the first header height the header still sits on the content
|
|
680
|
+
if (scrollState.value < heightPx || value <= 0f || value >= heightPx) return@collect
|
|
681
|
+
launch { hide.animateTo(if (value > heightPx / 2) heightPx else 0f, tween(200)) }
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return hide
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
internal fun NavigationOptions.usesDuoQuickReturnHeader(layout: ScreenLayout): Boolean =
|
|
688
|
+
usesDuoGlassHeader(layout) || (layout.sideBar != null && headerType is HeaderType.Animated &&
|
|
689
|
+
scrollData.scrollable && scrollData.scrollState is ScrollState)
|
|
690
|
+
|
|
691
|
+
// iPhone Duo side bar: content scrolls under a glass header instead of starting below it (RN Screen).
|
|
692
|
+
internal fun NavigationOptions.usesDuoGlassHeader(layout: ScreenLayout): Boolean =
|
|
693
|
+
layout.sideBar != null && headerType is HeaderType.DefaultOrExtended &&
|
|
694
|
+
getInputSearchType(this) == InputSearchType.None &&
|
|
695
|
+
scrollData.scrollable && scrollData.scrollState is ScrollState
|
|
696
|
+
|
|
585
697
|
internal fun getInputSearchType(options: NavigationOptions): InputSearchType {
|
|
586
698
|
return when (val headerType = options.headerType) {
|
|
587
699
|
is HeaderType.DefaultOrExtended -> when {
|
|
@@ -8,8 +8,10 @@ import androidx.compose.animation.scaleOut
|
|
|
8
8
|
import androidx.compose.foundation.background
|
|
9
9
|
import androidx.compose.foundation.layout.*
|
|
10
10
|
import androidx.compose.runtime.Composable
|
|
11
|
+
import androidx.compose.runtime.CompositionLocalProvider
|
|
11
12
|
import androidx.compose.runtime.DisposableEffect
|
|
12
13
|
import androidx.compose.runtime.LaunchedEffect
|
|
14
|
+
import androidx.compose.runtime.remember
|
|
13
15
|
import androidx.compose.ui.Alignment
|
|
14
16
|
import androidx.compose.ui.Modifier
|
|
15
17
|
import androidx.compose.ui.unit.dp
|
|
@@ -27,6 +29,7 @@ import vn.momo.kits.navigation.NavigationOptions
|
|
|
27
29
|
import vn.momo.kits.navigation.StackScreen
|
|
28
30
|
import vn.momo.kits.navigation.component.HeaderType
|
|
29
31
|
import vn.momo.kits.platform.reportFloatingArrowBack
|
|
32
|
+
import vn.momo.kits.layout.getScreenLayout
|
|
30
33
|
|
|
31
34
|
private var bottomTabOptionItems : MutableList<NavigationOptions?> = mutableListOf()
|
|
32
35
|
internal fun setBottomTabOption(index: Int, options: NavigationOptions){
|
|
@@ -43,16 +46,19 @@ internal fun getBottomTabOption(index: Int): NavigationOptions? {
|
|
|
43
46
|
@Composable
|
|
44
47
|
fun BottomTab(
|
|
45
48
|
items: List<BottomTabItem>,
|
|
46
|
-
floatingButton: BottomTabFloatingButton? = null
|
|
49
|
+
floatingButton: BottomTabFloatingButton? = null,
|
|
50
|
+
// iPhone Duo: when the side bar can't hold both bars, which one starts compressed;
|
|
51
|
+
// tapping the compressed one swaps them
|
|
52
|
+
compressionBehavior: DuoBarCompressionBehavior = DuoBarCompressionBehavior.TOOLBAR,
|
|
47
53
|
) {
|
|
48
54
|
val navigation = LocalNavigation.current
|
|
49
55
|
val navigator = LocalNavigator.current
|
|
50
56
|
val navController = rememberNavController()
|
|
51
57
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
// Only on new items: this recomposes on every tab switch and would drop options a tab set at runtime.
|
|
59
|
+
remember(items) {
|
|
60
|
+
bottomTabOptionItems = items.map { it.options ?: NavigationOptions() }.toMutableList()
|
|
61
|
+
}
|
|
56
62
|
|
|
57
63
|
LaunchedEffect(Unit){
|
|
58
64
|
navigation.markAsBottomTabRoot()
|
|
@@ -78,81 +84,109 @@ fun BottomTab(
|
|
|
78
84
|
}
|
|
79
85
|
}
|
|
80
86
|
|
|
87
|
+
val layout = getScreenLayout()
|
|
88
|
+
val hasQr = floatingButton != null
|
|
89
|
+
val compression = rememberDuoBarCompression(compressionBehavior, items.size, hasQr, layout)
|
|
90
|
+
val railReserve = layout.sideBar?.takeIf { layout.isIPhoneDuo }?.let {
|
|
91
|
+
it.bottom + duoRailHeight(items.size, hasQr, compressed = compression.tabBarCompressed) + DUO_GROUP_GAP
|
|
92
|
+
} ?: 0.dp
|
|
93
|
+
val onTabSelected: (Int) -> Unit = {
|
|
94
|
+
val targetRoute = "option$it"
|
|
95
|
+
if (currentRoute != targetRoute) {
|
|
96
|
+
// one entry per tab above the first, so switching never grows the back stack
|
|
97
|
+
navController.navigate(targetRoute) {
|
|
98
|
+
popUpTo("option0") { saveState = true }
|
|
99
|
+
launchSingleTop = true
|
|
100
|
+
restoreState = true
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
81
105
|
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
|
|
82
106
|
Box(modifier = Modifier
|
|
83
107
|
.fillMaxSize()
|
|
84
108
|
) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
109
|
+
CompositionLocalProvider(
|
|
110
|
+
LocalDuoRailReserve provides railReserve,
|
|
111
|
+
LocalDuoBarCompression provides compression,
|
|
88
112
|
) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
113
|
+
NavHost(
|
|
114
|
+
navController = navController,
|
|
115
|
+
startDestination = "option0"
|
|
116
|
+
) {
|
|
117
|
+
items.forEachIndexed { index, item ->
|
|
118
|
+
composable(
|
|
119
|
+
route = "option$index",
|
|
120
|
+
enterTransition = {
|
|
121
|
+
fadeIn(animationSpec = tween(200)) +
|
|
122
|
+
scaleIn(
|
|
123
|
+
initialScale = 0.97f,
|
|
124
|
+
animationSpec = tween(200)
|
|
125
|
+
)
|
|
126
|
+
},
|
|
127
|
+
exitTransition = {
|
|
128
|
+
fadeOut(animationSpec = tween(200)) +
|
|
129
|
+
scaleOut(
|
|
130
|
+
targetScale = 0.97f,
|
|
131
|
+
animationSpec = tween(200)
|
|
132
|
+
)
|
|
133
|
+
},
|
|
134
|
+
popEnterTransition = {
|
|
135
|
+
fadeIn(animationSpec = tween(200)) +
|
|
136
|
+
scaleIn(
|
|
137
|
+
initialScale = 0.97f,
|
|
138
|
+
animationSpec = tween(200)
|
|
139
|
+
)
|
|
140
|
+
},
|
|
141
|
+
popExitTransition = {
|
|
142
|
+
fadeOut(animationSpec = tween(200)) +
|
|
143
|
+
scaleOut(
|
|
144
|
+
targetScale = 0.97f,
|
|
145
|
+
animationSpec = tween(200)
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
) {
|
|
149
|
+
val option = getBottomTabOption(index)
|
|
122
150
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
151
|
+
StackScreen(
|
|
152
|
+
content = item.screen,
|
|
153
|
+
navigationOptions = option,
|
|
154
|
+
bottomTabIndex = index,
|
|
155
|
+
onBackHandler = {
|
|
156
|
+
if (index != 0) {
|
|
157
|
+
navController.popBackStack(
|
|
158
|
+
navController.graph.startDestinationRoute!!,
|
|
159
|
+
inclusive = false
|
|
160
|
+
)
|
|
161
|
+
} else {
|
|
162
|
+
navigator.pop()
|
|
163
|
+
}
|
|
135
164
|
}
|
|
136
|
-
|
|
137
|
-
|
|
165
|
+
)
|
|
166
|
+
}
|
|
138
167
|
}
|
|
139
168
|
}
|
|
140
169
|
}
|
|
141
170
|
}
|
|
142
|
-
|
|
143
|
-
|
|
171
|
+
if (layout.isIPhoneDuo) {
|
|
172
|
+
DuoTabBar(
|
|
173
|
+
layout = layout,
|
|
144
174
|
items = items,
|
|
145
175
|
floatingButton = floatingButton,
|
|
146
|
-
|
|
147
|
-
onTabSelected =
|
|
148
|
-
|
|
149
|
-
val targetRoute = "option$it"
|
|
150
|
-
if (currentRoute != targetRoute) {
|
|
151
|
-
navController.navigate(targetRoute)
|
|
152
|
-
}
|
|
153
|
-
}
|
|
176
|
+
selectedIndex = items.indices.firstOrNull { "option$it" == currentRoute } ?: 0,
|
|
177
|
+
onTabSelected = onTabSelected,
|
|
178
|
+
compression = compression,
|
|
154
179
|
)
|
|
155
|
-
|
|
180
|
+
} else {
|
|
181
|
+
Column {
|
|
182
|
+
BottomTabBar(
|
|
183
|
+
items = items,
|
|
184
|
+
floatingButton = floatingButton,
|
|
185
|
+
navController = navController,
|
|
186
|
+
onTabSelected = onTabSelected,
|
|
187
|
+
)
|
|
188
|
+
Spacer(modifier = Modifier.fillMaxWidth().height(AppNavigationBar.current + Spacing.S).background(AppTheme.current.colors.background.surface))
|
|
189
|
+
}
|
|
156
190
|
}
|
|
157
191
|
}
|
|
158
192
|
}
|
|
@@ -26,7 +26,8 @@ import vn.momo.kits.const.*
|
|
|
26
26
|
import vn.momo.kits.modifier.InternalApi
|
|
27
27
|
import vn.momo.kits.modifier.conditional
|
|
28
28
|
import vn.momo.kits.modifier.noFeedbackClickable
|
|
29
|
-
import vn.momo.kits.
|
|
29
|
+
import vn.momo.kits.layout.getScreenLayout
|
|
30
|
+
import vn.momo.kits.utils.horizontalSafeAreaPadding
|
|
30
31
|
|
|
31
32
|
internal val floatingButtonWidth = 75.dp
|
|
32
33
|
@InternalApi
|
|
@@ -40,18 +41,19 @@ internal fun BottomTabBar(
|
|
|
40
41
|
onTabSelected: (Int) -> Unit,
|
|
41
42
|
) {
|
|
42
43
|
val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
|
|
43
|
-
val selectedIndex = items.
|
|
44
|
+
val selectedIndex = items.indices.firstOrNull { "option$it" == currentRoute } ?: 0
|
|
44
45
|
|
|
45
|
-
val
|
|
46
|
+
val layout = getScreenLayout()
|
|
46
47
|
val mid = items.size / 2
|
|
47
48
|
|
|
48
49
|
val tabWidth = if (floatingButton != null) {
|
|
49
|
-
(
|
|
50
|
+
(layout.contentWidth - floatingButtonWidth) / items.size
|
|
50
51
|
} else {
|
|
51
|
-
|
|
52
|
+
layout.contentWidth / items.size
|
|
52
53
|
}
|
|
53
54
|
val adjustSize = if(floatingButton != null && selectedIndex >= mid) floatingButtonWidth else 0.dp
|
|
54
|
-
|
|
55
|
+
// offsets are from the bar center; tabs live between the side safe areas
|
|
56
|
+
val targetOffset = layout.insetLeft + tabWidth * selectedIndex + tabWidth / 2 - layout.width / 2 + adjustSize
|
|
55
57
|
|
|
56
58
|
val indicatorOffsetX by animateDpAsState(
|
|
57
59
|
targetValue = targetOffset,
|
|
@@ -69,7 +71,8 @@ internal fun BottomTabBar(
|
|
|
69
71
|
.background(AppTheme.current.colors.background.surface)
|
|
70
72
|
.conditional(IsShowBaseLineDebug) {
|
|
71
73
|
border(1.dp, Colors.blue_03)
|
|
72
|
-
}
|
|
74
|
+
}
|
|
75
|
+
.horizontalSafeAreaPadding(),
|
|
73
76
|
horizontalArrangement = Arrangement.SpaceAround,
|
|
74
77
|
) {
|
|
75
78
|
if (floatingButton == null) {
|
|
@@ -101,7 +104,11 @@ internal fun BottomTabBar(
|
|
|
101
104
|
)
|
|
102
105
|
)
|
|
103
106
|
)
|
|
104
|
-
floatingButton?.let {
|
|
107
|
+
floatingButton?.let {
|
|
108
|
+
Box(Modifier.offset(x = (layout.insetLeft - layout.insetRight) / 2)) {
|
|
109
|
+
FloatingButton(it)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
105
112
|
}
|
|
106
113
|
}
|
|
107
114
|
|
|
@@ -172,14 +179,13 @@ internal fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> U
|
|
|
172
179
|
overflow = TextOverflow.Ellipsis
|
|
173
180
|
)
|
|
174
181
|
}
|
|
175
|
-
if (badgeLabel != null) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
Badge(badgeLabel, modifier = Modifier.offset(x = 44.dp, y = (-42).dp))
|
|
182
|
+
if (badgeLabel != null && badgeLabel.isNotEmpty()) {
|
|
183
|
+
Badge(badgeLabel, modifier = Modifier.offset(x = 44.dp, y = (-42).dp))
|
|
184
|
+
} else if (badgeLabel != null || item.showDot) {
|
|
185
|
+
BadgeDot(
|
|
186
|
+
size = DotSize.Small,
|
|
187
|
+
modifier = Modifier.offset(x = 50.dp, y = (-48).dp)
|
|
188
|
+
)
|
|
183
189
|
}
|
|
184
190
|
}
|
|
185
191
|
}
|