@momo-kits/native-kits 0.300.3-debug → 0.300.4-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 +51 -7
- package/README.md +2 -2
- package/compose/build.gradle.kts +10 -2
- package/compose/build.gradle.kts.backup +8 -0
- package/compose/compose.podspec +1 -3
- package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +4 -3
- 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/Context.kt +0 -14
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/DesignSystemKits.kt +62 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +351 -111
- 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 +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 +55 -22
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +12 -1
- 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 +2 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +20 -15
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +1 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +306 -0
- 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/NavigationContainer.kt +6 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +6 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +197 -60
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +132 -83
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +54 -62
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +97 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +377 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +122 -24
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +34 -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 +113 -26
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +1 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HelpCenterRequest.kt +37 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +192 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +2 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/ToolkitRequests.kt +8 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +3 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +20 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +13 -0
- package/compose/src/commonTest/kotlin/vn/momo/kits/navigation/component/HelpCenterRequestTest.kt +44 -0
- package/compose/src/commonTest/kotlin/vn/momo/kits/navigation/component/ToolkitRequestsTest.kt +15 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +3 -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 +138 -0
- package/gradle/libs.versions.toml +3 -14
- package/gradle/wrapper/gradle-wrapper.properties +0 -2
- package/gradle.properties +3 -5
- package/ios/Application/ApplicationEnvironment.swift +24 -6
- package/ios/Application/DesignSystemKits.swift +100 -0
- package/ios/Application/DesignSystemKitsPayload.swift +68 -0
- package/ios/Application/MaxApi.swift +30 -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 +8 -2
- 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 +171 -148
- package/ios/Input/InputPhoneNumber.swift +10 -9
- package/ios/Input/InputSearch.swift +107 -78
- package/ios/Input/InputTextArea.swift +133 -148
- package/ios/InputDropDown/InputDropDown.swift +10 -7
- package/ios/InputMoney/InputMoney.swift +32 -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 +415 -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 +169 -29
- package/ios/Navigation/BottomTab/BottomTab.swift +332 -0
- package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTabBar.swift +18 -7
- package/ios/Navigation/BottomTab/DuoBarCompression.swift +104 -0
- package/ios/Navigation/BottomTab/DuoTabBar.swift +329 -0
- package/ios/{Application → Navigation/Component}/FloatingButton.swift +43 -54
- package/ios/Navigation/Component/Header.swift +545 -0
- package/ios/Navigation/Component/HeaderBackground.swift +154 -0
- package/ios/Navigation/Component/HeaderJourney.swift +73 -0
- package/ios/Navigation/Component/HeaderLocation.swift +48 -0
- package/ios/Navigation/Component/HeaderRight.swift +425 -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 +8 -8
- package/ios/Navigation/HostBackGestureBridge.swift +78 -0
- package/ios/{Application/Navigation/Overplay → Navigation}/ModalScreen.swift +9 -2
- package/ios/Navigation/Navigation.swift +204 -0
- package/ios/Navigation/NavigationContainer.swift +195 -0
- package/ios/{Application/Navigation → Navigation}/Navigator.swift +207 -107
- package/ios/Navigation/StackScreen.swift +930 -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 -135
- package/ios/Popup/PopupInput.swift +10 -13
- package/ios/Popup/PopupLayout.swift +138 -0
- package/ios/Popup/PopupNotify.swift +59 -69
- 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 +7 -3
- 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 +109 -55
- package/ios/Title/Title.swift +6 -17
- package/ios/Tooltip/Tooltip.swift +29 -14
- package/ios/Typography/FontScale.swift +17 -14
- package/ios/Typography/Text.swift +156 -20
- package/ios/Typography/Typography.swift +2 -2
- package/ios/Uploader/Uploader.swift +3 -1
- package/ios/native-kits.podspec +2 -3
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -3
- 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 +2 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IPhoneDuoDemo.swift +173 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +77 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +1 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +2 -2
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +2 -0
- package/package.json +1 -1
- package/publish.sh +19 -2
- 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/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +0 -18
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
- 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/Typography/FontScaleStore.swift +0 -58
- package/ios/Typography/KitDesignSystem.swift +0 -17
- /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,14 +68,24 @@ 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
|
|
76
|
+
import vn.momo.kits.navigation.bottomtab.LocalBottomTabFocused
|
|
70
77
|
import vn.momo.kits.platform.BackHandler
|
|
71
78
|
import vn.momo.kits.platform.supportsImePadding
|
|
72
79
|
import vn.momo.kits.navigation.tracking.ScreenTracker
|
|
73
80
|
import vn.momo.kits.navigation.tracking.ScreenTrackingState
|
|
74
81
|
import kotlinx.coroutines.delay
|
|
75
82
|
import vn.momo.kits.application.LocalContext
|
|
83
|
+
import vn.momo.kits.utils.horizontalSafeAreaPadding
|
|
84
|
+
import vn.momo.kits.layout.LocalScreenLayout
|
|
85
|
+
import vn.momo.kits.layout.ScreenLayout
|
|
86
|
+
import vn.momo.kits.layout.consumeHorizontalInsets
|
|
87
|
+
import vn.momo.kits.layout.DuoMetrics
|
|
88
|
+
import vn.momo.kits.layout.getScreenLayout
|
|
76
89
|
import kotlin.time.Clock
|
|
77
90
|
import kotlin.time.Duration.Companion.milliseconds
|
|
78
91
|
import kotlin.time.ExperimentalTime
|
|
@@ -80,11 +93,19 @@ import kotlin.time.ExperimentalTime
|
|
|
80
93
|
internal val LocalFooterHeightPx = staticCompositionLocalOf { mutableIntStateOf(0) }
|
|
81
94
|
internal val LocalHeaderRightWidthPx = staticCompositionLocalOf { mutableIntStateOf(0) }
|
|
82
95
|
|
|
96
|
+
// Space a tab's content leaves for the tab bar and home indicator. On iPhone Duo the floating bar
|
|
97
|
+
// overlays the content and the rail lives in the side strip, so only their own inset applies.
|
|
83
98
|
@Composable
|
|
84
|
-
private fun
|
|
85
|
-
if (LocalNavigation.current.bottomTabIndex == -1) return 0.dp
|
|
99
|
+
private fun bottomTabChildInset(): Dp {
|
|
86
100
|
val keyboard = keyboardSizeState().value
|
|
87
|
-
|
|
101
|
+
val navigationBar = AppNavigationBar.current
|
|
102
|
+
val layout = getScreenLayout()
|
|
103
|
+
return when {
|
|
104
|
+
layout.isIPhoneDuo && layout.sideBar == null -> (duoFloatingTabBarInset(layout) - keyboard).coerceAtLeast(0.dp)
|
|
105
|
+
layout.isIPhoneDuo -> (navigationBar - keyboard).coerceAtLeast(0.dp)
|
|
106
|
+
else -> (BOTTOM_TAB_BAR_HEIGHT.dp + Spacing.S - keyboard).coerceAtLeast(0.dp) +
|
|
107
|
+
(navigationBar - keyboard).coerceAtLeast(0.dp)
|
|
108
|
+
}
|
|
88
109
|
}
|
|
89
110
|
|
|
90
111
|
@OptIn(ExperimentalMaterialApi::class, ExperimentalTime::class)
|
|
@@ -100,7 +121,7 @@ internal fun StackScreen(
|
|
|
100
121
|
val navigator = LocalNavigator.current
|
|
101
122
|
val maxApi = LocalMaxApi.current
|
|
102
123
|
val context = LocalContext.current
|
|
103
|
-
val
|
|
124
|
+
val screenLayout = getScreenLayout()
|
|
104
125
|
val density = LocalDensity.current
|
|
105
126
|
val screenRegistry = LocalDynamicScreenRegistry.current
|
|
106
127
|
val navigation = remember { Navigation(id = id, bottomTabIndex = bottomTabIndex, initOptions = navigationOptions, registry = screenRegistry) }
|
|
@@ -109,20 +130,24 @@ internal fun StackScreen(
|
|
|
109
130
|
val isBottomTabChild = bottomTabIndex != -1
|
|
110
131
|
val isBottomTabRoot = navigation.isBottomTabRoot
|
|
111
132
|
|
|
133
|
+
// A mounted-but-hidden bottom tab tracks again each time it is focused (RN useFocusEffect).
|
|
134
|
+
val tabFocused = LocalBottomTabFocused.current
|
|
135
|
+
|
|
112
136
|
// Auto tracking state
|
|
113
|
-
val trackingState = remember {
|
|
137
|
+
val trackingState = remember(tabFocused) {
|
|
114
138
|
ScreenTrackingState().apply {
|
|
115
139
|
time = Clock.System.now().toEpochMilliseconds()
|
|
116
140
|
}
|
|
117
141
|
}
|
|
118
142
|
|
|
119
143
|
// Determine action: push or back
|
|
120
|
-
val action = remember {
|
|
144
|
+
val action = remember(tabFocused) {
|
|
121
145
|
if (ScreenTracker.getLastScreenName() != null) "push" else "back"
|
|
122
146
|
}
|
|
123
147
|
|
|
124
148
|
// Auto tracking effects
|
|
125
|
-
LaunchedEffect(name) {
|
|
149
|
+
LaunchedEffect(name, tabFocused) {
|
|
150
|
+
if (!tabFocused) return@LaunchedEffect
|
|
126
151
|
// Track screen navigated immediately
|
|
127
152
|
ScreenTracker.trackScreenNavigated(
|
|
128
153
|
maxApi = maxApi,
|
|
@@ -156,7 +181,7 @@ internal fun StackScreen(
|
|
|
156
181
|
}
|
|
157
182
|
|
|
158
183
|
val limit = with(density) {
|
|
159
|
-
|
|
184
|
+
screenLayout.headerHeight.toPx()
|
|
160
185
|
}.toInt()
|
|
161
186
|
|
|
162
187
|
val lazyListState = options.scrollData.scrollState as? LazyListState
|
|
@@ -165,11 +190,16 @@ internal fun StackScreen(
|
|
|
165
190
|
else
|
|
166
191
|
(options.scrollData.scrollState as? ScrollState ?: rememberScrollState()).proxyLimitedScrollState(limit, 1)
|
|
167
192
|
val snapState: ScrollableState = lazyListState ?: scrollState
|
|
193
|
+
val headerHide = rememberQuickReturnHide(
|
|
194
|
+
enabled = options.usesDuoQuickReturnHeader(screenLayout),
|
|
195
|
+
scrollState = scrollState,
|
|
196
|
+
heightPx = with(density) { screenLayout.headerHeight.toPx() },
|
|
197
|
+
)
|
|
168
198
|
|
|
169
199
|
val footerHeightPx = remember { mutableIntStateOf(0) }
|
|
170
200
|
val headerRightWidthPx = remember { mutableIntStateOf(0) }
|
|
171
201
|
|
|
172
|
-
if (navigation.options.scrollData.scrollToTopCallback != null) {
|
|
202
|
+
if (tabFocused && navigation.options.scrollData.scrollToTopCallback != null) {
|
|
173
203
|
RegisterScrollToTop(navigation.options.scrollData.scrollToTopCallback)
|
|
174
204
|
}
|
|
175
205
|
|
|
@@ -189,40 +219,74 @@ internal fun StackScreen(
|
|
|
189
219
|
LocalFooterHeightPx provides footerHeightPx,
|
|
190
220
|
LocalHeaderRightWidthPx provides headerRightWidthPx
|
|
191
221
|
) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
.
|
|
196
|
-
|
|
197
|
-
|
|
222
|
+
// AI-GENERATED START: custom screen background, full window and outside the keyboard padding
|
|
223
|
+
Box(Modifier.fillMaxSize()) {
|
|
224
|
+
options.backgroundComponent?.let { background ->
|
|
225
|
+
Box(Modifier.fillMaxSize()) { background() }
|
|
226
|
+
}
|
|
227
|
+
// AI-GENERATED END: custom screen background, full window and outside the keyboard padding
|
|
228
|
+
Box(
|
|
229
|
+
Modifier
|
|
230
|
+
.fillMaxSize()
|
|
231
|
+
// AI-GENERATED START: custom background replaces the colour
|
|
232
|
+
.background(
|
|
233
|
+
if (options.backgroundComponent != null) Color.Transparent
|
|
234
|
+
else options.backgroundColor ?: AppTheme.current.colors.background.default
|
|
235
|
+
)
|
|
236
|
+
// AI-GENERATED END: custom background replaces the colour
|
|
237
|
+
.conditional(options.keyboardOptions.keyboardShouldPersistTaps) {
|
|
238
|
+
hideKeyboardOnTap()
|
|
239
|
+
}
|
|
240
|
+
.conditional(options.keyboardOptions.useAvoidKeyboard && supportsImePadding()) {
|
|
241
|
+
imePadding()
|
|
242
|
+
}
|
|
243
|
+
) {
|
|
244
|
+
Box(Modifier.zIndex(1f)) {
|
|
245
|
+
HeaderBackground()
|
|
198
246
|
}
|
|
199
|
-
|
|
200
|
-
|
|
247
|
+
|
|
248
|
+
Box(Modifier.zIndex(4f).graphicsLayer { translationY = -headerHide.value }) {
|
|
249
|
+
Header(onBackHandler)
|
|
201
250
|
}
|
|
202
|
-
) {
|
|
203
|
-
Box(Modifier.zIndex(1f)) {
|
|
204
|
-
HeaderBackground()
|
|
205
|
-
}
|
|
206
251
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
252
|
+
HeaderSideBar(
|
|
253
|
+
bottomInset = maxOf(
|
|
254
|
+
bottomBarsHeight(isBottomTabChild) + Spacing.XL,
|
|
255
|
+
if (isBottomTabChild) LocalDuoRailReserve.current else 0.dp,
|
|
256
|
+
),
|
|
257
|
+
modifier = Modifier.zIndex(4f),
|
|
258
|
+
)
|
|
210
259
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
260
|
+
Column(Modifier.zIndex(5f).horizontalSafeAreaPadding()) {
|
|
261
|
+
SearchAnimated(snapState = snapState)
|
|
262
|
+
}
|
|
214
263
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
264
|
+
Column(Modifier.zIndex(2f).fillMaxSize()) {
|
|
265
|
+
MainContent(content = content, isBottomTabRoot = isBottomTabRoot, isBottomTabChild = isBottomTabChild)
|
|
266
|
+
FooterContent()
|
|
267
|
+
}
|
|
219
268
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
269
|
+
Box(Modifier.zIndex(6f).horizontalSafeAreaPadding()) {
|
|
270
|
+
FloatingContent(isBottomTabChild = isBottomTabChild)
|
|
271
|
+
}
|
|
223
272
|
|
|
224
|
-
|
|
273
|
+
OverplayView(isBottomTabChild = isBottomTabChild, id = id)
|
|
274
|
+
}
|
|
275
|
+
// AI-GENERATED START: custom screen background, full window and outside the keyboard padding
|
|
225
276
|
}
|
|
277
|
+
// AI-GENERATED END: custom screen background, full window and outside the keyboard padding
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Height the footer, tab bar or home indicator takes from the bottom of the screen.
|
|
282
|
+
@Composable
|
|
283
|
+
private fun bottomBarsHeight(isBottomTabChild: Boolean): Dp {
|
|
284
|
+
val options = LocalOptions.current
|
|
285
|
+
val navigationBar = AppNavigationBar.current
|
|
286
|
+
return when {
|
|
287
|
+
options.hasFooter() -> with(LocalDensity.current) { LocalFooterHeightPx.current.intValue.toDp() }
|
|
288
|
+
isBottomTabChild -> bottomTabChildInset()
|
|
289
|
+
else -> navigationBar
|
|
226
290
|
}
|
|
227
291
|
}
|
|
228
292
|
|
|
@@ -230,18 +294,10 @@ internal fun StackScreen(
|
|
|
230
294
|
@Composable
|
|
231
295
|
internal fun FloatingContent(isBottomTabChild: Boolean) {
|
|
232
296
|
val options = LocalOptions.current
|
|
233
|
-
val density = LocalDensity.current
|
|
234
|
-
val footerHeightPx = LocalFooterHeightPx.current
|
|
235
297
|
val scrollState = LocalScrollState.current
|
|
236
|
-
val navigationBar = AppNavigationBar.current
|
|
237
298
|
|
|
238
299
|
val fabProps = options.floatingButtonProps ?: return
|
|
239
|
-
val bottomPadding = fabProps.bottom
|
|
240
|
-
?: (Spacing.M + when {
|
|
241
|
-
options.hasFooter() -> with(density) { footerHeightPx.intValue.toDp() }
|
|
242
|
-
isBottomTabChild -> bottomTabBarInset() + (navigationBar - keyboardSizeState().value).coerceAtLeast(0.dp)
|
|
243
|
-
else -> navigationBar
|
|
244
|
-
})
|
|
300
|
+
val bottomPadding = fabProps.bottom ?: (Spacing.M + bottomBarsHeight(isBottomTabChild))
|
|
245
301
|
|
|
246
302
|
FloatingButton(
|
|
247
303
|
scrollPosition = fabProps.scrollState?.value ?: scrollState.value,
|
|
@@ -263,11 +319,13 @@ internal fun ColumnScope.MainContent(content: @Composable () -> Unit, isBottomTa
|
|
|
263
319
|
val density = LocalDensity.current
|
|
264
320
|
val scrollState = LocalScrollState.current
|
|
265
321
|
val navigationBar = AppNavigationBar.current
|
|
322
|
+
val screenLayout = getScreenLayout()
|
|
323
|
+
val useDuoGlass = options.usesDuoGlassHeader(screenLayout)
|
|
266
324
|
|
|
267
325
|
Spacer(
|
|
268
326
|
Modifier.height(
|
|
269
|
-
if (options.headerType is HeaderType.DefaultOrExtended || (options.headerType is HeaderType.Transparent && !options.headerType.isFullScreenContent))
|
|
270
|
-
|
|
327
|
+
if (!useDuoGlass && (options.headerType is HeaderType.DefaultOrExtended || (options.headerType is HeaderType.Transparent && !options.headerType.isFullScreenContent)))
|
|
328
|
+
screenLayout.headerHeight else 0.dp
|
|
271
329
|
)
|
|
272
330
|
)
|
|
273
331
|
if (inputSearchType == InputSearchType.Animated) {
|
|
@@ -275,31 +333,46 @@ internal fun ColumnScope.MainContent(content: @Composable () -> Unit, isBottomTa
|
|
|
275
333
|
|
|
276
334
|
Spacer(Modifier.height((HEADER_HEIGHT.dp - scrollDp).coerceIn(0.dp, HEADER_HEIGHT.dp)))
|
|
277
335
|
}
|
|
336
|
+
// a bottom tab root stays full width: the tab bar and each tab's own StackScreen handle the insets
|
|
337
|
+
val insetContent = !isBottomTabRoot
|
|
278
338
|
Column(
|
|
279
339
|
Modifier
|
|
280
340
|
.fillMaxWidth()
|
|
281
341
|
.weight(1f)
|
|
342
|
+
.conditional(insetContent && options.headerType !is HeaderType.Animated) {
|
|
343
|
+
horizontalSafeAreaPadding()
|
|
344
|
+
}
|
|
282
345
|
.conditional(options.scrollData.scrollable && options.scrollData.scrollState is ScrollState) {
|
|
283
346
|
verticalScroll(scrollState)
|
|
284
347
|
}
|
|
348
|
+
.conditional(useDuoGlass) {
|
|
349
|
+
padding(top = screenLayout.headerHeight)
|
|
350
|
+
}
|
|
285
351
|
.padding(
|
|
286
352
|
bottom = when {
|
|
287
353
|
options.hasFooter() || isBottomTabRoot -> 0.dp
|
|
288
|
-
isBottomTabChild ->
|
|
354
|
+
isBottomTabChild -> bottomTabChildInset()
|
|
289
355
|
!options.scrollData.scrollable && options.scrollData.scrollState == null -> 0.dp
|
|
290
356
|
else -> navigationBar
|
|
291
357
|
}
|
|
292
358
|
)
|
|
293
359
|
) {
|
|
294
|
-
ScreenContent(content = content)
|
|
360
|
+
ScreenContent(content = content, insetContent = insetContent)
|
|
295
361
|
}
|
|
296
362
|
|
|
297
363
|
}
|
|
298
364
|
|
|
299
365
|
@Composable
|
|
300
|
-
internal fun ScreenContent(content: @Composable () -> Unit) {
|
|
366
|
+
internal fun ScreenContent(content: @Composable () -> Unit, insetContent: Boolean) {
|
|
301
367
|
val scrollState = LocalScrollState.current
|
|
302
368
|
val options = LocalOptions.current
|
|
369
|
+
val screenLayout = getScreenLayout()
|
|
370
|
+
val insetBody: @Composable () -> Unit = {
|
|
371
|
+
CompositionLocalProvider(
|
|
372
|
+
LocalScreenLayout provides if (insetContent) screenLayout.consumeHorizontalInsets() else screenLayout,
|
|
373
|
+
content = content,
|
|
374
|
+
)
|
|
375
|
+
}
|
|
303
376
|
|
|
304
377
|
if (options.headerType is HeaderType.Animated) {
|
|
305
378
|
val animatedHeader = options.headerType
|
|
@@ -310,12 +383,16 @@ internal fun ScreenContent(content: @Composable () -> Unit) {
|
|
|
310
383
|
Box(bannerModifier) {
|
|
311
384
|
animatedHeader.composable.invoke(scrollState.value)
|
|
312
385
|
}
|
|
313
|
-
Box(
|
|
314
|
-
|
|
386
|
+
Box(
|
|
387
|
+
Modifier
|
|
388
|
+
.padding(top = screenLayout.headerHeight + animatedHeader.layoutOffSet)
|
|
389
|
+
.conditional(insetContent) { horizontalSafeAreaPadding() }
|
|
390
|
+
) {
|
|
391
|
+
insetBody()
|
|
315
392
|
}
|
|
316
393
|
}
|
|
317
394
|
} else {
|
|
318
|
-
|
|
395
|
+
insetBody()
|
|
319
396
|
}
|
|
320
397
|
}
|
|
321
398
|
|
|
@@ -324,7 +401,9 @@ internal fun FooterContent() {
|
|
|
324
401
|
val options = LocalOptions.current
|
|
325
402
|
if (options.hasFooter()) {
|
|
326
403
|
val keyboardSize = keyboardSizeState()
|
|
327
|
-
|
|
404
|
+
// the Duo floating tab bar overlays tab content, so a tab's footer sits above it
|
|
405
|
+
val duoTabInset = if (LocalNavigation.current.bottomTabIndex != -1) duoFloatingTabBarInset(getScreenLayout()) else 0.dp
|
|
406
|
+
val bottomPadding = (maxOf(AppNavigationBar.current, duoTabInset) - keyboardSize.value).coerceAtLeast(0.dp)
|
|
328
407
|
Footer(
|
|
329
408
|
footerComponent = options.footerComponent,
|
|
330
409
|
bottomPadding = bottomPadding,
|
|
@@ -372,9 +451,10 @@ fun Footer(
|
|
|
372
451
|
|
|
373
452
|
val backgroundColor = if (transparent) Color.Transparent else AppTheme.current.colors.background.surface
|
|
374
453
|
|
|
454
|
+
// inset as a whole, not padded inside, so the surface and its shadow stay off the iPhone Duo side bar
|
|
375
455
|
Box(Modifier.onGloballyPositioned {
|
|
376
456
|
if (footerHeightPx.intValue != it.size.height) footerHeightPx.intValue = it.size.height
|
|
377
|
-
}) {
|
|
457
|
+
}.horizontalSafeAreaPadding()) {
|
|
378
458
|
Box(
|
|
379
459
|
Modifier
|
|
380
460
|
.fillMaxWidth()
|
|
@@ -429,17 +509,28 @@ internal fun SearchAnimated(
|
|
|
429
509
|
if (inputSearchType == InputSearchType.None) return
|
|
430
510
|
val inputSearchProps = (options.headerType as? HeaderType.DefaultOrExtended)?.inputSearchProps ?: return
|
|
431
511
|
|
|
432
|
-
val
|
|
433
|
-
val
|
|
512
|
+
val screenLayout = getScreenLayout()
|
|
513
|
+
val useSideBar = screenLayout.sideBar != null
|
|
514
|
+
// collapsed, the search is centered in the bar row
|
|
515
|
+
val minTopPadding = screenLayout.headerStatusBarHeight + (screenLayout.headerBarHeight - HEADER_HEIGHT.dp) / 2
|
|
516
|
+
val maxTopPadding = screenLayout.headerHeight
|
|
517
|
+
// collapsed on the iPhone Duo status bar row, the search stays between its clusters (48pt back there)
|
|
518
|
+
val clearance = screenLayout.statusBarClearance
|
|
519
|
+
val backWidth = if (clearance != null && !useSideBar) DuoMetrics.controlSize else 28.dp
|
|
434
520
|
val minStartPadding = Spacing.M
|
|
435
|
-
val maxStartPadding =
|
|
521
|
+
val maxStartPadding = (clearance?.left ?: 0.dp) +
|
|
522
|
+
if (options.hiddenBack || useSideBar) Spacing.M else backWidth + Spacing.M + Spacing.S
|
|
436
523
|
val minEndPadding = Spacing.M
|
|
437
|
-
val maxEndPadding =
|
|
524
|
+
val maxEndPadding = (clearance?.right ?: 0.dp) + when {
|
|
525
|
+
options.headerRight is HeaderRight.None -> Spacing.M
|
|
526
|
+
useSideBar && options.headerRight is HeaderRight.Toolkit -> Spacing.M
|
|
527
|
+
else -> headerRightWidthDp + Spacing.M * 2
|
|
528
|
+
}
|
|
438
529
|
|
|
439
530
|
val targetColor = lerp(
|
|
440
531
|
AppTheme.current.colors.background.surface,
|
|
441
532
|
AppTheme.current.colors.background.default,
|
|
442
|
-
(scrollDp / minTopPadding).coerceIn(0f, 1f)
|
|
533
|
+
(scrollDp / minTopPadding.coerceAtLeast(1.dp)).coerceIn(0f, 1f)
|
|
443
534
|
)
|
|
444
535
|
val animatedColor by animateColorAsState(targetValue = targetColor, label = "BackgroundColor")
|
|
445
536
|
|
|
@@ -581,6 +672,52 @@ internal val LocalOptions = staticCompositionLocalOf<NavigationOptions> { error(
|
|
|
581
672
|
|
|
582
673
|
val StackScreenScrollableState = staticCompositionLocalOf<ScrollableState?> { null }
|
|
583
674
|
|
|
675
|
+
// iPhone Duo side bar: the header leaves with the content and slides back in on a reverse scroll
|
|
676
|
+
// (RN DuoQuickReturnHeader). The side bar is drawn apart from the header, so it stays put.
|
|
677
|
+
@Composable
|
|
678
|
+
private fun rememberQuickReturnHide(
|
|
679
|
+
enabled: Boolean,
|
|
680
|
+
scrollState: ScrollState,
|
|
681
|
+
heightPx: Float,
|
|
682
|
+
): Animatable<Float, AnimationVector1D> {
|
|
683
|
+
val hide = remember { Animatable(0f) }
|
|
684
|
+
LaunchedEffect(enabled, scrollState, heightPx) {
|
|
685
|
+
if (!enabled) {
|
|
686
|
+
hide.snapTo(0f)
|
|
687
|
+
return@LaunchedEffect
|
|
688
|
+
}
|
|
689
|
+
var last = scrollState.value
|
|
690
|
+
snapshotFlow { scrollState.value }.collect { y ->
|
|
691
|
+
val delta = y - last
|
|
692
|
+
last = y
|
|
693
|
+
hide.snapTo(if (y <= 0) 0f else (hide.value + delta).coerceIn(0f, heightPx))
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
// a half-shown header settles fully in or out once the scroll stops
|
|
697
|
+
LaunchedEffect(enabled, scrollState, heightPx) {
|
|
698
|
+
if (!enabled) return@LaunchedEffect
|
|
699
|
+
snapshotFlow { scrollState.isScrollInProgress }
|
|
700
|
+
.filter { !it }
|
|
701
|
+
.collect {
|
|
702
|
+
val value = hide.value
|
|
703
|
+
// within the first header height the header still sits on the content
|
|
704
|
+
if (scrollState.value < heightPx || value <= 0f || value >= heightPx) return@collect
|
|
705
|
+
launch { hide.animateTo(if (value > heightPx / 2) heightPx else 0f, tween(200)) }
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return hide
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
internal fun NavigationOptions.usesDuoQuickReturnHeader(layout: ScreenLayout): Boolean =
|
|
712
|
+
usesDuoGlassHeader(layout) || (layout.sideBar != null && headerType is HeaderType.Animated &&
|
|
713
|
+
scrollData.scrollable && scrollData.scrollState is ScrollState)
|
|
714
|
+
|
|
715
|
+
// iPhone Duo side bar: content scrolls under a glass header instead of starting below it (RN Screen).
|
|
716
|
+
internal fun NavigationOptions.usesDuoGlassHeader(layout: ScreenLayout): Boolean =
|
|
717
|
+
layout.sideBar != null && headerType is HeaderType.DefaultOrExtended &&
|
|
718
|
+
getInputSearchType(this) == InputSearchType.None &&
|
|
719
|
+
scrollData.scrollable && scrollData.scrollState is ScrollState
|
|
720
|
+
|
|
584
721
|
internal fun getInputSearchType(options: NavigationOptions): InputSearchType {
|
|
585
722
|
return when (val headerType = options.headerType) {
|
|
586
723
|
is HeaderType.DefaultOrExtended -> when {
|