@momo-kits/native-kits 0.167.1-duo.2-debug → 0.167.1-duo.20-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 +36 -8
- package/README.md +2 -2
- 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/application/LiteScreen.kt +168 -52
- 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/Icon.kt +24 -14
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.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/const/HostFont.kt +4 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +10 -4
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +3 -17
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +173 -21
- 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/Navigation.kt +10 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +153 -48
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +114 -71
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +8 -9
- 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 +84 -18
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +33 -13
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +2 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +51 -19
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +4 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +46 -23
- 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/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +38 -0
- 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 +94 -0
- package/gradle.properties +1 -1
- package/ios/Application/ApplicationEnvironment.swift +17 -0
- package/ios/Application/MaxApi.swift +12 -3
- 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/Colors+Radius+Spacing/Radius.swift +6 -0
- 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 +187 -3
- package/ios/Layout/Item.swift +5 -4
- package/ios/Layout/ScreenLayout.swift +380 -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 +128 -9
- package/ios/Navigation/BottomTab/BottomTab.swift +185 -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 +531 -0
- package/ios/Navigation/Component/HeaderBackground.swift +133 -0
- package/ios/Navigation/Component/HeaderJourney.swift +73 -0
- package/ios/Navigation/Component/HeaderLocation.swift +48 -0
- package/ios/Navigation/Component/HeaderRight.swift +377 -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 +183 -0
- package/ios/{Application/Navigation/Overplay → Navigation/Component}/SnackBar.swift +5 -6
- package/ios/Navigation/HostBackGestureBridge.swift +78 -0
- package/ios/{Application/Navigation/Overplay → Navigation}/ModalScreen.swift +5 -2
- package/ios/Navigation/Navigation.swift +204 -0
- package/ios/Navigation/NavigationContainer.swift +194 -0
- package/ios/{Application/Navigation → Navigation}/Navigator.swift +177 -91
- package/ios/Navigation/StackScreen.swift +820 -0
- package/ios/Navigation/StackTransition.swift +400 -0
- package/ios/Navigation/SwipeBackHaptic.swift +33 -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/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -52
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -183
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -207
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -39
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -293
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -110
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -378
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
- package/ios/Application/Components.swift +0 -270
- package/ios/Application/ComposeApi.swift +0 -22
- 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/NavigationContainer.swift +0 -213
- 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/Screen.swift +0 -250
- package/ios/Application/StackScreen.swift +0 -492
- /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
|
|
@@ -65,6 +69,8 @@ import vn.momo.kits.navigation.component.Header
|
|
|
65
69
|
import vn.momo.kits.navigation.component.HeaderBackground
|
|
66
70
|
import vn.momo.kits.navigation.component.HeaderRight
|
|
67
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
|
|
@@ -75,7 +81,9 @@ import kotlinx.coroutines.delay
|
|
|
75
81
|
import vn.momo.kits.application.LocalContext
|
|
76
82
|
import vn.momo.kits.utils.horizontalSafeAreaPadding
|
|
77
83
|
import vn.momo.kits.layout.LocalScreenLayout
|
|
84
|
+
import vn.momo.kits.layout.ScreenLayout
|
|
78
85
|
import vn.momo.kits.layout.consumeHorizontalInsets
|
|
86
|
+
import vn.momo.kits.layout.DuoMetrics
|
|
79
87
|
import vn.momo.kits.layout.getScreenLayout
|
|
80
88
|
import kotlin.time.Clock
|
|
81
89
|
import kotlin.time.Duration.Companion.milliseconds
|
|
@@ -84,11 +92,19 @@ import kotlin.time.ExperimentalTime
|
|
|
84
92
|
internal val LocalFooterHeightPx = staticCompositionLocalOf { mutableIntStateOf(0) }
|
|
85
93
|
internal val LocalHeaderRightWidthPx = staticCompositionLocalOf { mutableIntStateOf(0) }
|
|
86
94
|
|
|
95
|
+
// Space a tab's content leaves for the tab bar and home indicator. On iPhone Duo the floating bar
|
|
96
|
+
// overlays the content and the rail lives in the side strip, so only their own inset applies.
|
|
87
97
|
@Composable
|
|
88
|
-
private fun
|
|
89
|
-
if (LocalNavigation.current.bottomTabIndex == -1) return 0.dp
|
|
98
|
+
private fun bottomTabChildInset(): Dp {
|
|
90
99
|
val keyboard = keyboardSizeState().value
|
|
91
|
-
|
|
100
|
+
val navigationBar = AppNavigationBar.current
|
|
101
|
+
val layout = getScreenLayout()
|
|
102
|
+
return when {
|
|
103
|
+
layout.isIPhoneDuo && layout.sideBar == null -> (duoFloatingTabBarInset(layout) - keyboard).coerceAtLeast(0.dp)
|
|
104
|
+
layout.isIPhoneDuo -> (navigationBar - keyboard).coerceAtLeast(0.dp)
|
|
105
|
+
else -> (BOTTOM_TAB_BAR_HEIGHT.dp + Spacing.S - keyboard).coerceAtLeast(0.dp) +
|
|
106
|
+
(navigationBar - keyboard).coerceAtLeast(0.dp)
|
|
107
|
+
}
|
|
92
108
|
}
|
|
93
109
|
|
|
94
110
|
@OptIn(ExperimentalMaterialApi::class, ExperimentalTime::class)
|
|
@@ -169,6 +185,11 @@ internal fun StackScreen(
|
|
|
169
185
|
else
|
|
170
186
|
(options.scrollData.scrollState as? ScrollState ?: rememberScrollState()).proxyLimitedScrollState(limit, 1)
|
|
171
187
|
val snapState: ScrollableState = lazyListState ?: scrollState
|
|
188
|
+
val headerHide = rememberQuickReturnHide(
|
|
189
|
+
enabled = options.usesDuoQuickReturnHeader(screenLayout),
|
|
190
|
+
scrollState = scrollState,
|
|
191
|
+
heightPx = with(density) { screenLayout.headerHeight.toPx() },
|
|
192
|
+
)
|
|
172
193
|
|
|
173
194
|
val footerHeightPx = remember { mutableIntStateOf(0) }
|
|
174
195
|
val headerRightWidthPx = remember { mutableIntStateOf(0) }
|
|
@@ -193,42 +214,62 @@ internal fun StackScreen(
|
|
|
193
214
|
LocalFooterHeightPx provides footerHeightPx,
|
|
194
215
|
LocalHeaderRightWidthPx provides headerRightWidthPx
|
|
195
216
|
) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
.
|
|
200
|
-
|
|
201
|
-
|
|
217
|
+
// AI-GENERATED START: custom screen background, full window and outside the keyboard padding
|
|
218
|
+
Box(Modifier.fillMaxSize()) {
|
|
219
|
+
options.backgroundComponent?.let { background ->
|
|
220
|
+
Box(Modifier.fillMaxSize()) { background() }
|
|
221
|
+
}
|
|
222
|
+
// AI-GENERATED END: custom screen background, full window and outside the keyboard padding
|
|
223
|
+
Box(
|
|
224
|
+
Modifier
|
|
225
|
+
.fillMaxSize()
|
|
226
|
+
// AI-GENERATED START: custom background replaces the colour
|
|
227
|
+
.background(
|
|
228
|
+
if (options.backgroundComponent != null) Color.Transparent
|
|
229
|
+
else options.backgroundColor ?: AppTheme.current.colors.background.default
|
|
230
|
+
)
|
|
231
|
+
// AI-GENERATED END: custom background replaces the colour
|
|
232
|
+
.conditional(options.keyboardOptions.keyboardShouldPersistTaps) {
|
|
233
|
+
hideKeyboardOnTap()
|
|
234
|
+
}
|
|
235
|
+
.conditional(options.keyboardOptions.useAvoidKeyboard && supportsImePadding()) {
|
|
236
|
+
imePadding()
|
|
237
|
+
}
|
|
238
|
+
) {
|
|
239
|
+
Box(Modifier.zIndex(1f)) {
|
|
240
|
+
HeaderBackground()
|
|
202
241
|
}
|
|
203
|
-
|
|
204
|
-
|
|
242
|
+
|
|
243
|
+
Box(Modifier.zIndex(4f).graphicsLayer { translationY = -headerHide.value }) {
|
|
244
|
+
Header(onBackHandler)
|
|
205
245
|
}
|
|
206
|
-
) {
|
|
207
|
-
Box(Modifier.zIndex(1f)) {
|
|
208
|
-
HeaderBackground()
|
|
209
|
-
}
|
|
210
246
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
247
|
+
HeaderSideBar(
|
|
248
|
+
bottomInset = maxOf(
|
|
249
|
+
bottomBarsHeight(isBottomTabChild) + Spacing.XL,
|
|
250
|
+
if (isBottomTabChild) LocalDuoRailReserve.current else 0.dp,
|
|
251
|
+
),
|
|
252
|
+
modifier = Modifier.zIndex(4f),
|
|
253
|
+
)
|
|
214
254
|
|
|
215
|
-
|
|
255
|
+
Column(Modifier.zIndex(5f).horizontalSafeAreaPadding()) {
|
|
256
|
+
SearchAnimated(snapState = snapState)
|
|
257
|
+
}
|
|
216
258
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
259
|
+
Column(Modifier.zIndex(2f).fillMaxSize()) {
|
|
260
|
+
MainContent(content = content, isBottomTabRoot = isBottomTabRoot, isBottomTabChild = isBottomTabChild)
|
|
261
|
+
FooterContent()
|
|
262
|
+
}
|
|
220
263
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
264
|
+
Box(Modifier.zIndex(6f).horizontalSafeAreaPadding()) {
|
|
265
|
+
FloatingContent(isBottomTabChild = isBottomTabChild)
|
|
266
|
+
}
|
|
225
267
|
|
|
226
|
-
|
|
227
|
-
FloatingContent(isBottomTabChild = isBottomTabChild)
|
|
268
|
+
OverplayView(isBottomTabChild = isBottomTabChild, id = id)
|
|
228
269
|
}
|
|
229
|
-
|
|
230
|
-
OverplayView(isBottomTabChild = isBottomTabChild, id = id)
|
|
270
|
+
// AI-GENERATED START: custom screen background, full window and outside the keyboard padding
|
|
231
271
|
}
|
|
272
|
+
// AI-GENERATED END: custom screen background, full window and outside the keyboard padding
|
|
232
273
|
}
|
|
233
274
|
}
|
|
234
275
|
|
|
@@ -239,7 +280,7 @@ private fun bottomBarsHeight(isBottomTabChild: Boolean): Dp {
|
|
|
239
280
|
val navigationBar = AppNavigationBar.current
|
|
240
281
|
return when {
|
|
241
282
|
options.hasFooter() -> with(LocalDensity.current) { LocalFooterHeightPx.current.intValue.toDp() }
|
|
242
|
-
isBottomTabChild ->
|
|
283
|
+
isBottomTabChild -> bottomTabChildInset()
|
|
243
284
|
else -> navigationBar
|
|
244
285
|
}
|
|
245
286
|
}
|
|
@@ -274,10 +315,11 @@ internal fun ColumnScope.MainContent(content: @Composable () -> Unit, isBottomTa
|
|
|
274
315
|
val scrollState = LocalScrollState.current
|
|
275
316
|
val navigationBar = AppNavigationBar.current
|
|
276
317
|
val screenLayout = getScreenLayout()
|
|
318
|
+
val useDuoGlass = options.usesDuoGlassHeader(screenLayout)
|
|
277
319
|
|
|
278
320
|
Spacer(
|
|
279
321
|
Modifier.height(
|
|
280
|
-
if (options.headerType is HeaderType.DefaultOrExtended || (options.headerType is HeaderType.Transparent && !options.headerType.isFullScreenContent))
|
|
322
|
+
if (!useDuoGlass && (options.headerType is HeaderType.DefaultOrExtended || (options.headerType is HeaderType.Transparent && !options.headerType.isFullScreenContent)))
|
|
281
323
|
screenLayout.headerHeight else 0.dp
|
|
282
324
|
)
|
|
283
325
|
)
|
|
@@ -287,38 +329,45 @@ internal fun ColumnScope.MainContent(content: @Composable () -> Unit, isBottomTa
|
|
|
287
329
|
Spacer(Modifier.height((HEADER_HEIGHT.dp - scrollDp).coerceIn(0.dp, HEADER_HEIGHT.dp)))
|
|
288
330
|
}
|
|
289
331
|
// a bottom tab root stays full width: the tab bar and each tab's own StackScreen handle the insets
|
|
332
|
+
val insetContent = !isBottomTabRoot
|
|
290
333
|
Column(
|
|
291
334
|
Modifier
|
|
292
335
|
.fillMaxWidth()
|
|
293
336
|
.weight(1f)
|
|
294
|
-
.conditional(!
|
|
337
|
+
.conditional(insetContent && options.headerType !is HeaderType.Animated) {
|
|
295
338
|
horizontalSafeAreaPadding()
|
|
296
339
|
}
|
|
297
340
|
.conditional(options.scrollData.scrollable && options.scrollData.scrollState is ScrollState) {
|
|
298
341
|
verticalScroll(scrollState)
|
|
299
342
|
}
|
|
343
|
+
.conditional(useDuoGlass) {
|
|
344
|
+
padding(top = screenLayout.headerHeight)
|
|
345
|
+
}
|
|
300
346
|
.padding(
|
|
301
347
|
bottom = when {
|
|
302
348
|
options.hasFooter() || isBottomTabRoot -> 0.dp
|
|
303
|
-
isBottomTabChild ->
|
|
349
|
+
isBottomTabChild -> bottomTabChildInset()
|
|
304
350
|
!options.scrollData.scrollable && options.scrollData.scrollState == null -> 0.dp
|
|
305
351
|
else -> navigationBar
|
|
306
352
|
}
|
|
307
353
|
)
|
|
308
354
|
) {
|
|
309
|
-
|
|
310
|
-
LocalScreenLayout provides if (isBottomTabRoot) screenLayout else screenLayout.consumeHorizontalInsets()
|
|
311
|
-
) {
|
|
312
|
-
ScreenContent(content = content)
|
|
313
|
-
}
|
|
355
|
+
ScreenContent(content = content, insetContent = insetContent)
|
|
314
356
|
}
|
|
315
357
|
|
|
316
358
|
}
|
|
317
359
|
|
|
318
360
|
@Composable
|
|
319
|
-
internal fun ScreenContent(content: @Composable () -> Unit) {
|
|
361
|
+
internal fun ScreenContent(content: @Composable () -> Unit, insetContent: Boolean) {
|
|
320
362
|
val scrollState = LocalScrollState.current
|
|
321
363
|
val options = LocalOptions.current
|
|
364
|
+
val screenLayout = getScreenLayout()
|
|
365
|
+
val insetBody: @Composable () -> Unit = {
|
|
366
|
+
CompositionLocalProvider(
|
|
367
|
+
LocalScreenLayout provides if (insetContent) screenLayout.consumeHorizontalInsets() else screenLayout,
|
|
368
|
+
content = content,
|
|
369
|
+
)
|
|
370
|
+
}
|
|
322
371
|
|
|
323
372
|
if (options.headerType is HeaderType.Animated) {
|
|
324
373
|
val animatedHeader = options.headerType
|
|
@@ -329,12 +378,16 @@ internal fun ScreenContent(content: @Composable () -> Unit) {
|
|
|
329
378
|
Box(bannerModifier) {
|
|
330
379
|
animatedHeader.composable.invoke(scrollState.value)
|
|
331
380
|
}
|
|
332
|
-
Box(
|
|
333
|
-
|
|
381
|
+
Box(
|
|
382
|
+
Modifier
|
|
383
|
+
.padding(top = screenLayout.headerHeight + animatedHeader.layoutOffSet)
|
|
384
|
+
.conditional(insetContent) { horizontalSafeAreaPadding() }
|
|
385
|
+
) {
|
|
386
|
+
insetBody()
|
|
334
387
|
}
|
|
335
388
|
}
|
|
336
389
|
} else {
|
|
337
|
-
|
|
390
|
+
insetBody()
|
|
338
391
|
}
|
|
339
392
|
}
|
|
340
393
|
|
|
@@ -343,7 +396,9 @@ internal fun FooterContent() {
|
|
|
343
396
|
val options = LocalOptions.current
|
|
344
397
|
if (options.hasFooter()) {
|
|
345
398
|
val keyboardSize = keyboardSizeState()
|
|
346
|
-
|
|
399
|
+
// the Duo floating tab bar overlays tab content, so a tab's footer sits above it
|
|
400
|
+
val duoTabInset = if (LocalNavigation.current.bottomTabIndex != -1) duoFloatingTabBarInset(getScreenLayout()) else 0.dp
|
|
401
|
+
val bottomPadding = (maxOf(AppNavigationBar.current, duoTabInset) - keyboardSize.value).coerceAtLeast(0.dp)
|
|
347
402
|
Footer(
|
|
348
403
|
footerComponent = options.footerComponent,
|
|
349
404
|
bottomPadding = bottomPadding,
|
|
@@ -391,9 +446,10 @@ fun Footer(
|
|
|
391
446
|
|
|
392
447
|
val backgroundColor = if (transparent) Color.Transparent else AppTheme.current.colors.background.surface
|
|
393
448
|
|
|
449
|
+
// inset as a whole, not padded inside, so the surface and its shadow stay off the iPhone Duo side bar
|
|
394
450
|
Box(Modifier.onGloballyPositioned {
|
|
395
451
|
if (footerHeightPx.intValue != it.size.height) footerHeightPx.intValue = it.size.height
|
|
396
|
-
}) {
|
|
452
|
+
}.horizontalSafeAreaPadding()) {
|
|
397
453
|
Box(
|
|
398
454
|
Modifier
|
|
399
455
|
.fillMaxWidth()
|
|
@@ -401,7 +457,6 @@ fun Footer(
|
|
|
401
457
|
.conditional(IsShowBaseLineDebug) {
|
|
402
458
|
border(1.dp, Colors.blue_03)
|
|
403
459
|
}
|
|
404
|
-
.horizontalSafeAreaPadding()
|
|
405
460
|
.padding(
|
|
406
461
|
top = if (useMargin) Spacing.S else 0.dp,
|
|
407
462
|
start = if (useMargin) Spacing.M else 0.dp,
|
|
@@ -454,10 +509,14 @@ internal fun SearchAnimated(
|
|
|
454
509
|
// collapsed, the search is centered in the bar row
|
|
455
510
|
val minTopPadding = screenLayout.headerStatusBarHeight + (screenLayout.headerBarHeight - HEADER_HEIGHT.dp) / 2
|
|
456
511
|
val maxTopPadding = screenLayout.headerHeight
|
|
512
|
+
// collapsed on the iPhone Duo status bar row, the search stays between its clusters (48pt back there)
|
|
513
|
+
val clearance = screenLayout.statusBarClearance
|
|
514
|
+
val backWidth = if (clearance != null && !useSideBar) DuoMetrics.controlSize else 28.dp
|
|
457
515
|
val minStartPadding = Spacing.M
|
|
458
|
-
val maxStartPadding =
|
|
516
|
+
val maxStartPadding = (clearance?.left ?: 0.dp) +
|
|
517
|
+
if (options.hiddenBack || useSideBar) Spacing.M else backWidth + Spacing.M + Spacing.S
|
|
459
518
|
val minEndPadding = Spacing.M
|
|
460
|
-
val maxEndPadding = when {
|
|
519
|
+
val maxEndPadding = (clearance?.right ?: 0.dp) + when {
|
|
461
520
|
options.headerRight is HeaderRight.None -> Spacing.M
|
|
462
521
|
useSideBar && options.headerRight is HeaderRight.Toolkit -> Spacing.M
|
|
463
522
|
else -> headerRightWidthDp + Spacing.M * 2
|
|
@@ -608,6 +667,52 @@ internal val LocalOptions = staticCompositionLocalOf<NavigationOptions> { error(
|
|
|
608
667
|
|
|
609
668
|
val StackScreenScrollableState = staticCompositionLocalOf<ScrollableState?> { null }
|
|
610
669
|
|
|
670
|
+
// iPhone Duo side bar: the header leaves with the content and slides back in on a reverse scroll
|
|
671
|
+
// (RN DuoQuickReturnHeader). The side bar is drawn apart from the header, so it stays put.
|
|
672
|
+
@Composable
|
|
673
|
+
private fun rememberQuickReturnHide(
|
|
674
|
+
enabled: Boolean,
|
|
675
|
+
scrollState: ScrollState,
|
|
676
|
+
heightPx: Float,
|
|
677
|
+
): Animatable<Float, AnimationVector1D> {
|
|
678
|
+
val hide = remember { Animatable(0f) }
|
|
679
|
+
LaunchedEffect(enabled, scrollState, heightPx) {
|
|
680
|
+
if (!enabled) {
|
|
681
|
+
hide.snapTo(0f)
|
|
682
|
+
return@LaunchedEffect
|
|
683
|
+
}
|
|
684
|
+
var last = scrollState.value
|
|
685
|
+
snapshotFlow { scrollState.value }.collect { y ->
|
|
686
|
+
val delta = y - last
|
|
687
|
+
last = y
|
|
688
|
+
hide.snapTo(if (y <= 0) 0f else (hide.value + delta).coerceIn(0f, heightPx))
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
// a half-shown header settles fully in or out once the scroll stops
|
|
692
|
+
LaunchedEffect(enabled, scrollState, heightPx) {
|
|
693
|
+
if (!enabled) return@LaunchedEffect
|
|
694
|
+
snapshotFlow { scrollState.isScrollInProgress }
|
|
695
|
+
.filter { !it }
|
|
696
|
+
.collect {
|
|
697
|
+
val value = hide.value
|
|
698
|
+
// within the first header height the header still sits on the content
|
|
699
|
+
if (scrollState.value < heightPx || value <= 0f || value >= heightPx) return@collect
|
|
700
|
+
launch { hide.animateTo(if (value > heightPx / 2) heightPx else 0f, tween(200)) }
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
return hide
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
internal fun NavigationOptions.usesDuoQuickReturnHeader(layout: ScreenLayout): Boolean =
|
|
707
|
+
usesDuoGlassHeader(layout) || (layout.sideBar != null && headerType is HeaderType.Animated &&
|
|
708
|
+
scrollData.scrollable && scrollData.scrollState is ScrollState)
|
|
709
|
+
|
|
710
|
+
// iPhone Duo side bar: content scrolls under a glass header instead of starting below it (RN Screen).
|
|
711
|
+
internal fun NavigationOptions.usesDuoGlassHeader(layout: ScreenLayout): Boolean =
|
|
712
|
+
layout.sideBar != null && headerType is HeaderType.DefaultOrExtended &&
|
|
713
|
+
getInputSearchType(this) == InputSearchType.None &&
|
|
714
|
+
scrollData.scrollable && scrollData.scrollState is ScrollState
|
|
715
|
+
|
|
611
716
|
internal fun getInputSearchType(options: NavigationOptions): InputSearchType {
|
|
612
717
|
return when (val headerType = options.headerType) {
|
|
613
718
|
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
|
|
@@ -26,7 +28,9 @@ import vn.momo.kits.navigation.LocalNavigator
|
|
|
26
28
|
import vn.momo.kits.navigation.NavigationOptions
|
|
27
29
|
import vn.momo.kits.navigation.StackScreen
|
|
28
30
|
import vn.momo.kits.navigation.component.HeaderType
|
|
31
|
+
import vn.momo.kits.platform.BackHandler
|
|
29
32
|
import vn.momo.kits.platform.reportFloatingArrowBack
|
|
33
|
+
import vn.momo.kits.layout.getScreenLayout
|
|
30
34
|
|
|
31
35
|
private var bottomTabOptionItems : MutableList<NavigationOptions?> = mutableListOf()
|
|
32
36
|
internal fun setBottomTabOption(index: Int, options: NavigationOptions){
|
|
@@ -43,16 +47,25 @@ internal fun getBottomTabOption(index: Int): NavigationOptions? {
|
|
|
43
47
|
@Composable
|
|
44
48
|
fun BottomTab(
|
|
45
49
|
items: List<BottomTabItem>,
|
|
46
|
-
floatingButton: BottomTabFloatingButton? = null
|
|
50
|
+
floatingButton: BottomTabFloatingButton? = null,
|
|
51
|
+
// iPhone Duo: when the side bar can't hold both bars, which one starts compressed;
|
|
52
|
+
// tapping the compressed one swaps them
|
|
53
|
+
compressionBehavior: DuoBarCompressionBehavior = DuoBarCompressionBehavior.TOOLBAR,
|
|
54
|
+
initialRouteName: String? = null,
|
|
55
|
+
onTabPress: ((index: Int, switchTab: () -> Unit) -> Unit)? = null,
|
|
56
|
+
backToInitialTab: Boolean = true,
|
|
47
57
|
) {
|
|
48
58
|
val navigation = LocalNavigation.current
|
|
49
59
|
val navigator = LocalNavigator.current
|
|
50
60
|
val navController = rememberNavController()
|
|
61
|
+
val startRoute = remember {
|
|
62
|
+
"option${items.indexOfFirst { it.name == initialRouteName }.coerceAtLeast(0)}"
|
|
63
|
+
}
|
|
51
64
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
65
|
+
// Only on new items: this recomposes on every tab switch and would drop options a tab set at runtime.
|
|
66
|
+
remember(items) {
|
|
67
|
+
bottomTabOptionItems = items.map { it.options ?: NavigationOptions() }.toMutableList()
|
|
68
|
+
}
|
|
56
69
|
|
|
57
70
|
LaunchedEffect(Unit){
|
|
58
71
|
navigation.markAsBottomTabRoot()
|
|
@@ -62,15 +75,14 @@ fun BottomTab(
|
|
|
62
75
|
)
|
|
63
76
|
}
|
|
64
77
|
|
|
65
|
-
// Tab 0 keeps the seeked transition; other tabs go to the host's floating arrow. Matches RN.
|
|
66
78
|
val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
|
|
67
|
-
val
|
|
79
|
+
val onStartTab = currentRoute == null || currentRoute == startRoute
|
|
80
|
+
val isFirstTab = onStartTab || !backToInitialTab
|
|
81
|
+
val backToStartTab = { navController.popBackStack(startRoute, inclusive = false) }
|
|
68
82
|
|
|
69
83
|
DisposableEffect(isFirstTab) {
|
|
70
84
|
reportFloatingArrowBack(!isFirstTab)
|
|
71
|
-
navigator.tabBackHandler = if (isFirstTab) null else ({
|
|
72
|
-
navController.popBackStack(navController.graph.startDestinationRoute!!, false)
|
|
73
|
-
})
|
|
85
|
+
navigator.tabBackHandler = if (isFirstTab) null else ({ backToStartTab() })
|
|
74
86
|
// both live outside this composition
|
|
75
87
|
onDispose {
|
|
76
88
|
reportFloatingArrowBack(false)
|
|
@@ -78,81 +90,112 @@ fun BottomTab(
|
|
|
78
90
|
}
|
|
79
91
|
}
|
|
80
92
|
|
|
93
|
+
val layout = getScreenLayout()
|
|
94
|
+
val hasQr = floatingButton != null
|
|
95
|
+
val compression = rememberDuoBarCompression(compressionBehavior, items.size, hasQr, layout)
|
|
96
|
+
val railReserve = layout.sideBar?.takeIf { layout.isIPhoneDuo }?.let {
|
|
97
|
+
it.bottom + duoRailHeight(items.size, hasQr, compressed = compression.tabBarCompressed) + DUO_GROUP_GAP
|
|
98
|
+
} ?: 0.dp
|
|
99
|
+
val onTabSelected: (Int) -> Unit = { index ->
|
|
100
|
+
val switchTab = {
|
|
101
|
+
val targetRoute = "option$index"
|
|
102
|
+
if (navController.currentBackStackEntry?.destination?.route != targetRoute) {
|
|
103
|
+
// one entry per tab above the start tab, so switching never grows the back stack
|
|
104
|
+
navController.navigate(targetRoute) {
|
|
105
|
+
popUpTo(startRoute) { saveState = true }
|
|
106
|
+
launchSingleTop = true
|
|
107
|
+
restoreState = true
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
onTabPress?.invoke(index, switchTab) ?: switchTab()
|
|
112
|
+
}
|
|
113
|
+
|
|
81
114
|
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
|
|
82
115
|
Box(modifier = Modifier
|
|
83
116
|
.fillMaxSize()
|
|
84
117
|
) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
118
|
+
CompositionLocalProvider(
|
|
119
|
+
LocalDuoRailReserve provides railReserve,
|
|
120
|
+
LocalDuoBarCompression provides compression,
|
|
88
121
|
) {
|
|
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
|
-
|
|
122
|
+
NavHost(
|
|
123
|
+
navController = navController,
|
|
124
|
+
startDestination = startRoute
|
|
125
|
+
) {
|
|
126
|
+
items.forEachIndexed { index, item ->
|
|
127
|
+
composable(
|
|
128
|
+
route = "option$index",
|
|
129
|
+
enterTransition = {
|
|
130
|
+
fadeIn(animationSpec = tween(200)) +
|
|
131
|
+
scaleIn(
|
|
132
|
+
initialScale = 0.97f,
|
|
133
|
+
animationSpec = tween(200)
|
|
134
|
+
)
|
|
135
|
+
},
|
|
136
|
+
exitTransition = {
|
|
137
|
+
fadeOut(animationSpec = tween(200)) +
|
|
138
|
+
scaleOut(
|
|
139
|
+
targetScale = 0.97f,
|
|
140
|
+
animationSpec = tween(200)
|
|
141
|
+
)
|
|
142
|
+
},
|
|
143
|
+
popEnterTransition = {
|
|
144
|
+
fadeIn(animationSpec = tween(200)) +
|
|
145
|
+
scaleIn(
|
|
146
|
+
initialScale = 0.97f,
|
|
147
|
+
animationSpec = tween(200)
|
|
148
|
+
)
|
|
149
|
+
},
|
|
150
|
+
popExitTransition = {
|
|
151
|
+
fadeOut(animationSpec = tween(200)) +
|
|
152
|
+
scaleOut(
|
|
153
|
+
targetScale = 0.97f,
|
|
154
|
+
animationSpec = tween(200)
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
) {
|
|
158
|
+
val option = getBottomTabOption(index)
|
|
122
159
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
} else {
|
|
134
|
-
navigator.pop()
|
|
160
|
+
StackScreen(
|
|
161
|
+
content = item.screen,
|
|
162
|
+
navigationOptions = option,
|
|
163
|
+
bottomTabIndex = index,
|
|
164
|
+
onBackHandler = {
|
|
165
|
+
if (backToInitialTab && "option$index" != startRoute) {
|
|
166
|
+
backToStartTab()
|
|
167
|
+
} else {
|
|
168
|
+
navigator.pop()
|
|
169
|
+
}
|
|
135
170
|
}
|
|
136
|
-
|
|
137
|
-
|
|
171
|
+
)
|
|
172
|
+
}
|
|
138
173
|
}
|
|
139
174
|
}
|
|
140
175
|
}
|
|
141
176
|
}
|
|
142
|
-
|
|
143
|
-
|
|
177
|
+
BackHandler(enabled = !onStartTab) {
|
|
178
|
+
if (backToInitialTab) backToStartTab() else navigator.pop()
|
|
179
|
+
}
|
|
180
|
+
if (layout.isIPhoneDuo) {
|
|
181
|
+
DuoTabBar(
|
|
182
|
+
layout = layout,
|
|
144
183
|
items = items,
|
|
145
184
|
floatingButton = floatingButton,
|
|
146
|
-
|
|
147
|
-
onTabSelected =
|
|
148
|
-
|
|
149
|
-
val targetRoute = "option$it"
|
|
150
|
-
if (currentRoute != targetRoute) {
|
|
151
|
-
navController.navigate(targetRoute)
|
|
152
|
-
}
|
|
153
|
-
}
|
|
185
|
+
selectedIndex = items.indices.firstOrNull { "option$it" == currentRoute } ?: 0,
|
|
186
|
+
onTabSelected = onTabSelected,
|
|
187
|
+
compression = compression,
|
|
154
188
|
)
|
|
155
|
-
|
|
189
|
+
} else {
|
|
190
|
+
Column {
|
|
191
|
+
BottomTabBar(
|
|
192
|
+
items = items,
|
|
193
|
+
floatingButton = floatingButton,
|
|
194
|
+
navController = navController,
|
|
195
|
+
onTabSelected = onTabSelected,
|
|
196
|
+
)
|
|
197
|
+
Spacer(modifier = Modifier.fillMaxWidth().height(AppNavigationBar.current + Spacing.S).background(AppTheme.current.colors.background.surface))
|
|
198
|
+
}
|
|
156
199
|
}
|
|
157
200
|
}
|
|
158
201
|
}
|
|
@@ -41,7 +41,7 @@ internal fun BottomTabBar(
|
|
|
41
41
|
onTabSelected: (Int) -> Unit,
|
|
42
42
|
) {
|
|
43
43
|
val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
|
|
44
|
-
val selectedIndex = items.
|
|
44
|
+
val selectedIndex = items.indices.firstOrNull { "option$it" == currentRoute } ?: 0
|
|
45
45
|
|
|
46
46
|
val layout = getScreenLayout()
|
|
47
47
|
val mid = items.size / 2
|
|
@@ -179,14 +179,13 @@ internal fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> U
|
|
|
179
179
|
overflow = TextOverflow.Ellipsis
|
|
180
180
|
)
|
|
181
181
|
}
|
|
182
|
-
if (badgeLabel != null) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
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
|
+
)
|
|
190
189
|
}
|
|
191
190
|
}
|
|
192
191
|
}
|