@momo-kits/native-kits 0.162.44-debug → 0.162.45-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 +45 -3
- package/README.md +2 -2
- package/compose/build.gradle.kts +9 -1
- package/compose/build.gradle.kts.backup +8 -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 +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/Typography.kt +2 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +1 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +314 -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 +4 -0
- 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 +127 -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 +116 -26
- 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 +192 -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 +13 -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/libs.versions.toml +1 -1
- package/gradle.properties +1 -1
- package/ios/Application/ApplicationEnvironment.swift +17 -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 +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 +107 -77
- 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 +385 -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 +140 -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 +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/FontScale.swift +3 -3
- package/ios/Typography/Text.swift +81 -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 +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/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
|
@@ -3,23 +3,27 @@ package vn.momo.kits.navigation.component
|
|
|
3
3
|
import androidx.compose.animation.core.animateFloatAsState
|
|
4
4
|
import androidx.compose.foundation.background
|
|
5
5
|
import androidx.compose.foundation.layout.Box
|
|
6
|
+
import androidx.compose.foundation.layout.fillMaxHeight
|
|
6
7
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
7
8
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
8
9
|
import androidx.compose.foundation.layout.height
|
|
9
10
|
import androidx.compose.runtime.Composable
|
|
10
11
|
import androidx.compose.runtime.getValue
|
|
11
12
|
import androidx.compose.ui.Modifier
|
|
12
|
-
import androidx.compose.ui.
|
|
13
|
+
import androidx.compose.ui.draw.clipToBounds
|
|
14
|
+
import androidx.compose.ui.graphics.graphicsLayer
|
|
13
15
|
import androidx.compose.ui.graphics.Brush
|
|
14
16
|
import androidx.compose.ui.graphics.Color
|
|
15
17
|
import androidx.compose.ui.platform.LocalDensity
|
|
16
18
|
import androidx.compose.ui.unit.dp
|
|
17
19
|
import vn.momo.kits.components.Image
|
|
18
|
-
import vn.momo.kits.const.AppStatusBar
|
|
19
20
|
import vn.momo.kits.const.AppTheme
|
|
21
|
+
import vn.momo.kits.layout.getScreenLayout
|
|
20
22
|
import vn.momo.kits.modifier.conditional
|
|
21
23
|
import vn.momo.kits.navigation.LocalOptions
|
|
22
24
|
import vn.momo.kits.navigation.LocalScrollState
|
|
25
|
+
import vn.momo.kits.navigation.getInputSearchType
|
|
26
|
+
import vn.momo.kits.navigation.usesDuoGlassHeader
|
|
23
27
|
|
|
24
28
|
enum class AnimatedHeaderRatio(val value: Float){
|
|
25
29
|
RATIO_16_9(16f / 9f),
|
|
@@ -29,6 +33,9 @@ enum class AnimatedHeaderRatio(val value: Float){
|
|
|
29
33
|
|
|
30
34
|
internal val HEADER_BACKGROUND_HEIGHT = 154.dp
|
|
31
35
|
|
|
36
|
+
// RN DuoScrollGradient: extended gradient runs this far below the header
|
|
37
|
+
private val DUO_GRADIENT_EXTRA = 52.dp
|
|
38
|
+
|
|
32
39
|
@Suppress("FrequentlyChangingValue")
|
|
33
40
|
@Composable
|
|
34
41
|
internal fun HeaderBackground() {
|
|
@@ -38,7 +45,8 @@ internal fun HeaderBackground() {
|
|
|
38
45
|
val scrollState = LocalScrollState.current
|
|
39
46
|
val backgroundColor = options.backgroundColor ?: AppTheme.current.colors.background.default
|
|
40
47
|
|
|
41
|
-
val
|
|
48
|
+
val layout = getScreenLayout()
|
|
49
|
+
val minHeight = layout.headerHeight
|
|
42
50
|
val maxHeight = HEADER_BACKGROUND_HEIGHT
|
|
43
51
|
val opacity by animateFloatAsState(
|
|
44
52
|
targetValue = (1 - (scrollState.value * 1f / HEADER_HEIGHT * 1f)).coerceIn(0f, 1f),
|
|
@@ -57,20 +65,32 @@ internal fun HeaderBackground() {
|
|
|
57
65
|
else -> 0.dp
|
|
58
66
|
}
|
|
59
67
|
|
|
60
|
-
|
|
68
|
+
// iPhone Duo glass header: the gradient leaves with the content, as the quick return header does
|
|
69
|
+
val useDuoGlass = options.usesDuoGlassHeader(layout)
|
|
70
|
+
// RN HeaderExtendHeader with search on the side bar layout: the gradient fills the header box,
|
|
71
|
+
// which for extended shrinks from 52pt below the header over the first 100pt of scroll
|
|
72
|
+
val duoSearch = layout.sideBar != null && getInputSearchType(options) != InputSearchType.None
|
|
73
|
+
val fillsBox = useDuoGlass || duoSearch
|
|
74
|
+
val duoModifier = when {
|
|
75
|
+
useDuoGlass -> {
|
|
76
|
+
val extra = if (options.headerType is HeaderType.Extended) DUO_GRADIENT_EXTRA else 0.dp
|
|
77
|
+
Modifier.height(minHeight + extra).graphicsLayer { translationY = -scrollState.value.toFloat() }
|
|
78
|
+
}
|
|
79
|
+
duoSearch && options.headerType is HeaderType.Extended -> {
|
|
80
|
+
val progress = (with(density) { scrollState.value.toDp() } / 100.dp).coerceIn(0f, 1f)
|
|
81
|
+
Modifier.height(minHeight + DUO_GRADIENT_EXTRA * (1 - progress))
|
|
82
|
+
}
|
|
83
|
+
duoSearch -> Modifier.height(minHeight)
|
|
84
|
+
else -> Modifier.height(height)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
Box(Modifier.fillMaxWidth().then(duoModifier).clipToBounds().background(backgroundColor)) {
|
|
88
|
+
// on Duo the fade spans the gradient box; a banner image keeps its 375:154 shape, clipped
|
|
61
89
|
Box(Modifier
|
|
62
|
-
.height(maxHeight)
|
|
63
90
|
.fillMaxWidth()
|
|
91
|
+
.then(if (fillsBox && !theme.isHeaderImage()) Modifier.fillMaxHeight() else Modifier.height(maxHeight))
|
|
64
92
|
.conditional(!theme.isHeaderImage()) {
|
|
65
|
-
background(Brush.
|
|
66
|
-
colors = listOf(
|
|
67
|
-
color,
|
|
68
|
-
backgroundColor
|
|
69
|
-
),
|
|
70
|
-
start = Offset(0f, 0f),
|
|
71
|
-
end = Offset(0f, with(density) { maxHeight.toPx() })
|
|
72
|
-
)
|
|
73
|
-
)
|
|
93
|
+
background(Brush.verticalGradient(colors = listOf(color, backgroundColor)))
|
|
74
94
|
}
|
|
75
95
|
){
|
|
76
96
|
if (!theme.isHeaderImage()) return@Box
|
|
@@ -23,7 +23,6 @@ import vn.momo.kits.components.Text
|
|
|
23
23
|
import vn.momo.kits.const.Spacing
|
|
24
24
|
import vn.momo.kits.const.Typography
|
|
25
25
|
import vn.momo.kits.modifier.activeOpacityClickable
|
|
26
|
-
import vn.momo.kits.platform.getScreenDimensions
|
|
27
26
|
|
|
28
27
|
@Composable
|
|
29
28
|
internal fun HeaderJourney(
|
|
@@ -33,7 +32,7 @@ internal fun HeaderJourney(
|
|
|
33
32
|
if (data !is HeaderTitle.Journey) return
|
|
34
33
|
if (data.isLoading) return TitleJourneyShimmer()
|
|
35
34
|
|
|
36
|
-
val maxWidth =
|
|
35
|
+
val maxWidth = headerTitleMaxWidth()
|
|
37
36
|
val tint = parseHeaderTint(data.tintColor) ?: tintIconColor
|
|
38
37
|
val iconTint = parseHeaderTint(data.iconColor) ?: tint
|
|
39
38
|
|
|
@@ -98,7 +97,8 @@ internal fun parseHeaderTint(value: String?): Color? {
|
|
|
98
97
|
val hex = when (raw.length) {
|
|
99
98
|
3 -> "FF" + raw.map { "$it$it" }.joinToString("")
|
|
100
99
|
6 -> "FF$raw"
|
|
101
|
-
|
|
100
|
+
// CSS #RRGGBBAA, as RN passes it; Compose wants AARRGGBB
|
|
101
|
+
8 -> raw.substring(6) + raw.substring(0, 6)
|
|
102
102
|
else -> return null
|
|
103
103
|
}
|
|
104
104
|
val v = hex.toLongOrNull(16) ?: return null
|
|
@@ -22,7 +22,6 @@ import vn.momo.kits.components.Text
|
|
|
22
22
|
import vn.momo.kits.const.Spacing
|
|
23
23
|
import vn.momo.kits.const.Typography
|
|
24
24
|
import vn.momo.kits.modifier.activeOpacityClickable
|
|
25
|
-
import vn.momo.kits.platform.getScreenDimensions
|
|
26
25
|
|
|
27
26
|
@Composable
|
|
28
27
|
internal fun HeaderLocation(
|
|
@@ -32,7 +31,7 @@ internal fun HeaderLocation(
|
|
|
32
31
|
if (data !is HeaderTitle.Location) return
|
|
33
32
|
if (data.isLoading) return TitleLocationShimmer()
|
|
34
33
|
|
|
35
|
-
val maxWidth =
|
|
34
|
+
val maxWidth = headerTitleMaxWidth()
|
|
36
35
|
val tint = parseHeaderTint(data.tintColor) ?: tintIconColor
|
|
37
36
|
|
|
38
37
|
Column(
|
|
@@ -6,6 +6,7 @@ import androidx.compose.foundation.background
|
|
|
6
6
|
import androidx.compose.foundation.border
|
|
7
7
|
import androidx.compose.foundation.layout.Arrangement
|
|
8
8
|
import androidx.compose.foundation.layout.Box
|
|
9
|
+
import androidx.compose.foundation.layout.Column
|
|
9
10
|
import androidx.compose.foundation.layout.Row
|
|
10
11
|
import androidx.compose.foundation.layout.height
|
|
11
12
|
import androidx.compose.foundation.layout.offset
|
|
@@ -25,7 +26,9 @@ import androidx.compose.ui.Alignment
|
|
|
25
26
|
import androidx.compose.ui.Modifier
|
|
26
27
|
import androidx.compose.ui.draw.clip
|
|
27
28
|
import androidx.compose.ui.graphics.Color
|
|
29
|
+
import androidx.compose.ui.unit.Dp
|
|
28
30
|
import androidx.compose.ui.unit.dp
|
|
31
|
+
import vn.momo.kits.layout.DuoMetrics
|
|
29
32
|
import vn.momo.kits.application.LocalContext
|
|
30
33
|
import vn.momo.kits.application.MiniAppContext
|
|
31
34
|
import vn.momo.kits.components.BadgeDot
|
|
@@ -59,11 +62,32 @@ fun HeaderRight(
|
|
|
59
62
|
}
|
|
60
63
|
}
|
|
61
64
|
|
|
65
|
+
private const val HELP_CENTER_APP_ID = "vn.momo.helpcenter"
|
|
66
|
+
|
|
67
|
+
private fun usesHelpCenter(context: MiniAppContext?): Boolean =
|
|
68
|
+
context != null && context.appId != HELP_CENTER_APP_ID
|
|
69
|
+
|
|
70
|
+
private fun showsShortcut(headerRight: HeaderRight.Toolkit, context: MiniAppContext?): Boolean =
|
|
71
|
+
headerRight.useShortcut && !(headerRight.useMore && context == null)
|
|
72
|
+
|
|
73
|
+
// Height of the vertical toolkit: the optional shortcut, then the help/home capsule.
|
|
74
|
+
@Composable
|
|
75
|
+
internal fun toolkitColumnHeight(headerRight: HeaderRight.Toolkit): Dp {
|
|
76
|
+
val context = LocalContext.current
|
|
77
|
+
val shortcut = if (showsShortcut(headerRight, context)) DuoMetrics.controlSize + Spacing.M else 0.dp
|
|
78
|
+
val groupCount = if (usesHelpCenter(context)) 2 else 1
|
|
79
|
+
return shortcut + if (groupCount > 1) DuoMetrics.itemPitch * groupCount else DuoMetrics.controlSize
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// SideBarColumn: the iPhone Duo side bar; SideBarRow: the Duo header row on the status bar, same 48pt controls.
|
|
83
|
+
internal enum class ToolkitLayout { Bar, SideBarColumn, SideBarRow }
|
|
84
|
+
|
|
62
85
|
@Composable
|
|
63
86
|
internal fun Toolkit(
|
|
64
87
|
headerRight: HeaderRight.Toolkit,
|
|
65
88
|
tintColor: Color? = null,
|
|
66
|
-
headerColor: HeaderColor
|
|
89
|
+
headerColor: HeaderColor,
|
|
90
|
+
layout: ToolkitLayout = ToolkitLayout.Bar,
|
|
67
91
|
) {
|
|
68
92
|
val api = LocalMaxApi.current
|
|
69
93
|
val context = LocalContext.current
|
|
@@ -103,6 +127,26 @@ internal fun Toolkit(
|
|
|
103
127
|
}
|
|
104
128
|
}
|
|
105
129
|
|
|
130
|
+
fun onPressHelpCenter() {
|
|
131
|
+
if (context == null) return
|
|
132
|
+
api?.showHelpCenter(
|
|
133
|
+
mapOf(
|
|
134
|
+
"appId" to context.appId,
|
|
135
|
+
"code" to context.appCode,
|
|
136
|
+
"name" to context.appName,
|
|
137
|
+
"icon" to context.appIcon,
|
|
138
|
+
"description" to context.description
|
|
139
|
+
)
|
|
140
|
+
) {}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
fun onPressClose() {
|
|
144
|
+
if (headerRight.useCloseIcon) api?.dismiss { } else api?.dismissAll { }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
val useHelpCenter = usesHelpCenter(context)
|
|
148
|
+
val closeIcon = if (headerRight.useCloseIcon) "16_navigation_close_circle" else "16_basic_home"
|
|
149
|
+
|
|
106
150
|
val navButtonConfig = getNavigationButtonConfig(
|
|
107
151
|
headerRight,
|
|
108
152
|
::onPressShortcut,
|
|
@@ -113,12 +157,7 @@ internal fun Toolkit(
|
|
|
113
157
|
group.items.any { it.showBadge }
|
|
114
158
|
}
|
|
115
159
|
|
|
116
|
-
val isShowShortcut
|
|
117
|
-
derivedStateOf {
|
|
118
|
-
(headerRight.useShortcut == true) &&
|
|
119
|
-
!(headerRight.useMore == true && context == null)
|
|
120
|
-
}
|
|
121
|
-
}
|
|
160
|
+
val isShowShortcut = showsShortcut(headerRight, context)
|
|
122
161
|
|
|
123
162
|
val icon by remember(navButtonConfig.icon, isFavorite) {
|
|
124
163
|
derivedStateOf {
|
|
@@ -127,6 +166,47 @@ internal fun Toolkit(
|
|
|
127
166
|
}
|
|
128
167
|
}
|
|
129
168
|
|
|
169
|
+
if (layout != ToolkitLayout.Bar) {
|
|
170
|
+
val horizontal = layout == ToolkitLayout.SideBarRow
|
|
171
|
+
val controls = @Composable {
|
|
172
|
+
if (isShowShortcut) {
|
|
173
|
+
SideBarButton(
|
|
174
|
+
icon = icon,
|
|
175
|
+
headerColor = headerColor,
|
|
176
|
+
enabled = !isLoading,
|
|
177
|
+
showBadge = showBadge,
|
|
178
|
+
onClick = navButtonConfig.onPress,
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
SideBarGroup(headerColor = headerColor, itemCount = if (useHelpCenter) 2 else 1, horizontal = horizontal) { item ->
|
|
182
|
+
if (useHelpCenter) {
|
|
183
|
+
SideBarGroupItem(
|
|
184
|
+
icon = "help_center",
|
|
185
|
+
headerColor = headerColor,
|
|
186
|
+
automationId = "btn_navigation_help_center",
|
|
187
|
+
onClick = ::onPressHelpCenter,
|
|
188
|
+
modifier = item,
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
SideBarGroupItem(
|
|
192
|
+
icon = closeIcon,
|
|
193
|
+
headerColor = headerColor,
|
|
194
|
+
automationId = "btn_navigation_close",
|
|
195
|
+
onClick = ::onPressClose,
|
|
196
|
+
modifier = item,
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (horizontal) {
|
|
201
|
+
Row(horizontalArrangement = Arrangement.spacedBy(Spacing.M), verticalAlignment = Alignment.CenterVertically) {
|
|
202
|
+
controls()
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
Column(verticalArrangement = Arrangement.spacedBy(Spacing.M)) { controls() }
|
|
206
|
+
}
|
|
207
|
+
return
|
|
208
|
+
}
|
|
209
|
+
|
|
130
210
|
Row(
|
|
131
211
|
verticalAlignment = Alignment.CenterVertically
|
|
132
212
|
) {
|
|
@@ -150,22 +230,12 @@ internal fun Toolkit(
|
|
|
150
230
|
.clip(shape = RoundedCornerShape(14.dp))
|
|
151
231
|
.background(headerColor.backgroundButton)
|
|
152
232
|
) {
|
|
153
|
-
if (
|
|
233
|
+
if (useHelpCenter) {
|
|
154
234
|
Icon(
|
|
155
235
|
source = "help_center",
|
|
156
236
|
size = 20.dp,
|
|
157
237
|
color = headerColor.tintIconColor,
|
|
158
|
-
modifier = Modifier.padding(4.dp).activeOpacityClickable
|
|
159
|
-
api?.showHelpCenter(
|
|
160
|
-
mapOf(
|
|
161
|
-
"appId" to context.appId,
|
|
162
|
-
"code" to context.appCode,
|
|
163
|
-
"name" to context.appName,
|
|
164
|
-
"icon" to context.appIcon,
|
|
165
|
-
"description" to context.description
|
|
166
|
-
)
|
|
167
|
-
) {}
|
|
168
|
-
}
|
|
238
|
+
modifier = Modifier.padding(4.dp).activeOpacityClickable(onClick = ::onPressHelpCenter)
|
|
169
239
|
)
|
|
170
240
|
Box(
|
|
171
241
|
modifier = Modifier
|
|
@@ -175,12 +245,10 @@ internal fun Toolkit(
|
|
|
175
245
|
)
|
|
176
246
|
}
|
|
177
247
|
Icon(
|
|
178
|
-
source =
|
|
248
|
+
source = closeIcon,
|
|
179
249
|
size = 20.dp,
|
|
180
250
|
color = headerColor.tintIconColor,
|
|
181
|
-
modifier = Modifier.padding(4.dp).activeOpacityClickable
|
|
182
|
-
api?.dismissAll { }
|
|
183
|
-
}
|
|
251
|
+
modifier = Modifier.padding(4.dp).activeOpacityClickable(onClick = ::onPressClose)
|
|
184
252
|
)
|
|
185
253
|
}
|
|
186
254
|
}
|
|
@@ -188,16 +256,36 @@ internal fun Toolkit(
|
|
|
188
256
|
|
|
189
257
|
|
|
190
258
|
|
|
259
|
+
// Colors follow the enclosing header, so it fits a `HeaderRight.Custom` next to the toolkit.
|
|
260
|
+
@Composable
|
|
261
|
+
fun NavigationButton(
|
|
262
|
+
icon: String,
|
|
263
|
+
onClick: () -> Unit,
|
|
264
|
+
showBadge: Boolean = false,
|
|
265
|
+
disabled: Boolean = false,
|
|
266
|
+
modifier: Modifier = Modifier,
|
|
267
|
+
) {
|
|
268
|
+
NavigationButton(
|
|
269
|
+
disabled = disabled,
|
|
270
|
+
icon = icon,
|
|
271
|
+
showBadge = showBadge,
|
|
272
|
+
onClick = onClick,
|
|
273
|
+
headerColor = LocalHeaderColor.current ?: getHeaderColor(HeaderType.Default(), 1f, null, Colors.black_17),
|
|
274
|
+
modifier = modifier,
|
|
275
|
+
)
|
|
276
|
+
}
|
|
277
|
+
|
|
191
278
|
@Composable
|
|
192
279
|
internal fun NavigationButton(
|
|
193
280
|
disabled: Boolean,
|
|
194
281
|
icon: String,
|
|
195
282
|
showBadge: Boolean? = false,
|
|
196
283
|
onClick: () -> Unit,
|
|
197
|
-
headerColor: HeaderColor
|
|
284
|
+
headerColor: HeaderColor,
|
|
285
|
+
modifier: Modifier = Modifier,
|
|
198
286
|
) {
|
|
199
287
|
Box(
|
|
200
|
-
modifier =
|
|
288
|
+
modifier = modifier
|
|
201
289
|
.size(28.dp)
|
|
202
290
|
.activeOpacityClickable(enabled = !disabled, onClick = onClick)
|
|
203
291
|
) {
|
|
@@ -270,7 +358,9 @@ sealed interface HeaderRight {
|
|
|
270
358
|
val useMore: Boolean = false,
|
|
271
359
|
val useSystemTools: Boolean = true,
|
|
272
360
|
val tools: List<ToolGroup> = emptyList(),
|
|
273
|
-
val toolCallback: ((String) -> Unit)? = { _ -> }
|
|
361
|
+
val toolCallback: ((String) -> Unit)? = { _ -> },
|
|
362
|
+
// Close dismisses only this mini app instead of returning home.
|
|
363
|
+
val useCloseIcon: Boolean = false,
|
|
274
364
|
) : HeaderRight
|
|
275
365
|
}
|
|
276
366
|
|
|
@@ -32,7 +32,6 @@ import vn.momo.kits.const.Typography
|
|
|
32
32
|
import vn.momo.kits.const.getFontFamily
|
|
33
33
|
import vn.momo.kits.modifier.activeOpacityClickable
|
|
34
34
|
import vn.momo.kits.navigation.component.HeaderTitle.User
|
|
35
|
-
import vn.momo.kits.platform.getScreenDimensions
|
|
36
35
|
|
|
37
36
|
@Composable
|
|
38
37
|
internal fun HeaderUser(
|
|
@@ -44,7 +43,7 @@ internal fun HeaderUser(
|
|
|
44
43
|
return TitleUserShimmer()
|
|
45
44
|
}
|
|
46
45
|
|
|
47
|
-
val maxWidth =
|
|
46
|
+
val maxWidth = headerTitleMaxWidth()
|
|
48
47
|
|
|
49
48
|
Row(
|
|
50
49
|
modifier = Modifier
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
package vn.momo.kits.navigation.component
|
|
2
|
+
|
|
3
|
+
import androidx.compose.foundation.background
|
|
4
|
+
import androidx.compose.foundation.border
|
|
5
|
+
import androidx.compose.foundation.layout.Arrangement
|
|
6
|
+
import androidx.compose.foundation.layout.Box
|
|
7
|
+
import androidx.compose.foundation.layout.BoxScope
|
|
8
|
+
import androidx.compose.foundation.layout.Column
|
|
9
|
+
import androidx.compose.foundation.layout.Row
|
|
10
|
+
import androidx.compose.foundation.layout.Spacer
|
|
11
|
+
import androidx.compose.foundation.layout.absolutePadding
|
|
12
|
+
import androidx.compose.foundation.layout.offset
|
|
13
|
+
import androidx.compose.foundation.layout.size
|
|
14
|
+
import androidx.compose.foundation.layout.width
|
|
15
|
+
import androidx.compose.foundation.shape.CircleShape
|
|
16
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
17
|
+
import androidx.compose.runtime.Composable
|
|
18
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
19
|
+
import androidx.compose.ui.AbsoluteAlignment
|
|
20
|
+
import androidx.compose.ui.Alignment
|
|
21
|
+
import androidx.compose.ui.Modifier
|
|
22
|
+
import androidx.compose.ui.draw.clip
|
|
23
|
+
import androidx.compose.ui.draw.shadow
|
|
24
|
+
import androidx.compose.ui.graphics.Shape
|
|
25
|
+
import androidx.compose.ui.layout.layout
|
|
26
|
+
import androidx.compose.ui.unit.Dp
|
|
27
|
+
import androidx.compose.ui.unit.constrainHeight
|
|
28
|
+
import androidx.compose.ui.unit.dp
|
|
29
|
+
import vn.momo.kits.components.BadgeDot
|
|
30
|
+
import vn.momo.kits.components.DotSize
|
|
31
|
+
import vn.momo.kits.components.Icon
|
|
32
|
+
import vn.momo.kits.const.Colors
|
|
33
|
+
import vn.momo.kits.const.Radius
|
|
34
|
+
import vn.momo.kits.const.Spacing
|
|
35
|
+
import vn.momo.kits.layout.DuoMetrics
|
|
36
|
+
import vn.momo.kits.layout.SideBarEdge
|
|
37
|
+
import vn.momo.kits.layout.getScreenLayout
|
|
38
|
+
import vn.momo.kits.modifier.activeOpacityClickable
|
|
39
|
+
import vn.momo.kits.modifier.conditional
|
|
40
|
+
import vn.momo.kits.modifier.setAutomationId
|
|
41
|
+
import vn.momo.kits.navigation.LocalNavigator
|
|
42
|
+
import vn.momo.kits.navigation.LocalOptions
|
|
43
|
+
import vn.momo.kits.navigation.bottomtab.LocalDuoBarCompression
|
|
44
|
+
|
|
45
|
+
private val SIDE_BAR_ICON_SIZE = 24.dp
|
|
46
|
+
|
|
47
|
+
// iPhone Duo: back and toolkit leave the bar for a column in the side safe area, as the system bars do.
|
|
48
|
+
// bottomInset is what footer / tab bar / home indicator take from the bottom, gap included; the column stays above it.
|
|
49
|
+
@Composable
|
|
50
|
+
internal fun BoxScope.HeaderSideBar(bottomInset: Dp, modifier: Modifier = Modifier) {
|
|
51
|
+
val options = LocalOptions.current
|
|
52
|
+
val navigator = LocalNavigator.current
|
|
53
|
+
val screenLayout = getScreenLayout()
|
|
54
|
+
val sideBar = screenLayout.sideBar ?: return
|
|
55
|
+
val toolkit = options.headerRight as? HeaderRight.Toolkit
|
|
56
|
+
val empty = options.headerType == HeaderType.None || (options.hiddenBack && toolkit == null)
|
|
57
|
+
val compression = LocalDuoBarCompression.current
|
|
58
|
+
val fullHeight = if (empty) 0.dp else DuoMetrics.controlSize + (toolkit?.let { Spacing.M + toolkitColumnHeight(it) } ?: 0.dp)
|
|
59
|
+
LaunchedEffect(fullHeight, compression.setToolbarHeight) { compression.setToolbarHeight(fullHeight) }
|
|
60
|
+
if (empty) return
|
|
61
|
+
|
|
62
|
+
val headerColor = getHeaderColor(options.headerType, headerOpacity(), options.tintColor, Colors.black_17)
|
|
63
|
+
val isRight = sideBar.edge == SideBarEdge.RIGHT
|
|
64
|
+
|
|
65
|
+
Column(
|
|
66
|
+
modifier = modifier
|
|
67
|
+
.align(if (isRight) AbsoluteAlignment.TopRight else AbsoluteAlignment.TopLeft)
|
|
68
|
+
.width(sideBar.width)
|
|
69
|
+
.layout { measurable, constraints ->
|
|
70
|
+
val placeable = measurable.measure(constraints.copy(minHeight = 0))
|
|
71
|
+
val maxY = screenLayout.height.roundToPx() - bottomInset.roundToPx() - placeable.height
|
|
72
|
+
val y = minOf(sideBar.top.roundToPx(), maxY).coerceAtLeast(0)
|
|
73
|
+
layout(placeable.width, constraints.constrainHeight(y + placeable.height)) {
|
|
74
|
+
placeable.place(0, y)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
.absolutePadding(left = if (isRight) Spacing.M else 0.dp, right = if (isRight) 0.dp else Spacing.M),
|
|
78
|
+
horizontalAlignment = if (isRight) AbsoluteAlignment.Left else AbsoluteAlignment.Right,
|
|
79
|
+
verticalArrangement = Arrangement.spacedBy(Spacing.M),
|
|
80
|
+
) {
|
|
81
|
+
if (options.hiddenBack) {
|
|
82
|
+
// keep the slot so the toolkit does not jump between root and pushed screens
|
|
83
|
+
Spacer(Modifier.size(DuoMetrics.controlSize))
|
|
84
|
+
} else {
|
|
85
|
+
SideBarButton(
|
|
86
|
+
icon = "arrow-back",
|
|
87
|
+
headerColor = headerColor,
|
|
88
|
+
automationId = "btn_navigation_back",
|
|
89
|
+
onClick = { navigator.onBackSafe { } },
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
when {
|
|
93
|
+
toolkit == null || compression.toolbarHidden -> Unit
|
|
94
|
+
// HIG "Toolbar compressed": tools fold into one button, distinct from the useMore icon
|
|
95
|
+
compression.toolbarCompressed -> SideBarButton(
|
|
96
|
+
icon = "navigation_menu",
|
|
97
|
+
headerColor = headerColor,
|
|
98
|
+
showBadge = toolkit.tools.any { group -> group.items.any { it.showBadge } },
|
|
99
|
+
automationId = "btn_navigation_toolbar",
|
|
100
|
+
onClick = compression.expandToolbar,
|
|
101
|
+
)
|
|
102
|
+
else -> Toolkit(
|
|
103
|
+
headerRight = toolkit,
|
|
104
|
+
tintColor = options.tintColor,
|
|
105
|
+
headerColor = headerColor,
|
|
106
|
+
layout = ToolkitLayout.SideBarColumn,
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@Composable
|
|
113
|
+
internal fun SideBarButton(
|
|
114
|
+
icon: String,
|
|
115
|
+
headerColor: HeaderColor,
|
|
116
|
+
onClick: () -> Unit,
|
|
117
|
+
enabled: Boolean = true,
|
|
118
|
+
showBadge: Boolean = false,
|
|
119
|
+
automationId: String? = null,
|
|
120
|
+
) {
|
|
121
|
+
Box {
|
|
122
|
+
Box(
|
|
123
|
+
modifier = Modifier
|
|
124
|
+
.size(DuoMetrics.controlSize)
|
|
125
|
+
.sideBarGlass(headerColor, CircleShape)
|
|
126
|
+
.activeOpacityClickable(enabled = enabled, onClick = onClick)
|
|
127
|
+
.conditional(automationId != null) { setAutomationId(automationId.orEmpty()) },
|
|
128
|
+
contentAlignment = Alignment.Center,
|
|
129
|
+
) {
|
|
130
|
+
Icon(source = icon, size = SIDE_BAR_ICON_SIZE, color = headerColor.tintIconColor)
|
|
131
|
+
}
|
|
132
|
+
if (showBadge) {
|
|
133
|
+
BadgeDot(
|
|
134
|
+
size = DotSize.Small,
|
|
135
|
+
modifier = Modifier.align(Alignment.TopEnd).offset(x = -Spacing.XXS, y = Spacing.XXS),
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Capsule grouping related controls, one item per 50pt: vertical in the side bar, horizontal on a header row.
|
|
142
|
+
@Composable
|
|
143
|
+
internal fun SideBarGroup(
|
|
144
|
+
headerColor: HeaderColor,
|
|
145
|
+
itemCount: Int,
|
|
146
|
+
horizontal: Boolean = false,
|
|
147
|
+
content: @Composable (itemModifier: Modifier) -> Unit,
|
|
148
|
+
) {
|
|
149
|
+
val length = if (itemCount > 1) DuoMetrics.itemPitch * itemCount else DuoMetrics.controlSize
|
|
150
|
+
val item = length / itemCount.coerceAtLeast(1)
|
|
151
|
+
val glass = Modifier.sideBarGlass(headerColor, RoundedCornerShape(Radius.XL))
|
|
152
|
+
if (horizontal) {
|
|
153
|
+
Row(Modifier.size(width = length, height = DuoMetrics.controlSize).then(glass)) {
|
|
154
|
+
content(Modifier.size(width = item, height = DuoMetrics.controlSize))
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
Column(Modifier.size(width = DuoMetrics.controlSize, height = length).then(glass)) {
|
|
158
|
+
content(Modifier.size(width = DuoMetrics.controlSize, height = item))
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@Composable
|
|
164
|
+
internal fun SideBarGroupItem(
|
|
165
|
+
icon: String,
|
|
166
|
+
headerColor: HeaderColor,
|
|
167
|
+
automationId: String,
|
|
168
|
+
onClick: () -> Unit,
|
|
169
|
+
modifier: Modifier = Modifier,
|
|
170
|
+
) {
|
|
171
|
+
Box(
|
|
172
|
+
modifier = modifier
|
|
173
|
+
.activeOpacityClickable(onClick = onClick)
|
|
174
|
+
.setAutomationId(automationId),
|
|
175
|
+
contentAlignment = Alignment.Center,
|
|
176
|
+
) {
|
|
177
|
+
Icon(source = icon, size = SIDE_BAR_ICON_SIZE, color = headerColor.tintIconColor)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Dark glass under light icons (banner headers), light glass otherwise. The light one is opaque:
|
|
182
|
+
// Compose has no backdrop blur and a translucent fill shows its own shadow through (RN uses 85% white).
|
|
183
|
+
private fun Modifier.sideBarGlass(headerColor: HeaderColor, shape: Shape): Modifier =
|
|
184
|
+
if (headerColor.tintIconColor == Colors.black_01) {
|
|
185
|
+
clip(shape)
|
|
186
|
+
.background(Colors.black_20.copy(alpha = 0.5f))
|
|
187
|
+
.border(0.5.dp, Colors.black_01.copy(alpha = 0.2f), shape)
|
|
188
|
+
} else {
|
|
189
|
+
shadow(3.dp, shape)
|
|
190
|
+
.background(Colors.black_01)
|
|
191
|
+
.border(0.5.dp, Colors.black_20.copy(alpha = 0.08f), shape)
|
|
192
|
+
}
|
|
@@ -14,6 +14,9 @@ data class ScreenDimension(
|
|
|
14
14
|
|
|
15
15
|
expect fun getPlatformName(): String
|
|
16
16
|
|
|
17
|
+
// iPhone idiom only; iPad windows and Android never get the iPhone / iPhone Duo layout.
|
|
18
|
+
expect fun isPhoneIdiom(): Boolean
|
|
19
|
+
|
|
17
20
|
@Composable
|
|
18
21
|
expect fun getScreenDimensions(): ScreenDimension
|
|
19
22
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
package vn.momo.kits.platform
|
|
2
|
+
|
|
3
|
+
import androidx.compose.runtime.Composable
|
|
4
|
+
import androidx.compose.runtime.Immutable
|
|
5
|
+
import androidx.compose.ui.unit.Dp
|
|
6
|
+
|
|
7
|
+
@Immutable
|
|
8
|
+
internal data class ReservedRegion(val x: Dp, val y: Dp, val width: Dp, val height: Dp)
|
|
9
|
+
|
|
10
|
+
// Regions the system occludes (iOS 27.1+), in the coordinates of the Compose root view: the
|
|
11
|
+
// iPhone Duo camera, and the status bar cluster as it grows for a call or a Live Activity.
|
|
12
|
+
@Composable
|
|
13
|
+
internal expect fun rememberReservedRegions(): List<ReservedRegion>
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
package vn.momo.kits.utils
|
|
2
2
|
|
|
3
3
|
import androidx.compose.foundation.layout.WindowInsets
|
|
4
|
+
import androidx.compose.foundation.layout.WindowInsetsSides
|
|
5
|
+
import androidx.compose.foundation.layout.displayCutout
|
|
6
|
+
import androidx.compose.foundation.layout.only
|
|
7
|
+
import androidx.compose.foundation.layout.union
|
|
8
|
+
import androidx.compose.foundation.layout.windowInsetsPadding
|
|
4
9
|
import androidx.compose.foundation.layout.asPaddingValues
|
|
5
10
|
import androidx.compose.foundation.layout.systemBars
|
|
6
11
|
import androidx.compose.runtime.Composable
|
|
@@ -90,6 +95,14 @@ fun getAppStatusBarHeight(): Dp {
|
|
|
90
95
|
}
|
|
91
96
|
}
|
|
92
97
|
|
|
98
|
+
internal val horizontalSafeAreaInsets: WindowInsets
|
|
99
|
+
@Composable get() = WindowInsets.systemBars.union(WindowInsets.displayCutout).only(WindowInsetsSides.Horizontal)
|
|
100
|
+
|
|
101
|
+
// Left/right safe area like SwiftUI (landscape camera/notch); consumed, so nested StackScreens don't pad twice.
|
|
102
|
+
internal fun Modifier.horizontalSafeAreaPadding(): Modifier = composed {
|
|
103
|
+
windowInsetsPadding(horizontalSafeAreaInsets)
|
|
104
|
+
}
|
|
105
|
+
|
|
93
106
|
@Composable
|
|
94
107
|
internal fun getNavigationBarHeight(): Dp {
|
|
95
108
|
return if (AppNavigationBar.current == 0.dp) {
|
|
@@ -39,11 +39,15 @@ import platform.Foundation.NSBundle
|
|
|
39
39
|
import platform.UIKit.UIColor
|
|
40
40
|
import platform.UIKit.UIDevice
|
|
41
41
|
import platform.UIKit.UIScreen
|
|
42
|
+
import platform.UIKit.UIUserInterfaceIdiomPhone
|
|
42
43
|
import vn.momo.kits.navigation.AttachHostBackGesture
|
|
43
44
|
import vn.momo.kits.navigation.Navigator
|
|
44
45
|
|
|
45
46
|
actual fun getPlatformName(): String = "iOS"
|
|
46
47
|
|
|
48
|
+
actual fun isPhoneIdiom(): Boolean =
|
|
49
|
+
UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPhone
|
|
50
|
+
|
|
47
51
|
@OptIn(ExperimentalForeignApi::class)
|
|
48
52
|
@Composable
|
|
49
53
|
actual fun getScreenDimensions(): ScreenDimension = memScoped {
|