@momo-kits/native-kits 0.151.2-chip.1 → 0.151.2-test.1
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/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +8 -7
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +3 -4
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +385 -0
- package/package.json +1 -1
|
@@ -126,14 +126,16 @@ fun RowScope.renderHeaderContent(headerTitle: HeaderTitle, tintIconColor: Color)
|
|
|
126
126
|
is HeaderTitle.Default -> {
|
|
127
127
|
HeaderTitle(
|
|
128
128
|
title = headerTitle.title,
|
|
129
|
-
color = tintIconColor
|
|
130
|
-
modifier = Modifier.fillMaxWidth(),
|
|
131
|
-
textAlign = TextAlign.Start
|
|
129
|
+
color = tintIconColor
|
|
132
130
|
)
|
|
133
131
|
}
|
|
134
132
|
is HeaderTitle.Journey -> {}
|
|
135
133
|
is HeaderTitle.Location -> {}
|
|
136
|
-
is HeaderTitle.User -> {
|
|
134
|
+
is HeaderTitle.User -> {
|
|
135
|
+
HeaderUser(
|
|
136
|
+
data = headerTitle
|
|
137
|
+
)
|
|
138
|
+
}
|
|
137
139
|
}
|
|
138
140
|
}
|
|
139
141
|
}
|
|
@@ -161,9 +163,8 @@ sealed class HeaderTitle {
|
|
|
161
163
|
val title: String,
|
|
162
164
|
val subTitle: String? = null,
|
|
163
165
|
val image: List<String>? = null,
|
|
164
|
-
val dotColor:
|
|
165
|
-
val
|
|
166
|
-
val tintColor: String? = null,
|
|
166
|
+
val dotColor: Color? = null,
|
|
167
|
+
val tintColor: Color? = null,
|
|
167
168
|
val onPress: (() -> Unit)? = null,
|
|
168
169
|
val icons: List<String> = emptyList(),
|
|
169
170
|
val isLoading: Boolean = false
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package vn.momo.kits.navigation.component
|
|
2
2
|
|
|
3
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
3
4
|
import androidx.compose.runtime.Composable
|
|
4
5
|
import androidx.compose.ui.Modifier
|
|
5
6
|
import androidx.compose.ui.graphics.Color
|
|
@@ -13,14 +14,12 @@ import vn.momo.kits.const.Typography
|
|
|
13
14
|
@Composable
|
|
14
15
|
fun HeaderTitle(
|
|
15
16
|
title: String = "",
|
|
16
|
-
modifier: Modifier = Modifier,
|
|
17
|
-
textAlign: TextAlign = TextAlign.Start,
|
|
18
17
|
color: Color? = null,
|
|
19
18
|
) {
|
|
20
19
|
Text(
|
|
21
|
-
modifier = Modifier.
|
|
20
|
+
modifier = Modifier.fillMaxWidth().zIndex(1f),
|
|
22
21
|
text = title,
|
|
23
|
-
textAlign =
|
|
22
|
+
textAlign = TextAlign.Start,
|
|
24
23
|
style = Typography.actionSBold.copy(
|
|
25
24
|
fontSize = 15.sp,
|
|
26
25
|
lineHeight = 22.sp,
|
|
@@ -0,0 +1,385 @@
|
|
|
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.Column
|
|
8
|
+
import androidx.compose.foundation.layout.Row
|
|
9
|
+
import androidx.compose.foundation.layout.Spacer
|
|
10
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
11
|
+
import androidx.compose.foundation.layout.height
|
|
12
|
+
import androidx.compose.foundation.layout.padding
|
|
13
|
+
import androidx.compose.foundation.layout.size
|
|
14
|
+
import androidx.compose.foundation.layout.width
|
|
15
|
+
import androidx.compose.foundation.layout.widthIn
|
|
16
|
+
import androidx.compose.foundation.shape.CircleShape
|
|
17
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
18
|
+
import androidx.compose.runtime.Composable
|
|
19
|
+
import androidx.compose.runtime.Immutable
|
|
20
|
+
import androidx.compose.runtime.remember
|
|
21
|
+
import androidx.compose.ui.Alignment
|
|
22
|
+
import androidx.compose.ui.Modifier
|
|
23
|
+
import androidx.compose.ui.draw.clip
|
|
24
|
+
import androidx.compose.ui.draw.clipToBounds
|
|
25
|
+
import androidx.compose.ui.layout.ContentScale
|
|
26
|
+
import androidx.compose.ui.text.style.TextOverflow
|
|
27
|
+
import androidx.compose.ui.unit.Dp
|
|
28
|
+
import androidx.compose.ui.unit.dp
|
|
29
|
+
import androidx.compose.ui.unit.sp
|
|
30
|
+
import vn.momo.kits.components.Image
|
|
31
|
+
import vn.momo.kits.components.Options
|
|
32
|
+
import vn.momo.kits.components.Skeleton
|
|
33
|
+
import vn.momo.kits.components.Text
|
|
34
|
+
import vn.momo.kits.const.AppTheme
|
|
35
|
+
import vn.momo.kits.const.Colors
|
|
36
|
+
import vn.momo.kits.const.Spacing
|
|
37
|
+
import vn.momo.kits.const.Typography
|
|
38
|
+
import vn.momo.kits.const.scaleSize
|
|
39
|
+
import vn.momo.kits.modifier.activeOpacityClickable
|
|
40
|
+
import vn.momo.kits.navigation.component.HeaderTitle.User
|
|
41
|
+
import vn.momo.kits.platform.getScreenDimensions
|
|
42
|
+
|
|
43
|
+
@Composable
|
|
44
|
+
fun HeaderUser(
|
|
45
|
+
data: HeaderTitle
|
|
46
|
+
) {
|
|
47
|
+
if (data !is User) return
|
|
48
|
+
|
|
49
|
+
if (data.isLoading) {
|
|
50
|
+
return TitleUserShimmer()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
val maxWidth = getScreenDimensions().width.dp - scaleSize(172.dp)- (data.icons.size * 16).dp
|
|
54
|
+
|
|
55
|
+
Row(
|
|
56
|
+
modifier = Modifier
|
|
57
|
+
.fillMaxWidth()
|
|
58
|
+
.padding(vertical = Spacing.XS)
|
|
59
|
+
.activeOpacityClickable(enabled = data.onPress != null, onClick = { data.onPress?.invoke() }),
|
|
60
|
+
horizontalArrangement = Arrangement.SpaceBetween,
|
|
61
|
+
verticalAlignment = Alignment.CenterVertically
|
|
62
|
+
) {
|
|
63
|
+
Row(
|
|
64
|
+
modifier = Modifier.widthIn(max = maxWidth),
|
|
65
|
+
verticalAlignment = Alignment.CenterVertically
|
|
66
|
+
) {
|
|
67
|
+
Box(modifier = Modifier.size(Spacing.XXL)) {
|
|
68
|
+
AvatarGroup(urls = AvatarUrls(data.image ?: listOf()), title = data.title)
|
|
69
|
+
if (data.dotColor != null) {
|
|
70
|
+
Box(
|
|
71
|
+
modifier = Modifier
|
|
72
|
+
.align(Alignment.BottomEnd)
|
|
73
|
+
.size(10.dp)
|
|
74
|
+
.clip(CircleShape)
|
|
75
|
+
.background(data.dotColor)
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Spacer(Modifier.width(6.dp))
|
|
81
|
+
|
|
82
|
+
Column(modifier = Modifier.weight(1f, fill = false)) {
|
|
83
|
+
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
84
|
+
Text(
|
|
85
|
+
text = data.title,
|
|
86
|
+
color = data.tintColor,
|
|
87
|
+
style = Typography.actionXsBold,
|
|
88
|
+
maxLines = 1,
|
|
89
|
+
overflow = TextOverflow.Ellipsis
|
|
90
|
+
)
|
|
91
|
+
if (data.icons.isNotEmpty()) {
|
|
92
|
+
Spacer(Modifier.width(6.dp))
|
|
93
|
+
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
94
|
+
data.icons.forEach { icon ->
|
|
95
|
+
RemoteIcon16(url = icon)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!data.subTitle.isNullOrEmpty()) {
|
|
102
|
+
Spacer(Modifier.height(2.dp))
|
|
103
|
+
Text(
|
|
104
|
+
text = data.subTitle,
|
|
105
|
+
color = data.tintColor,
|
|
106
|
+
style = Typography.descriptionXsRegular,
|
|
107
|
+
maxLines = 1,
|
|
108
|
+
overflow = TextOverflow.Ellipsis
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@Composable
|
|
117
|
+
private fun TitleUserShimmer() {
|
|
118
|
+
val theme = AppTheme.current
|
|
119
|
+
Row(
|
|
120
|
+
modifier = Modifier
|
|
121
|
+
.fillMaxWidth()
|
|
122
|
+
.padding(vertical = Spacing.XS),
|
|
123
|
+
verticalAlignment = Alignment.CenterVertically
|
|
124
|
+
) {
|
|
125
|
+
Box(
|
|
126
|
+
modifier = Modifier
|
|
127
|
+
.size(Spacing.XXL)
|
|
128
|
+
.clip(CircleShape)
|
|
129
|
+
.background(theme.colors.border.default)
|
|
130
|
+
) {
|
|
131
|
+
Skeleton()
|
|
132
|
+
}
|
|
133
|
+
Spacer(Modifier.width(Spacing.M / 2))
|
|
134
|
+
Column(modifier = Modifier.fillMaxWidth()) {
|
|
135
|
+
Box(
|
|
136
|
+
modifier = Modifier
|
|
137
|
+
.height(18.dp)
|
|
138
|
+
.width(120.dp)
|
|
139
|
+
.clip(RoundedCornerShape(8.dp))
|
|
140
|
+
) { Skeleton() }
|
|
141
|
+
Spacer(Modifier.height(Spacing.XXS))
|
|
142
|
+
Box(
|
|
143
|
+
modifier = Modifier
|
|
144
|
+
.height(12.dp)
|
|
145
|
+
.width(120.dp)
|
|
146
|
+
.clip(RoundedCornerShape(8.dp))
|
|
147
|
+
) { Skeleton() }
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@Composable
|
|
153
|
+
private fun AvatarGroup(
|
|
154
|
+
urls: AvatarUrls?,
|
|
155
|
+
title: String? = null,
|
|
156
|
+
size: Dp = Spacing.XXL
|
|
157
|
+
) {
|
|
158
|
+
val theme = AppTheme.current
|
|
159
|
+
val modifierBorder = remember {
|
|
160
|
+
Modifier.clip(CircleShape).border(width = (0.5).dp, color = theme.colors.border.default, shape = CircleShape)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
when (urls?.items?.size) {
|
|
164
|
+
0 -> InitialsAvatar(
|
|
165
|
+
modifier = modifierBorder,
|
|
166
|
+
name = title ?: "",
|
|
167
|
+
fallbackInitials = null,
|
|
168
|
+
size = size
|
|
169
|
+
)
|
|
170
|
+
1 -> SingleAvatar(modifier = modifierBorder, urls.items[0], size)
|
|
171
|
+
2 -> TwoAvatar(modifier = modifierBorder, urls, size)
|
|
172
|
+
3 -> ThreeAvatar(modifier = modifierBorder, urls, size)
|
|
173
|
+
4 -> FourAvatar(modifier = modifierBorder, urls, size)
|
|
174
|
+
else -> ManyAvatar(modifier = modifierBorder, urls!!, size)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
@Composable
|
|
179
|
+
private fun SingleAvatar(modifier: Modifier = Modifier, url: String, size: Dp) {
|
|
180
|
+
Image(
|
|
181
|
+
source = url,
|
|
182
|
+
options = Options(
|
|
183
|
+
contentScale = ContentScale.Crop
|
|
184
|
+
),
|
|
185
|
+
modifier = modifier
|
|
186
|
+
.size(size)
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
@Composable
|
|
191
|
+
private fun TwoAvatar(modifier: Modifier = Modifier, urls: AvatarUrls, size: Dp) {
|
|
192
|
+
val child = 24.dp
|
|
193
|
+
Box(modifier = Modifier.size(size)) {
|
|
194
|
+
Image(
|
|
195
|
+
source = urls.items[0],
|
|
196
|
+
options = Options(
|
|
197
|
+
contentScale = ContentScale.Crop
|
|
198
|
+
),
|
|
199
|
+
modifier = modifier
|
|
200
|
+
.size(child)
|
|
201
|
+
.align(Alignment.TopEnd)
|
|
202
|
+
)
|
|
203
|
+
Image(
|
|
204
|
+
source = urls.items[1],
|
|
205
|
+
options = Options(
|
|
206
|
+
contentScale = ContentScale.Crop
|
|
207
|
+
),
|
|
208
|
+
modifier = modifier
|
|
209
|
+
.size(child)
|
|
210
|
+
.align(Alignment.BottomStart)
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
@Composable
|
|
216
|
+
private fun ThreeAvatar(modifier: Modifier = Modifier, urls: AvatarUrls, size: Dp) {
|
|
217
|
+
val child = 16.dp
|
|
218
|
+
Box(modifier = Modifier.size(size).padding(2.dp)) {
|
|
219
|
+
Image(
|
|
220
|
+
source = urls.items[0],
|
|
221
|
+
options = Options(
|
|
222
|
+
contentScale = ContentScale.Crop
|
|
223
|
+
),
|
|
224
|
+
modifier = modifier
|
|
225
|
+
.size(child)
|
|
226
|
+
.align(Alignment.BottomEnd)
|
|
227
|
+
)
|
|
228
|
+
Image(
|
|
229
|
+
source = urls.items[1],
|
|
230
|
+
options = Options(
|
|
231
|
+
contentScale = ContentScale.Crop
|
|
232
|
+
),
|
|
233
|
+
modifier = modifier
|
|
234
|
+
.size(child)
|
|
235
|
+
.align(Alignment.TopCenter)
|
|
236
|
+
)
|
|
237
|
+
Image(
|
|
238
|
+
source = urls.items[2],
|
|
239
|
+
options = Options(
|
|
240
|
+
contentScale = ContentScale.Crop
|
|
241
|
+
),
|
|
242
|
+
modifier = modifier
|
|
243
|
+
.size(child)
|
|
244
|
+
.align(Alignment.BottomStart)
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
@Composable
|
|
250
|
+
private fun FourAvatar(modifier: Modifier = Modifier, urls: AvatarUrls, size: Dp) {
|
|
251
|
+
val child = 16.dp
|
|
252
|
+
Box(modifier = Modifier.size(size).padding(1.dp)) {
|
|
253
|
+
Image(
|
|
254
|
+
source = urls.items[0],
|
|
255
|
+
options = Options(
|
|
256
|
+
contentScale = ContentScale.Crop
|
|
257
|
+
),
|
|
258
|
+
modifier = modifier
|
|
259
|
+
.size(child)
|
|
260
|
+
.align(Alignment.BottomStart)
|
|
261
|
+
)
|
|
262
|
+
Image(
|
|
263
|
+
source = urls.items[1],
|
|
264
|
+
options = Options(
|
|
265
|
+
contentScale = ContentScale.Crop
|
|
266
|
+
),
|
|
267
|
+
modifier = modifier
|
|
268
|
+
.size(child)
|
|
269
|
+
.align(Alignment.BottomEnd)
|
|
270
|
+
)
|
|
271
|
+
Image(
|
|
272
|
+
source = urls.items[2],
|
|
273
|
+
options = Options(
|
|
274
|
+
contentScale = ContentScale.Crop
|
|
275
|
+
),
|
|
276
|
+
modifier = modifier
|
|
277
|
+
.size(child)
|
|
278
|
+
.align(Alignment.TopEnd)
|
|
279
|
+
)
|
|
280
|
+
Image(
|
|
281
|
+
source = urls.items[3],
|
|
282
|
+
options = Options(
|
|
283
|
+
contentScale = ContentScale.Crop
|
|
284
|
+
),
|
|
285
|
+
modifier = modifier
|
|
286
|
+
.size(child)
|
|
287
|
+
.align(Alignment.TopStart)
|
|
288
|
+
)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
@Composable
|
|
293
|
+
private fun ManyAvatar(modifier: Modifier = Modifier, urls: AvatarUrls, size: Dp) {
|
|
294
|
+
val child = 16.dp
|
|
295
|
+
val more = urls.items.size - 3
|
|
296
|
+
Box(modifier = Modifier.size(size).padding(1.dp)) {
|
|
297
|
+
Image(
|
|
298
|
+
source = urls.items[0],
|
|
299
|
+
options = Options(
|
|
300
|
+
contentScale = ContentScale.Crop
|
|
301
|
+
),
|
|
302
|
+
modifier = modifier
|
|
303
|
+
.size(child)
|
|
304
|
+
.align(Alignment.BottomStart)
|
|
305
|
+
)
|
|
306
|
+
Image(
|
|
307
|
+
source = urls.items[1],
|
|
308
|
+
options = Options(
|
|
309
|
+
contentScale = ContentScale.Crop
|
|
310
|
+
),
|
|
311
|
+
modifier = modifier
|
|
312
|
+
.size(child)
|
|
313
|
+
.align(Alignment.TopEnd)
|
|
314
|
+
)
|
|
315
|
+
Image(
|
|
316
|
+
source = urls.items[2],
|
|
317
|
+
options = Options(
|
|
318
|
+
contentScale = ContentScale.Crop
|
|
319
|
+
),
|
|
320
|
+
modifier = modifier
|
|
321
|
+
.size(child)
|
|
322
|
+
.align(Alignment.TopStart)
|
|
323
|
+
)
|
|
324
|
+
Box(
|
|
325
|
+
modifier = modifier
|
|
326
|
+
.size(child)
|
|
327
|
+
.align(Alignment.BottomEnd)
|
|
328
|
+
.background(Colors.pink_09)
|
|
329
|
+
.padding(1.dp)
|
|
330
|
+
.clipToBounds(),
|
|
331
|
+
contentAlignment = Alignment.Center
|
|
332
|
+
) {
|
|
333
|
+
Text(
|
|
334
|
+
text = "+$more",
|
|
335
|
+
color = Colors.pink_03,
|
|
336
|
+
style = Typography.descriptionXsRegular.copy(fontSize = 8.sp),
|
|
337
|
+
maxLines = 1,
|
|
338
|
+
overflow = TextOverflow.Visible
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
@Composable
|
|
345
|
+
private fun InitialsAvatar(
|
|
346
|
+
modifier: Modifier = Modifier,
|
|
347
|
+
name: String,
|
|
348
|
+
fallbackInitials: String? = null,
|
|
349
|
+
size: Dp = Spacing.XXL
|
|
350
|
+
) {
|
|
351
|
+
val initials = remember(name, fallbackInitials) {
|
|
352
|
+
fallbackInitials ?: run {
|
|
353
|
+
val words = name.trim().split(Regex("\\s+")).takeLast(2)
|
|
354
|
+
words.joinToString("") { it.firstOrNull()?.uppercase() ?: "" }
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
Box(
|
|
358
|
+
modifier = modifier
|
|
359
|
+
.size(size)
|
|
360
|
+
.background(Colors.pink_09),
|
|
361
|
+
contentAlignment = Alignment.Center
|
|
362
|
+
) {
|
|
363
|
+
Text(
|
|
364
|
+
text = initials,
|
|
365
|
+
color = Colors.pink_03,
|
|
366
|
+
style = Typography.descriptionXsRegular
|
|
367
|
+
)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
@Composable
|
|
372
|
+
private fun RemoteIcon16(url: String) {
|
|
373
|
+
Image(
|
|
374
|
+
source = url,
|
|
375
|
+
options = Options(
|
|
376
|
+
contentScale = ContentScale.Crop
|
|
377
|
+
),
|
|
378
|
+
modifier = Modifier
|
|
379
|
+
.size(16.dp)
|
|
380
|
+
.clip(RoundedCornerShape(4.dp)),
|
|
381
|
+
)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
@Immutable
|
|
385
|
+
data class AvatarUrls(val items: List<String>)
|