@momo-kits/native-kits 0.89.19 → 0.89.21
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/compose/build.gradle.kts +11 -15
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +22 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Components.kt +71 -27
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +200 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +47 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +7 -139
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +89 -22
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +17 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -7
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +6 -8
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +37 -17
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +54 -21
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +155 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +2 -20
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +185 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -19
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +2 -18
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +58 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +37 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +65 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +43 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +14 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +28 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +1 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +18 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -23
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +33 -0
- package/gradle.properties +1 -1
- package/package.json +1 -1
- package/settings.gradle.kts +1 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/utils/Platform.kt +0 -3
- package/compose/src/androidMain/kotlin/vn/momo/kits/utils/SetMaskFilter.kt +0 -8
- package/compose/src/androidMain/kotlin/vn/momo/kits/utils/Utils.android.kt +0 -14
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Platform.kt +0 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/SetMaskFilter.kt +0 -5
- package/compose/src/iosMain/kotlin/vn/momo/kits/utils/Platform.kt +0 -3
- package/compose/src/iosMain/kotlin/vn/momo/kits/utils/SetMaskFilter.kt +0 -9
- package/compose/src/iosMain/kotlin/vn/momo/kits/utils/Utils.ios.kt +0 -18
|
@@ -32,6 +32,8 @@ import androidx.compose.ui.graphics.Color
|
|
|
32
32
|
import androidx.compose.ui.text.TextStyle
|
|
33
33
|
import androidx.compose.ui.text.font.FontWeight
|
|
34
34
|
import androidx.compose.ui.text.input.KeyboardType
|
|
35
|
+
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
|
36
|
+
import androidx.compose.ui.text.input.VisualTransformation
|
|
35
37
|
import androidx.compose.ui.unit.Dp
|
|
36
38
|
import androidx.compose.ui.unit.dp
|
|
37
39
|
import androidx.compose.ui.unit.sp
|
|
@@ -116,6 +118,36 @@ fun RenderRightIcon(loading: Boolean, icon: String, color: Color, onClick: () ->
|
|
|
116
118
|
}
|
|
117
119
|
}
|
|
118
120
|
|
|
121
|
+
@Composable
|
|
122
|
+
fun ErrorView(errorMessage: String, errorSpacing: Boolean, hintText: String) {
|
|
123
|
+
val errorColor = AppTheme.current.colors.error.primary
|
|
124
|
+
val hintColor = AppTheme.current.colors.text.hint
|
|
125
|
+
val hintTextDefault = hintText.ifEmpty { "Không thể chỉnh sửa" }
|
|
126
|
+
|
|
127
|
+
if (errorMessage.isNotEmpty() || hintText.isNotEmpty()) {
|
|
128
|
+
val color: Color =
|
|
129
|
+
if (errorMessage.isNotEmpty()) errorColor else hintColor
|
|
130
|
+
Row(
|
|
131
|
+
Modifier.padding(top = Spacing.XS),
|
|
132
|
+
verticalAlignment = Alignment.CenterVertically
|
|
133
|
+
) {
|
|
134
|
+
Icon(
|
|
135
|
+
size = 16.dp,
|
|
136
|
+
color = color,
|
|
137
|
+
source = "ic_error"
|
|
138
|
+
)
|
|
139
|
+
Text(
|
|
140
|
+
errorMessage.ifEmpty { hintTextDefault },
|
|
141
|
+
style = Typography.descriptionS,
|
|
142
|
+
modifier = Modifier.padding(start = Spacing.XS),
|
|
143
|
+
color = color,
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
} else if (errorSpacing) {
|
|
147
|
+
Spacer(Modifier.padding(top = Spacing.XS).height(18.dp))
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
119
151
|
@Composable
|
|
120
152
|
fun Input(
|
|
121
153
|
text: MutableState<String> = remember { mutableStateOf("") },
|
|
@@ -126,8 +158,12 @@ fun Input(
|
|
|
126
158
|
placeholder: String = "",
|
|
127
159
|
size: InputSize = InputSize.SMALL,
|
|
128
160
|
onChangeText: (String) -> Unit = {},
|
|
161
|
+
hintText: String = "",
|
|
129
162
|
error: String = "",
|
|
163
|
+
errorSpacing: Boolean = false,
|
|
130
164
|
disabled: Boolean = false,
|
|
165
|
+
readOnly: Boolean = false,
|
|
166
|
+
secureTextEntry: Boolean = false,
|
|
131
167
|
icon: String = "",
|
|
132
168
|
iconColor: Color = AppTheme.current.colors.text.default,
|
|
133
169
|
onRightIconPressed: () -> Unit = {},
|
|
@@ -141,6 +177,7 @@ fun Input(
|
|
|
141
177
|
keyboardType: KeyboardType = KeyboardType.Text,
|
|
142
178
|
) {
|
|
143
179
|
var isFocused by remember { mutableStateOf(false) }
|
|
180
|
+
var passHidden by remember { mutableStateOf(false) }
|
|
144
181
|
var isBlurred = false
|
|
145
182
|
val disabledColor = AppTheme.current.colors.text.disable
|
|
146
183
|
var textColor = AppTheme.current.colors.text.default
|
|
@@ -164,6 +201,7 @@ fun Input(
|
|
|
164
201
|
Column {
|
|
165
202
|
BasicTextField(
|
|
166
203
|
enabled = !disabled,
|
|
204
|
+
readOnly = readOnly,
|
|
167
205
|
singleLine = true,
|
|
168
206
|
value = text.value,
|
|
169
207
|
textStyle = TextStyle(
|
|
@@ -172,7 +210,8 @@ fun Input(
|
|
|
172
210
|
lineHeight = 24.sp,
|
|
173
211
|
fontWeight = fontWeight.value
|
|
174
212
|
),
|
|
175
|
-
|
|
213
|
+
visualTransformation = if (secureTextEntry && !passHidden) PasswordVisualTransformation() else VisualTransformation.None,
|
|
214
|
+
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = if (secureTextEntry) KeyboardType.Ascii else keyboardType),
|
|
176
215
|
modifier = Modifier.height(size.values.height).onFocusChanged {
|
|
177
216
|
isFocused = it.isFocused
|
|
178
217
|
if (it.isFocused) {
|
|
@@ -237,7 +276,7 @@ fun Input(
|
|
|
237
276
|
if (leadingIcon.isNotEmpty()) {
|
|
238
277
|
Icon(
|
|
239
278
|
source = leadingIcon,
|
|
240
|
-
modifier = Modifier.padding(end = Spacing.
|
|
279
|
+
modifier = Modifier.padding(end = Spacing.M),
|
|
241
280
|
size = if (size.name == InputSize.SMALL.name) 24.dp else 32.dp,
|
|
242
281
|
color = leadingIconColor
|
|
243
282
|
)
|
|
@@ -271,29 +310,23 @@ fun Input(
|
|
|
271
310
|
)
|
|
272
311
|
}
|
|
273
312
|
}
|
|
274
|
-
|
|
313
|
+
if (secureTextEntry && passHidden) {
|
|
314
|
+
val hidePassword = { onRightIconPressed(); passHidden = !passHidden}
|
|
315
|
+
RenderRightIcon(loading, "24_security_eye_open", iconTintColor,
|
|
316
|
+
hidePassword)
|
|
317
|
+
} else if (secureTextEntry && !passHidden) {
|
|
318
|
+
val showPassword = { onRightIconPressed(); passHidden = !passHidden}
|
|
319
|
+
RenderRightIcon(loading, "24_security_eye_off", iconTintColor,
|
|
320
|
+
showPassword)
|
|
321
|
+
} else {
|
|
322
|
+
RenderRightIcon(loading, icon, iconTintColor,
|
|
323
|
+
onRightIconPressed)
|
|
324
|
+
}
|
|
275
325
|
}
|
|
276
326
|
}
|
|
277
327
|
},
|
|
278
328
|
)
|
|
279
329
|
// show error
|
|
280
|
-
|
|
281
|
-
Row(
|
|
282
|
-
Modifier.padding(top = Spacing.XS),
|
|
283
|
-
verticalAlignment = Alignment.CenterVertically
|
|
284
|
-
) {
|
|
285
|
-
Icon(
|
|
286
|
-
size = 16.dp,
|
|
287
|
-
color = AppTheme.current.colors.error.primary,
|
|
288
|
-
source = "ic_error"
|
|
289
|
-
)
|
|
290
|
-
Text(
|
|
291
|
-
error,
|
|
292
|
-
style = Typography.descriptionS,
|
|
293
|
-
modifier = Modifier.padding(start = Spacing.XS),
|
|
294
|
-
color = AppTheme.current.colors.error.primary,
|
|
295
|
-
)
|
|
296
|
-
}
|
|
297
|
-
}
|
|
330
|
+
ErrorView(error, errorSpacing, hintText)
|
|
298
331
|
}
|
|
299
332
|
}
|
|
@@ -0,0 +1,155 @@
|
|
|
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.Column
|
|
8
|
+
import androidx.compose.foundation.layout.Row
|
|
9
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
10
|
+
import androidx.compose.foundation.layout.height
|
|
11
|
+
import androidx.compose.foundation.layout.offset
|
|
12
|
+
import androidx.compose.foundation.layout.padding
|
|
13
|
+
import androidx.compose.foundation.layout.wrapContentSize
|
|
14
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
15
|
+
import androidx.compose.runtime.Composable
|
|
16
|
+
import androidx.compose.runtime.MutableState
|
|
17
|
+
import androidx.compose.runtime.getValue
|
|
18
|
+
import androidx.compose.runtime.mutableStateOf
|
|
19
|
+
import androidx.compose.runtime.remember
|
|
20
|
+
import androidx.compose.runtime.setValue
|
|
21
|
+
import androidx.compose.ui.Alignment
|
|
22
|
+
import androidx.compose.ui.Modifier
|
|
23
|
+
import androidx.compose.ui.graphics.Color
|
|
24
|
+
import androidx.compose.ui.text.TextStyle
|
|
25
|
+
import androidx.compose.ui.unit.dp
|
|
26
|
+
import androidx.compose.ui.unit.sp
|
|
27
|
+
import androidx.compose.ui.zIndex
|
|
28
|
+
import vn.momo.kits.const.AppTheme
|
|
29
|
+
import vn.momo.kits.const.Spacing
|
|
30
|
+
import vn.momo.kits.const.Typography
|
|
31
|
+
|
|
32
|
+
@Composable
|
|
33
|
+
fun InputDropDown(
|
|
34
|
+
value: MutableState<String> = remember { mutableStateOf("") },
|
|
35
|
+
floatingValue: String = "",
|
|
36
|
+
floatingValueColor: Color = AppTheme.current.colors.text.hint,
|
|
37
|
+
floatingIcon: String = "",
|
|
38
|
+
floatingIconColor: Color = AppTheme.current.colors.text.default,
|
|
39
|
+
placeholder: String = "",
|
|
40
|
+
size: InputSize = InputSize.SMALL,
|
|
41
|
+
onPress: () -> Unit = {},
|
|
42
|
+
hintText: String = "",
|
|
43
|
+
error: String = "",
|
|
44
|
+
errorSpacing: Boolean = false,
|
|
45
|
+
disabled: Boolean = false,
|
|
46
|
+
icon: String = "arrow_chevron_down_small",
|
|
47
|
+
iconColor: Color = AppTheme.current.colors.text.default,
|
|
48
|
+
onRightIconPressed: () -> Unit = {},
|
|
49
|
+
leadingIcon: String = "",
|
|
50
|
+
leadingIconColor: Color = AppTheme.current.colors.text.hint,
|
|
51
|
+
loading: Boolean = false,
|
|
52
|
+
required: Boolean = false,
|
|
53
|
+
) {
|
|
54
|
+
var isFocused by remember { mutableStateOf(false) }
|
|
55
|
+
|
|
56
|
+
val disabledColor = AppTheme.current.colors.text.disable
|
|
57
|
+
var textColor = AppTheme.current.colors.text.default
|
|
58
|
+
var placeholderColor = AppTheme.current.colors.text.hint
|
|
59
|
+
var iconTintColor = iconColor
|
|
60
|
+
val floatingTitleColor = when {
|
|
61
|
+
disabled -> AppTheme.current.colors.text.disable
|
|
62
|
+
else -> floatingValueColor
|
|
63
|
+
}
|
|
64
|
+
val floatingIconTintColor = when {
|
|
65
|
+
disabled -> AppTheme.current.colors.text.disable
|
|
66
|
+
else -> floatingIconColor
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (disabled) {
|
|
70
|
+
textColor = disabledColor
|
|
71
|
+
placeholderColor = disabledColor
|
|
72
|
+
iconTintColor = disabledColor
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
Column(modifier = Modifier.clickable(enabled = !disabled, onClick = onPress)) {
|
|
77
|
+
Box {
|
|
78
|
+
if (floatingValue.isNotEmpty() || floatingIcon.isNotEmpty()) {
|
|
79
|
+
Box(
|
|
80
|
+
modifier = Modifier.wrapContentSize()
|
|
81
|
+
.offset(y = -Spacing.S, x = Spacing.S)
|
|
82
|
+
.background(AppTheme.current.colors.background.surface)
|
|
83
|
+
.zIndex(10f),
|
|
84
|
+
) {
|
|
85
|
+
Row(
|
|
86
|
+
modifier = Modifier
|
|
87
|
+
.padding(horizontal = Spacing.S),
|
|
88
|
+
verticalAlignment = Alignment.Bottom
|
|
89
|
+
) {
|
|
90
|
+
Text(
|
|
91
|
+
floatingValue,
|
|
92
|
+
style = Typography.labelS,
|
|
93
|
+
color = floatingTitleColor
|
|
94
|
+
)
|
|
95
|
+
if (required) {
|
|
96
|
+
Text(
|
|
97
|
+
"*",
|
|
98
|
+
style = Typography.labelS,
|
|
99
|
+
color = AppTheme.current.colors.error.primary,
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
if (floatingIcon.isNotEmpty()) {
|
|
103
|
+
Icon(
|
|
104
|
+
source = floatingIcon,
|
|
105
|
+
modifier = Modifier.padding(start = Spacing.XS),
|
|
106
|
+
size = 16.dp,
|
|
107
|
+
color = floatingIconTintColor
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
Box(
|
|
114
|
+
modifier = Modifier.fillMaxWidth()
|
|
115
|
+
.height(if (size == InputSize.SMALL) 48.dp else 56.dp)
|
|
116
|
+
.border(
|
|
117
|
+
1.dp,
|
|
118
|
+
getBorderColor(isFocused, error, disabled),
|
|
119
|
+
RoundedCornerShape(size.values.borderRadius)
|
|
120
|
+
),
|
|
121
|
+
contentAlignment = Alignment.CenterStart
|
|
122
|
+
) {
|
|
123
|
+
Row(
|
|
124
|
+
modifier = Modifier.padding(horizontal = Spacing.M),
|
|
125
|
+
verticalAlignment = Alignment.CenterVertically
|
|
126
|
+
) {
|
|
127
|
+
//leading icon
|
|
128
|
+
if (leadingIcon.isNotEmpty()) {
|
|
129
|
+
Icon(
|
|
130
|
+
source = leadingIcon,
|
|
131
|
+
modifier = Modifier.padding(end = Spacing.M),
|
|
132
|
+
size = if (size.name == InputSize.SMALL.name) 24.dp else 32.dp,
|
|
133
|
+
color = leadingIconColor
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
Box(Modifier.weight(1f)) {
|
|
137
|
+
Text(
|
|
138
|
+
text = value.value.ifEmpty { placeholder },
|
|
139
|
+
style = TextStyle(
|
|
140
|
+
fontSize = 16.sp,
|
|
141
|
+
lineHeight = 24.sp
|
|
142
|
+
),
|
|
143
|
+
color = if (value.value.isEmpty()) placeholderColor else textColor
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
RenderRightIcon(
|
|
147
|
+
loading, icon, iconTintColor,
|
|
148
|
+
onRightIconPressed
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
ErrorView(error, errorSpacing, hintText)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -69,6 +69,7 @@ fun InputMoney(
|
|
|
69
69
|
size: InputSize = InputSize.SMALL,
|
|
70
70
|
onChangeText: (String) -> Unit = {},
|
|
71
71
|
error: String = "",
|
|
72
|
+
errorSpacing: Boolean = false,
|
|
72
73
|
disabled: Boolean = false,
|
|
73
74
|
icon: String = "",
|
|
74
75
|
iconColor: Color = AppTheme.current.colors.text.default,
|
|
@@ -222,25 +223,6 @@ fun InputMoney(
|
|
|
222
223
|
}
|
|
223
224
|
},
|
|
224
225
|
)
|
|
225
|
-
|
|
226
|
-
if (error.isNotEmpty()) {
|
|
227
|
-
Row(
|
|
228
|
-
Modifier.padding(top = Spacing.XS),
|
|
229
|
-
verticalAlignment = Alignment.CenterVertically
|
|
230
|
-
) {
|
|
231
|
-
Icon(
|
|
232
|
-
size = 16.dp,
|
|
233
|
-
color = AppTheme.current.colors.error.primary,
|
|
234
|
-
source = "ic_error"
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
Text(
|
|
238
|
-
error,
|
|
239
|
-
style = Typography.descriptionS,
|
|
240
|
-
modifier = Modifier.padding(start = Spacing.XS),
|
|
241
|
-
color = AppTheme.current.colors.error.primary,
|
|
242
|
-
)
|
|
243
|
-
}
|
|
244
|
-
}
|
|
226
|
+
ErrorView(error, errorSpacing, "")
|
|
245
227
|
}
|
|
246
228
|
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
package vn.momo.kits.components
|
|
2
|
+
|
|
3
|
+
import androidx.compose.animation.core.LinearEasing
|
|
4
|
+
import androidx.compose.animation.core.RepeatMode
|
|
5
|
+
import androidx.compose.animation.core.animateFloat
|
|
6
|
+
import androidx.compose.animation.core.infiniteRepeatable
|
|
7
|
+
import androidx.compose.animation.core.rememberInfiniteTransition
|
|
8
|
+
import androidx.compose.animation.core.tween
|
|
9
|
+
import androidx.compose.foundation.background
|
|
10
|
+
import androidx.compose.foundation.border
|
|
11
|
+
import androidx.compose.foundation.layout.Arrangement
|
|
12
|
+
import androidx.compose.foundation.layout.Box
|
|
13
|
+
import androidx.compose.foundation.layout.Column
|
|
14
|
+
import androidx.compose.foundation.layout.Row
|
|
15
|
+
import androidx.compose.foundation.layout.Spacer
|
|
16
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
17
|
+
import androidx.compose.foundation.layout.height
|
|
18
|
+
import androidx.compose.foundation.layout.offset
|
|
19
|
+
import androidx.compose.foundation.layout.padding
|
|
20
|
+
import androidx.compose.foundation.layout.size
|
|
21
|
+
import androidx.compose.foundation.layout.width
|
|
22
|
+
import androidx.compose.foundation.layout.wrapContentSize
|
|
23
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
24
|
+
import androidx.compose.foundation.text.BasicTextField
|
|
25
|
+
import androidx.compose.foundation.text.KeyboardOptions
|
|
26
|
+
import androidx.compose.runtime.Composable
|
|
27
|
+
import androidx.compose.runtime.getValue
|
|
28
|
+
import androidx.compose.runtime.mutableStateOf
|
|
29
|
+
import androidx.compose.runtime.remember
|
|
30
|
+
import androidx.compose.runtime.setValue
|
|
31
|
+
import androidx.compose.ui.Alignment
|
|
32
|
+
import androidx.compose.ui.Modifier
|
|
33
|
+
import androidx.compose.ui.focus.onFocusChanged
|
|
34
|
+
import androidx.compose.ui.text.input.KeyboardType
|
|
35
|
+
import androidx.compose.ui.unit.dp
|
|
36
|
+
import androidx.compose.ui.zIndex
|
|
37
|
+
import vn.momo.kits.const.AppTheme
|
|
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.const.scaleSize
|
|
42
|
+
|
|
43
|
+
@Composable
|
|
44
|
+
fun OTPCaret() {
|
|
45
|
+
val DURATION = 500
|
|
46
|
+
var backgroundColor = AppTheme.current.colors.primary
|
|
47
|
+
|
|
48
|
+
val infiniteTransition = rememberInfiniteTransition()
|
|
49
|
+
val alpha by infiniteTransition.animateFloat(
|
|
50
|
+
initialValue = 1f,
|
|
51
|
+
targetValue = 0f,
|
|
52
|
+
animationSpec = infiniteRepeatable(
|
|
53
|
+
animation = tween(
|
|
54
|
+
durationMillis = DURATION,
|
|
55
|
+
delayMillis = DURATION,
|
|
56
|
+
easing = LinearEasing
|
|
57
|
+
),
|
|
58
|
+
repeatMode = RepeatMode.Reverse
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
63
|
+
Box(
|
|
64
|
+
modifier = Modifier
|
|
65
|
+
.size(1.dp, scaleSize(12f).dp)
|
|
66
|
+
.background(backgroundColor.copy(alpha = alpha))
|
|
67
|
+
)
|
|
68
|
+
Text(
|
|
69
|
+
text = "-",
|
|
70
|
+
color = AppTheme.current.colors.text.hint,
|
|
71
|
+
style = Typography.descriptionDefault
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@Composable
|
|
77
|
+
fun InputOTP(
|
|
78
|
+
length: Int = 4,
|
|
79
|
+
errorMessage: String = "",
|
|
80
|
+
errorSpacing: Boolean = false,
|
|
81
|
+
floatingValue: String = "Label",
|
|
82
|
+
placeholder: String = "",
|
|
83
|
+
onChangeText: (String) -> Unit,
|
|
84
|
+
onFocus: () -> Unit = {},
|
|
85
|
+
onBlur: () -> Unit = {},
|
|
86
|
+
dataType: String = "number",
|
|
87
|
+
accessibilityLabel: String? = null,
|
|
88
|
+
) {
|
|
89
|
+
val MAX_LENGTH = 10
|
|
90
|
+
var value by remember { mutableStateOf("") }
|
|
91
|
+
var isFocused by remember { mutableStateOf(false) }
|
|
92
|
+
var isBlurred = false
|
|
93
|
+
|
|
94
|
+
val handleChangeText: (String) -> Unit = { text ->
|
|
95
|
+
if (text.length <= MAX_LENGTH && (text.length <= value.length || value.length < length) && (dataType != "number" || !text.any { !it.isDigit() })) {
|
|
96
|
+
value = text
|
|
97
|
+
onChangeText(text)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
Column {
|
|
103
|
+
BasicTextField(
|
|
104
|
+
value = value,
|
|
105
|
+
onValueChange = handleChangeText,
|
|
106
|
+
singleLine = true,
|
|
107
|
+
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = if (dataType == "number") KeyboardType.Number else KeyboardType.Ascii),
|
|
108
|
+
modifier = Modifier.height(56.dp).onFocusChanged {
|
|
109
|
+
isFocused = it.isFocused
|
|
110
|
+
if (it.isFocused) {
|
|
111
|
+
onFocus()
|
|
112
|
+
}
|
|
113
|
+
if (!it.isFocused && isBlurred) onBlur()
|
|
114
|
+
if (it.isFocused && !isBlurred) isBlurred = true
|
|
115
|
+
},
|
|
116
|
+
decorationBox = { innerTextField ->
|
|
117
|
+
Box {
|
|
118
|
+
if (floatingValue.isNotEmpty()) {
|
|
119
|
+
Box(
|
|
120
|
+
modifier = Modifier.wrapContentSize()
|
|
121
|
+
.offset(y = ((-56).dp / 2))
|
|
122
|
+
.align(Alignment.Center)
|
|
123
|
+
.background(AppTheme.current.colors.background.surface)
|
|
124
|
+
.zIndex(10f),
|
|
125
|
+
) {
|
|
126
|
+
Row(
|
|
127
|
+
modifier = Modifier
|
|
128
|
+
.padding(horizontal = Spacing.S),
|
|
129
|
+
verticalAlignment = Alignment.Bottom
|
|
130
|
+
) {
|
|
131
|
+
Text(
|
|
132
|
+
floatingValue,
|
|
133
|
+
style = Typography.labelS,
|
|
134
|
+
color = AppTheme.current.colors.text.hint
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
Box(
|
|
140
|
+
modifier = Modifier
|
|
141
|
+
.fillMaxWidth()
|
|
142
|
+
.height(56.dp)
|
|
143
|
+
.border(
|
|
144
|
+
1.dp,
|
|
145
|
+
getBorderColor(isFocused, errorMessage, false),
|
|
146
|
+
RoundedCornerShape(
|
|
147
|
+
Radius.S
|
|
148
|
+
)
|
|
149
|
+
), contentAlignment = Alignment.Center
|
|
150
|
+
) {
|
|
151
|
+
Row(
|
|
152
|
+
modifier = Modifier.padding(vertical = 8.dp),
|
|
153
|
+
horizontalArrangement = Arrangement.SpaceBetween,
|
|
154
|
+
verticalAlignment = Alignment.CenterVertically
|
|
155
|
+
) {
|
|
156
|
+
for (i in 0 until length) {
|
|
157
|
+
if (value.isEmpty() && !isFocused && placeholder.isNotEmpty()) {
|
|
158
|
+
return@BasicTextField Text(
|
|
159
|
+
text = placeholder,
|
|
160
|
+
color = AppTheme.current.colors.text.hint
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
if (isFocused && value.length == i) {
|
|
164
|
+
OTPCaret();
|
|
165
|
+
} else {
|
|
166
|
+
val textColor =
|
|
167
|
+
if (value.getOrNull(i) != null) AppTheme.current.colors.text.default else AppTheme.current.colors.text.hint
|
|
168
|
+
val typo =
|
|
169
|
+
if (value.getOrNull(i) != null) Typography.titleS else Typography.descriptionDefault
|
|
170
|
+
Text(
|
|
171
|
+
text = value.getOrNull(i)?.toString() ?: "-",
|
|
172
|
+
color = textColor,
|
|
173
|
+
style = typo
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
if (i != length - 1) Spacer(Modifier.width(Spacing.L))
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
)
|
|
183
|
+
ErrorView(errorMessage, errorSpacing, "")
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -200,24 +200,5 @@ fun InputSearch(
|
|
|
200
200
|
}
|
|
201
201
|
},
|
|
202
202
|
)
|
|
203
|
-
// show error
|
|
204
|
-
if (error.isNotEmpty()) {
|
|
205
|
-
Row(
|
|
206
|
-
Modifier.padding(top = Spacing.XS),
|
|
207
|
-
verticalAlignment = Alignment.CenterVertically
|
|
208
|
-
) {
|
|
209
|
-
Icon(
|
|
210
|
-
size = 16.dp,
|
|
211
|
-
color = AppTheme.current.colors.error.primary,
|
|
212
|
-
source = "ic_error"
|
|
213
|
-
)
|
|
214
|
-
Text(
|
|
215
|
-
error,
|
|
216
|
-
style = Typography.descriptionS,
|
|
217
|
-
modifier = Modifier.padding(start = Spacing.XS),
|
|
218
|
-
color = AppTheme.current.colors.error.primary,
|
|
219
|
-
)
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
203
|
}
|
|
223
204
|
}
|
|
@@ -53,6 +53,7 @@ fun InputTextArea(
|
|
|
53
53
|
size: InputSize = InputSize.SMALL,
|
|
54
54
|
onChangeText: (String) -> Unit = {},
|
|
55
55
|
error: String = "",
|
|
56
|
+
errorSpacing: Boolean = false,
|
|
56
57
|
disabled: Boolean = false,
|
|
57
58
|
icon: String = "",
|
|
58
59
|
iconColor: Color = AppTheme.current.colors.text.default,
|
|
@@ -222,23 +223,6 @@ fun InputTextArea(
|
|
|
222
223
|
},
|
|
223
224
|
)
|
|
224
225
|
// show error
|
|
225
|
-
|
|
226
|
-
Row(
|
|
227
|
-
Modifier.padding(top = Spacing.XS),
|
|
228
|
-
verticalAlignment = Alignment.CenterVertically
|
|
229
|
-
) {
|
|
230
|
-
Icon(
|
|
231
|
-
size = 16.dp,
|
|
232
|
-
color = AppTheme.current.colors.error.primary,
|
|
233
|
-
source = "ic_error"
|
|
234
|
-
)
|
|
235
|
-
Text(
|
|
236
|
-
error,
|
|
237
|
-
style = Typography.descriptionS,
|
|
238
|
-
modifier = Modifier.padding(start = Spacing.XS),
|
|
239
|
-
color = AppTheme.current.colors.error.primary,
|
|
240
|
-
)
|
|
241
|
-
}
|
|
242
|
-
}
|
|
226
|
+
ErrorView(error, errorSpacing, "")
|
|
243
227
|
}
|
|
244
228
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
package vn.momo.kits.components
|
|
2
|
+
|
|
3
|
+
import androidx.compose.foundation.background
|
|
4
|
+
import androidx.compose.foundation.layout.Box
|
|
5
|
+
import androidx.compose.foundation.layout.BoxWithConstraints
|
|
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.size
|
|
10
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
11
|
+
import androidx.compose.runtime.Composable
|
|
12
|
+
import androidx.compose.ui.Alignment
|
|
13
|
+
import androidx.compose.ui.Modifier
|
|
14
|
+
import androidx.compose.ui.draw.alpha
|
|
15
|
+
import androidx.compose.ui.draw.clip
|
|
16
|
+
import androidx.compose.ui.draw.clipToBounds
|
|
17
|
+
import androidx.compose.ui.graphics.Color
|
|
18
|
+
import androidx.compose.ui.unit.dp
|
|
19
|
+
import vn.momo.kits.const.AppTheme
|
|
20
|
+
import vn.momo.kits.const.Colors
|
|
21
|
+
import vn.momo.kits.const.Spacing
|
|
22
|
+
import vn.momo.kits.const.Typography
|
|
23
|
+
|
|
24
|
+
@Composable
|
|
25
|
+
fun Dot(active: Boolean = false, activeColor: Color, modifier: Modifier = Modifier) {
|
|
26
|
+
return if (active) {
|
|
27
|
+
Box(
|
|
28
|
+
modifier = modifier.size(width = 12.dp, height = 4.dp).background(
|
|
29
|
+
color = activeColor,
|
|
30
|
+
shape = RoundedCornerShape(4.dp)
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
} else {
|
|
34
|
+
Box(
|
|
35
|
+
modifier = modifier.size(4.dp)
|
|
36
|
+
.background(
|
|
37
|
+
color = AppTheme.current.colors.background.pressed,
|
|
38
|
+
shape = RoundedCornerShape(4.dp)
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@Composable
|
|
45
|
+
fun PaginationDot(
|
|
46
|
+
activeIndex: Int = 0,
|
|
47
|
+
dataLength: Int = 3
|
|
48
|
+
) {
|
|
49
|
+
Row {
|
|
50
|
+
for (i in 0 until dataLength) {
|
|
51
|
+
Dot(
|
|
52
|
+
i == activeIndex,
|
|
53
|
+
AppTheme.current.colors.primary,
|
|
54
|
+
modifier = Modifier.padding(end = if (i != dataLength - 1) Spacing.XS else 0.dp)
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
package vn.momo.kits.components
|
|
2
|
+
|
|
3
|
+
import androidx.compose.foundation.background
|
|
4
|
+
import androidx.compose.foundation.layout.Box
|
|
5
|
+
import androidx.compose.foundation.layout.BoxWithConstraints
|
|
6
|
+
import androidx.compose.foundation.layout.Row
|
|
7
|
+
import androidx.compose.foundation.layout.padding
|
|
8
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
9
|
+
import androidx.compose.runtime.Composable
|
|
10
|
+
import androidx.compose.ui.Alignment
|
|
11
|
+
import androidx.compose.ui.Modifier
|
|
12
|
+
import androidx.compose.ui.draw.alpha
|
|
13
|
+
import androidx.compose.ui.graphics.Color
|
|
14
|
+
import vn.momo.kits.const.Colors
|
|
15
|
+
import vn.momo.kits.const.Spacing
|
|
16
|
+
import vn.momo.kits.const.Typography
|
|
17
|
+
|
|
18
|
+
@Composable
|
|
19
|
+
fun PaginationNumber(
|
|
20
|
+
activeIndex: Int = 0,
|
|
21
|
+
dataLength: Int = 2
|
|
22
|
+
) {
|
|
23
|
+
BoxWithConstraints(
|
|
24
|
+
modifier = Modifier.background(
|
|
25
|
+
color = Color(0x33000000),
|
|
26
|
+
shape = RoundedCornerShape(Spacing.L)
|
|
27
|
+
),
|
|
28
|
+
contentAlignment = Alignment.Center
|
|
29
|
+
) {
|
|
30
|
+
Text(
|
|
31
|
+
modifier = Modifier.padding(horizontal = Spacing.S),
|
|
32
|
+
text = "${1 + activeIndex}/$dataLength",
|
|
33
|
+
color = Colors.black_01,
|
|
34
|
+
style = Typography.labelDefault
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
}
|