@momo-kits/native-kits 0.160.1-lottie.2-debug → 0.160.1-navigatorevent.1-debug
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compose/build.gradle.kts +9 -3
- package/compose/build.gradle.kts.backup +8 -2
- package/compose/compose.podspec +1 -1
- package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +9 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +8 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +54 -34
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +4 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +5 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +5 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +19 -14
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +1 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +19 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationEvent.kt +53 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +15 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +11 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +63 -49
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +62 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +22 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +12 -4
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +147 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +10 -4
- package/gradle/libs.versions.toml +2 -0
- package/gradle.properties +1 -1
- package/ios/Input/Input.swift +33 -4
- package/package.json +1 -1
- package/settings.gradle.kts +15 -3
package/compose/build.gradle.kts
CHANGED
|
@@ -30,7 +30,7 @@ kotlin {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
val fwName = gitlabArtifactId
|
|
33
|
-
val iosTargets = listOf(
|
|
33
|
+
val iosTargets = listOf(iosArm64(), iosSimulatorArm64())
|
|
34
34
|
iosTargets.forEach {
|
|
35
35
|
it.binaries.framework {
|
|
36
36
|
baseName = fwName
|
|
@@ -40,7 +40,7 @@ kotlin {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
cocoapods {
|
|
43
|
-
version = "0.160.1-
|
|
43
|
+
version = "0.160.1-navigatorevent.1-debug"
|
|
44
44
|
summary = "IOS Shared module"
|
|
45
45
|
homepage = "https://momo.vn"
|
|
46
46
|
ios.deploymentTarget = "15.0"
|
|
@@ -73,6 +73,7 @@ kotlin {
|
|
|
73
73
|
implementation(libs.jetbrains.serialization.json)
|
|
74
74
|
implementation(libs.kotlinx.datetime)
|
|
75
75
|
api(libs.native.max.api)
|
|
76
|
+
implementation(libs.compottie)
|
|
76
77
|
}
|
|
77
78
|
androidMain.dependencies {
|
|
78
79
|
implementation(libs.ktor.client.okhttp)
|
|
@@ -108,12 +109,17 @@ repositories {
|
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
mavenCentral()
|
|
112
|
+
maven { url = uri("https://maven.pkg.jetbrains.space/public/p/compose/dev") }
|
|
111
113
|
maven {
|
|
112
114
|
url = uri("https://gitlab.mservice.com.vn/api/v4/projects/5400/packages/maven")
|
|
113
115
|
credentials {username = "download_packages"; password = "gldt-bjDqLpU_sPcHDuXau2ws" }
|
|
114
116
|
}
|
|
115
117
|
maven {
|
|
116
|
-
url = uri("
|
|
118
|
+
url = uri("https://artifacts.mservice.com.vn/repository/maven-momo-app/")
|
|
119
|
+
credentials {
|
|
120
|
+
username = "viewer"
|
|
121
|
+
password = "viewer"
|
|
122
|
+
}
|
|
117
123
|
isAllowInsecureProtocol = true
|
|
118
124
|
}
|
|
119
125
|
}
|
|
@@ -30,7 +30,7 @@ kotlin {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
val fwName = gitlabArtifactId
|
|
33
|
-
val iosTargets = listOf(
|
|
33
|
+
val iosTargets = listOf(iosArm64(), iosSimulatorArm64())
|
|
34
34
|
iosTargets.forEach {
|
|
35
35
|
it.binaries.framework {
|
|
36
36
|
baseName = fwName
|
|
@@ -73,6 +73,7 @@ kotlin {
|
|
|
73
73
|
implementation(libs.jetbrains.serialization.json)
|
|
74
74
|
implementation(libs.kotlinx.datetime)
|
|
75
75
|
api(libs.native.max.api)
|
|
76
|
+
implementation(libs.compottie)
|
|
76
77
|
}
|
|
77
78
|
androidMain.dependencies {
|
|
78
79
|
implementation(libs.ktor.client.okhttp)
|
|
@@ -108,12 +109,17 @@ repositories {
|
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
mavenCentral()
|
|
112
|
+
maven { url = uri("https://maven.pkg.jetbrains.space/public/p/compose/dev") }
|
|
111
113
|
maven {
|
|
112
114
|
url = uri("https://gitlab.mservice.com.vn/api/v4/projects/5400/packages/maven")
|
|
113
115
|
credentials {username = "download_packages"; password = "gldt-bjDqLpU_sPcHDuXau2ws" }
|
|
114
116
|
}
|
|
115
117
|
maven {
|
|
116
|
-
url = uri("
|
|
118
|
+
url = uri("https://artifacts.mservice.com.vn/repository/maven-momo-app/")
|
|
119
|
+
credentials {
|
|
120
|
+
username = "viewer"
|
|
121
|
+
password = "viewer"
|
|
122
|
+
}
|
|
117
123
|
isAllowInsecureProtocol = true
|
|
118
124
|
}
|
|
119
125
|
}
|
package/compose/compose.podspec
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
package vn.momo.kits.navigation
|
|
2
|
+
|
|
3
|
+
import androidx.compose.runtime.Composable
|
|
4
|
+
|
|
5
|
+
@Composable
|
|
6
|
+
internal actual fun RegisterScrollToTop(callback: (() -> Unit)?) = Unit
|
|
7
|
+
|
|
8
|
+
internal actual fun onNavigatorEntered(navigator: Navigator) = Unit
|
|
9
|
+
internal actual fun onNavigatorExited(navigator: Navigator) = Unit
|
|
@@ -70,7 +70,7 @@ actual fun getScreenHeight(): Dp {
|
|
|
70
70
|
return getScreenDimensions().height.dp + if (getOSVersion() >= 35) 0.dp else AppStatusBar.current + AppNavigationBar.current
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
actual fun getOSVersion():
|
|
73
|
+
actual fun getOSVersion(): OSVersion = OSVersion.Android(sdk = Build.VERSION.SDK_INT)
|
|
74
74
|
|
|
75
75
|
@Composable
|
|
76
76
|
actual fun LottieAnimation(
|
|
@@ -78,8 +78,14 @@ actual fun LottieAnimation(
|
|
|
78
78
|
tintColor: Color?,
|
|
79
79
|
bgColor: Color?,
|
|
80
80
|
placedAsOverlay: Boolean,
|
|
81
|
-
modifier: Modifier
|
|
81
|
+
modifier: Modifier,
|
|
82
|
+
useCompose: Boolean
|
|
82
83
|
) {
|
|
84
|
+
if (useCompose) {
|
|
85
|
+
ComposeLottieAnimation(path, tintColor, bgColor, modifier)
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
83
89
|
val json = readJson(path)
|
|
84
90
|
|
|
85
91
|
if (json.isEmpty()) {
|
|
@@ -63,6 +63,7 @@ import androidx.compose.ui.platform.LocalDensity
|
|
|
63
63
|
import androidx.compose.ui.platform.LocalFocusManager
|
|
64
64
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
|
65
65
|
import androidx.compose.ui.text.style.TextOverflow
|
|
66
|
+
import androidx.compose.ui.text.TextStyle
|
|
66
67
|
import androidx.compose.ui.unit.Constraints
|
|
67
68
|
import androidx.compose.ui.unit.Dp
|
|
68
69
|
import androidx.compose.ui.unit.IntOffset
|
|
@@ -78,6 +79,7 @@ import vn.momo.kits.const.Colors
|
|
|
78
79
|
import vn.momo.kits.const.Radius
|
|
79
80
|
import vn.momo.kits.const.Spacing
|
|
80
81
|
import vn.momo.kits.const.Typography
|
|
82
|
+
import vn.momo.kits.modifier.conditional
|
|
81
83
|
import vn.momo.kits.modifier.kitsAutomationId
|
|
82
84
|
import vn.momo.kits.modifier.noFeedbackClickable
|
|
83
85
|
import vn.momo.kits.modifier.setAutomationId
|
|
@@ -101,6 +103,9 @@ fun LiteScreen(
|
|
|
101
103
|
useAnimationSearch: Boolean = true,
|
|
102
104
|
titlePosition: TitlePosition = TitlePosition.LEFT,
|
|
103
105
|
headerRightData: HeaderRightData? = null,
|
|
106
|
+
headerTintColor: Color? = null,
|
|
107
|
+
headerBackgroundColor: Color? = null,
|
|
108
|
+
headerSpaceBetween: Dp? = null,
|
|
104
109
|
/* End of header props */
|
|
105
110
|
|
|
106
111
|
screenContent: @Composable () -> Unit,
|
|
@@ -135,6 +140,9 @@ fun LiteScreen(
|
|
|
135
140
|
titlePosition = titlePosition,
|
|
136
141
|
useAnimationSearch = useAnimationSearch,
|
|
137
142
|
headerRightData = headerRightData,
|
|
143
|
+
tintColor = headerTintColor,
|
|
144
|
+
headerBackgroundColor = headerBackgroundColor,
|
|
145
|
+
headerSpaceBetween = headerSpaceBetween,
|
|
138
146
|
)
|
|
139
147
|
|
|
140
148
|
Box(
|
|
@@ -166,6 +174,8 @@ private fun LiteScreenHeader(
|
|
|
166
174
|
titlePosition: TitlePosition = TitlePosition.LEFT,
|
|
167
175
|
useAnimationSearch: Boolean = true,
|
|
168
176
|
onGoBack: (() -> Unit)? = null,
|
|
177
|
+
headerBackgroundColor: Color? = null,
|
|
178
|
+
headerSpaceBetween: Dp? = null,
|
|
169
179
|
inputSearchProps: LiteInputSearchProps? = null,
|
|
170
180
|
headerRight: @Composable (() -> Unit)? = null,
|
|
171
181
|
) {
|
|
@@ -242,6 +252,7 @@ private fun LiteScreenHeader(
|
|
|
242
252
|
statusBarHeight,
|
|
243
253
|
titlePosition,
|
|
244
254
|
scrollPercentage,
|
|
255
|
+
headerSpaceBetween,
|
|
245
256
|
) {
|
|
246
257
|
LiteScreenHeaderPolicy(
|
|
247
258
|
useAnimationSearch = useAnimationSearch,
|
|
@@ -249,6 +260,7 @@ private fun LiteScreenHeader(
|
|
|
249
260
|
statusBarHeight = statusBarHeight,
|
|
250
261
|
titlePosition = titlePosition,
|
|
251
262
|
scrollPercentage = scrollPercentage,
|
|
263
|
+
headerSpaceBetween = headerSpaceBetween,
|
|
252
264
|
)
|
|
253
265
|
}
|
|
254
266
|
|
|
@@ -260,17 +272,21 @@ private fun LiteScreenHeader(
|
|
|
260
272
|
HeaderId.EXTENDED_HEADER_HEIGHT.toPx(),
|
|
261
273
|
size.height,
|
|
262
274
|
)
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
275
|
+
headerBackgroundColor?.let {
|
|
276
|
+
drawRect(color = it)
|
|
277
|
+
} ?: run {
|
|
278
|
+
drawRect(color = Colors.black_01)
|
|
279
|
+
drawRect(
|
|
280
|
+
brush = Brush.linearGradient(
|
|
281
|
+
colors = listGradientColors,
|
|
282
|
+
start = Offset.Zero,
|
|
283
|
+
end = Offset(
|
|
284
|
+
x = 0f,
|
|
285
|
+
y = headerHeight * (1 - scrollPercentage.value),
|
|
286
|
+
),
|
|
287
|
+
)
|
|
272
288
|
)
|
|
273
|
-
|
|
289
|
+
}
|
|
274
290
|
},
|
|
275
291
|
content = {
|
|
276
292
|
if (onGoBack != null) {
|
|
@@ -279,17 +295,19 @@ private fun LiteScreenHeader(
|
|
|
279
295
|
.size(28.dp)
|
|
280
296
|
.layoutId(HeaderId.BACK_ID)
|
|
281
297
|
.clip(CircleShape)
|
|
282
|
-
.
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
298
|
+
.conditional(inputSearchProps?.customBackIcon == null) {
|
|
299
|
+
this.border(
|
|
300
|
+
width = 0.2.dp,
|
|
301
|
+
color = headerColor.borderColor,
|
|
302
|
+
shape = CircleShape,
|
|
303
|
+
)
|
|
304
|
+
.background(color = headerColor.backgroundButton)
|
|
305
|
+
}
|
|
288
306
|
.noFeedbackClickable(onClick = onGoBack)
|
|
289
307
|
.setAutomationId("btn_navigation_back")
|
|
290
308
|
.padding(Spacing.XS),
|
|
291
309
|
) {
|
|
292
|
-
Icon(
|
|
310
|
+
inputSearchProps?.customBackIcon?.invoke() ?: Icon(
|
|
293
311
|
source = "arrow-back",
|
|
294
312
|
color = headerColor.tintIconColor,
|
|
295
313
|
size = 20.dp,
|
|
@@ -338,6 +356,7 @@ private class LiteScreenHeaderPolicy(
|
|
|
338
356
|
private val statusBarHeight: Dp,
|
|
339
357
|
private val scrollPercentage: State<Float>,
|
|
340
358
|
private val titlePosition: TitlePosition,
|
|
359
|
+
private val headerSpaceBetween: Dp? = null,
|
|
341
360
|
) : MeasurePolicy {
|
|
342
361
|
|
|
343
362
|
override fun MeasureScope.measure(
|
|
@@ -345,6 +364,7 @@ private class LiteScreenHeaderPolicy(
|
|
|
345
364
|
constraints: Constraints
|
|
346
365
|
): MeasureResult {
|
|
347
366
|
val spacing12 = Spacing.M.roundToPx()
|
|
367
|
+
val spaceBetween = headerSpaceBetween?.roundToPx() ?: spacing12
|
|
348
368
|
val statusBarPx = statusBarHeight.roundToPx()
|
|
349
369
|
val scrollPercent = scrollPercentage.value
|
|
350
370
|
|
|
@@ -364,7 +384,7 @@ private class LiteScreenHeaderPolicy(
|
|
|
364
384
|
)
|
|
365
385
|
val inputSearchConstraints = if (isHeaderExtend) {
|
|
366
386
|
val minWidth =
|
|
367
|
-
if (useAnimationSearch) realConstraints.maxWidth - backIconPlaceable.safeWidth - headerRightPlaceable.safeWidth -
|
|
387
|
+
if (useAnimationSearch) realConstraints.maxWidth - backIconPlaceable.safeWidth - headerRightPlaceable.safeWidth - spaceBetween * 2
|
|
368
388
|
else realConstraints.maxWidth
|
|
369
389
|
realConstraints.copy(
|
|
370
390
|
maxWidth = (realConstraints.maxWidth * (1 - scrollPercent)).toInt()
|
|
@@ -372,8 +392,8 @@ private class LiteScreenHeaderPolicy(
|
|
|
372
392
|
)
|
|
373
393
|
} else {
|
|
374
394
|
var spaceConsumed = 0
|
|
375
|
-
if (backIconPlaceable.safeWidth != 0) spaceConsumed += backIconPlaceable.safeWidth +
|
|
376
|
-
if (headerRightPlaceable.safeWidth != 0) spaceConsumed += headerRightPlaceable.safeWidth +
|
|
395
|
+
if (backIconPlaceable.safeWidth != 0) spaceConsumed += backIconPlaceable.safeWidth + spaceBetween
|
|
396
|
+
if (headerRightPlaceable.safeWidth != 0) spaceConsumed += headerRightPlaceable.safeWidth + spaceBetween
|
|
377
397
|
realConstraints.copy(
|
|
378
398
|
maxWidth = realConstraints.maxWidth - spaceConsumed
|
|
379
399
|
)
|
|
@@ -382,7 +402,7 @@ private class LiteScreenHeaderPolicy(
|
|
|
382
402
|
?.measure(inputSearchConstraints)
|
|
383
403
|
val titlePlaceable = measurables.find { it.layoutId == HeaderId.TITLE_ID }?.measure(
|
|
384
404
|
constraints = realConstraints.copy(
|
|
385
|
-
maxWidth = realConstraints.maxWidth - backIconPlaceable.safeWidth - headerRightPlaceable.safeWidth -
|
|
405
|
+
maxWidth = realConstraints.maxWidth - backIconPlaceable.safeWidth - headerRightPlaceable.safeWidth - spaceBetween * 2
|
|
386
406
|
)
|
|
387
407
|
)
|
|
388
408
|
|
|
@@ -420,7 +440,7 @@ private class LiteScreenHeaderPolicy(
|
|
|
420
440
|
x = startX,
|
|
421
441
|
y = startY + backIconPlaceable.verticalCenterOffset(firstRowMaxHeight),
|
|
422
442
|
)
|
|
423
|
-
curX += backIconPlaceable.safeWidth +
|
|
443
|
+
curX += backIconPlaceable.safeWidth + spaceBetween
|
|
424
444
|
}
|
|
425
445
|
|
|
426
446
|
headerRightPlaceable?.place(
|
|
@@ -445,7 +465,7 @@ private class LiteScreenHeaderPolicy(
|
|
|
445
465
|
|
|
446
466
|
val inputSearchOffset = if (isHeaderExtend) {
|
|
447
467
|
IntOffset(
|
|
448
|
-
x = startX + ((backIconPlaceable.safeWidth +
|
|
468
|
+
x = startX + ((backIconPlaceable.safeWidth + spaceBetween) * (scrollPercent * 2f).coerceIn(
|
|
449
469
|
0f, 1f
|
|
450
470
|
)).toInt(),
|
|
451
471
|
y = (curY * (1 - scrollPercent)).toInt().coerceAtLeast(
|
|
@@ -504,6 +524,11 @@ data class LiteInputSearchProps(
|
|
|
504
524
|
|
|
505
525
|
val placeHolder: String? = null,
|
|
506
526
|
|
|
527
|
+
val customBackIcon: @Composable (() -> Unit)? = null,
|
|
528
|
+
val customSearchIcon: @Composable (() -> Unit)? = null,
|
|
529
|
+
val customPlaceHolder: @Composable (() -> Unit)? = null,
|
|
530
|
+
val customTextStyle: TextStyle? = null,
|
|
531
|
+
val customIconClear: @Composable (() -> Unit)? = null,
|
|
507
532
|
val iconRightTextField: @Composable ((Modifier) -> Unit)? = null,
|
|
508
533
|
)
|
|
509
534
|
|
|
@@ -545,7 +570,6 @@ private fun LiteInputSearch(
|
|
|
545
570
|
val textFieldModifier = remember(inputSearchProps.modifier) {
|
|
546
571
|
inputSearchProps.modifier
|
|
547
572
|
.weight(1f)
|
|
548
|
-
.sizeIn(minHeight = 36.dp)
|
|
549
573
|
}
|
|
550
574
|
BasicTextField(
|
|
551
575
|
value = textState,
|
|
@@ -554,7 +578,7 @@ private fun LiteInputSearch(
|
|
|
554
578
|
keyboardOptions = inputSearchProps.keyboardOptions,
|
|
555
579
|
keyboardActions = inputSearchProps.keyboardActions,
|
|
556
580
|
modifier = textFieldModifier,
|
|
557
|
-
textStyle = inputFieldStyle,
|
|
581
|
+
textStyle = inputSearchProps.customTextStyle ?: inputFieldStyle,
|
|
558
582
|
singleLine = true,
|
|
559
583
|
interactionSource = interactionSource,
|
|
560
584
|
decorationBox = { innerTextField ->
|
|
@@ -592,10 +616,6 @@ private fun LiteInputSearch(
|
|
|
592
616
|
|
|
593
617
|
Row(
|
|
594
618
|
modifier = Modifier
|
|
595
|
-
.background(
|
|
596
|
-
color = theme.colors.background.surface,
|
|
597
|
-
shape = RoundedCornerShape(Radius.XL),
|
|
598
|
-
)
|
|
599
619
|
.padding(
|
|
600
620
|
horizontal = Spacing.M,
|
|
601
621
|
vertical = Spacing.S,
|
|
@@ -603,7 +623,7 @@ private fun LiteInputSearch(
|
|
|
603
623
|
horizontalArrangement = Arrangement.Start,
|
|
604
624
|
verticalAlignment = Alignment.CenterVertically,
|
|
605
625
|
) {
|
|
606
|
-
Icon(
|
|
626
|
+
inputSearchProps.customSearchIcon?.invoke() ?: Icon(
|
|
607
627
|
source = "navigation_search",
|
|
608
628
|
modifier = Modifier.padding(end = Spacing.XS),
|
|
609
629
|
size = 24.dp,
|
|
@@ -614,9 +634,9 @@ private fun LiteInputSearch(
|
|
|
614
634
|
contentAlignment = Alignment.CenterStart,
|
|
615
635
|
) {
|
|
616
636
|
if (!placeHolder.isNullOrEmpty()) {
|
|
617
|
-
Text(
|
|
637
|
+
inputSearchProps.customPlaceHolder?.invoke() ?: Text(
|
|
618
638
|
text = placeHolder ?: "",
|
|
619
|
-
style = Typography.bodyDefaultRegular,
|
|
639
|
+
style = inputSearchProps.customTextStyle ?: Typography.bodyDefaultRegular,
|
|
620
640
|
maxLines = 1,
|
|
621
641
|
color = theme.colors.text.hint,
|
|
622
642
|
overflow = TextOverflow.Ellipsis
|
|
@@ -626,7 +646,7 @@ private fun LiteInputSearch(
|
|
|
626
646
|
}
|
|
627
647
|
|
|
628
648
|
if (isShowClear) {
|
|
629
|
-
Icon(
|
|
649
|
+
inputSearchProps.customIconClear?.invoke() ?: Icon(
|
|
630
650
|
source = "24_navigation_close_circle_full",
|
|
631
651
|
size = 16.dp,
|
|
632
652
|
color = theme.colors.text.hint,
|
|
@@ -717,4 +737,4 @@ fun Modifier.hideKeyboardOnTap() = composed {
|
|
|
717
737
|
focusManager.clearFocus()
|
|
718
738
|
}
|
|
719
739
|
}
|
|
720
|
-
}
|
|
740
|
+
}
|
|
@@ -59,8 +59,9 @@ import vn.momo.kits.modifier.DeprecatedModifier
|
|
|
59
59
|
import vn.momo.kits.modifier.conditional
|
|
60
60
|
import vn.momo.kits.modifier.shadow
|
|
61
61
|
import vn.momo.kits.navigation.component.SnackBar
|
|
62
|
-
import vn.momo.kits.platform.
|
|
62
|
+
import vn.momo.kits.platform.supportsImePadding
|
|
63
63
|
import vn.momo.kits.utils.getAppStatusBarHeight
|
|
64
|
+
import vn.momo.kits.utils.getNavigationBarHeight
|
|
64
65
|
|
|
65
66
|
enum class HeaderType {
|
|
66
67
|
DEFAULT,
|
|
@@ -102,7 +103,7 @@ fun Screen(
|
|
|
102
103
|
val keyboardController = LocalSoftwareKeyboardController.current
|
|
103
104
|
|
|
104
105
|
val isKeyboardVisible = isKeyboardVisible()
|
|
105
|
-
val indicator =
|
|
106
|
+
val indicator = getNavigationBarHeight()
|
|
106
107
|
val bottomPadding = if (isKeyboardVisible) 0.dp else indicator
|
|
107
108
|
|
|
108
109
|
val headerHeight = if (animatedHeader !== null)
|
|
@@ -134,7 +135,7 @@ fun Screen(
|
|
|
134
135
|
Box(
|
|
135
136
|
Modifier.fillMaxSize()
|
|
136
137
|
.background(backgroundColor ?: AppTheme.current.colors.background.default)
|
|
137
|
-
.conditional(useAvoidKeyboard &&
|
|
138
|
+
.conditional(useAvoidKeyboard && supportsImePadding()) {
|
|
138
139
|
imePadding()
|
|
139
140
|
}.then(DeprecatedModifier())
|
|
140
141
|
) {
|
|
@@ -221,6 +221,7 @@ fun Input(
|
|
|
221
221
|
onBlur: () -> Unit = {},
|
|
222
222
|
loading: Boolean = false,
|
|
223
223
|
required: Boolean = false,
|
|
224
|
+
maxLength: Int? = null,
|
|
224
225
|
fontWeight: InputFontWeight = InputFontWeight.REGULAR,
|
|
225
226
|
keyboardType: KeyboardType = KeyboardType.Text,
|
|
226
227
|
modifier: Modifier = Modifier,
|
|
@@ -319,7 +320,10 @@ fun Input(
|
|
|
319
320
|
onBlur()
|
|
320
321
|
}
|
|
321
322
|
},
|
|
322
|
-
onValueChange =
|
|
323
|
+
onValueChange = { newText ->
|
|
324
|
+
val limitedText = maxLength?.let { newText.take(it) } ?: newText
|
|
325
|
+
onChangeText(limitedText)
|
|
326
|
+
},
|
|
323
327
|
decorationBox = { innerTextField ->
|
|
324
328
|
// Floating label
|
|
325
329
|
if (floatingValue.isNotEmpty() || floatingIcon.isNotEmpty()) {
|
|
@@ -8,6 +8,7 @@ import androidx.compose.animation.core.rememberInfiniteTransition
|
|
|
8
8
|
import androidx.compose.animation.core.tween
|
|
9
9
|
import androidx.compose.foundation.background
|
|
10
10
|
import androidx.compose.foundation.border
|
|
11
|
+
import androidx.compose.ui.draw.alpha
|
|
11
12
|
import androidx.compose.foundation.clickable
|
|
12
13
|
import androidx.compose.foundation.layout.Arrangement
|
|
13
14
|
import androidx.compose.foundation.layout.Box
|
|
@@ -129,8 +130,11 @@ fun InputOTP(
|
|
|
129
130
|
if (!it.isFocused && isBlurred) onBlur()
|
|
130
131
|
if (it.isFocused && !isBlurred) isBlurred = true
|
|
131
132
|
},
|
|
132
|
-
decorationBox = {
|
|
133
|
+
decorationBox = { innerTextField ->
|
|
133
134
|
Box {
|
|
135
|
+
Box(
|
|
136
|
+
modifier = Modifier.fillMaxWidth().height(56.dp).alpha(0f)
|
|
137
|
+
) { innerTextField() }
|
|
134
138
|
if (floatingValue.isNotEmpty()) {
|
|
135
139
|
Box(
|
|
136
140
|
modifier = Modifier.wrapContentSize()
|
|
@@ -105,6 +105,8 @@ data class InputSearchProps(
|
|
|
105
105
|
val iconModifier: Modifier = Modifier,
|
|
106
106
|
val onClearPress: () -> Unit = {},
|
|
107
107
|
val leftPosition: Dp? = null,
|
|
108
|
+
val placeholderCustomRender: (@Composable () -> Unit)? = null,
|
|
109
|
+
val searchIcon: (@Composable () -> Unit)? = null
|
|
108
110
|
)
|
|
109
111
|
|
|
110
112
|
@Composable
|
|
@@ -145,12 +147,13 @@ fun InputSearch(
|
|
|
145
147
|
)
|
|
146
148
|
}
|
|
147
149
|
|
|
148
|
-
Row(
|
|
149
|
-
.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
150
|
+
Row(
|
|
151
|
+
verticalAlignment = Alignment.CenterVertically, modifier = Modifier
|
|
152
|
+
.fillMaxWidth()
|
|
153
|
+
.height(36.dp)
|
|
154
|
+
.conditional(IsShowBaseLineDebug) {
|
|
155
|
+
border(1.dp, Colors.blue_03)
|
|
156
|
+
}
|
|
154
157
|
) {
|
|
155
158
|
BasicTextField(
|
|
156
159
|
enabled = !inputSearchProps.disabled,
|
|
@@ -184,13 +187,14 @@ fun InputSearch(
|
|
|
184
187
|
horizontalArrangement = Arrangement.Start,
|
|
185
188
|
verticalAlignment = Alignment.CenterVertically
|
|
186
189
|
) {
|
|
190
|
+
|
|
187
191
|
Row(
|
|
188
192
|
modifier = Modifier.padding(
|
|
189
193
|
horizontal = Spacing.M
|
|
190
194
|
),
|
|
191
195
|
verticalAlignment = Alignment.CenterVertically
|
|
192
196
|
) {
|
|
193
|
-
Icon(
|
|
197
|
+
inputSearchProps.searchIcon?.invoke() ?: Icon(
|
|
194
198
|
source = "navigation_search",
|
|
195
199
|
modifier = Modifier.padding(end = Spacing.XS),
|
|
196
200
|
size = 24.dp,
|
|
@@ -198,13 +202,14 @@ fun InputSearch(
|
|
|
198
202
|
)
|
|
199
203
|
Box(Modifier.weight(1f)) {
|
|
200
204
|
if (inputSearchProps.text.value.isEmpty()) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
205
|
+
inputSearchProps.placeholderCustomRender?.invoke()
|
|
206
|
+
?: Text(
|
|
207
|
+
text = inputSearchProps.placeholder,
|
|
208
|
+
style = placeHolderStyle,
|
|
209
|
+
maxLines = 1,
|
|
210
|
+
color = placeholderColor,
|
|
211
|
+
overflow = TextOverflow.Ellipsis
|
|
212
|
+
)
|
|
208
213
|
}
|
|
209
214
|
innerTextField()
|
|
210
215
|
}
|
|
@@ -4,6 +4,7 @@ import androidx.compose.animation.*
|
|
|
4
4
|
import androidx.compose.animation.core.tween
|
|
5
5
|
import androidx.compose.runtime.*
|
|
6
6
|
import androidx.compose.ui.unit.Dp
|
|
7
|
+
import androidx.navigation.NavController
|
|
7
8
|
import androidx.navigation.compose.NavHost
|
|
8
9
|
import androidx.navigation.compose.composable
|
|
9
10
|
import androidx.navigation.compose.rememberNavController
|
|
@@ -26,10 +27,17 @@ fun NavigationContainer(
|
|
|
26
27
|
setNavigator: ((Navigator) -> Unit)? = null,
|
|
27
28
|
statusBarHeight: Dp? = null,
|
|
28
29
|
config: KitConfig? = null,
|
|
29
|
-
language: String? = null
|
|
30
|
+
language: String? = null,
|
|
31
|
+
onNavigationEvent: ((NavigationEvent) -> Unit)? = null
|
|
30
32
|
){
|
|
31
33
|
val navController = rememberNavController()
|
|
32
|
-
val navigator = remember { Navigator(navController = navController, maxApi = maxApi) }
|
|
34
|
+
val navigator = remember { Navigator(navController = navController, maxApi = maxApi, onNavigationEvent = onNavigationEvent) }
|
|
35
|
+
|
|
36
|
+
val recomposeCount = remember { mutableStateOf(0) }
|
|
37
|
+
SideEffect {
|
|
38
|
+
recomposeCount.value += 1
|
|
39
|
+
onNavigationEvent?.invoke(NavigationEvent.ContainerRecompose(recomposeCount.value))
|
|
40
|
+
}
|
|
33
41
|
val statusBarHeight = statusBarHeight ?: getAppStatusBarHeight()
|
|
34
42
|
val navigationBarHeight = getNavigationBarHeight()
|
|
35
43
|
|
|
@@ -129,8 +137,16 @@ fun NavigationContainer(
|
|
|
129
137
|
}
|
|
130
138
|
}
|
|
131
139
|
|
|
132
|
-
DisposableEffect(
|
|
140
|
+
DisposableEffect(navController) {
|
|
141
|
+
val listener = NavController.OnDestinationChangedListener { _, destination, _ ->
|
|
142
|
+
val current = DynamicScreenRegistry.getLatestScreen()
|
|
143
|
+
onNavigationEvent?.invoke(
|
|
144
|
+
NavigationEvent.DestinationChanged(destination.route, current?.id, current?.name)
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
navController.addOnDestinationChangedListener(listener)
|
|
133
148
|
onDispose {
|
|
149
|
+
navController.removeOnDestinationChangedListener(listener)
|
|
134
150
|
navigator.dispose()
|
|
135
151
|
}
|
|
136
152
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
package vn.momo.kits.navigation
|
|
2
|
+
|
|
3
|
+
sealed class NavigationEvent {
|
|
4
|
+
abstract val label: String
|
|
5
|
+
|
|
6
|
+
data class Push(val screenName: String, val id: Int) : NavigationEvent() {
|
|
7
|
+
override val label get() = "Push(screen=$screenName, id=$id)"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
data class Replace(val screenName: String, val id: Int) : NavigationEvent() {
|
|
11
|
+
override val label get() = "Replace(screen=$screenName, id=$id)"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
data class Pop(val count: Int, val overlayType: OverplayComponentType?) : NavigationEvent() {
|
|
15
|
+
override val label get() = "Pop(count=$count, overlay=$overlayType)"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
data class Present(val screenName: String, val id: Int) : NavigationEvent() {
|
|
19
|
+
override val label get() = "Present(screen=$screenName, id=$id)"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
data class Reset(val screenName: String, val id: Int) : NavigationEvent() {
|
|
23
|
+
override val label get() = "Reset(screen=$screenName, id=$id)"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
data class ShowModal(val parentId: Int, val barrierDismissible: Boolean) : NavigationEvent() {
|
|
27
|
+
override val label get() = "ShowModal(parentId=$parentId, barrierDismissible=$barrierDismissible)"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
data class ShowBottomSheet(val parentId: Int, val barrierDismissible: Boolean) : NavigationEvent() {
|
|
31
|
+
override val label get() = "ShowBottomSheet(parentId=$parentId, barrierDismissible=$barrierDismissible)"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
data class ShowSnackBar(val parentId: Int) : NavigationEvent() {
|
|
35
|
+
override val label get() = "ShowSnackBar(parentId=$parentId)"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
data class DestinationChanged(
|
|
39
|
+
val route: String?,
|
|
40
|
+
val id: Int?,
|
|
41
|
+
val screenName: String?
|
|
42
|
+
) : NavigationEvent() {
|
|
43
|
+
override val label get() = "DestinationChanged(route=$route, id=$id, screen=$screenName)"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
data class ContainerRecompose(val recomposeCount: Int) : NavigationEvent() {
|
|
47
|
+
override val label get() = "ContainerRecompose(count=$recomposeCount)"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
object Dispose : NavigationEvent() {
|
|
51
|
+
override val label get() = "Dispose"
|
|
52
|
+
}
|
|
53
|
+
}
|