@momo-kits/native-kits 0.152.4-beta.6 → 0.152.4-maxapi
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/CODE_OF_CONDUCT.md +133 -0
- package/CONTRIBUTING.md +114 -0
- package/LICENSE +20 -0
- package/README.md +7 -0
- package/build.gradle.kts +32 -0
- package/compose/MoMoComposeKits.podspec +54 -0
- package/compose/build.gradle.kts +149 -0
- package/compose/src/androidMain/AndroidManifest.xml +2 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +105 -0
- package/compose/src/commonMain/composeResources/files/lottie_circle_loader.json +1 -0
- package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
- package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +57 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +201 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +222 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +48 -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 +306 -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 +715 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +214 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +236 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +77 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +27 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +334 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +345 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +90 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +131 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +543 -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 +58 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +143 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +179 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +111 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +384 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +160 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +234 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +223 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +232 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +236 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +228 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +364 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +50 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +34 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +85 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +33 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +338 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +95 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +64 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +89 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +91 -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 +84 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +208 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +172 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +199 -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 +237 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +191 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +306 -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 +13 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +191 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +258 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +2 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +35 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +2 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +59 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +68 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +49 -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 +232 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +111 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +94 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +159 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +232 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScaleSizeScope.kt +17 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +459 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +169 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +216 -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 +180 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +251 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +80 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +306 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +31 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +385 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +38 -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 +62 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +88 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +144 -0
- package/gradle.properties +19 -0
- package/gradlew +240 -0
- package/gradlew.bat +91 -0
- package/ios/Application/ApplicationEnvironment.swift +50 -0
- package/ios/Application/Components.swift +263 -0
- package/ios/Application/ComposeApi.swift +22 -0
- package/ios/Application/FloatingButton.swift +172 -0
- package/ios/Application/HeaderRight.swift +271 -0
- package/ios/Application/Screen.swift +249 -0
- package/ios/Badge/BadgeDot.swift +31 -0
- package/ios/Button/Button.swift +211 -0
- package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +126 -0
- package/ios/Checkbox/Checkbox.swift +81 -0
- package/ios/Chip/Chip.swift +96 -0
- package/ios/Colors+Radius+Spacing/Colors.swift +172 -0
- package/ios/Colors+Radius+Spacing/Radius.swift +22 -0
- package/ios/Colors+Radius+Spacing/Spacing.swift +12 -0
- package/ios/Extensions/Color++.swift +25 -0
- package/ios/Icon/Icon.swift +51 -0
- package/ios/Image/Image.swift +70 -0
- package/ios/Input/Input.swift +207 -0
- package/ios/Input/InputPhoneNumber.swift +176 -0
- package/ios/Input/InputSearch.swift +238 -0
- package/ios/Input/InputTextArea.swift +242 -0
- package/ios/Lottie/LottieView.swift +86 -0
- package/ios/OTPKeyboard/KeyboardButton.swift +41 -0
- package/ios/OTPKeyboard/OTPKeyboard.swift +145 -0
- package/ios/Popup/PopupDisplay.swift +284 -0
- package/ios/Popup/PopupInput.swift +96 -0
- package/ios/Popup/PopupPromotion.swift +73 -0
- package/ios/PopupView/FullscreenPopup.swift +251 -0
- package/ios/PopupView/Modifiers.swift +158 -0
- package/ios/PopupView/PopupView.swift +289 -0
- package/ios/PopupView/Utils++.swift +281 -0
- package/ios/ScrollIndicator/ScrollIndicator.swift +110 -0
- package/ios/Swipeable/SwipeCell.swift +278 -0
- package/ios/Swipeable/SwipeCellModel.swift +86 -0
- package/ios/Switch/Switch.swift +44 -0
- package/ios/Template/Logo/Logo.swift +75 -0
- package/ios/Template/TrustBanner/TrustBanner.swift +120 -0
- package/ios/Theme.md +18 -0
- package/ios/Typography/Text.swift +140 -0
- package/ios/Typography/Typography.swift +95 -0
- package/ios/native-kits.podspec +18 -0
- package/package.json +6 -7
- package/settings.gradle.kts +25 -0
- package/shared/build.gradle.kts +0 -74
|
@@ -0,0 +1,251 @@
|
|
|
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.border
|
|
6
|
+
import androidx.compose.foundation.layout.Arrangement
|
|
7
|
+
import androidx.compose.foundation.layout.Box
|
|
8
|
+
import androidx.compose.foundation.layout.Row
|
|
9
|
+
import androidx.compose.foundation.layout.RowScope
|
|
10
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
11
|
+
import androidx.compose.foundation.layout.height
|
|
12
|
+
import androidx.compose.foundation.layout.offset
|
|
13
|
+
import androidx.compose.foundation.layout.padding
|
|
14
|
+
import androidx.compose.foundation.layout.size
|
|
15
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
16
|
+
import androidx.compose.runtime.Composable
|
|
17
|
+
import androidx.compose.runtime.getValue
|
|
18
|
+
import androidx.compose.ui.Alignment
|
|
19
|
+
import androidx.compose.ui.Modifier
|
|
20
|
+
import androidx.compose.ui.graphics.Brush
|
|
21
|
+
import androidx.compose.ui.graphics.Color
|
|
22
|
+
import androidx.compose.ui.layout.onGloballyPositioned
|
|
23
|
+
import androidx.compose.ui.platform.LocalDensity
|
|
24
|
+
import androidx.compose.ui.text.style.TextAlign
|
|
25
|
+
import androidx.compose.ui.unit.Dp
|
|
26
|
+
import androidx.compose.ui.unit.dp
|
|
27
|
+
import vn.momo.kits.components.Icon
|
|
28
|
+
import vn.momo.kits.components.InputSearchProps
|
|
29
|
+
import vn.momo.kits.const.AppStatusBar
|
|
30
|
+
import vn.momo.kits.const.AppTheme
|
|
31
|
+
import vn.momo.kits.const.Colors
|
|
32
|
+
import vn.momo.kits.const.Spacing
|
|
33
|
+
import vn.momo.kits.modifier.activeOpacityClickable
|
|
34
|
+
import vn.momo.kits.navigation.LocalHeaderRightWidthPx
|
|
35
|
+
import vn.momo.kits.navigation.LocalNavigator
|
|
36
|
+
import vn.momo.kits.navigation.LocalOptions
|
|
37
|
+
import vn.momo.kits.navigation.LocalScrollState
|
|
38
|
+
import vn.momo.kits.navigation.getInputSearchType
|
|
39
|
+
|
|
40
|
+
const val HEADER_HEIGHT = 52
|
|
41
|
+
enum class InputSearchType { None, Header, Animated }
|
|
42
|
+
|
|
43
|
+
@Composable
|
|
44
|
+
fun Header() {
|
|
45
|
+
val options = LocalOptions.current
|
|
46
|
+
val navigator = LocalNavigator.current
|
|
47
|
+
val scrollState = LocalScrollState.current
|
|
48
|
+
val headerRightWidthPx = LocalHeaderRightWidthPx.current
|
|
49
|
+
val inputSearchType = getInputSearchType(options)
|
|
50
|
+
|
|
51
|
+
val opacityHeight = when (val header = options.headerType) {
|
|
52
|
+
is HeaderType.Animated -> with(LocalDensity.current) { header.layoutOffSet.roundToPx() }
|
|
53
|
+
else -> HEADER_HEIGHT
|
|
54
|
+
}
|
|
55
|
+
val opacity by animateFloatAsState(
|
|
56
|
+
targetValue = ((scrollState.value * 1f / opacityHeight * 1f)).coerceIn(0f, 1f),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
val headerColor = getHeaderColor(options.headerType, opacity, options.tintColor, Colors.black_17)
|
|
60
|
+
|
|
61
|
+
val animatedAlpha by animateFloatAsState(targetValue = opacity, label = "BackgroundAlpha")
|
|
62
|
+
|
|
63
|
+
if (options.headerType == HeaderType.None) return
|
|
64
|
+
Box(
|
|
65
|
+
Modifier.height(AppStatusBar.current + HEADER_HEIGHT.dp)
|
|
66
|
+
.fillMaxWidth()
|
|
67
|
+
.background(AppTheme.current.colors.background.surface.copy(alpha = animatedAlpha)),
|
|
68
|
+
contentAlignment = Alignment.BottomCenter
|
|
69
|
+
) {
|
|
70
|
+
Row(
|
|
71
|
+
modifier = Modifier.height(HEADER_HEIGHT.dp)
|
|
72
|
+
.fillMaxWidth()
|
|
73
|
+
.padding(horizontal = Spacing.M),
|
|
74
|
+
verticalAlignment = Alignment.CenterVertically,
|
|
75
|
+
horizontalArrangement = Arrangement.SpaceBetween
|
|
76
|
+
) {
|
|
77
|
+
if(!options.hiddenBack) {
|
|
78
|
+
BackButton(
|
|
79
|
+
borderColor = headerColor.borderColor,
|
|
80
|
+
backgroundButton = headerColor.backgroundButton,
|
|
81
|
+
tintIconColor = headerColor.tintIconColor,
|
|
82
|
+
onBackHandler = {
|
|
83
|
+
options.onBackHandler?.invoke() ?: navigator.pop()
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
HeaderContent(
|
|
89
|
+
options.headerTitle,
|
|
90
|
+
headerColor.tintIconColor
|
|
91
|
+
.copy(alpha = if (inputSearchType == InputSearchType.Animated) 1f - animatedAlpha else 1f)
|
|
92
|
+
)
|
|
93
|
+
Box(Modifier.onGloballyPositioned {
|
|
94
|
+
if(headerRightWidthPx.intValue != it.size.width) headerRightWidthPx.intValue = it.size.width
|
|
95
|
+
}){
|
|
96
|
+
HeaderRight(options.headerRight, options.tintColor, headerColor)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
VerticalShadow(opacity)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@Composable
|
|
104
|
+
fun BackButton(borderColor: Color, backgroundButton: Color, tintIconColor: Color, onBackHandler: () -> Unit){
|
|
105
|
+
Box(
|
|
106
|
+
modifier = Modifier
|
|
107
|
+
.size(28.dp)
|
|
108
|
+
.background(backgroundButton, RoundedCornerShape(100))
|
|
109
|
+
.border(width = 0.2.dp, color = borderColor, shape = RoundedCornerShape(100))
|
|
110
|
+
.activeOpacityClickable(onClick = onBackHandler)
|
|
111
|
+
.padding(Spacing.XS),
|
|
112
|
+
contentAlignment = Alignment.Center
|
|
113
|
+
) {
|
|
114
|
+
Icon(
|
|
115
|
+
source = "arrow-back",
|
|
116
|
+
color = tintIconColor,
|
|
117
|
+
size = 20.dp,
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@Composable
|
|
123
|
+
fun RowScope.HeaderContent(headerTitle: HeaderTitle, tintIconColor: Color){
|
|
124
|
+
Box(
|
|
125
|
+
Modifier.weight(1f).padding(horizontal = Spacing.M)
|
|
126
|
+
) {
|
|
127
|
+
when (headerTitle){
|
|
128
|
+
is HeaderTitle.Default -> {
|
|
129
|
+
HeaderTitle(
|
|
130
|
+
title = headerTitle.title,
|
|
131
|
+
color = tintIconColor
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
is HeaderTitle.Journey -> {}
|
|
135
|
+
is HeaderTitle.Location -> {}
|
|
136
|
+
is HeaderTitle.User -> {
|
|
137
|
+
HeaderUser(
|
|
138
|
+
data = headerTitle
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@Composable
|
|
146
|
+
fun VerticalShadow(opacity: Float){
|
|
147
|
+
if(opacity == 1f){
|
|
148
|
+
Box(modifier = Modifier
|
|
149
|
+
.fillMaxWidth()
|
|
150
|
+
.height(6.dp)
|
|
151
|
+
.offset(x = 0.dp, y = 6.dp)
|
|
152
|
+
.background(
|
|
153
|
+
brush = Brush.verticalGradient(
|
|
154
|
+
colors = listOf(Color.Black.copy(alpha = 0.05f), Color.Transparent)
|
|
155
|
+
)
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
sealed class HeaderTitle {
|
|
162
|
+
class Default(val title: String) : HeaderTitle()
|
|
163
|
+
|
|
164
|
+
class User(
|
|
165
|
+
val title: String,
|
|
166
|
+
val subTitle: String? = null,
|
|
167
|
+
val image: List<String>? = null,
|
|
168
|
+
val dotColor: Color? = null,
|
|
169
|
+
val tintColor: Color? = null,
|
|
170
|
+
val onPress: (() -> Unit)? = null,
|
|
171
|
+
val icons: List<String> = emptyList(),
|
|
172
|
+
val isLoading: Boolean = false
|
|
173
|
+
) : HeaderTitle()
|
|
174
|
+
|
|
175
|
+
class Location(
|
|
176
|
+
val description: String? = null,
|
|
177
|
+
val location: String,
|
|
178
|
+
val tintColor: String? = null,
|
|
179
|
+
val onPress: (() -> Unit)? = null,
|
|
180
|
+
val isLoading: Boolean = false
|
|
181
|
+
) : HeaderTitle()
|
|
182
|
+
|
|
183
|
+
class Journey(
|
|
184
|
+
val start: String,
|
|
185
|
+
val end: String? = null,
|
|
186
|
+
val description: String? = null,
|
|
187
|
+
val icon: String,
|
|
188
|
+
val iconColor: String? = null,
|
|
189
|
+
val tintColor: String? = null,
|
|
190
|
+
val onPress: (() -> Unit)? = null,
|
|
191
|
+
val isLoading: Boolean = false
|
|
192
|
+
) : HeaderTitle()
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
sealed class HeaderType {
|
|
196
|
+
interface DefaultOrExtended {
|
|
197
|
+
val useAnimated: Boolean
|
|
198
|
+
val inputSearchProps: InputSearchProps?
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
data class Default(
|
|
202
|
+
override val useAnimated: Boolean = false,
|
|
203
|
+
override val inputSearchProps: InputSearchProps? = null
|
|
204
|
+
) : HeaderType(), DefaultOrExtended
|
|
205
|
+
|
|
206
|
+
data class Extended(
|
|
207
|
+
override val useAnimated: Boolean = false,
|
|
208
|
+
override val inputSearchProps: InputSearchProps? = null
|
|
209
|
+
) : HeaderType(), DefaultOrExtended
|
|
210
|
+
|
|
211
|
+
data object None : HeaderType()
|
|
212
|
+
|
|
213
|
+
data class Animated(
|
|
214
|
+
val aspectRatio: AnimatedHeaderRatio = AnimatedHeaderRatio.RATIO_16_9,
|
|
215
|
+
val isSurface: Boolean = true,
|
|
216
|
+
val layoutOffSet: Dp = 56.dp,
|
|
217
|
+
val composable: @Composable (scrollState: Int) -> Unit = {}
|
|
218
|
+
) : HeaderType()
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
data class HeaderColor(val tintIconColor: Color, val backgroundButton: Color, val borderColor: Color)
|
|
222
|
+
fun getHeaderColor(headerType: HeaderType, opacity: Float, tintColor: Color?, defaultColor: Color): HeaderColor{
|
|
223
|
+
return if(headerType is HeaderType.Animated) {
|
|
224
|
+
if (opacity == 1f || !headerType.isSurface)
|
|
225
|
+
HeaderColor(
|
|
226
|
+
tintIconColor = Colors.black_17,
|
|
227
|
+
backgroundButton = Colors.black_01.copy(alpha = 0.6f),
|
|
228
|
+
borderColor = Colors.black_20.copy(alpha = 0.2f)
|
|
229
|
+
)
|
|
230
|
+
else
|
|
231
|
+
HeaderColor(
|
|
232
|
+
tintIconColor = Colors.black_01,
|
|
233
|
+
backgroundButton = Colors.black_20.copy(alpha = 0.6f),
|
|
234
|
+
borderColor = Color.Transparent
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
if (tintColor == Colors.black_01)
|
|
239
|
+
HeaderColor(
|
|
240
|
+
tintIconColor = tintColor,
|
|
241
|
+
backgroundButton = Colors.black_20.copy(alpha = 0.6f),
|
|
242
|
+
borderColor = Colors.black_01.copy(alpha = 0.2f)
|
|
243
|
+
)
|
|
244
|
+
else
|
|
245
|
+
HeaderColor(
|
|
246
|
+
tintIconColor = tintColor ?: defaultColor,
|
|
247
|
+
backgroundButton = Colors.black_01.copy(alpha = 0.6f),
|
|
248
|
+
borderColor = Colors.black_20.copy(alpha = 0.2f)
|
|
249
|
+
)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
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
|
+
@Composable
|
|
31
|
+
fun HeaderBackground() {
|
|
32
|
+
val density = LocalDensity.current
|
|
33
|
+
val theme = AppTheme.current
|
|
34
|
+
val options = LocalOptions.current
|
|
35
|
+
val scrollState = LocalScrollState.current
|
|
36
|
+
val backgroundColor = options.backgroundColor ?: AppTheme.current.colors.background.default
|
|
37
|
+
|
|
38
|
+
val minHeight = AppStatusBar.current + HEADER_HEIGHT.dp
|
|
39
|
+
val maxHeight = 154.dp
|
|
40
|
+
val opacity by animateFloatAsState(
|
|
41
|
+
targetValue = (1 - (scrollState.value * 1f / HEADER_HEIGHT * 1f)).coerceIn(0f, 1f),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
val color = if (opacity == 0f) backgroundColor else Color(0xFFFDCADE)
|
|
45
|
+
val height = when (options.headerType) {
|
|
46
|
+
is HeaderType.Default -> minHeight
|
|
47
|
+
is HeaderType.Extended -> {
|
|
48
|
+
if (AppTheme.current.isHeaderImage()){
|
|
49
|
+
if (opacity == 0f) minHeight else maxHeight
|
|
50
|
+
} else {
|
|
51
|
+
maxHeight
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else -> 0.dp
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
Box(Modifier.fillMaxWidth().height(height).background(backgroundColor)) {
|
|
58
|
+
Box(Modifier
|
|
59
|
+
.height(maxHeight)
|
|
60
|
+
.fillMaxWidth()
|
|
61
|
+
.conditional(!theme.isHeaderImage()) {
|
|
62
|
+
background(Brush.linearGradient(
|
|
63
|
+
colors = listOf(
|
|
64
|
+
color,
|
|
65
|
+
backgroundColor
|
|
66
|
+
),
|
|
67
|
+
start = Offset(0f, 0f),
|
|
68
|
+
end = Offset(0f, with(density) { maxHeight.toPx() })
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
){
|
|
73
|
+
if (!theme.isHeaderImage()) return@Box
|
|
74
|
+
Image(
|
|
75
|
+
source = theme.assets.headerBackground!!,
|
|
76
|
+
modifier = Modifier.fillMaxSize(),
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
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.Row
|
|
8
|
+
import androidx.compose.foundation.layout.height
|
|
9
|
+
import androidx.compose.foundation.layout.offset
|
|
10
|
+
import androidx.compose.foundation.layout.padding
|
|
11
|
+
import androidx.compose.foundation.layout.size
|
|
12
|
+
import androidx.compose.foundation.layout.width
|
|
13
|
+
import androidx.compose.foundation.shape.CircleShape
|
|
14
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
15
|
+
import androidx.compose.runtime.Composable
|
|
16
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
17
|
+
import androidx.compose.runtime.derivedStateOf
|
|
18
|
+
import androidx.compose.runtime.getValue
|
|
19
|
+
import androidx.compose.runtime.mutableStateOf
|
|
20
|
+
import androidx.compose.runtime.remember
|
|
21
|
+
import androidx.compose.runtime.setValue
|
|
22
|
+
import androidx.compose.ui.Alignment
|
|
23
|
+
import androidx.compose.ui.Modifier
|
|
24
|
+
import androidx.compose.ui.draw.clip
|
|
25
|
+
import androidx.compose.ui.graphics.Color
|
|
26
|
+
import androidx.compose.ui.unit.dp
|
|
27
|
+
import vn.momo.kits.application.ApplicationContext
|
|
28
|
+
import vn.momo.kits.application.MiniAppContext
|
|
29
|
+
import vn.momo.kits.application.Spacing
|
|
30
|
+
import vn.momo.kits.components.BadgeDot
|
|
31
|
+
import vn.momo.kits.components.DotSize
|
|
32
|
+
import vn.momo.kits.components.Icon
|
|
33
|
+
import vn.momo.kits.const.Colors
|
|
34
|
+
import vn.momo.kits.modifier.activeOpacityClickable
|
|
35
|
+
import vn.momo.kits.navigation.LocalMaxApi
|
|
36
|
+
|
|
37
|
+
@Composable
|
|
38
|
+
fun HeaderRight(
|
|
39
|
+
headerRight: HeaderRight,
|
|
40
|
+
tintColor: Color? = null,
|
|
41
|
+
headerColor: HeaderColor
|
|
42
|
+
) {
|
|
43
|
+
when (headerRight) {
|
|
44
|
+
is HeaderRight.None -> {}
|
|
45
|
+
is HeaderRight.Custom -> {
|
|
46
|
+
headerRight.content()
|
|
47
|
+
}
|
|
48
|
+
is HeaderRight.OnBoarding -> {}
|
|
49
|
+
is HeaderRight.Toolkit -> {
|
|
50
|
+
Toolkit(
|
|
51
|
+
headerRight = headerRight,
|
|
52
|
+
tintColor = tintColor,
|
|
53
|
+
headerColor = headerColor
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@Composable
|
|
60
|
+
fun Toolkit(
|
|
61
|
+
headerRight: HeaderRight.Toolkit,
|
|
62
|
+
tintColor: Color? = null,
|
|
63
|
+
headerColor: HeaderColor
|
|
64
|
+
) {
|
|
65
|
+
val api = LocalMaxApi.current
|
|
66
|
+
val context = ApplicationContext.current
|
|
67
|
+
|
|
68
|
+
var isFavorite by remember { mutableStateOf(false) }
|
|
69
|
+
val isLoading by remember { mutableStateOf(false) }
|
|
70
|
+
|
|
71
|
+
LaunchedEffect(context?.appCode) {
|
|
72
|
+
api?.isFavoriteApp(mapOf("code" to context?.appCode)) { callback ->
|
|
73
|
+
val response = callback?.get("response") as? Boolean
|
|
74
|
+
isFavorite = response == true
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
fun onPressShortcut() {
|
|
79
|
+
api?.onToolAction(mapOf("item" to mapOf("key" to "onFavorite"), "context" to MiniAppContext.toMap(context))) { callback ->
|
|
80
|
+
val response = callback?.get("response") as? Map<*, *>?
|
|
81
|
+
val success = response?.get("success") as? Boolean
|
|
82
|
+
if (success == true){
|
|
83
|
+
isFavorite = !isFavorite
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fun onPressMore() {
|
|
89
|
+
api?.showTools(
|
|
90
|
+
mapOf(
|
|
91
|
+
"useSystemTools" to headerRight.useSystemTools,
|
|
92
|
+
"tools" to headerRight.tools.map { it.toMap() },
|
|
93
|
+
"context" to MiniAppContext.toMap(context)
|
|
94
|
+
)
|
|
95
|
+
) { callback ->
|
|
96
|
+
val response = callback?.get("response") as? String
|
|
97
|
+
if (response != null) {
|
|
98
|
+
headerRight.toolCallback?.invoke(response)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
val navButtonConfig = getNavigationButtonConfig(
|
|
104
|
+
headerRight,
|
|
105
|
+
::onPressShortcut,
|
|
106
|
+
::onPressMore
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
val showBadge = headerRight.tools.any { group ->
|
|
110
|
+
group.items.any { it.showBadge }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
val isShowShortcut by remember(headerRight.useShortcut, headerRight.useMore, context) {
|
|
114
|
+
derivedStateOf {
|
|
115
|
+
(headerRight.useShortcut == true) &&
|
|
116
|
+
!(headerRight.useMore == true && context == null)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
val icon by remember(navButtonConfig.icon, isFavorite) {
|
|
121
|
+
derivedStateOf {
|
|
122
|
+
navButtonConfig.icon.takeUnless { it == "star" }
|
|
123
|
+
?: if (isFavorite) "pin_star_checked" else "pin_star"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
Row(
|
|
128
|
+
verticalAlignment = Alignment.CenterVertically
|
|
129
|
+
) {
|
|
130
|
+
if (isShowShortcut) {
|
|
131
|
+
NavigationButton(
|
|
132
|
+
disabled = isLoading,
|
|
133
|
+
icon = icon,
|
|
134
|
+
showBadge = showBadge,
|
|
135
|
+
onClick = navButtonConfig.onPress,
|
|
136
|
+
headerColor = headerColor
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
Row(
|
|
141
|
+
verticalAlignment = Alignment.CenterVertically,
|
|
142
|
+
horizontalArrangement = Arrangement.Center,
|
|
143
|
+
modifier = Modifier
|
|
144
|
+
.padding(start = Spacing.S)
|
|
145
|
+
.border(0.2.dp, headerColor.borderColor, shape = RoundedCornerShape(14.dp))
|
|
146
|
+
.height(28.dp)
|
|
147
|
+
.clip(shape = RoundedCornerShape(14.dp))
|
|
148
|
+
.background(headerColor.backgroundButton)
|
|
149
|
+
) {
|
|
150
|
+
if (context != null) {
|
|
151
|
+
Icon(
|
|
152
|
+
source = "help_center",
|
|
153
|
+
size = 20.dp,
|
|
154
|
+
color = headerColor.tintIconColor,
|
|
155
|
+
modifier = Modifier.padding(4.dp).activeOpacityClickable {
|
|
156
|
+
api?.showHelpCenter(
|
|
157
|
+
mapOf(
|
|
158
|
+
"appId" to context.appId,
|
|
159
|
+
"code" to context.appCode,
|
|
160
|
+
"name" to context.appName,
|
|
161
|
+
"icon" to context.appIcon,
|
|
162
|
+
"description" to context.description
|
|
163
|
+
)
|
|
164
|
+
) {}
|
|
165
|
+
}
|
|
166
|
+
)
|
|
167
|
+
Box(
|
|
168
|
+
modifier = Modifier
|
|
169
|
+
.width(0.5.dp)
|
|
170
|
+
.height(12.dp)
|
|
171
|
+
.background(tintColor ?: Colors.black_20)
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
Icon(
|
|
175
|
+
source = "16_basic_home",
|
|
176
|
+
size = 20.dp,
|
|
177
|
+
color = headerColor.tintIconColor,
|
|
178
|
+
modifier = Modifier.padding(4.dp).activeOpacityClickable {
|
|
179
|
+
api?.dismissAll { }
|
|
180
|
+
}
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@Composable
|
|
189
|
+
fun NavigationButton(
|
|
190
|
+
disabled: Boolean,
|
|
191
|
+
icon: String,
|
|
192
|
+
showBadge: Boolean? = false,
|
|
193
|
+
onClick: () -> Unit,
|
|
194
|
+
headerColor: HeaderColor
|
|
195
|
+
) {
|
|
196
|
+
Box(
|
|
197
|
+
modifier = Modifier
|
|
198
|
+
.size(28.dp)
|
|
199
|
+
.activeOpacityClickable(enabled = !disabled, onClick = onClick)
|
|
200
|
+
) {
|
|
201
|
+
Box(
|
|
202
|
+
modifier = Modifier
|
|
203
|
+
.matchParentSize()
|
|
204
|
+
.clip(CircleShape)
|
|
205
|
+
.background(headerColor.backgroundButton)
|
|
206
|
+
.border(0.2.dp, headerColor.borderColor, CircleShape)
|
|
207
|
+
)
|
|
208
|
+
Box(
|
|
209
|
+
modifier = Modifier.matchParentSize(),
|
|
210
|
+
contentAlignment = Alignment.Center
|
|
211
|
+
) {
|
|
212
|
+
Icon(
|
|
213
|
+
source = icon,
|
|
214
|
+
size = 20.dp,
|
|
215
|
+
color = headerColor.tintIconColor
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (showBadge == true) {
|
|
220
|
+
BadgeDot(
|
|
221
|
+
size = DotSize.Small,
|
|
222
|
+
modifier = Modifier
|
|
223
|
+
.align(Alignment.TopEnd)
|
|
224
|
+
.offset(x = -Spacing.XXS, y = -Spacing.XXS)
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private fun getNavigationButtonConfig(
|
|
231
|
+
headerRight: HeaderRight.Toolkit,
|
|
232
|
+
onPressShortcut: () -> Unit,
|
|
233
|
+
onPressMore: () -> Unit
|
|
234
|
+
): NavigationButtonConfig {
|
|
235
|
+
val totalTools = headerRight.tools.sumOf { it.items.size }
|
|
236
|
+
val config = NavigationButtonConfig(icon = "star", onPress = onPressShortcut)
|
|
237
|
+
return if (totalTools > 1 || headerRight.useMore == true) {
|
|
238
|
+
NavigationButtonConfig(
|
|
239
|
+
icon = "navigation_more_icon",
|
|
240
|
+
onPress = onPressMore
|
|
241
|
+
)
|
|
242
|
+
} else if (totalTools == 1 && headerRight.tools.isNotEmpty()) {
|
|
243
|
+
val singleTool = headerRight.tools.first().items.firstOrNull()
|
|
244
|
+
return if (singleTool != null){
|
|
245
|
+
NavigationButtonConfig(
|
|
246
|
+
icon = singleTool.icon,
|
|
247
|
+
onPress = {
|
|
248
|
+
headerRight.toolCallback?.invoke(singleTool.key)
|
|
249
|
+
}
|
|
250
|
+
)
|
|
251
|
+
} else {
|
|
252
|
+
config
|
|
253
|
+
}
|
|
254
|
+
} else {
|
|
255
|
+
config
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
sealed interface HeaderRight {
|
|
260
|
+
data object None : HeaderRight
|
|
261
|
+
data class Custom(
|
|
262
|
+
val content: @Composable () -> Unit
|
|
263
|
+
) : HeaderRight
|
|
264
|
+
data object OnBoarding : HeaderRight
|
|
265
|
+
data class Toolkit(
|
|
266
|
+
val useShortcut: Boolean = false,
|
|
267
|
+
val useMore: Boolean = false,
|
|
268
|
+
val useSystemTools: Boolean = true,
|
|
269
|
+
val tools: List<ToolGroup> = emptyList(),
|
|
270
|
+
val toolCallback: ((String) -> Unit)? = { _ -> }
|
|
271
|
+
) : HeaderRight
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
data class ToolGroup(
|
|
275
|
+
val title: Map<String, String> = emptyMap(),
|
|
276
|
+
val items: List<Tool>
|
|
277
|
+
) {
|
|
278
|
+
fun toMap(): Map<String, Any> {
|
|
279
|
+
return mapOf(
|
|
280
|
+
"title" to title,
|
|
281
|
+
"items" to items.map { it.toMap() }
|
|
282
|
+
)
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
data class Tool(
|
|
286
|
+
val key: String,
|
|
287
|
+
val icon: String,
|
|
288
|
+
val showBadge: Boolean = false,
|
|
289
|
+
val name: Map<String, String> = emptyMap(),
|
|
290
|
+
val showRightIcon: Boolean = true,
|
|
291
|
+
) {
|
|
292
|
+
fun toMap(): Map<String, Any> {
|
|
293
|
+
return mapOf(
|
|
294
|
+
"key" to key,
|
|
295
|
+
"icon" to icon,
|
|
296
|
+
"showBadge" to showBadge,
|
|
297
|
+
"name" to name,
|
|
298
|
+
"showRightIcon" to showRightIcon
|
|
299
|
+
)
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
data class NavigationButtonConfig(
|
|
304
|
+
val icon: String,
|
|
305
|
+
val onPress: () -> Unit
|
|
306
|
+
)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package vn.momo.kits.navigation.component
|
|
2
|
+
|
|
3
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
4
|
+
import androidx.compose.runtime.Composable
|
|
5
|
+
import androidx.compose.ui.Modifier
|
|
6
|
+
import androidx.compose.ui.graphics.Color
|
|
7
|
+
import androidx.compose.ui.text.style.TextAlign
|
|
8
|
+
import androidx.compose.ui.text.style.TextOverflow
|
|
9
|
+
import androidx.compose.ui.unit.sp
|
|
10
|
+
import androidx.compose.ui.zIndex
|
|
11
|
+
import vn.momo.kits.components.Text
|
|
12
|
+
import vn.momo.kits.const.Typography
|
|
13
|
+
|
|
14
|
+
@Composable
|
|
15
|
+
fun HeaderTitle(
|
|
16
|
+
title: String = "",
|
|
17
|
+
color: Color? = null,
|
|
18
|
+
) {
|
|
19
|
+
Text(
|
|
20
|
+
modifier = Modifier.fillMaxWidth().zIndex(1f),
|
|
21
|
+
text = title,
|
|
22
|
+
textAlign = TextAlign.Start,
|
|
23
|
+
style = Typography.actionSBold.copy(
|
|
24
|
+
fontSize = 15.sp,
|
|
25
|
+
lineHeight = 22.sp,
|
|
26
|
+
),
|
|
27
|
+
color = color,
|
|
28
|
+
maxLines = 1,
|
|
29
|
+
overflow = TextOverflow.Ellipsis
|
|
30
|
+
)
|
|
31
|
+
}
|