@momo-kits/native-kits 0.164.1-beta.4 → 0.164.1-beta.5-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/settings.local.json +185 -0
- package/build.gradle.kts +11 -0
- package/compose/build.gradle.kts +190 -0
- package/compose/build.gradle.kts.backup +190 -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 +150 -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 +106 -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 +826 -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 +82 -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 +356 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +113 -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 +23 -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 +134 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +475 -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 +214 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +240 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +216 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +259 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +182 -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 +328 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +97 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +310 -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 +323 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +217 -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 +199 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +91 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +470 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +88 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +131 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +203 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +498 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +175 -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 +234 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +188 -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/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 +263 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +52 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +38 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +39 -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 +299 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +129 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +103 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +191 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +385 -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 +599 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +157 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +39 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +156 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +225 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -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 +320 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +81 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +309 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +34 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +360 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +132 -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 +15 -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 +69 -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 +105 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +40 -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 +35 -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 +225 -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/native-kits.podspec +1 -1
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
- 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,169 @@
|
|
|
1
|
+
package vn.momo.kits.navigation.component
|
|
2
|
+
|
|
3
|
+
import androidx.compose.animation.*
|
|
4
|
+
import androidx.compose.animation.core.CubicBezierEasing
|
|
5
|
+
import androidx.compose.animation.core.LinearEasing
|
|
6
|
+
import androidx.compose.animation.core.animateDpAsState
|
|
7
|
+
import androidx.compose.animation.core.tween
|
|
8
|
+
import androidx.compose.foundation.ScrollState
|
|
9
|
+
import androidx.compose.foundation.background
|
|
10
|
+
import androidx.compose.foundation.border
|
|
11
|
+
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
12
|
+
import androidx.compose.foundation.layout.*
|
|
13
|
+
import androidx.compose.material3.AlertDialogDefaults
|
|
14
|
+
import androidx.compose.material3.FloatingActionButton
|
|
15
|
+
import androidx.compose.material3.FloatingActionButtonDefaults
|
|
16
|
+
import androidx.compose.material3.FloatingActionButtonElevation
|
|
17
|
+
import androidx.compose.runtime.*
|
|
18
|
+
import androidx.compose.runtime.saveable.rememberSaveable
|
|
19
|
+
import androidx.compose.ui.Alignment
|
|
20
|
+
import androidx.compose.ui.Modifier
|
|
21
|
+
import androidx.compose.ui.graphics.Color
|
|
22
|
+
import androidx.compose.ui.unit.Dp
|
|
23
|
+
import androidx.compose.ui.unit.dp
|
|
24
|
+
import kotlinx.coroutines.delay
|
|
25
|
+
import kotlinx.coroutines.flow.MutableStateFlow
|
|
26
|
+
import kotlinx.coroutines.flow.collectLatest
|
|
27
|
+
import kotlinx.coroutines.launch
|
|
28
|
+
import vn.momo.kits.application.IsShowBaseLineDebug
|
|
29
|
+
import vn.momo.kits.components.Icon
|
|
30
|
+
import vn.momo.kits.components.Text
|
|
31
|
+
import vn.momo.kits.const.Colors
|
|
32
|
+
import vn.momo.kits.const.Typography
|
|
33
|
+
import vn.momo.kits.modifier.conditional
|
|
34
|
+
import kotlin.time.Duration.Companion.milliseconds
|
|
35
|
+
|
|
36
|
+
enum class FABSize { SMALL, DEFAULT }
|
|
37
|
+
enum class FABPosition { END, CENTER }
|
|
38
|
+
|
|
39
|
+
data class FloatingButtonProps(
|
|
40
|
+
val icon: String,
|
|
41
|
+
val iconColor: Color? = null,
|
|
42
|
+
val label: String? = null,
|
|
43
|
+
val onClick: () -> Unit,
|
|
44
|
+
val size: FABSize = FABSize.DEFAULT,
|
|
45
|
+
val bottom: Dp? = null,
|
|
46
|
+
val scrollState: ScrollState? = null,
|
|
47
|
+
val position: FABPosition? = FABPosition.END,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
private val FabSmallSize = 36.dp
|
|
51
|
+
private val FabDefaultSize = 48.dp
|
|
52
|
+
private val FabExpandedWidth = 80.dp
|
|
53
|
+
private val FabIconSizeSmall = 12.dp
|
|
54
|
+
private val FabIconSizeDefault = 24.dp
|
|
55
|
+
private val FabPaddingHorizontal = 12.dp
|
|
56
|
+
|
|
57
|
+
@Composable
|
|
58
|
+
internal fun FloatingButton(
|
|
59
|
+
scrollPosition: Int,
|
|
60
|
+
bottom: Dp,
|
|
61
|
+
onClick: () -> Unit,
|
|
62
|
+
containerColor: Color,
|
|
63
|
+
contentColor: Color = containerColor,
|
|
64
|
+
icon: String,
|
|
65
|
+
iconColor: Color? = null,
|
|
66
|
+
text: String? = null,
|
|
67
|
+
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(4.dp),
|
|
68
|
+
size: FABSize = FABSize.DEFAULT,
|
|
69
|
+
position: FABPosition = FABPosition.END,
|
|
70
|
+
keyboardSize: Dp = 0.dp,
|
|
71
|
+
) {
|
|
72
|
+
val scrollStateFlow = remember { MutableStateFlow(scrollPosition) }
|
|
73
|
+
var lastScrollPosition by rememberSaveable { mutableStateOf(0) }
|
|
74
|
+
var isExpanded by rememberSaveable { mutableStateOf(false) }
|
|
75
|
+
|
|
76
|
+
val coroutineScope = rememberCoroutineScope()
|
|
77
|
+
|
|
78
|
+
// Animations
|
|
79
|
+
val baseSize = if (size == FABSize.SMALL) FabSmallSize else FabDefaultSize
|
|
80
|
+
val width by animateDpAsState(
|
|
81
|
+
targetValue = if (isExpanded && text != null) FabExpandedWidth else baseSize,
|
|
82
|
+
animationSpec = tween(100, easing = CubicBezierEasing(0.2f, 0.2f, 0.2f, 0.2f))
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
// Scroll listener
|
|
86
|
+
LaunchedEffect(text) {
|
|
87
|
+
if (text != null) {
|
|
88
|
+
coroutineScope.launch {
|
|
89
|
+
scrollStateFlow.collectLatest { newScroll ->
|
|
90
|
+
isExpanded = newScroll <= lastScrollPosition
|
|
91
|
+
delay(100.milliseconds)
|
|
92
|
+
lastScrollPosition = newScroll
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
LaunchedEffect(scrollPosition) {
|
|
99
|
+
scrollStateFlow.value = scrollPosition
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
Box(
|
|
103
|
+
modifier = Modifier
|
|
104
|
+
.fillMaxSize()
|
|
105
|
+
.padding(end = 12.dp, bottom = bottom)
|
|
106
|
+
.offset(y = -keyboardSize)
|
|
107
|
+
.background(Color.Transparent),
|
|
108
|
+
contentAlignment = if (position == FABPosition.END) Alignment.BottomEnd else Alignment.BottomCenter,
|
|
109
|
+
) {
|
|
110
|
+
FloatingActionButton(
|
|
111
|
+
onClick = onClick,
|
|
112
|
+
shape = AlertDialogDefaults.shape,
|
|
113
|
+
containerColor = containerColor,
|
|
114
|
+
contentColor = contentColor,
|
|
115
|
+
elevation = elevation,
|
|
116
|
+
interactionSource = remember { MutableInteractionSource() },
|
|
117
|
+
modifier = Modifier
|
|
118
|
+
.sizeIn(minWidth = width)
|
|
119
|
+
.height(baseSize)
|
|
120
|
+
.conditional(IsShowBaseLineDebug) {
|
|
121
|
+
border(1.dp, Colors.blue_03)
|
|
122
|
+
}
|
|
123
|
+
) {
|
|
124
|
+
Row(
|
|
125
|
+
modifier = Modifier.padding(
|
|
126
|
+
start = if (isExpanded) FabPaddingHorizontal else 0.dp,
|
|
127
|
+
end = if (isExpanded) FabPaddingHorizontal else 0.dp,
|
|
128
|
+
),
|
|
129
|
+
verticalAlignment = Alignment.CenterVertically,
|
|
130
|
+
horizontalArrangement = if (isExpanded) Arrangement.Start else Arrangement.Center
|
|
131
|
+
) {
|
|
132
|
+
Icon(
|
|
133
|
+
source = icon,
|
|
134
|
+
size = if (size == FABSize.SMALL) FabIconSizeSmall else FabIconSizeDefault,
|
|
135
|
+
color = iconColor ?: Color.White
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
AnimatedVisibility(
|
|
139
|
+
visible = isExpanded,
|
|
140
|
+
enter = fadeInExpandAnimation,
|
|
141
|
+
exit = fadeOutShrinkAnimation,
|
|
142
|
+
) {
|
|
143
|
+
Row {
|
|
144
|
+
Spacer(Modifier.width(12.dp))
|
|
145
|
+
Text(
|
|
146
|
+
text = text ?: "",
|
|
147
|
+
color = Color.White,
|
|
148
|
+
style = Typography.actionDefaultBold
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private val fadeOutShrinkAnimation = fadeOut(
|
|
158
|
+
animationSpec = tween(100, easing = LinearEasing)
|
|
159
|
+
) + shrinkHorizontally(
|
|
160
|
+
animationSpec = tween(100, easing = LinearEasing),
|
|
161
|
+
shrinkTowards = Alignment.Start
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
private val fadeInExpandAnimation = fadeIn(
|
|
165
|
+
animationSpec = tween(100, easing = LinearEasing)
|
|
166
|
+
) + expandHorizontally(
|
|
167
|
+
animationSpec = tween(100, easing = LinearEasing),
|
|
168
|
+
expandFrom = Alignment.Start
|
|
169
|
+
)
|
|
@@ -0,0 +1,320 @@
|
|
|
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.shape.RoundedCornerShape
|
|
19
|
+
import androidx.compose.runtime.Composable
|
|
20
|
+
import androidx.compose.runtime.getValue
|
|
21
|
+
import androidx.compose.ui.Alignment
|
|
22
|
+
import androidx.compose.ui.Modifier
|
|
23
|
+
import androidx.compose.ui.graphics.Brush
|
|
24
|
+
import androidx.compose.ui.graphics.Color
|
|
25
|
+
import androidx.compose.ui.layout.onGloballyPositioned
|
|
26
|
+
import androidx.compose.ui.platform.LocalDensity
|
|
27
|
+
import androidx.compose.ui.text.style.TextAlign
|
|
28
|
+
import androidx.compose.ui.unit.Dp
|
|
29
|
+
import androidx.compose.ui.unit.dp
|
|
30
|
+
import vn.momo.kits.components.Icon
|
|
31
|
+
import vn.momo.kits.components.InputSearchProps
|
|
32
|
+
import vn.momo.kits.const.AppStatusBar
|
|
33
|
+
import vn.momo.kits.const.AppTheme
|
|
34
|
+
import vn.momo.kits.const.Colors
|
|
35
|
+
import vn.momo.kits.components.PopupNotifyProps
|
|
36
|
+
import vn.momo.kits.application.IsShowBaseLineDebug
|
|
37
|
+
import vn.momo.kits.const.Spacing
|
|
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.LocalHeaderRightWidthPx
|
|
42
|
+
import vn.momo.kits.navigation.LocalNavigator
|
|
43
|
+
import vn.momo.kits.navigation.LocalOptions
|
|
44
|
+
import vn.momo.kits.navigation.LocalScrollState
|
|
45
|
+
import vn.momo.kits.navigation.getInputSearchType
|
|
46
|
+
|
|
47
|
+
@InternalApi
|
|
48
|
+
const val HEADER_HEIGHT = 52
|
|
49
|
+
internal enum class InputSearchType { None, Header, Animated }
|
|
50
|
+
enum class TitlePosition { LEFT, CENTER }
|
|
51
|
+
|
|
52
|
+
@Suppress("FrequentlyChangingValue")
|
|
53
|
+
@Composable
|
|
54
|
+
internal fun Header(onBackHandler: (() -> Unit)? = null) {
|
|
55
|
+
val options = LocalOptions.current
|
|
56
|
+
val navigator = LocalNavigator.current
|
|
57
|
+
val scrollState = LocalScrollState.current
|
|
58
|
+
val headerRightWidthPx = LocalHeaderRightWidthPx.current
|
|
59
|
+
val inputSearchType = getInputSearchType(options)
|
|
60
|
+
|
|
61
|
+
val opacityHeight = when (val header = options.headerType) {
|
|
62
|
+
is HeaderType.Animated -> with(LocalDensity.current) { header.layoutOffSet.roundToPx() }
|
|
63
|
+
else -> HEADER_HEIGHT
|
|
64
|
+
}
|
|
65
|
+
val opacity by animateFloatAsState(
|
|
66
|
+
targetValue = if (options.headerType is HeaderType.Transparent) {
|
|
67
|
+
0f
|
|
68
|
+
} else {
|
|
69
|
+
(scrollState.value / opacityHeight.toFloat())
|
|
70
|
+
.coerceIn(0f, 1f)
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
val headerColor = getHeaderColor(options.headerType, opacity, options.tintColor, Colors.black_17)
|
|
75
|
+
|
|
76
|
+
val animatedAlpha by animateFloatAsState(targetValue = opacity, label = "BackgroundAlpha")
|
|
77
|
+
|
|
78
|
+
val background = if (options.headerType is HeaderType.Transparent)
|
|
79
|
+
options.backgroundColor ?: AppTheme.current.colors.background.surface
|
|
80
|
+
else
|
|
81
|
+
AppTheme.current.colors.background.surface
|
|
82
|
+
|
|
83
|
+
if (options.headerType == HeaderType.None) return
|
|
84
|
+
|
|
85
|
+
val titlePosition = options.titlePosition
|
|
86
|
+
val titleColor = headerColor.tintIconColor
|
|
87
|
+
.copy(alpha = if (inputSearchType == InputSearchType.Animated) 1f - animatedAlpha else 1f)
|
|
88
|
+
// Centered title needs a full-width overlay to stay screen-centered, not boxed between back button and header right.
|
|
89
|
+
val centerTitle = titlePosition == TitlePosition.CENTER && options.headerTitle is HeaderTitle.Default
|
|
90
|
+
|
|
91
|
+
Box(
|
|
92
|
+
Modifier.height(AppStatusBar.current + HEADER_HEIGHT.dp)
|
|
93
|
+
.fillMaxWidth()
|
|
94
|
+
.background(background.copy(alpha = animatedAlpha))
|
|
95
|
+
.conditional(IsShowBaseLineDebug) {
|
|
96
|
+
border(1.dp, Colors.blue_03)
|
|
97
|
+
},
|
|
98
|
+
contentAlignment = Alignment.BottomCenter
|
|
99
|
+
) {
|
|
100
|
+
Row(
|
|
101
|
+
modifier = Modifier.height(HEADER_HEIGHT.dp)
|
|
102
|
+
.fillMaxWidth()
|
|
103
|
+
.padding(horizontal = Spacing.M),
|
|
104
|
+
verticalAlignment = Alignment.CenterVertically,
|
|
105
|
+
horizontalArrangement = Arrangement.SpaceBetween
|
|
106
|
+
) {
|
|
107
|
+
if(!options.hiddenBack) {
|
|
108
|
+
BackButton(
|
|
109
|
+
borderColor = headerColor.borderColor,
|
|
110
|
+
backgroundButton = headerColor.backgroundButton,
|
|
111
|
+
tintIconColor = headerColor.tintIconColor,
|
|
112
|
+
onBackHandler = {
|
|
113
|
+
navigator.onBackSafe { }
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
Spacer(Modifier.width(Spacing.S))
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (centerTitle) {
|
|
120
|
+
Spacer(Modifier.weight(1f))
|
|
121
|
+
} else {
|
|
122
|
+
HeaderContent(
|
|
123
|
+
headerTitle = options.headerTitle,
|
|
124
|
+
tintIconColor = titleColor,
|
|
125
|
+
titlePosition = titlePosition,
|
|
126
|
+
modifier = Modifier.weight(1f)
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
Box(Modifier.onGloballyPositioned {
|
|
130
|
+
if(headerRightWidthPx.intValue != it.size.width) headerRightWidthPx.intValue = it.size.width
|
|
131
|
+
}){
|
|
132
|
+
HeaderRight(options.headerRight, options.tintColor, headerColor)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (centerTitle) {
|
|
136
|
+
Box(
|
|
137
|
+
modifier = Modifier.height(HEADER_HEIGHT.dp)
|
|
138
|
+
.fillMaxWidth()
|
|
139
|
+
.padding(horizontal = Spacing.M),
|
|
140
|
+
contentAlignment = Alignment.Center
|
|
141
|
+
) {
|
|
142
|
+
HeaderContent(
|
|
143
|
+
headerTitle = options.headerTitle,
|
|
144
|
+
tintIconColor = titleColor,
|
|
145
|
+
titlePosition = titlePosition,
|
|
146
|
+
modifier = Modifier.fillMaxWidth()
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
VerticalShadow(opacity)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
@Composable
|
|
155
|
+
private fun BackButton(borderColor: Color, backgroundButton: Color, tintIconColor: Color, onBackHandler: () -> Unit){
|
|
156
|
+
Box(
|
|
157
|
+
modifier = Modifier
|
|
158
|
+
.size(28.dp)
|
|
159
|
+
.background(backgroundButton, RoundedCornerShape(100))
|
|
160
|
+
.border(width = 0.2.dp, color = borderColor, shape = RoundedCornerShape(100))
|
|
161
|
+
.activeOpacityClickable(onClick = onBackHandler)
|
|
162
|
+
.padding(Spacing.XS)
|
|
163
|
+
.setAutomationId("btn_navigation_back"),
|
|
164
|
+
contentAlignment = Alignment.Center
|
|
165
|
+
) {
|
|
166
|
+
Icon(
|
|
167
|
+
source = "arrow-back",
|
|
168
|
+
color = tintIconColor,
|
|
169
|
+
size = 20.dp,
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@Composable
|
|
175
|
+
internal fun HeaderContent(
|
|
176
|
+
headerTitle: HeaderTitle,
|
|
177
|
+
tintIconColor: Color,
|
|
178
|
+
titlePosition: TitlePosition = TitlePosition.LEFT,
|
|
179
|
+
modifier: Modifier = Modifier,
|
|
180
|
+
){
|
|
181
|
+
Box(
|
|
182
|
+
modifier = modifier,
|
|
183
|
+
contentAlignment = if (titlePosition == TitlePosition.CENTER) Alignment.Center else Alignment.CenterStart
|
|
184
|
+
) {
|
|
185
|
+
when (headerTitle){
|
|
186
|
+
is HeaderTitle.Default -> {
|
|
187
|
+
HeaderTitle(
|
|
188
|
+
title = headerTitle.title,
|
|
189
|
+
color = tintIconColor,
|
|
190
|
+
modifier = Modifier.fillMaxWidth(fraction = if (titlePosition == TitlePosition.CENTER) 0.5f else 1f),
|
|
191
|
+
textAlign = if (titlePosition == TitlePosition.CENTER) TextAlign.Center else TextAlign.Start
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
is HeaderTitle.Journey -> {}
|
|
195
|
+
is HeaderTitle.Location -> {}
|
|
196
|
+
is HeaderTitle.User -> {
|
|
197
|
+
HeaderUser(
|
|
198
|
+
data = headerTitle
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
@Composable
|
|
206
|
+
internal fun VerticalShadow(opacity: Float){
|
|
207
|
+
if(opacity == 1f){
|
|
208
|
+
Box(modifier = Modifier
|
|
209
|
+
.fillMaxWidth()
|
|
210
|
+
.height(6.dp)
|
|
211
|
+
.offset(x = 0.dp, y = 6.dp)
|
|
212
|
+
.background(
|
|
213
|
+
brush = Brush.verticalGradient(
|
|
214
|
+
colors = listOf(Color.Black.copy(alpha = 0.05f), Color.Transparent)
|
|
215
|
+
)
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
sealed class HeaderTitle {
|
|
222
|
+
class Default(val title: String) : HeaderTitle()
|
|
223
|
+
|
|
224
|
+
class User(
|
|
225
|
+
val title: String,
|
|
226
|
+
val subTitle: String? = null,
|
|
227
|
+
val image: List<String>? = null,
|
|
228
|
+
val dotColor: Color? = null,
|
|
229
|
+
val tintColor: Color? = null,
|
|
230
|
+
val onPress: (() -> Unit)? = null,
|
|
231
|
+
val icons: List<String> = emptyList(),
|
|
232
|
+
val isLoading: Boolean = false
|
|
233
|
+
) : HeaderTitle()
|
|
234
|
+
|
|
235
|
+
class Location(
|
|
236
|
+
val description: String? = null,
|
|
237
|
+
val location: String,
|
|
238
|
+
val tintColor: String? = null,
|
|
239
|
+
val onPress: (() -> Unit)? = null,
|
|
240
|
+
val isLoading: Boolean = false
|
|
241
|
+
) : HeaderTitle()
|
|
242
|
+
|
|
243
|
+
class Journey(
|
|
244
|
+
val start: String,
|
|
245
|
+
val end: String? = null,
|
|
246
|
+
val description: String? = null,
|
|
247
|
+
val icon: String,
|
|
248
|
+
val iconColor: String? = null,
|
|
249
|
+
val tintColor: String? = null,
|
|
250
|
+
val onPress: (() -> Unit)? = null,
|
|
251
|
+
val isLoading: Boolean = false
|
|
252
|
+
) : HeaderTitle()
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
sealed class HeaderType {
|
|
256
|
+
interface DefaultOrExtended {
|
|
257
|
+
val useAnimated: Boolean
|
|
258
|
+
val inputSearchProps: InputSearchProps?
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
data class Default(
|
|
262
|
+
override val useAnimated: Boolean = false,
|
|
263
|
+
override val inputSearchProps: InputSearchProps? = null
|
|
264
|
+
) : HeaderType(), DefaultOrExtended
|
|
265
|
+
|
|
266
|
+
data class Extended(
|
|
267
|
+
override val useAnimated: Boolean = false,
|
|
268
|
+
override val inputSearchProps: InputSearchProps? = null
|
|
269
|
+
) : HeaderType(), DefaultOrExtended
|
|
270
|
+
|
|
271
|
+
data object None : HeaderType()
|
|
272
|
+
|
|
273
|
+
data class Animated(
|
|
274
|
+
val aspectRatio: AnimatedHeaderRatio = AnimatedHeaderRatio.RATIO_16_9,
|
|
275
|
+
val isSurface: Boolean = true,
|
|
276
|
+
val layoutOffSet: Dp = 56.dp,
|
|
277
|
+
val composable: @Composable (scrollState: Int) -> Unit = {}
|
|
278
|
+
) : HeaderType()
|
|
279
|
+
|
|
280
|
+
data class Transparent(
|
|
281
|
+
val isFullScreenContent: Boolean = false
|
|
282
|
+
) : HeaderType()
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
data class HeaderBackProps(
|
|
286
|
+
val preventBack: PopupNotifyProps? = null,
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
@InternalApi
|
|
290
|
+
data class HeaderColor(val tintIconColor: Color, val backgroundButton: Color, val borderColor: Color)
|
|
291
|
+
internal fun getHeaderColor(headerType: HeaderType, opacity: Float, tintColor: Color?, defaultColor: Color): HeaderColor{
|
|
292
|
+
return if(headerType is HeaderType.Animated) {
|
|
293
|
+
if (opacity == 1f || !headerType.isSurface)
|
|
294
|
+
HeaderColor(
|
|
295
|
+
tintIconColor = Colors.black_17,
|
|
296
|
+
backgroundButton = Colors.black_01.copy(alpha = 0.6f),
|
|
297
|
+
borderColor = Colors.black_20.copy(alpha = 0.2f)
|
|
298
|
+
)
|
|
299
|
+
else
|
|
300
|
+
HeaderColor(
|
|
301
|
+
tintIconColor = Colors.black_01,
|
|
302
|
+
backgroundButton = Colors.black_20.copy(alpha = 0.6f),
|
|
303
|
+
borderColor = Color.Transparent
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
if (tintColor == Colors.black_01)
|
|
308
|
+
HeaderColor(
|
|
309
|
+
tintIconColor = tintColor,
|
|
310
|
+
backgroundButton = Colors.black_20.copy(alpha = 0.6f),
|
|
311
|
+
borderColor = Colors.black_01.copy(alpha = 0.2f)
|
|
312
|
+
)
|
|
313
|
+
else
|
|
314
|
+
HeaderColor(
|
|
315
|
+
tintIconColor = tintColor ?: defaultColor,
|
|
316
|
+
backgroundButton = Colors.black_01.copy(alpha = 0.6f),
|
|
317
|
+
borderColor = Colors.black_20.copy(alpha = 0.2f)
|
|
318
|
+
)
|
|
319
|
+
}
|
|
320
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
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.fillMaxSize
|
|
7
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
8
|
+
import androidx.compose.foundation.layout.height
|
|
9
|
+
import androidx.compose.runtime.Composable
|
|
10
|
+
import androidx.compose.runtime.getValue
|
|
11
|
+
import androidx.compose.ui.Modifier
|
|
12
|
+
import androidx.compose.ui.geometry.Offset
|
|
13
|
+
import androidx.compose.ui.graphics.Brush
|
|
14
|
+
import androidx.compose.ui.graphics.Color
|
|
15
|
+
import androidx.compose.ui.platform.LocalDensity
|
|
16
|
+
import androidx.compose.ui.unit.dp
|
|
17
|
+
import vn.momo.kits.components.Image
|
|
18
|
+
import vn.momo.kits.const.AppStatusBar
|
|
19
|
+
import vn.momo.kits.const.AppTheme
|
|
20
|
+
import vn.momo.kits.modifier.conditional
|
|
21
|
+
import vn.momo.kits.navigation.LocalOptions
|
|
22
|
+
import vn.momo.kits.navigation.LocalScrollState
|
|
23
|
+
|
|
24
|
+
enum class AnimatedHeaderRatio(val value: Float){
|
|
25
|
+
RATIO_16_9(16f / 9f),
|
|
26
|
+
RATIO_1_1(1f),
|
|
27
|
+
RATIO_3_2(3f / 2f)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Suppress("FrequentlyChangingValue")
|
|
31
|
+
@Composable
|
|
32
|
+
internal fun HeaderBackground() {
|
|
33
|
+
val density = LocalDensity.current
|
|
34
|
+
val theme = AppTheme.current
|
|
35
|
+
val options = LocalOptions.current
|
|
36
|
+
val scrollState = LocalScrollState.current
|
|
37
|
+
val backgroundColor = options.backgroundColor ?: AppTheme.current.colors.background.default
|
|
38
|
+
|
|
39
|
+
val minHeight = AppStatusBar.current + HEADER_HEIGHT.dp
|
|
40
|
+
val maxHeight = 154.dp
|
|
41
|
+
val opacity by animateFloatAsState(
|
|
42
|
+
targetValue = (1 - (scrollState.value * 1f / HEADER_HEIGHT * 1f)).coerceIn(0f, 1f),
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
val color = if (opacity == 0f) backgroundColor else Color(0xFFFDCADE)
|
|
46
|
+
val height = when (options.headerType) {
|
|
47
|
+
is HeaderType.Default -> minHeight
|
|
48
|
+
is HeaderType.Extended -> {
|
|
49
|
+
if (AppTheme.current.isHeaderImage()){
|
|
50
|
+
if (opacity == 0f) minHeight else maxHeight
|
|
51
|
+
} else {
|
|
52
|
+
maxHeight
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else -> 0.dp
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
Box(Modifier.fillMaxWidth().height(height).background(backgroundColor)) {
|
|
59
|
+
Box(Modifier
|
|
60
|
+
.height(maxHeight)
|
|
61
|
+
.fillMaxWidth()
|
|
62
|
+
.conditional(!theme.isHeaderImage()) {
|
|
63
|
+
background(Brush.linearGradient(
|
|
64
|
+
colors = listOf(
|
|
65
|
+
color,
|
|
66
|
+
backgroundColor
|
|
67
|
+
),
|
|
68
|
+
start = Offset(0f, 0f),
|
|
69
|
+
end = Offset(0f, with(density) { maxHeight.toPx() })
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
){
|
|
74
|
+
if (!theme.isHeaderImage()) return@Box
|
|
75
|
+
Image(
|
|
76
|
+
source = theme.assets.headerBackground!!,
|
|
77
|
+
modifier = Modifier.fillMaxSize(),
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|