@momo-kits/native-kits 0.167.1-duo.13 → 0.167.1-duo.13-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/build.gradle.kts +11 -0
- package/compose/build.gradle.kts +204 -0
- package/compose/build.gradle.kts.backup +204 -0
- package/compose/compose.podspec +47 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +19 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +6 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +12 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +7 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +153 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +6 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +52 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +124 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +183 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +207 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +39 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +76 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +76 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +293 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +831 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +110 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +378 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +56 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +157 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +81 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +32 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +338 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +175 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +357 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +115 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +89 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +128 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +209 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +542 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +49 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +76 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +125 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +231 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +189 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +478 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +114 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +265 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +235 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +223 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +265 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +185 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +343 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +108 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +56 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +41 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +84 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +40 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +361 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +108 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +308 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +59 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +87 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +21 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +90 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +330 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +220 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +473 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +122 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +207 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +92 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +560 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +125 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +207 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +501 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +176 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +182 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +193 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +236 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +180 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +325 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +16 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +18 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +22 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +16 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +188 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +297 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +54 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +50 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +260 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +41 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +52 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +66 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +104 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +48 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +338 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +58 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +137 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +106 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +205 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +393 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +8 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +708 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +148 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +35 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +209 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +231 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
- 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/FloatingButton.kt +169 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +408 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +103 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +106 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +74 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +366 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +37 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +363 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +185 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +133 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +127 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +12 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +91 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +9 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +74 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +13 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +55 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +15 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +118 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +40 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +45 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +32 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +38 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +66 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +217 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +94 -0
- package/gradle/libs.versions.toml +69 -0
- package/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/gradle/wrapper/gradle-wrapper.properties +8 -0
- package/gradle.properties +26 -0
- package/gradlew +252 -0
- package/gradlew.bat +94 -0
- package/ios-demo/README.md +120 -0
- package/local.properties +8 -0
- package/package.json +1 -1
- package/settings.gradle.kts +64 -0
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
package vn.momo.kits.navigation.component
|
|
2
|
+
|
|
3
|
+
import vn.momo.kits.modifier.InternalApi
|
|
4
|
+
|
|
5
|
+
import androidx.compose.animation.core.animateFloatAsState
|
|
6
|
+
import androidx.compose.foundation.background
|
|
7
|
+
import androidx.compose.foundation.border
|
|
8
|
+
import androidx.compose.foundation.layout.Arrangement
|
|
9
|
+
import androidx.compose.foundation.layout.Box
|
|
10
|
+
import androidx.compose.foundation.layout.Row
|
|
11
|
+
import androidx.compose.foundation.layout.Spacer
|
|
12
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
13
|
+
import androidx.compose.foundation.layout.height
|
|
14
|
+
import androidx.compose.foundation.layout.offset
|
|
15
|
+
import androidx.compose.foundation.layout.padding
|
|
16
|
+
import androidx.compose.foundation.layout.size
|
|
17
|
+
import androidx.compose.foundation.layout.width
|
|
18
|
+
import androidx.compose.foundation.layout.wrapContentHeight
|
|
19
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
20
|
+
import androidx.compose.runtime.Composable
|
|
21
|
+
import androidx.compose.runtime.getValue
|
|
22
|
+
import androidx.compose.ui.Alignment
|
|
23
|
+
import androidx.compose.ui.Modifier
|
|
24
|
+
import androidx.compose.ui.draw.clipToBounds
|
|
25
|
+
import androidx.compose.ui.draw.drawBehind
|
|
26
|
+
import androidx.compose.ui.geometry.Offset
|
|
27
|
+
import androidx.compose.ui.graphics.graphicsLayer
|
|
28
|
+
import androidx.compose.ui.graphics.Brush
|
|
29
|
+
import androidx.compose.ui.graphics.Color
|
|
30
|
+
import androidx.compose.ui.layout.onGloballyPositioned
|
|
31
|
+
import androidx.compose.ui.platform.LocalDensity
|
|
32
|
+
import androidx.compose.ui.text.style.TextAlign
|
|
33
|
+
import androidx.compose.ui.unit.Dp
|
|
34
|
+
import androidx.compose.ui.unit.dp
|
|
35
|
+
import vn.momo.kits.components.Icon
|
|
36
|
+
import vn.momo.kits.components.Image
|
|
37
|
+
import vn.momo.kits.components.InputSearchProps
|
|
38
|
+
import vn.momo.kits.const.AppTheme
|
|
39
|
+
import vn.momo.kits.const.Colors
|
|
40
|
+
import vn.momo.kits.components.PopupNotifyProps
|
|
41
|
+
import vn.momo.kits.application.IsShowBaseLineDebug
|
|
42
|
+
import vn.momo.kits.const.Spacing
|
|
43
|
+
import vn.momo.kits.layout.getScreenLayout
|
|
44
|
+
import vn.momo.kits.modifier.activeOpacityClickable
|
|
45
|
+
import vn.momo.kits.modifier.conditional
|
|
46
|
+
import vn.momo.kits.modifier.setAutomationId
|
|
47
|
+
import vn.momo.kits.navigation.LocalHeaderRightWidthPx
|
|
48
|
+
import vn.momo.kits.navigation.LocalNavigator
|
|
49
|
+
import vn.momo.kits.navigation.LocalOptions
|
|
50
|
+
import vn.momo.kits.navigation.LocalScrollState
|
|
51
|
+
import vn.momo.kits.navigation.getInputSearchType
|
|
52
|
+
import vn.momo.kits.navigation.usesDuoGlassHeader
|
|
53
|
+
import vn.momo.kits.utils.horizontalSafeAreaPadding
|
|
54
|
+
|
|
55
|
+
@InternalApi
|
|
56
|
+
const val HEADER_HEIGHT = 52
|
|
57
|
+
internal enum class InputSearchType { None, Header, Animated }
|
|
58
|
+
enum class TitlePosition { LEFT, CENTER }
|
|
59
|
+
|
|
60
|
+
private val HEADER_ACTIONS_WIDTH = 172.dp
|
|
61
|
+
|
|
62
|
+
@Suppress("FrequentlyChangingValue")
|
|
63
|
+
@Composable
|
|
64
|
+
internal fun headerOpacity(): Float {
|
|
65
|
+
val options = LocalOptions.current
|
|
66
|
+
val scrollState = LocalScrollState.current
|
|
67
|
+
val opacityHeight = when (val header = options.headerType) {
|
|
68
|
+
is HeaderType.Animated -> with(LocalDensity.current) { header.layoutOffSet.roundToPx() }
|
|
69
|
+
else -> HEADER_HEIGHT
|
|
70
|
+
}
|
|
71
|
+
val opacity by animateFloatAsState(
|
|
72
|
+
targetValue = when {
|
|
73
|
+
options.headerType is HeaderType.Transparent -> 0f
|
|
74
|
+
opacityHeight <= 0 -> if (scrollState.value > 0) 1f else 0f
|
|
75
|
+
else -> (scrollState.value / opacityHeight.toFloat()).coerceIn(0f, 1f)
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
return opacity
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Back button and toolkit share the bar with the title unless the iPhone Duo side bar holds them.
|
|
82
|
+
@Composable
|
|
83
|
+
internal fun headerTitleMaxWidth(): Dp {
|
|
84
|
+
val layout = getScreenLayout()
|
|
85
|
+
if (layout.sideBar != null) return layout.contentWidth - Spacing.M * 2
|
|
86
|
+
return layout.width - HEADER_ACTIONS_WIDTH
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Scroll distance over which the Duo glass bar fades in.
|
|
90
|
+
private val DUO_GLASS_FADE_DISTANCE = 16.dp
|
|
91
|
+
|
|
92
|
+
// RN DuoGlassBackground fallback: translucent surface with a hairline once content scrolls under it.
|
|
93
|
+
@Suppress("FrequentlyChangingValue")
|
|
94
|
+
@Composable
|
|
95
|
+
private fun Modifier.duoGlassBar(): Modifier {
|
|
96
|
+
val scrollState = LocalScrollState.current
|
|
97
|
+
val fadePx = with(LocalDensity.current) { DUO_GLASS_FADE_DISTANCE.toPx() }
|
|
98
|
+
val alpha by animateFloatAsState((scrollState.value / fadePx).coerceIn(0f, 1f), label = "DuoGlassAlpha")
|
|
99
|
+
val colors = AppTheme.current.colors
|
|
100
|
+
return background(colors.background.surface.copy(alpha = 0.8f * alpha))
|
|
101
|
+
.drawBehind {
|
|
102
|
+
val stroke = 0.5.dp.toPx()
|
|
103
|
+
drawLine(
|
|
104
|
+
color = colors.border.default.copy(alpha = alpha),
|
|
105
|
+
start = Offset(0f, size.height - stroke / 2),
|
|
106
|
+
end = Offset(size.width, size.height - stroke / 2),
|
|
107
|
+
strokeWidth = stroke,
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@Composable
|
|
113
|
+
internal fun Header(onBackHandler: (() -> Unit)? = null) {
|
|
114
|
+
val options = LocalOptions.current
|
|
115
|
+
val navigator = LocalNavigator.current
|
|
116
|
+
val headerRightWidthPx = LocalHeaderRightWidthPx.current
|
|
117
|
+
val inputSearchType = getInputSearchType(options)
|
|
118
|
+
val layout = getScreenLayout()
|
|
119
|
+
val useSideBar = layout.sideBar != null
|
|
120
|
+
val opacity = headerOpacity()
|
|
121
|
+
|
|
122
|
+
val headerColor = getHeaderColor(options.headerType, opacity, options.tintColor, Colors.black_17)
|
|
123
|
+
|
|
124
|
+
val animatedAlpha by animateFloatAsState(targetValue = opacity, label = "BackgroundAlpha")
|
|
125
|
+
|
|
126
|
+
val background = if (options.headerType is HeaderType.Transparent)
|
|
127
|
+
options.backgroundColor ?: AppTheme.current.colors.background.surface
|
|
128
|
+
else
|
|
129
|
+
AppTheme.current.colors.background.surface
|
|
130
|
+
|
|
131
|
+
if (options.headerType == HeaderType.None) return
|
|
132
|
+
|
|
133
|
+
val titlePosition = options.titlePosition
|
|
134
|
+
val titleColor = headerColor.tintIconColor
|
|
135
|
+
.copy(alpha = if (inputSearchType == InputSearchType.Animated) 1f - animatedAlpha else 1f)
|
|
136
|
+
// Centered title needs a full-width overlay to stay screen-centered, not boxed between back button and header right.
|
|
137
|
+
val centerTitle = titlePosition == TitlePosition.CENTER && options.headerTitle is HeaderTitle.Default
|
|
138
|
+
|
|
139
|
+
val useDuoGlass = options.usesDuoGlassHeader(layout)
|
|
140
|
+
val headerImage = AppTheme.current.assets.headerBackground
|
|
141
|
+
.takeUnless { options.headerType is HeaderType.Transparent || useDuoGlass }
|
|
142
|
+
|
|
143
|
+
Box(
|
|
144
|
+
Modifier.height(layout.headerHeight)
|
|
145
|
+
.fillMaxWidth()
|
|
146
|
+
.then(if (useDuoGlass) Modifier.duoGlassBar() else Modifier)
|
|
147
|
+
.conditional(headerImage.isNullOrEmpty() && !useDuoGlass) {
|
|
148
|
+
background(background.copy(alpha = animatedAlpha))
|
|
149
|
+
}
|
|
150
|
+
.conditional(IsShowBaseLineDebug) {
|
|
151
|
+
border(1.dp, Colors.blue_03)
|
|
152
|
+
},
|
|
153
|
+
contentAlignment = Alignment.BottomCenter
|
|
154
|
+
) {
|
|
155
|
+
if (!headerImage.isNullOrEmpty()) {
|
|
156
|
+
Box(
|
|
157
|
+
Modifier
|
|
158
|
+
.matchParentSize()
|
|
159
|
+
.clipToBounds()
|
|
160
|
+
.graphicsLayer { alpha = animatedAlpha }
|
|
161
|
+
.background(background)
|
|
162
|
+
) {
|
|
163
|
+
Image(
|
|
164
|
+
source = headerImage,
|
|
165
|
+
loading = false,
|
|
166
|
+
modifier = Modifier
|
|
167
|
+
.fillMaxWidth()
|
|
168
|
+
.wrapContentHeight(Alignment.Top, unbounded = true)
|
|
169
|
+
.height(HEADER_BACKGROUND_HEIGHT),
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
Row(
|
|
174
|
+
modifier = Modifier.height(layout.headerBarHeight)
|
|
175
|
+
.fillMaxWidth()
|
|
176
|
+
.horizontalSafeAreaPadding()
|
|
177
|
+
.padding(horizontal = Spacing.M),
|
|
178
|
+
verticalAlignment = Alignment.CenterVertically,
|
|
179
|
+
horizontalArrangement = Arrangement.SpaceBetween
|
|
180
|
+
) {
|
|
181
|
+
if (!options.hiddenBack && !useSideBar) {
|
|
182
|
+
BackButton(
|
|
183
|
+
borderColor = headerColor.borderColor,
|
|
184
|
+
backgroundButton = headerColor.backgroundButton,
|
|
185
|
+
tintIconColor = headerColor.tintIconColor,
|
|
186
|
+
onBackHandler = {
|
|
187
|
+
navigator.onBackSafe { }
|
|
188
|
+
}
|
|
189
|
+
)
|
|
190
|
+
Spacer(Modifier.width(Spacing.S))
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (centerTitle) {
|
|
194
|
+
Spacer(Modifier.weight(1f))
|
|
195
|
+
} else {
|
|
196
|
+
HeaderContent(
|
|
197
|
+
headerTitle = options.headerTitle,
|
|
198
|
+
tintIconColor = titleColor,
|
|
199
|
+
titlePosition = titlePosition,
|
|
200
|
+
modifier = Modifier.weight(1f)
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
Box(Modifier.onGloballyPositioned {
|
|
204
|
+
if(headerRightWidthPx.intValue != it.size.width) headerRightWidthPx.intValue = it.size.width
|
|
205
|
+
}){
|
|
206
|
+
if (!(useSideBar && options.headerRight is HeaderRight.Toolkit)) {
|
|
207
|
+
HeaderRight(options.headerRight, options.tintColor, headerColor)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (centerTitle) {
|
|
212
|
+
Box(
|
|
213
|
+
modifier = Modifier.height(layout.headerBarHeight)
|
|
214
|
+
.fillMaxWidth()
|
|
215
|
+
.horizontalSafeAreaPadding()
|
|
216
|
+
.padding(horizontal = Spacing.M),
|
|
217
|
+
contentAlignment = Alignment.Center
|
|
218
|
+
) {
|
|
219
|
+
HeaderContent(
|
|
220
|
+
headerTitle = options.headerTitle,
|
|
221
|
+
tintIconColor = titleColor,
|
|
222
|
+
titlePosition = titlePosition,
|
|
223
|
+
modifier = Modifier.fillMaxWidth()
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
VerticalShadow(opacity)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
@Composable
|
|
232
|
+
private fun BackButton(borderColor: Color, backgroundButton: Color, tintIconColor: Color, onBackHandler: () -> Unit){
|
|
233
|
+
Box(
|
|
234
|
+
modifier = Modifier
|
|
235
|
+
.size(28.dp)
|
|
236
|
+
.background(backgroundButton, RoundedCornerShape(100))
|
|
237
|
+
.border(width = 0.2.dp, color = borderColor, shape = RoundedCornerShape(100))
|
|
238
|
+
.activeOpacityClickable(onClick = onBackHandler)
|
|
239
|
+
.padding(Spacing.XS)
|
|
240
|
+
.setAutomationId("btn_navigation_back"),
|
|
241
|
+
contentAlignment = Alignment.Center
|
|
242
|
+
) {
|
|
243
|
+
Icon(
|
|
244
|
+
source = "arrow-back",
|
|
245
|
+
color = tintIconColor,
|
|
246
|
+
size = 20.dp,
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
@Composable
|
|
252
|
+
internal fun HeaderContent(
|
|
253
|
+
headerTitle: HeaderTitle,
|
|
254
|
+
tintIconColor: Color,
|
|
255
|
+
titlePosition: TitlePosition = TitlePosition.LEFT,
|
|
256
|
+
modifier: Modifier = Modifier,
|
|
257
|
+
){
|
|
258
|
+
Box(
|
|
259
|
+
modifier = modifier,
|
|
260
|
+
contentAlignment = if (titlePosition == TitlePosition.CENTER) Alignment.Center else Alignment.CenterStart
|
|
261
|
+
) {
|
|
262
|
+
when (headerTitle){
|
|
263
|
+
is HeaderTitle.Default -> {
|
|
264
|
+
HeaderTitle(
|
|
265
|
+
title = headerTitle.title,
|
|
266
|
+
color = tintIconColor,
|
|
267
|
+
modifier = Modifier.fillMaxWidth(fraction = if (titlePosition == TitlePosition.CENTER) 0.5f else 1f),
|
|
268
|
+
textAlign = if (titlePosition == TitlePosition.CENTER) TextAlign.Center else TextAlign.Start
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
is HeaderTitle.Journey -> {
|
|
272
|
+
HeaderJourney(
|
|
273
|
+
data = headerTitle,
|
|
274
|
+
tintIconColor = tintIconColor
|
|
275
|
+
)
|
|
276
|
+
}
|
|
277
|
+
is HeaderTitle.Location -> {
|
|
278
|
+
HeaderLocation(
|
|
279
|
+
data = headerTitle,
|
|
280
|
+
tintIconColor = tintIconColor
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
is HeaderTitle.User -> {
|
|
284
|
+
HeaderUser(
|
|
285
|
+
data = headerTitle
|
|
286
|
+
)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
@Composable
|
|
293
|
+
internal fun VerticalShadow(opacity: Float){
|
|
294
|
+
if(opacity == 1f){
|
|
295
|
+
Box(modifier = Modifier
|
|
296
|
+
.fillMaxWidth()
|
|
297
|
+
.height(6.dp)
|
|
298
|
+
.offset(x = 0.dp, y = 6.dp)
|
|
299
|
+
.background(
|
|
300
|
+
brush = Brush.verticalGradient(
|
|
301
|
+
colors = listOf(Color.Black.copy(alpha = 0.05f), Color.Transparent)
|
|
302
|
+
)
|
|
303
|
+
)
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
sealed class HeaderTitle {
|
|
309
|
+
class Default(val title: String) : HeaderTitle()
|
|
310
|
+
|
|
311
|
+
class User(
|
|
312
|
+
val title: String,
|
|
313
|
+
val subTitle: String? = null,
|
|
314
|
+
val image: List<String>? = null,
|
|
315
|
+
val dotColor: Color? = null,
|
|
316
|
+
val tintColor: Color? = null,
|
|
317
|
+
val onPress: (() -> Unit)? = null,
|
|
318
|
+
val icons: List<String> = emptyList(),
|
|
319
|
+
val isLoading: Boolean = false
|
|
320
|
+
) : HeaderTitle()
|
|
321
|
+
|
|
322
|
+
class Location(
|
|
323
|
+
val description: String? = null,
|
|
324
|
+
val location: String,
|
|
325
|
+
val tintColor: String? = null,
|
|
326
|
+
val onPress: (() -> Unit)? = null,
|
|
327
|
+
val isLoading: Boolean = false
|
|
328
|
+
) : HeaderTitle()
|
|
329
|
+
|
|
330
|
+
class Journey(
|
|
331
|
+
val start: String,
|
|
332
|
+
val end: String? = null,
|
|
333
|
+
val description: String? = null,
|
|
334
|
+
val icon: String,
|
|
335
|
+
val iconColor: String? = null,
|
|
336
|
+
val tintColor: String? = null,
|
|
337
|
+
val onPress: (() -> Unit)? = null,
|
|
338
|
+
val isLoading: Boolean = false
|
|
339
|
+
) : HeaderTitle()
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
sealed class HeaderType {
|
|
343
|
+
interface DefaultOrExtended {
|
|
344
|
+
val useAnimated: Boolean
|
|
345
|
+
val inputSearchProps: InputSearchProps?
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
data class Default(
|
|
349
|
+
override val useAnimated: Boolean = false,
|
|
350
|
+
override val inputSearchProps: InputSearchProps? = null
|
|
351
|
+
) : HeaderType(), DefaultOrExtended
|
|
352
|
+
|
|
353
|
+
data class Extended(
|
|
354
|
+
override val useAnimated: Boolean = false,
|
|
355
|
+
override val inputSearchProps: InputSearchProps? = null
|
|
356
|
+
) : HeaderType(), DefaultOrExtended
|
|
357
|
+
|
|
358
|
+
data object None : HeaderType()
|
|
359
|
+
|
|
360
|
+
data class Animated(
|
|
361
|
+
val aspectRatio: AnimatedHeaderRatio = AnimatedHeaderRatio.RATIO_16_9,
|
|
362
|
+
val isSurface: Boolean = true,
|
|
363
|
+
val layoutOffSet: Dp = 56.dp,
|
|
364
|
+
val height: Dp? = null,
|
|
365
|
+
val composable: @Composable (scrollState: Int) -> Unit = {}
|
|
366
|
+
) : HeaderType()
|
|
367
|
+
|
|
368
|
+
data class Transparent(
|
|
369
|
+
val isFullScreenContent: Boolean = false
|
|
370
|
+
) : HeaderType()
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
data class HeaderBackProps(
|
|
374
|
+
val preventBack: PopupNotifyProps? = null,
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
@InternalApi
|
|
378
|
+
data class HeaderColor(val tintIconColor: Color, val backgroundButton: Color, val borderColor: Color)
|
|
379
|
+
internal fun getHeaderColor(headerType: HeaderType, opacity: Float, tintColor: Color?, defaultColor: Color): HeaderColor{
|
|
380
|
+
return if(headerType is HeaderType.Animated) {
|
|
381
|
+
if (opacity == 1f || !headerType.isSurface)
|
|
382
|
+
HeaderColor(
|
|
383
|
+
tintIconColor = Colors.black_17,
|
|
384
|
+
backgroundButton = Colors.black_01.copy(alpha = 0.6f),
|
|
385
|
+
borderColor = Colors.black_20.copy(alpha = 0.2f)
|
|
386
|
+
)
|
|
387
|
+
else
|
|
388
|
+
HeaderColor(
|
|
389
|
+
tintIconColor = Colors.black_01,
|
|
390
|
+
backgroundButton = Colors.black_20.copy(alpha = 0.6f),
|
|
391
|
+
borderColor = Color.Transparent
|
|
392
|
+
)
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
if (tintColor == Colors.black_01)
|
|
396
|
+
HeaderColor(
|
|
397
|
+
tintIconColor = tintColor,
|
|
398
|
+
backgroundButton = Colors.black_20.copy(alpha = 0.6f),
|
|
399
|
+
borderColor = Colors.black_01.copy(alpha = 0.2f)
|
|
400
|
+
)
|
|
401
|
+
else
|
|
402
|
+
HeaderColor(
|
|
403
|
+
tintIconColor = tintColor ?: defaultColor,
|
|
404
|
+
backgroundButton = Colors.black_01.copy(alpha = 0.6f),
|
|
405
|
+
borderColor = Colors.black_20.copy(alpha = 0.2f)
|
|
406
|
+
)
|
|
407
|
+
}
|
|
408
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
package vn.momo.kits.navigation.component
|
|
2
|
+
|
|
3
|
+
import androidx.compose.animation.core.animateFloatAsState
|
|
4
|
+
import androidx.compose.foundation.background
|
|
5
|
+
import androidx.compose.foundation.layout.Box
|
|
6
|
+
import androidx.compose.foundation.layout.fillMaxHeight
|
|
7
|
+
import androidx.compose.foundation.layout.fillMaxSize
|
|
8
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
9
|
+
import androidx.compose.foundation.layout.height
|
|
10
|
+
import androidx.compose.runtime.Composable
|
|
11
|
+
import androidx.compose.runtime.getValue
|
|
12
|
+
import androidx.compose.ui.Modifier
|
|
13
|
+
import androidx.compose.ui.draw.clipToBounds
|
|
14
|
+
import androidx.compose.ui.graphics.graphicsLayer
|
|
15
|
+
import androidx.compose.ui.graphics.Brush
|
|
16
|
+
import androidx.compose.ui.graphics.Color
|
|
17
|
+
import androidx.compose.ui.platform.LocalDensity
|
|
18
|
+
import androidx.compose.ui.unit.dp
|
|
19
|
+
import vn.momo.kits.components.Image
|
|
20
|
+
import vn.momo.kits.const.AppTheme
|
|
21
|
+
import vn.momo.kits.layout.getScreenLayout
|
|
22
|
+
import vn.momo.kits.modifier.conditional
|
|
23
|
+
import vn.momo.kits.navigation.LocalOptions
|
|
24
|
+
import vn.momo.kits.navigation.LocalScrollState
|
|
25
|
+
import vn.momo.kits.navigation.getInputSearchType
|
|
26
|
+
import vn.momo.kits.navigation.usesDuoGlassHeader
|
|
27
|
+
|
|
28
|
+
enum class AnimatedHeaderRatio(val value: Float){
|
|
29
|
+
RATIO_16_9(16f / 9f),
|
|
30
|
+
RATIO_1_1(1f),
|
|
31
|
+
RATIO_3_2(3f / 2f)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
internal val HEADER_BACKGROUND_HEIGHT = 154.dp
|
|
35
|
+
|
|
36
|
+
// RN DuoScrollGradient: extended gradient runs this far below the header
|
|
37
|
+
private val DUO_GRADIENT_EXTRA = 52.dp
|
|
38
|
+
|
|
39
|
+
@Suppress("FrequentlyChangingValue")
|
|
40
|
+
@Composable
|
|
41
|
+
internal fun HeaderBackground() {
|
|
42
|
+
val density = LocalDensity.current
|
|
43
|
+
val theme = AppTheme.current
|
|
44
|
+
val options = LocalOptions.current
|
|
45
|
+
val scrollState = LocalScrollState.current
|
|
46
|
+
val backgroundColor = options.backgroundColor ?: AppTheme.current.colors.background.default
|
|
47
|
+
|
|
48
|
+
val layout = getScreenLayout()
|
|
49
|
+
val minHeight = layout.headerHeight
|
|
50
|
+
val maxHeight = HEADER_BACKGROUND_HEIGHT
|
|
51
|
+
val opacity by animateFloatAsState(
|
|
52
|
+
targetValue = (1 - (scrollState.value * 1f / HEADER_HEIGHT * 1f)).coerceIn(0f, 1f),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
val color = if (opacity == 0f) backgroundColor else Color(0xFFFDCADE)
|
|
56
|
+
val height = when (options.headerType) {
|
|
57
|
+
is HeaderType.Default -> minHeight
|
|
58
|
+
is HeaderType.Extended -> {
|
|
59
|
+
if (AppTheme.current.isHeaderImage()){
|
|
60
|
+
if (opacity == 0f) minHeight else maxHeight
|
|
61
|
+
} else {
|
|
62
|
+
maxHeight
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else -> 0.dp
|
|
66
|
+
}
|
|
67
|
+
|
|
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
|
|
89
|
+
Box(Modifier
|
|
90
|
+
.fillMaxWidth()
|
|
91
|
+
.then(if (fillsBox && !theme.isHeaderImage()) Modifier.fillMaxHeight() else Modifier.height(maxHeight))
|
|
92
|
+
.conditional(!theme.isHeaderImage()) {
|
|
93
|
+
background(Brush.verticalGradient(colors = listOf(color, backgroundColor)))
|
|
94
|
+
}
|
|
95
|
+
){
|
|
96
|
+
if (!theme.isHeaderImage()) return@Box
|
|
97
|
+
Image(
|
|
98
|
+
source = theme.assets.headerBackground!!,
|
|
99
|
+
modifier = Modifier.fillMaxSize(),
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
package vn.momo.kits.navigation.component
|
|
2
|
+
|
|
3
|
+
import androidx.compose.foundation.layout.Arrangement
|
|
4
|
+
import androidx.compose.foundation.layout.Box
|
|
5
|
+
import androidx.compose.foundation.layout.Column
|
|
6
|
+
import androidx.compose.foundation.layout.Row
|
|
7
|
+
import androidx.compose.foundation.layout.Spacer
|
|
8
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
9
|
+
import androidx.compose.foundation.layout.height
|
|
10
|
+
import androidx.compose.foundation.layout.width
|
|
11
|
+
import androidx.compose.foundation.layout.widthIn
|
|
12
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
13
|
+
import androidx.compose.runtime.Composable
|
|
14
|
+
import androidx.compose.ui.Alignment
|
|
15
|
+
import androidx.compose.ui.Modifier
|
|
16
|
+
import androidx.compose.ui.draw.clip
|
|
17
|
+
import androidx.compose.ui.graphics.Color
|
|
18
|
+
import androidx.compose.ui.text.style.TextOverflow
|
|
19
|
+
import androidx.compose.ui.unit.dp
|
|
20
|
+
import vn.momo.kits.components.Icon
|
|
21
|
+
import vn.momo.kits.components.Skeleton
|
|
22
|
+
import vn.momo.kits.components.Text
|
|
23
|
+
import vn.momo.kits.const.Spacing
|
|
24
|
+
import vn.momo.kits.const.Typography
|
|
25
|
+
import vn.momo.kits.modifier.activeOpacityClickable
|
|
26
|
+
|
|
27
|
+
@Composable
|
|
28
|
+
internal fun HeaderJourney(
|
|
29
|
+
data: HeaderTitle,
|
|
30
|
+
tintIconColor: Color,
|
|
31
|
+
) {
|
|
32
|
+
if (data !is HeaderTitle.Journey) return
|
|
33
|
+
if (data.isLoading) return TitleJourneyShimmer()
|
|
34
|
+
|
|
35
|
+
val maxWidth = headerTitleMaxWidth()
|
|
36
|
+
val tint = parseHeaderTint(data.tintColor) ?: tintIconColor
|
|
37
|
+
val iconTint = parseHeaderTint(data.iconColor) ?: tint
|
|
38
|
+
|
|
39
|
+
Column(
|
|
40
|
+
modifier = Modifier
|
|
41
|
+
.widthIn(max = maxWidth)
|
|
42
|
+
.activeOpacityClickable(enabled = data.onPress != null, onClick = { data.onPress?.invoke() }),
|
|
43
|
+
verticalArrangement = Arrangement.Center,
|
|
44
|
+
) {
|
|
45
|
+
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
46
|
+
Text(
|
|
47
|
+
text = data.start,
|
|
48
|
+
color = tint,
|
|
49
|
+
style = Typography.actionXsBold,
|
|
50
|
+
maxLines = 1,
|
|
51
|
+
overflow = TextOverflow.Ellipsis,
|
|
52
|
+
modifier = Modifier.weight(1f, fill = false),
|
|
53
|
+
)
|
|
54
|
+
Spacer(Modifier.width(Spacing.S))
|
|
55
|
+
if (data.icon.isNotBlank()) {
|
|
56
|
+
Icon(source = data.icon, size = 16.dp, color = iconTint)
|
|
57
|
+
} else {
|
|
58
|
+
Spacer(Modifier.width(16.dp))
|
|
59
|
+
}
|
|
60
|
+
Spacer(Modifier.width(Spacing.S))
|
|
61
|
+
if (!data.end.isNullOrEmpty()) {
|
|
62
|
+
Text(
|
|
63
|
+
text = data.end,
|
|
64
|
+
color = tint,
|
|
65
|
+
style = Typography.actionXsBold,
|
|
66
|
+
maxLines = 1,
|
|
67
|
+
overflow = TextOverflow.Ellipsis,
|
|
68
|
+
modifier = Modifier.weight(1f, fill = false),
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
Spacer(Modifier.width(Spacing.XS))
|
|
72
|
+
Icon(source = "16_arrow_chevron_down_small", size = 16.dp, color = tint)
|
|
73
|
+
}
|
|
74
|
+
if (!data.description.isNullOrEmpty()) {
|
|
75
|
+
Text(
|
|
76
|
+
text = data.description,
|
|
77
|
+
color = tint,
|
|
78
|
+
style = Typography.descriptionXsRegular,
|
|
79
|
+
maxLines = 1,
|
|
80
|
+
overflow = TextOverflow.Ellipsis,
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@Composable
|
|
87
|
+
private fun TitleJourneyShimmer() {
|
|
88
|
+
Column(modifier = Modifier.fillMaxWidth()) {
|
|
89
|
+
Box(modifier = Modifier.height(18.dp).width(120.dp).clip(RoundedCornerShape(8.dp))) { Skeleton() }
|
|
90
|
+
Spacer(Modifier.height(Spacing.XXS))
|
|
91
|
+
Box(modifier = Modifier.height(12.dp).width(120.dp).clip(RoundedCornerShape(8.dp))) { Skeleton() }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
internal fun parseHeaderTint(value: String?): Color? {
|
|
96
|
+
val raw = value?.trim()?.removePrefix("#") ?: return null
|
|
97
|
+
val hex = when (raw.length) {
|
|
98
|
+
3 -> "FF" + raw.map { "$it$it" }.joinToString("")
|
|
99
|
+
6 -> "FF$raw"
|
|
100
|
+
// CSS #RRGGBBAA, as RN passes it; Compose wants AARRGGBB
|
|
101
|
+
8 -> raw.substring(6) + raw.substring(0, 6)
|
|
102
|
+
else -> return null
|
|
103
|
+
}
|
|
104
|
+
val v = hex.toLongOrNull(16) ?: return null
|
|
105
|
+
return Color(v)
|
|
106
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
package vn.momo.kits.navigation.component
|
|
2
|
+
|
|
3
|
+
import androidx.compose.foundation.layout.Box
|
|
4
|
+
import androidx.compose.foundation.layout.Column
|
|
5
|
+
import androidx.compose.foundation.layout.Row
|
|
6
|
+
import androidx.compose.foundation.layout.Spacer
|
|
7
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
8
|
+
import androidx.compose.foundation.layout.height
|
|
9
|
+
import androidx.compose.foundation.layout.width
|
|
10
|
+
import androidx.compose.foundation.layout.widthIn
|
|
11
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
12
|
+
import androidx.compose.runtime.Composable
|
|
13
|
+
import androidx.compose.ui.Alignment
|
|
14
|
+
import androidx.compose.ui.Modifier
|
|
15
|
+
import androidx.compose.ui.draw.clip
|
|
16
|
+
import androidx.compose.ui.graphics.Color
|
|
17
|
+
import androidx.compose.ui.text.style.TextOverflow
|
|
18
|
+
import androidx.compose.ui.unit.dp
|
|
19
|
+
import vn.momo.kits.components.Icon
|
|
20
|
+
import vn.momo.kits.components.Skeleton
|
|
21
|
+
import vn.momo.kits.components.Text
|
|
22
|
+
import vn.momo.kits.const.Spacing
|
|
23
|
+
import vn.momo.kits.const.Typography
|
|
24
|
+
import vn.momo.kits.modifier.activeOpacityClickable
|
|
25
|
+
|
|
26
|
+
@Composable
|
|
27
|
+
internal fun HeaderLocation(
|
|
28
|
+
data: HeaderTitle,
|
|
29
|
+
tintIconColor: Color,
|
|
30
|
+
) {
|
|
31
|
+
if (data !is HeaderTitle.Location) return
|
|
32
|
+
if (data.isLoading) return TitleLocationShimmer()
|
|
33
|
+
|
|
34
|
+
val maxWidth = headerTitleMaxWidth()
|
|
35
|
+
val tint = parseHeaderTint(data.tintColor) ?: tintIconColor
|
|
36
|
+
|
|
37
|
+
Column(
|
|
38
|
+
modifier = Modifier
|
|
39
|
+
.widthIn(max = maxWidth)
|
|
40
|
+
.activeOpacityClickable(enabled = data.onPress != null, onClick = { data.onPress?.invoke() }),
|
|
41
|
+
horizontalAlignment = Alignment.Start,
|
|
42
|
+
) {
|
|
43
|
+
if (!data.description.isNullOrEmpty()) {
|
|
44
|
+
Text(
|
|
45
|
+
text = data.description,
|
|
46
|
+
color = tint,
|
|
47
|
+
style = Typography.descriptionXsRegular,
|
|
48
|
+
maxLines = 1,
|
|
49
|
+
overflow = TextOverflow.Ellipsis,
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
53
|
+
Text(
|
|
54
|
+
text = data.location,
|
|
55
|
+
color = tint,
|
|
56
|
+
style = Typography.actionXsBold,
|
|
57
|
+
maxLines = 1,
|
|
58
|
+
overflow = TextOverflow.Ellipsis,
|
|
59
|
+
modifier = Modifier.weight(1f, fill = false),
|
|
60
|
+
)
|
|
61
|
+
Spacer(Modifier.width(Spacing.XS))
|
|
62
|
+
Icon(source = "16_arrow_chevron_down_small", size = 16.dp, color = tint)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@Composable
|
|
68
|
+
private fun TitleLocationShimmer() {
|
|
69
|
+
Column(modifier = Modifier.fillMaxWidth()) {
|
|
70
|
+
Box(modifier = Modifier.height(12.dp).width(120.dp).clip(RoundedCornerShape(8.dp))) { Skeleton() }
|
|
71
|
+
Spacer(Modifier.height(Spacing.XXS))
|
|
72
|
+
Box(modifier = Modifier.height(18.dp).width(120.dp).clip(RoundedCornerShape(8.dp))) { Skeleton() }
|
|
73
|
+
}
|
|
74
|
+
}
|