@momo-kits/native-kits 0.152.4-beta.6 → 0.152.4-beta.7
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 +392 -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 +302 -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 +484 -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/navigation/component/SnackBar.kt +123 -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,345 @@
|
|
|
1
|
+
package vn.momo.kits.components
|
|
2
|
+
|
|
3
|
+
import androidx.compose.animation.core.animateDpAsState
|
|
4
|
+
import androidx.compose.animation.core.animateFloatAsState
|
|
5
|
+
import androidx.compose.animation.core.tween
|
|
6
|
+
import androidx.compose.foundation.background
|
|
7
|
+
import androidx.compose.foundation.border
|
|
8
|
+
import androidx.compose.foundation.clickable
|
|
9
|
+
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
10
|
+
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
|
11
|
+
import androidx.compose.foundation.layout.Arrangement
|
|
12
|
+
import androidx.compose.foundation.layout.Box
|
|
13
|
+
import androidx.compose.foundation.layout.Row
|
|
14
|
+
import androidx.compose.foundation.layout.defaultMinSize
|
|
15
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
16
|
+
import androidx.compose.foundation.layout.height
|
|
17
|
+
import androidx.compose.foundation.layout.padding
|
|
18
|
+
import androidx.compose.foundation.layout.size
|
|
19
|
+
import androidx.compose.foundation.layout.width
|
|
20
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
21
|
+
import androidx.compose.runtime.Composable
|
|
22
|
+
import androidx.compose.runtime.CompositionLocalProvider
|
|
23
|
+
import androidx.compose.runtime.getValue
|
|
24
|
+
import androidx.compose.runtime.remember
|
|
25
|
+
import androidx.compose.runtime.staticCompositionLocalOf
|
|
26
|
+
import androidx.compose.ui.Alignment
|
|
27
|
+
import androidx.compose.ui.Modifier
|
|
28
|
+
import androidx.compose.ui.draw.alpha
|
|
29
|
+
import androidx.compose.ui.draw.clip
|
|
30
|
+
import androidx.compose.ui.graphics.Color
|
|
31
|
+
import androidx.compose.ui.graphics.graphicsLayer
|
|
32
|
+
import androidx.compose.ui.text.TextStyle
|
|
33
|
+
import androidx.compose.ui.text.style.TextOverflow
|
|
34
|
+
import androidx.compose.ui.unit.Dp
|
|
35
|
+
import androidx.compose.ui.unit.dp
|
|
36
|
+
import vn.momo.kits.const.AppTheme
|
|
37
|
+
import vn.momo.kits.const.Colors
|
|
38
|
+
import vn.momo.kits.const.Radius
|
|
39
|
+
import vn.momo.kits.const.Spacing
|
|
40
|
+
import vn.momo.kits.const.Typography
|
|
41
|
+
import vn.momo.kits.platform.LottieAnimation
|
|
42
|
+
|
|
43
|
+
enum class ButtonType {
|
|
44
|
+
PRIMARY,
|
|
45
|
+
SECONDARY,
|
|
46
|
+
TONAL,
|
|
47
|
+
OUTLINE,
|
|
48
|
+
DANGER,
|
|
49
|
+
TEXT,
|
|
50
|
+
DISABLED
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
data class ButtonSpecs(
|
|
54
|
+
val height: Dp,
|
|
55
|
+
val radius: Dp,
|
|
56
|
+
val padding: Dp,
|
|
57
|
+
val width: Dp,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
enum class Size(val value: ButtonSpecs) {
|
|
61
|
+
LARGE(
|
|
62
|
+
ButtonSpecs(
|
|
63
|
+
height = 48.dp,
|
|
64
|
+
radius = Radius.S,
|
|
65
|
+
padding = Spacing.L,
|
|
66
|
+
width = 128.dp
|
|
67
|
+
)
|
|
68
|
+
),
|
|
69
|
+
MEDIUM(
|
|
70
|
+
ButtonSpecs(
|
|
71
|
+
height = 36.dp,
|
|
72
|
+
radius = Radius.S,
|
|
73
|
+
padding = Spacing.M,
|
|
74
|
+
width = 80.dp
|
|
75
|
+
)
|
|
76
|
+
),
|
|
77
|
+
SMALL(
|
|
78
|
+
ButtonSpecs(
|
|
79
|
+
height = 28.dp,
|
|
80
|
+
radius = Radius.S,
|
|
81
|
+
padding = Spacing.S,
|
|
82
|
+
width = 60.dp
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private val styleCache = mapOf(
|
|
88
|
+
Size.LARGE to Typography.actionDefaultBold,
|
|
89
|
+
Size.MEDIUM to Typography.actionSBold,
|
|
90
|
+
Size.SMALL to Typography.actionXsBold
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
private val iconSizeCache = mapOf(
|
|
94
|
+
Size.LARGE to 24.dp,
|
|
95
|
+
Size.MEDIUM to 16.dp,
|
|
96
|
+
Size.SMALL to 16.dp
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
private val iconSpaceCache = mapOf(
|
|
100
|
+
Size.SMALL to Spacing.XS,
|
|
101
|
+
Size.MEDIUM to Spacing.S,
|
|
102
|
+
Size.LARGE to Spacing.S
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
fun getStyle(size: Size): TextStyle {
|
|
106
|
+
return styleCache[size] ?: Typography.actionDefaultBold
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
fun getIconSize(size: Size): Dp = iconSizeCache[size] ?: 24.dp
|
|
110
|
+
fun getIconSpace(size: Size): Dp = iconSpaceCache[size] ?: Spacing.S
|
|
111
|
+
|
|
112
|
+
@Composable
|
|
113
|
+
fun getTextColor(loading: Boolean, type: ButtonType): Color {
|
|
114
|
+
val theme = AppTheme.current
|
|
115
|
+
|
|
116
|
+
return remember(type, theme, loading) {
|
|
117
|
+
when (type) {
|
|
118
|
+
ButtonType.DISABLED -> theme.colors.text.disable
|
|
119
|
+
ButtonType.PRIMARY -> Colors.black_01
|
|
120
|
+
ButtonType.SECONDARY -> theme.colors.text.default
|
|
121
|
+
ButtonType.OUTLINE -> theme.colors.primary
|
|
122
|
+
ButtonType.TONAL -> theme.colors.primary
|
|
123
|
+
ButtonType.DANGER -> Colors.black_01
|
|
124
|
+
ButtonType.TEXT -> theme.colors.primary
|
|
125
|
+
}.withLoading(loading)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@Composable
|
|
130
|
+
fun RenderTitle(size: Size, title: String = "", type: ButtonType) {
|
|
131
|
+
val style = remember(size) { getStyle(size) }
|
|
132
|
+
val color = TextColor.current
|
|
133
|
+
Text(
|
|
134
|
+
style = style,
|
|
135
|
+
text = title,
|
|
136
|
+
color = color,
|
|
137
|
+
overflow = TextOverflow.Ellipsis,
|
|
138
|
+
maxLines = 1
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@Composable
|
|
143
|
+
fun RenderIcon(
|
|
144
|
+
size: Size,
|
|
145
|
+
isIconLeft: Boolean,
|
|
146
|
+
useTintColor: Boolean = true,
|
|
147
|
+
icon: String = "",
|
|
148
|
+
forceLoading: Boolean = false
|
|
149
|
+
) {
|
|
150
|
+
val bgColor = BackgroundColor.current
|
|
151
|
+
val iconSize = remember(size) { getIconSize(size) }
|
|
152
|
+
val margin = remember(size) { getIconSpace(size) }
|
|
153
|
+
val color = if (useTintColor) TextColor.current else Color.Unspecified
|
|
154
|
+
|
|
155
|
+
val showLoading = forceLoading
|
|
156
|
+
|
|
157
|
+
val modifier = Modifier.padding(
|
|
158
|
+
start = if (isIconLeft) 0.dp else margin,
|
|
159
|
+
end = if (isIconLeft) margin else 0.dp
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
if (showLoading) {
|
|
163
|
+
Box(modifier) {
|
|
164
|
+
LottieAnimation(
|
|
165
|
+
modifier = Modifier.size(iconSize),
|
|
166
|
+
bgColor = bgColor,
|
|
167
|
+
tintColor = color,
|
|
168
|
+
path = "files/lottie_circle_loader"
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
} else if (icon.isNotEmpty()) {
|
|
172
|
+
Icon(
|
|
173
|
+
source = icon,
|
|
174
|
+
color = color,
|
|
175
|
+
size = iconSize,
|
|
176
|
+
modifier = modifier
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private fun shouldLoadingOnLeft(iconLeft: String, iconRight: String): Boolean {
|
|
182
|
+
val hasLeft = iconLeft.isNotEmpty()
|
|
183
|
+
val hasRight = iconRight.isNotEmpty()
|
|
184
|
+
return when {
|
|
185
|
+
!hasLeft && !hasRight -> true
|
|
186
|
+
hasLeft && !hasRight -> true
|
|
187
|
+
!hasLeft && hasRight -> false
|
|
188
|
+
hasLeft && hasRight -> false
|
|
189
|
+
else -> true
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
@Composable
|
|
194
|
+
fun getTypeStyle(
|
|
195
|
+
type: ButtonType,
|
|
196
|
+
color: Color? = AppTheme.current.colors.primary,
|
|
197
|
+
size: Size,
|
|
198
|
+
): Modifier {
|
|
199
|
+
val theme = AppTheme.current
|
|
200
|
+
val bgColor = BackgroundColor.current
|
|
201
|
+
val radius = remember(size) { size.value.radius }
|
|
202
|
+
val modifier = Modifier.background(bgColor)
|
|
203
|
+
|
|
204
|
+
return remember(type, color, theme, radius, bgColor) {
|
|
205
|
+
when (type) {
|
|
206
|
+
ButtonType.DISABLED -> modifier
|
|
207
|
+
.border(0.dp, Color.Unspecified, RoundedCornerShape(radius))
|
|
208
|
+
|
|
209
|
+
ButtonType.PRIMARY -> modifier
|
|
210
|
+
.border(0.dp, Color.Unspecified, RoundedCornerShape(radius))
|
|
211
|
+
|
|
212
|
+
ButtonType.SECONDARY -> modifier
|
|
213
|
+
.border(1.dp, theme.colors.border.default, RoundedCornerShape(radius))
|
|
214
|
+
|
|
215
|
+
ButtonType.OUTLINE -> modifier
|
|
216
|
+
.border(1.dp, color ?: theme.colors.primary, RoundedCornerShape(radius))
|
|
217
|
+
|
|
218
|
+
ButtonType.TONAL -> modifier
|
|
219
|
+
.border(0.dp, Color.Unspecified, RoundedCornerShape(radius))
|
|
220
|
+
|
|
221
|
+
ButtonType.DANGER -> modifier
|
|
222
|
+
.border(0.dp, Color.Unspecified, RoundedCornerShape(radius))
|
|
223
|
+
|
|
224
|
+
ButtonType.TEXT -> modifier
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
@Composable
|
|
230
|
+
fun getButtonBackgroundColor(
|
|
231
|
+
loading: Boolean,
|
|
232
|
+
type: ButtonType
|
|
233
|
+
): Color {
|
|
234
|
+
val theme = AppTheme.current
|
|
235
|
+
|
|
236
|
+
return remember(loading, type, theme) {
|
|
237
|
+
when (type) {
|
|
238
|
+
ButtonType.DISABLED -> theme.colors.background.disable.withLoading(loading)
|
|
239
|
+
ButtonType.PRIMARY -> theme.colors.primary.withLoading(loading)
|
|
240
|
+
ButtonType.SECONDARY -> theme.colors.background.surface.withLoading(loading)
|
|
241
|
+
ButtonType.OUTLINE -> theme.colors.background.surface.withLoading(loading)
|
|
242
|
+
ButtonType.TONAL -> theme.colors.background.tonal.withLoading(loading)
|
|
243
|
+
ButtonType.DANGER -> theme.colors.error.primary.withLoading(loading)
|
|
244
|
+
ButtonType.TEXT -> Color.Unspecified
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
fun Color.withLoading(loading: Boolean): Color =
|
|
250
|
+
this.copy(alpha = if (loading) 0.75f else 1f)
|
|
251
|
+
|
|
252
|
+
@Composable
|
|
253
|
+
fun Button(
|
|
254
|
+
onClick: () -> Unit,
|
|
255
|
+
type: ButtonType = ButtonType.PRIMARY,
|
|
256
|
+
size: Size = Size.LARGE,
|
|
257
|
+
iconRight: String = "",
|
|
258
|
+
iconLeft: String = "",
|
|
259
|
+
title: String = "Button",
|
|
260
|
+
loading: Boolean = false,
|
|
261
|
+
useTintColor: Boolean = true,
|
|
262
|
+
isFull: Boolean = true,
|
|
263
|
+
modifier: Modifier = Modifier,
|
|
264
|
+
) {
|
|
265
|
+
val radius = remember(size) { size.value.radius }
|
|
266
|
+
val isEnabled = remember(type) { type != ButtonType.DISABLED }
|
|
267
|
+
val loadingOnLeft = remember(iconLeft, iconRight) {
|
|
268
|
+
shouldLoadingOnLeft(iconLeft, iconRight)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
val sizeSpecs = remember(size) { size.value }
|
|
272
|
+
|
|
273
|
+
val interactionSource = remember { MutableInteractionSource() }
|
|
274
|
+
val isPressed by interactionSource.collectIsPressedAsState()
|
|
275
|
+
|
|
276
|
+
val animatedPadding by animateDpAsState(
|
|
277
|
+
targetValue = if (isPressed && isEnabled && !loading) 2.dp else 0.dp,
|
|
278
|
+
animationSpec = tween(100),
|
|
279
|
+
label = "pressPadding"
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
val targetAlpha = if (isPressed && isEnabled && !loading) 0.5f else 1f
|
|
283
|
+
val alpha by animateFloatAsState(
|
|
284
|
+
targetValue = targetAlpha,
|
|
285
|
+
animationSpec = tween(100),
|
|
286
|
+
label = "buttonPressAlpha"
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
val clickableModifier =
|
|
290
|
+
if (isEnabled && !loading) {
|
|
291
|
+
modifier
|
|
292
|
+
.then(if (isFull) Modifier.fillMaxWidth() else Modifier)
|
|
293
|
+
.clip(RoundedCornerShape(radius))
|
|
294
|
+
.clickable(
|
|
295
|
+
enabled = isEnabled && !loading,
|
|
296
|
+
interactionSource = interactionSource,
|
|
297
|
+
indication = null,
|
|
298
|
+
onClick = onClick
|
|
299
|
+
)
|
|
300
|
+
.alpha(alpha)
|
|
301
|
+
} else {
|
|
302
|
+
modifier
|
|
303
|
+
.then(if (isFull) Modifier.fillMaxWidth() else Modifier)
|
|
304
|
+
.clip(RoundedCornerShape(radius))
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
CompositionLocalProvider(
|
|
308
|
+
IsLoading provides loading,
|
|
309
|
+
BackgroundColor provides getButtonBackgroundColor(loading, type),
|
|
310
|
+
TextColor provides getTextColor(loading, type)
|
|
311
|
+
) {
|
|
312
|
+
Row(
|
|
313
|
+
modifier = clickableModifier
|
|
314
|
+
.padding(horizontal = animatedPadding)
|
|
315
|
+
.clip(RoundedCornerShape(radius))
|
|
316
|
+
.then(getTypeStyle(type, size = size))
|
|
317
|
+
.padding(horizontal = sizeSpecs.padding)
|
|
318
|
+
.height(sizeSpecs.height),
|
|
319
|
+
horizontalArrangement = Arrangement.Center,
|
|
320
|
+
verticalAlignment = Alignment.CenterVertically,
|
|
321
|
+
) {
|
|
322
|
+
RenderIcon(
|
|
323
|
+
size = size,
|
|
324
|
+
isIconLeft = true,
|
|
325
|
+
useTintColor = useTintColor,
|
|
326
|
+
icon = iconLeft,
|
|
327
|
+
forceLoading = loading && loadingOnLeft
|
|
328
|
+
)
|
|
329
|
+
RenderTitle(size, title, type = type)
|
|
330
|
+
RenderIcon(
|
|
331
|
+
size = size,
|
|
332
|
+
isIconLeft = false,
|
|
333
|
+
useTintColor = useTintColor,
|
|
334
|
+
icon = iconRight,
|
|
335
|
+
forceLoading = loading && !loadingOnLeft
|
|
336
|
+
)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
private val IsLoading = staticCompositionLocalOf<Boolean> { false }
|
|
343
|
+
private val BackgroundColor = staticCompositionLocalOf<Color> { Color.Transparent }
|
|
344
|
+
private val TextColor = staticCompositionLocalOf<Color> { Color.Transparent }
|
|
345
|
+
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
package vn.momo.kits.components
|
|
2
|
+
|
|
3
|
+
import androidx.compose.foundation.background
|
|
4
|
+
import androidx.compose.foundation.border
|
|
5
|
+
import androidx.compose.foundation.clickable
|
|
6
|
+
import androidx.compose.foundation.layout.Box
|
|
7
|
+
import androidx.compose.foundation.layout.Row
|
|
8
|
+
import androidx.compose.foundation.layout.Spacer
|
|
9
|
+
import androidx.compose.foundation.layout.size
|
|
10
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
11
|
+
import androidx.compose.runtime.Composable
|
|
12
|
+
import androidx.compose.runtime.remember
|
|
13
|
+
import androidx.compose.ui.Alignment
|
|
14
|
+
import androidx.compose.ui.Modifier
|
|
15
|
+
import androidx.compose.ui.graphics.Color
|
|
16
|
+
import androidx.compose.ui.unit.dp
|
|
17
|
+
import vn.momo.kits.const.AppTheme
|
|
18
|
+
import vn.momo.kits.const.Colors
|
|
19
|
+
import vn.momo.kits.const.Radius
|
|
20
|
+
import vn.momo.kits.const.Spacing
|
|
21
|
+
import vn.momo.kits.const.Typography
|
|
22
|
+
import vn.momo.kits.modifier.activeOpacityClickable
|
|
23
|
+
|
|
24
|
+
// Pre-computed shape for performance
|
|
25
|
+
private val checkboxShape = RoundedCornerShape(Radius.XS)
|
|
26
|
+
|
|
27
|
+
@Composable
|
|
28
|
+
fun CheckBox(
|
|
29
|
+
disabled: Boolean = false,
|
|
30
|
+
checked: Boolean = false,
|
|
31
|
+
onCheckedChange: (Boolean) -> Unit,
|
|
32
|
+
indeterminate: Boolean = false,
|
|
33
|
+
title: String = "",
|
|
34
|
+
) {
|
|
35
|
+
// Cache theme access
|
|
36
|
+
val theme = AppTheme.current
|
|
37
|
+
|
|
38
|
+
// Memoize color calculations to avoid repeated conditional logic
|
|
39
|
+
val colors = remember(checked, disabled, theme) {
|
|
40
|
+
when {
|
|
41
|
+
disabled && checked -> Pair(theme.colors.background.tonal, theme.colors.background.tonal)
|
|
42
|
+
disabled -> Pair(theme.colors.border.disable, Color.Transparent)
|
|
43
|
+
checked -> Pair(theme.colors.primary, theme.colors.primary)
|
|
44
|
+
else -> Pair(theme.colors.text.default, Color.Transparent)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
val (borderColor, backgroundColor) = colors
|
|
49
|
+
|
|
50
|
+
// Memoize icon source to avoid repeated conditional check
|
|
51
|
+
val iconSource = remember(indeterminate) {
|
|
52
|
+
if (indeterminate) "navigation_minus" else "ic_checked"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Memoize click handler to avoid lambda recreation
|
|
56
|
+
val onClickHandler = remember(onCheckedChange, checked) {
|
|
57
|
+
{ onCheckedChange(!checked) }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
61
|
+
Box(
|
|
62
|
+
modifier = Modifier
|
|
63
|
+
.size(20.dp)
|
|
64
|
+
.background(backgroundColor, checkboxShape)
|
|
65
|
+
.border(Spacing.XXS, borderColor, checkboxShape)
|
|
66
|
+
.activeOpacityClickable(
|
|
67
|
+
activeOpacity = 0.8f,
|
|
68
|
+
onClick = onClickHandler,
|
|
69
|
+
enabled = !disabled,
|
|
70
|
+
),
|
|
71
|
+
) {
|
|
72
|
+
if (checked) {
|
|
73
|
+
Icon(
|
|
74
|
+
source = iconSource,
|
|
75
|
+
size = 20.dp,
|
|
76
|
+
color = Colors.black_01
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (title.isNotEmpty()) {
|
|
82
|
+
Spacer(Modifier.size(Spacing.S))
|
|
83
|
+
Text(
|
|
84
|
+
style = Typography.descriptionDefaultRegular,
|
|
85
|
+
text = title,
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
package vn.momo.kits.components
|
|
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.Row
|
|
7
|
+
import androidx.compose.foundation.layout.height
|
|
8
|
+
import androidx.compose.foundation.layout.padding
|
|
9
|
+
import androidx.compose.foundation.layout.wrapContentWidth
|
|
10
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
11
|
+
import androidx.compose.runtime.Composable
|
|
12
|
+
import androidx.compose.runtime.Immutable
|
|
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.TextAlign
|
|
18
|
+
import androidx.compose.ui.text.style.TextOverflow
|
|
19
|
+
import androidx.compose.ui.unit.Dp
|
|
20
|
+
import androidx.compose.ui.unit.dp
|
|
21
|
+
import vn.momo.kits.const.AppTheme
|
|
22
|
+
import vn.momo.kits.const.Colors
|
|
23
|
+
import vn.momo.kits.const.Radius
|
|
24
|
+
import vn.momo.kits.const.Typography
|
|
25
|
+
import vn.momo.kits.const.scaleSize
|
|
26
|
+
import vn.momo.kits.modifier.activeOpacityClickable
|
|
27
|
+
import vn.momo.kits.modifier.conditional
|
|
28
|
+
import vn.momo.kits.modifier.setAutomationId
|
|
29
|
+
|
|
30
|
+
@Composable
|
|
31
|
+
fun Chip(
|
|
32
|
+
modifier: Modifier = Modifier,
|
|
33
|
+
label: String? = "Label",
|
|
34
|
+
iconLeft: String? = null,
|
|
35
|
+
iconRight: String? = null,
|
|
36
|
+
selected: Boolean = false,
|
|
37
|
+
onClick: () -> Unit = {},
|
|
38
|
+
size: ChipSize = ChipSize.LARGE,
|
|
39
|
+
iconLeftTint: Color? = null,
|
|
40
|
+
iconRightTint: Color? = null,
|
|
41
|
+
backgroundColor: Color? = null,
|
|
42
|
+
accessibilityLabel: String? = null,
|
|
43
|
+
) {
|
|
44
|
+
val theme = AppTheme.current
|
|
45
|
+
val bg = when {
|
|
46
|
+
selected -> theme.colors.background.selected
|
|
47
|
+
backgroundColor != null -> backgroundColor
|
|
48
|
+
else -> Colors.black_03
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
val textColor = if (selected) theme.colors.primary else theme.colors.text.default
|
|
52
|
+
val leftTint = if (selected) theme.colors.primary else iconLeftTint ?: theme.colors.text.default
|
|
53
|
+
val rightTint = if (selected) theme.colors.primary else iconRightTint ?: theme.colors.text.default
|
|
54
|
+
|
|
55
|
+
val dims = when (size) {
|
|
56
|
+
ChipSize.SMALL -> ChipDefaults.Small
|
|
57
|
+
ChipSize.LARGE -> ChipDefaults.Large
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
val (height, horizontal, iconSize, iconSpacing) =
|
|
61
|
+
listOf(scaleSize(dims.height), scaleSize(dims.horizontal), scaleSize(dims.iconSize), scaleSize(dims.iconSpacing))
|
|
62
|
+
|
|
63
|
+
Row(
|
|
64
|
+
modifier
|
|
65
|
+
.wrapContentWidth()
|
|
66
|
+
.height(height)
|
|
67
|
+
.clip(RoundedCornerShape(Radius.L))
|
|
68
|
+
.background(bg)
|
|
69
|
+
.conditional(selected) {
|
|
70
|
+
Modifier.border(width = 2.dp, color = theme.colors.secondary, shape = RoundedCornerShape(Radius.L))
|
|
71
|
+
}
|
|
72
|
+
.activeOpacityClickable {
|
|
73
|
+
onClick()
|
|
74
|
+
}
|
|
75
|
+
.padding(horizontal = horizontal)
|
|
76
|
+
.conditional(accessibilityLabel != null) {
|
|
77
|
+
setAutomationId(accessibilityLabel.toString())
|
|
78
|
+
},
|
|
79
|
+
verticalAlignment = Alignment.CenterVertically,
|
|
80
|
+
horizontalArrangement = Arrangement.spacedBy(iconSpacing)
|
|
81
|
+
) {
|
|
82
|
+
if (iconLeft != null) {
|
|
83
|
+
Icon(source = iconLeft, size = dims.iconSize, color = leftTint)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!label.isNullOrEmpty()) {
|
|
87
|
+
Text(
|
|
88
|
+
text = label,
|
|
89
|
+
textAlign = TextAlign.Center,
|
|
90
|
+
color = textColor,
|
|
91
|
+
style = when (size) {
|
|
92
|
+
ChipSize.SMALL -> Typography.labelSMedium
|
|
93
|
+
ChipSize.LARGE -> Typography.labelDefaultMedium
|
|
94
|
+
},
|
|
95
|
+
maxLines = 1,
|
|
96
|
+
overflow = TextOverflow.Ellipsis,
|
|
97
|
+
modifier = Modifier.weight(1f, fill = false),
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (iconRight != null) {
|
|
102
|
+
Icon(source = iconRight, size = iconSize, color = rightTint)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
enum class ChipSize { SMALL, LARGE }
|
|
108
|
+
|
|
109
|
+
object ChipDefaults {
|
|
110
|
+
@Immutable
|
|
111
|
+
data class Dimensions(
|
|
112
|
+
val height: Dp,
|
|
113
|
+
val horizontal: Dp,
|
|
114
|
+
val iconSize: Dp,
|
|
115
|
+
val iconSpacing: Dp,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
val Large = Dimensions(
|
|
119
|
+
height = 32.dp,
|
|
120
|
+
horizontal = 12.dp,
|
|
121
|
+
iconSize = 20.dp,
|
|
122
|
+
iconSpacing = 4.dp,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
val Small = Dimensions(
|
|
126
|
+
height = 24.dp,
|
|
127
|
+
horizontal = 10.dp,
|
|
128
|
+
iconSize = 16.dp,
|
|
129
|
+
iconSpacing = 4.dp,
|
|
130
|
+
)
|
|
131
|
+
}
|