@momo-kits/native-kits 0.151.2-test.11 → 0.151.2-test.13
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/MoMoComposeKits.podspec +54 -0
- package/compose/build.gradle.kts +31 -3
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +47 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +76 -46
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +2 -4
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +11 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +42 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +104 -2
- package/ios/Button/Button.swift +134 -92
- package/ios/Lottie/LottieView.swift +3 -3
- package/package.json +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/LottieView.kt +0 -39
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
Pod::Spec.new do |spec|
|
|
2
|
+
spec.name = 'MoMoComposeKits'
|
|
3
|
+
spec.version = '0.0.1'
|
|
4
|
+
spec.homepage = 'MoMoComposeKits'
|
|
5
|
+
spec.source = { :http=> ''}
|
|
6
|
+
spec.authors = 'M_SERVICE'
|
|
7
|
+
spec.license = ''
|
|
8
|
+
spec.summary = 'MoMoComposeKits'
|
|
9
|
+
spec.vendored_frameworks = 'build/cocoapods/framework/compose.framework'
|
|
10
|
+
spec.libraries = 'c++'
|
|
11
|
+
spec.ios.deployment_target = '13.0'
|
|
12
|
+
spec.dependency 'lottie-ios', '4.4.3'
|
|
13
|
+
|
|
14
|
+
if !Dir.exist?('build/cocoapods/framework/compose.framework') || Dir.empty?('build/cocoapods/framework/compose.framework')
|
|
15
|
+
raise "
|
|
16
|
+
|
|
17
|
+
Kotlin framework 'compose' doesn't exist yet, so a proper Xcode project can't be generated.
|
|
18
|
+
'pod install' should be executed after running ':generateDummyFramework' Gradle task:
|
|
19
|
+
|
|
20
|
+
./gradlew :compose:generateDummyFramework
|
|
21
|
+
|
|
22
|
+
Alternatively, proper pod installation is performed during Gradle sync in the IDE (if Podfile location is set)"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
spec.xcconfig = {
|
|
26
|
+
'ENABLE_USER_SCRIPT_SANDBOXING' => 'NO',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
spec.pod_target_xcconfig = {
|
|
30
|
+
'KOTLIN_PROJECT_PATH' => ':compose',
|
|
31
|
+
'PRODUCT_MODULE_NAME' => 'compose',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
spec.script_phases = [
|
|
35
|
+
{
|
|
36
|
+
:name => 'Build MoMoComposeKits',
|
|
37
|
+
:execution_position => :before_compile,
|
|
38
|
+
:shell_path => '/bin/sh',
|
|
39
|
+
:script => <<-SCRIPT
|
|
40
|
+
if [ "YES" = "$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED" ]; then
|
|
41
|
+
echo "Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\""
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
set -ev
|
|
45
|
+
REPO_ROOT="$PODS_TARGET_SRCROOT"
|
|
46
|
+
"$REPO_ROOT/../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \
|
|
47
|
+
-Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \
|
|
48
|
+
-Pkotlin.native.cocoapods.archs="$ARCHS" \
|
|
49
|
+
-Pkotlin.native.cocoapods.configuration="$CONFIGURATION"
|
|
50
|
+
SCRIPT
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
spec.resources = ['build/compose/cocoapods/compose-resources']
|
|
54
|
+
end
|
package/compose/build.gradle.kts
CHANGED
|
@@ -3,13 +3,23 @@ plugins {
|
|
|
3
3
|
alias(libs.plugins.jetbrains.kotlin.multiplatform)
|
|
4
4
|
alias(libs.plugins.compose)
|
|
5
5
|
alias(libs.plugins.kotlin.compose.compiler)
|
|
6
|
+
id(libs.plugins.jetbrains.kotlin.cocoapods.get().pluginId)
|
|
6
7
|
kotlin("plugin.serialization")
|
|
7
8
|
id("maven-publish")
|
|
8
9
|
}
|
|
10
|
+
fun getVersionFromPackageJson(): String {
|
|
11
|
+
val packageJsonFile = file("../package.json")
|
|
12
|
+
val packageJsonText = packageJsonFile.readText()
|
|
13
|
+
val versionRegex = "\"version\"\\s*:\\s*\"([^\"]*)\"".toRegex()
|
|
14
|
+
val matchResult = versionRegex.find(packageJsonText)
|
|
15
|
+
return matchResult?.groupValues?.get(1) ?: throw IllegalStateException("Version not found in package.json")
|
|
16
|
+
}
|
|
9
17
|
|
|
18
|
+
val libName = "MoMoComposeKits"
|
|
10
19
|
val libGroup = "vn.momo.kits"
|
|
11
20
|
val libVersion = "0.0.1"
|
|
12
21
|
|
|
22
|
+
version = getVersionFromPackageJson()
|
|
13
23
|
kotlin {
|
|
14
24
|
androidTarget {
|
|
15
25
|
publishLibraryVariants("release")
|
|
@@ -26,11 +36,30 @@ kotlin {
|
|
|
26
36
|
|
|
27
37
|
iosTargets.forEach { iosTarget ->
|
|
28
38
|
iosTarget.binaries.framework {
|
|
29
|
-
baseName =
|
|
39
|
+
baseName = libName
|
|
30
40
|
isStatic = true
|
|
31
41
|
}
|
|
32
42
|
}
|
|
33
43
|
|
|
44
|
+
cocoapods {
|
|
45
|
+
name = libName
|
|
46
|
+
summary = libName
|
|
47
|
+
homepage = libName
|
|
48
|
+
authors = "M_SERVICE"
|
|
49
|
+
ios.deploymentTarget = "13.0"
|
|
50
|
+
|
|
51
|
+
framework {
|
|
52
|
+
isStatic = true
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
pod("lottie-ios") {
|
|
56
|
+
moduleName = "Lottie"
|
|
57
|
+
version = "4.4.3"
|
|
58
|
+
extraOpts += listOf("-compiler-option", "-fmodules")
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
34
63
|
sourceSets {
|
|
35
64
|
val commonMain by getting {
|
|
36
65
|
dependencies {
|
|
@@ -47,8 +76,6 @@ kotlin {
|
|
|
47
76
|
implementation(libs.coil.multiplatform.network.ktor)
|
|
48
77
|
implementation(libs.jetbrains.serialization.json)
|
|
49
78
|
implementation(libs.kotlinx.datetime)
|
|
50
|
-
implementation(libs.compottie)
|
|
51
|
-
implementation(libs.compottie.res)
|
|
52
79
|
api(project(":NativeMaxApi"))
|
|
53
80
|
}
|
|
54
81
|
}
|
|
@@ -57,6 +84,7 @@ kotlin {
|
|
|
57
84
|
implementation(libs.ktor.client.okhttp)
|
|
58
85
|
implementation(libs.jetbrains.coroutines.android)
|
|
59
86
|
api("androidx.activity:activity-compose:1.8.2")
|
|
87
|
+
implementation(libs.airbnb.lottie)
|
|
60
88
|
}
|
|
61
89
|
}
|
|
62
90
|
if (isDebug) {
|
|
@@ -3,16 +3,28 @@ package vn.momo.kits.platform
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.res.Resources
|
|
5
5
|
import android.graphics.BlurMaskFilter
|
|
6
|
+
import android.graphics.PorterDuff
|
|
7
|
+
import android.graphics.PorterDuffColorFilter
|
|
6
8
|
import android.os.Build
|
|
7
9
|
import androidx.compose.runtime.Composable
|
|
10
|
+
import androidx.compose.runtime.getValue
|
|
11
|
+
import androidx.compose.ui.Modifier
|
|
12
|
+
import androidx.compose.ui.graphics.Color
|
|
8
13
|
import androidx.compose.ui.graphics.NativePaint
|
|
14
|
+
import androidx.compose.ui.graphics.toArgb
|
|
9
15
|
import androidx.compose.ui.platform.LocalConfiguration
|
|
10
|
-
import androidx.compose.ui.platform.LocalView
|
|
11
16
|
import androidx.compose.ui.unit.Dp
|
|
12
17
|
import androidx.compose.ui.unit.dp
|
|
13
|
-
import
|
|
18
|
+
import com.airbnb.lottie.LottieProperty
|
|
19
|
+
import com.airbnb.lottie.compose.LottieAnimation
|
|
20
|
+
import com.airbnb.lottie.compose.LottieCompositionSpec
|
|
21
|
+
import com.airbnb.lottie.compose.LottieConstants
|
|
22
|
+
import com.airbnb.lottie.compose.rememberLottieComposition
|
|
23
|
+
import com.airbnb.lottie.compose.rememberLottieDynamicProperties
|
|
24
|
+
import com.airbnb.lottie.compose.rememberLottieDynamicProperty
|
|
14
25
|
import vn.momo.kits.const.AppNavigationBar
|
|
15
26
|
import vn.momo.kits.const.AppStatusBar
|
|
27
|
+
import vn.momo.kits.utils.readJson
|
|
16
28
|
import androidx.activity.compose.BackHandler as AndroidBackHandler
|
|
17
29
|
|
|
18
30
|
actual fun getPlatformName(): String = "Android"
|
|
@@ -50,4 +62,36 @@ actual fun getScreenHeight(): Dp {
|
|
|
50
62
|
return getScreenDimensions().height.dp + if (getAndroidBuildVersion() >= 35) 0.dp else AppStatusBar.current + AppNavigationBar.current
|
|
51
63
|
}
|
|
52
64
|
|
|
53
|
-
actual fun getAndroidBuildVersion(): Int = Build.VERSION.SDK_INT
|
|
65
|
+
actual fun getAndroidBuildVersion(): Int = Build.VERSION.SDK_INT
|
|
66
|
+
|
|
67
|
+
@Composable
|
|
68
|
+
actual fun LottieAnimation(
|
|
69
|
+
path: String,
|
|
70
|
+
tintColor: Color?,
|
|
71
|
+
bgColor: Color?,
|
|
72
|
+
modifier: Modifier
|
|
73
|
+
) {
|
|
74
|
+
val composition by rememberLottieComposition(
|
|
75
|
+
LottieCompositionSpec.JsonString(readJson(path))
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
val colorFilter = PorterDuffColorFilter(
|
|
79
|
+
tintColor?.toArgb() ?: Color.White.toArgb(),
|
|
80
|
+
PorterDuff.Mode.SRC_ATOP
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
val dynamicProperties = rememberLottieDynamicProperties(
|
|
84
|
+
rememberLottieDynamicProperty(
|
|
85
|
+
property = LottieProperty.COLOR_FILTER,
|
|
86
|
+
value = colorFilter,
|
|
87
|
+
keyPath = arrayOf("**")
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
LottieAnimation(
|
|
92
|
+
composition = composition,
|
|
93
|
+
iterations = LottieConstants.IterateForever,
|
|
94
|
+
dynamicProperties = if (tintColor != null ) dynamicProperties else null,
|
|
95
|
+
modifier = modifier
|
|
96
|
+
)
|
|
97
|
+
}
|
|
@@ -12,10 +12,11 @@ import androidx.compose.foundation.layout.padding
|
|
|
12
12
|
import androidx.compose.foundation.layout.size
|
|
13
13
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
14
14
|
import androidx.compose.runtime.Composable
|
|
15
|
+
import androidx.compose.runtime.CompositionLocalProvider
|
|
15
16
|
import androidx.compose.runtime.remember
|
|
17
|
+
import androidx.compose.runtime.staticCompositionLocalOf
|
|
16
18
|
import androidx.compose.ui.Alignment
|
|
17
19
|
import androidx.compose.ui.Modifier
|
|
18
|
-
import androidx.compose.ui.draw.alpha
|
|
19
20
|
import androidx.compose.ui.draw.clip
|
|
20
21
|
import androidx.compose.ui.graphics.Color
|
|
21
22
|
import androidx.compose.ui.text.TextStyle
|
|
@@ -28,6 +29,7 @@ import vn.momo.kits.const.Radius
|
|
|
28
29
|
import vn.momo.kits.const.Spacing
|
|
29
30
|
import vn.momo.kits.const.Typography
|
|
30
31
|
import vn.momo.kits.modifier.activeOpacityClickable
|
|
32
|
+
import vn.momo.kits.platform.LottieAnimation
|
|
31
33
|
|
|
32
34
|
enum class ButtonType {
|
|
33
35
|
PRIMARY,
|
|
@@ -105,8 +107,9 @@ fun getIconSpace(size: Size): Dp {
|
|
|
105
107
|
}
|
|
106
108
|
|
|
107
109
|
@Composable
|
|
108
|
-
fun getTextColor(type: ButtonType): Color {
|
|
110
|
+
fun getTextColor(loading: Boolean, type: ButtonType): Color {
|
|
109
111
|
val theme = AppTheme.current
|
|
112
|
+
|
|
110
113
|
return remember(type, theme) {
|
|
111
114
|
when (type) {
|
|
112
115
|
ButtonType.DISABLED -> theme.colors.text.disable
|
|
@@ -116,28 +119,28 @@ fun getTextColor(type: ButtonType): Color {
|
|
|
116
119
|
ButtonType.TONAL -> theme.colors.primary
|
|
117
120
|
ButtonType.DANGER -> Colors.black_01
|
|
118
121
|
ButtonType.TEXT -> theme.colors.primary
|
|
119
|
-
}
|
|
122
|
+
}.withLoading(loading)
|
|
120
123
|
}
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
@Composable
|
|
124
127
|
fun RenderTitle(size: Size, title: String = "", type: ButtonType) {
|
|
125
128
|
val style = remember(size) { getStyle(size) }
|
|
126
|
-
val color =
|
|
129
|
+
val color = TextColor.current
|
|
127
130
|
Text(style = style, text = title, color = color, overflow = TextOverflow.Ellipsis, maxLines = 1)
|
|
128
131
|
}
|
|
129
132
|
|
|
130
133
|
@Composable
|
|
131
134
|
fun RenderLeading(
|
|
132
|
-
loading: Boolean,
|
|
133
135
|
size: Size,
|
|
134
136
|
useTintColor: Boolean = true,
|
|
135
|
-
type: ButtonType,
|
|
136
137
|
iconLeft: String = "",
|
|
137
138
|
) {
|
|
139
|
+
val loading = IsLoading.current
|
|
140
|
+
val bgColor = BackgroundColor.current
|
|
138
141
|
val iconSize = remember(size) { getIconSize(size) }
|
|
139
142
|
val marginRight = remember(size) { getIconSpace(size) }
|
|
140
|
-
val color = if (useTintColor)
|
|
143
|
+
val color = if (useTintColor) TextColor.current else Color.Unspecified
|
|
141
144
|
|
|
142
145
|
Row(
|
|
143
146
|
verticalAlignment = Alignment.CenterVertically,
|
|
@@ -145,10 +148,11 @@ fun RenderLeading(
|
|
|
145
148
|
) {
|
|
146
149
|
if (loading) {
|
|
147
150
|
Box(Modifier.padding(end = marginRight)) {
|
|
148
|
-
|
|
149
|
-
path = "files/lottie_circle_loader.json",
|
|
150
|
-
tintColor = color,
|
|
151
|
+
LottieAnimation(
|
|
151
152
|
modifier = Modifier.size(iconSize),
|
|
153
|
+
bgColor = bgColor,
|
|
154
|
+
tintColor = color,
|
|
155
|
+
path = "files/lottie_circle_loader"
|
|
152
156
|
)
|
|
153
157
|
}
|
|
154
158
|
} else {
|
|
@@ -169,9 +173,8 @@ fun RenderTrailing(
|
|
|
169
173
|
iconRight: String,
|
|
170
174
|
size: Size,
|
|
171
175
|
useTintColor: Boolean,
|
|
172
|
-
type: ButtonType,
|
|
173
176
|
) {
|
|
174
|
-
val color = if (useTintColor)
|
|
177
|
+
val color = if (useTintColor) TextColor.current else Color.Unspecified
|
|
175
178
|
val marginLeft = remember(size) { getIconSpace(size) }
|
|
176
179
|
|
|
177
180
|
if (iconRight.isNotEmpty()) {
|
|
@@ -192,39 +195,60 @@ fun getTypeStyle(
|
|
|
192
195
|
size: Size,
|
|
193
196
|
): Modifier {
|
|
194
197
|
val theme = AppTheme.current
|
|
198
|
+
val bgColor = BackgroundColor.current
|
|
195
199
|
val radius = remember(size) { size.value.radius }
|
|
200
|
+
val modifier = Modifier.background(bgColor)
|
|
196
201
|
|
|
197
202
|
return remember(type, color, theme, radius) {
|
|
198
203
|
when (type) {
|
|
199
|
-
ButtonType.DISABLED ->
|
|
204
|
+
ButtonType.DISABLED -> modifier
|
|
200
205
|
.border(0.dp, Color.Unspecified, RoundedCornerShape(radius))
|
|
201
206
|
|
|
202
|
-
ButtonType.PRIMARY ->
|
|
207
|
+
ButtonType.PRIMARY -> modifier
|
|
203
208
|
.border(0.dp, Color.Unspecified, RoundedCornerShape(radius))
|
|
204
209
|
|
|
205
|
-
ButtonType.SECONDARY ->
|
|
206
|
-
.background(theme.colors.background.surface)
|
|
210
|
+
ButtonType.SECONDARY -> modifier
|
|
207
211
|
.border(1.dp, theme.colors.border.default, RoundedCornerShape(radius))
|
|
208
212
|
|
|
209
|
-
ButtonType.OUTLINE ->
|
|
210
|
-
.background(theme.colors.background.surface)
|
|
213
|
+
ButtonType.OUTLINE -> modifier
|
|
211
214
|
.border(
|
|
212
215
|
1.dp,
|
|
213
216
|
color ?: theme.colors.primary,
|
|
214
217
|
RoundedCornerShape(radius)
|
|
215
218
|
)
|
|
216
219
|
|
|
217
|
-
ButtonType.TONAL ->
|
|
220
|
+
ButtonType.TONAL -> modifier
|
|
218
221
|
.border(0.dp, Color.Unspecified, RoundedCornerShape(radius))
|
|
219
222
|
|
|
220
|
-
ButtonType.DANGER ->
|
|
223
|
+
ButtonType.DANGER -> modifier
|
|
221
224
|
.border(0.dp, Color.Unspecified, RoundedCornerShape(radius))
|
|
222
225
|
|
|
223
|
-
ButtonType.TEXT ->
|
|
226
|
+
ButtonType.TEXT -> modifier
|
|
224
227
|
}
|
|
225
228
|
}
|
|
226
229
|
}
|
|
227
230
|
|
|
231
|
+
@Composable
|
|
232
|
+
fun getButtonBackgroundColor(
|
|
233
|
+
loading: Boolean,
|
|
234
|
+
type: ButtonType
|
|
235
|
+
): Color {
|
|
236
|
+
val theme = AppTheme.current
|
|
237
|
+
|
|
238
|
+
return when (type) {
|
|
239
|
+
ButtonType.DISABLED -> theme.colors.background.disable.withLoading(loading)
|
|
240
|
+
ButtonType.PRIMARY -> theme.colors.primary.withLoading(loading)
|
|
241
|
+
ButtonType.SECONDARY -> theme.colors.background.surface.withLoading(loading)
|
|
242
|
+
ButtonType.OUTLINE -> theme.colors.background.surface.withLoading(loading)
|
|
243
|
+
ButtonType.TONAL -> theme.colors.background.tonal.withLoading(loading)
|
|
244
|
+
ButtonType.DANGER -> theme.colors.error.primary.withLoading(loading)
|
|
245
|
+
ButtonType.TEXT -> Color.Unspecified
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
fun Color.withLoading(loading: Boolean): Color =
|
|
250
|
+
this.copy(alpha = if (loading) 0.75f else 1f)
|
|
251
|
+
|
|
228
252
|
/**
|
|
229
253
|
* @param onClick called when this button is clicked
|
|
230
254
|
* @param type [ButtonType] that define the type of button.
|
|
@@ -266,30 +290,36 @@ fun Button(
|
|
|
266
290
|
)
|
|
267
291
|
|
|
268
292
|
val sizeSpecs = remember(size) { size.value }
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
293
|
+
CompositionLocalProvider(
|
|
294
|
+
IsLoading provides loading,
|
|
295
|
+
BackgroundColor provides getButtonBackgroundColor(loading, type),
|
|
296
|
+
TextColor provides getTextColor(loading, type)
|
|
297
|
+
){
|
|
298
|
+
Row(
|
|
299
|
+
modifier = customModifier
|
|
300
|
+
.then(getTypeStyle(type, size = size))
|
|
301
|
+
.padding(horizontal = sizeSpecs.padding)
|
|
302
|
+
.defaultMinSize(minWidth = sizeSpecs.width)
|
|
303
|
+
.height(sizeSpecs.height),
|
|
304
|
+
horizontalArrangement = Arrangement.Center,
|
|
305
|
+
verticalAlignment = Alignment.CenterVertically,
|
|
306
|
+
) {
|
|
307
|
+
RenderLeading(
|
|
308
|
+
size = size,
|
|
309
|
+
useTintColor = useTintColor,
|
|
310
|
+
iconLeft = iconLeft
|
|
311
|
+
)
|
|
312
|
+
RenderTitle(size, title, type = type)
|
|
313
|
+
RenderTrailing(
|
|
314
|
+
size = size,
|
|
315
|
+
useTintColor = useTintColor,
|
|
316
|
+
iconRight = iconRight
|
|
317
|
+
)
|
|
318
|
+
}
|
|
294
319
|
}
|
|
295
320
|
}
|
|
321
|
+
|
|
322
|
+
private val IsLoading = staticCompositionLocalOf<Boolean> { false }
|
|
323
|
+
private val BackgroundColor = staticCompositionLocalOf<Color> { Color.Transparent }
|
|
324
|
+
private val TextColor = staticCompositionLocalOf<Color> { Color.Transparent }
|
|
325
|
+
|
package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt
CHANGED
|
@@ -2,6 +2,7 @@ package vn.momo.kits.components.datetimepicker
|
|
|
2
2
|
|
|
3
3
|
import androidx.compose.runtime.Composable
|
|
4
4
|
import androidx.compose.runtime.remember
|
|
5
|
+
import kotlinx.datetime.Clock
|
|
5
6
|
import kotlinx.datetime.DatePeriod
|
|
6
7
|
import kotlinx.datetime.LocalDateTime
|
|
7
8
|
import kotlinx.datetime.TimeZone
|
|
@@ -9,8 +10,6 @@ import kotlinx.datetime.minus
|
|
|
9
10
|
import kotlinx.datetime.number
|
|
10
11
|
import kotlinx.datetime.plus
|
|
11
12
|
import kotlinx.datetime.toLocalDateTime
|
|
12
|
-
import kotlin.time.Clock.System.now
|
|
13
|
-
import kotlin.time.ExperimentalTime
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* Format a LocalDateTime object into a string
|
|
@@ -150,9 +149,8 @@ val timeMode = listOf("AM", "PM")
|
|
|
150
149
|
/**
|
|
151
150
|
* Get today's date
|
|
152
151
|
*/
|
|
153
|
-
@OptIn(ExperimentalTime::class)
|
|
154
152
|
fun getCurrentDateTime(): LocalDateTime {
|
|
155
|
-
return now().toLocalDateTime(TimeZone.currentSystemDefault())
|
|
153
|
+
return Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault())
|
|
156
154
|
}
|
|
157
155
|
|
|
158
156
|
/**
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
package vn.momo.kits.platform
|
|
2
2
|
|
|
3
3
|
import androidx.compose.runtime.Composable
|
|
4
|
+
import androidx.compose.ui.Modifier
|
|
5
|
+
import androidx.compose.ui.graphics.Color
|
|
4
6
|
import androidx.compose.ui.graphics.NativePaint
|
|
5
7
|
import androidx.compose.ui.unit.Dp
|
|
6
8
|
|
|
@@ -25,4 +27,12 @@ expect fun BackHandler(enabled: Boolean, onBack: () -> Unit)
|
|
|
25
27
|
@Composable
|
|
26
28
|
expect fun getScreenHeight(): Dp
|
|
27
29
|
|
|
28
|
-
expect fun getAndroidBuildVersion(): Int
|
|
30
|
+
expect fun getAndroidBuildVersion(): Int
|
|
31
|
+
|
|
32
|
+
@Composable
|
|
33
|
+
expect fun LottieAnimation(
|
|
34
|
+
path: String,
|
|
35
|
+
tintColor: Color? = null,
|
|
36
|
+
bgColor: Color? = null,
|
|
37
|
+
modifier: Modifier = Modifier
|
|
38
|
+
)
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
package vn.momo.kits.utils
|
|
2
2
|
|
|
3
3
|
import androidx.compose.runtime.Composable
|
|
4
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
5
|
+
import androidx.compose.runtime.MutableState
|
|
6
|
+
import androidx.compose.runtime.getValue
|
|
7
|
+
import androidx.compose.runtime.mutableStateOf
|
|
8
|
+
import androidx.compose.runtime.remember
|
|
4
9
|
import org.jetbrains.compose.resources.DrawableResource
|
|
5
10
|
import org.jetbrains.compose.resources.InternalResourceApi
|
|
6
11
|
import org.jetbrains.compose.resources.ResourceItem
|
|
12
|
+
import org.jetbrains.compose.resources.readResourceBytes
|
|
7
13
|
|
|
8
14
|
@OptIn(InternalResourceApi::class)
|
|
9
15
|
@Composable
|
|
@@ -12,4 +18,40 @@ fun getResource(name: String): DrawableResource {
|
|
|
12
18
|
"drawable:$name",
|
|
13
19
|
setOf(ResourceItem(setOf(), "drawable/$name", -1, -1))
|
|
14
20
|
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@OptIn(InternalResourceApi::class)
|
|
24
|
+
@Composable
|
|
25
|
+
fun readJson(name: String): String {
|
|
26
|
+
val path = name.plus(".json")
|
|
27
|
+
|
|
28
|
+
val jsonContent by rememberState(path, { "" }) {
|
|
29
|
+
val cached = resourceCache.getOrPut(path) {
|
|
30
|
+
ResourceCache.JSON(
|
|
31
|
+
readResourceBytes(path).decodeToString()
|
|
32
|
+
)
|
|
33
|
+
} as ResourceCache.JSON
|
|
34
|
+
cached.json
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return jsonContent
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private val resourceCache = mutableMapOf<String, ResourceCache>()
|
|
41
|
+
|
|
42
|
+
private sealed interface ResourceCache {
|
|
43
|
+
class JSON(val json: String) : ResourceCache
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@Composable
|
|
47
|
+
fun <T> rememberState(
|
|
48
|
+
key: Any,
|
|
49
|
+
getDefault: () -> T,
|
|
50
|
+
block: suspend () -> T
|
|
51
|
+
): MutableState<T> {
|
|
52
|
+
val state = remember(key) { mutableStateOf(getDefault()) }
|
|
53
|
+
LaunchedEffect(key) {
|
|
54
|
+
state.value = block()
|
|
55
|
+
}
|
|
56
|
+
return state
|
|
15
57
|
}
|
|
@@ -1,18 +1,33 @@
|
|
|
1
1
|
package vn.momo.kits.platform
|
|
2
2
|
|
|
3
|
-
import androidx.compose.foundation.
|
|
3
|
+
import androidx.compose.foundation.layout.Box
|
|
4
4
|
import androidx.compose.foundation.layout.WindowInsets
|
|
5
5
|
import androidx.compose.foundation.layout.asPaddingValues
|
|
6
|
+
import androidx.compose.foundation.layout.fillMaxSize
|
|
6
7
|
import androidx.compose.foundation.layout.systemBars
|
|
7
8
|
import androidx.compose.runtime.Composable
|
|
9
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
10
|
+
import androidx.compose.runtime.getValue
|
|
11
|
+
import androidx.compose.runtime.mutableStateOf
|
|
12
|
+
import androidx.compose.runtime.remember
|
|
13
|
+
import androidx.compose.runtime.setValue
|
|
14
|
+
import androidx.compose.ui.Modifier
|
|
15
|
+
import androidx.compose.ui.graphics.Color
|
|
8
16
|
import androidx.compose.ui.graphics.NativePaint
|
|
17
|
+
import androidx.compose.ui.graphics.toArgb
|
|
18
|
+
import androidx.compose.ui.interop.UIKitView
|
|
9
19
|
import androidx.compose.ui.unit.Dp
|
|
10
20
|
import androidx.compose.ui.unit.dp
|
|
21
|
+
import cocoapods.lottie_ios.CompatibleAnimation
|
|
22
|
+
import cocoapods.lottie_ios.CompatibleAnimationView
|
|
23
|
+
import cocoapods.lottie_ios.CompatibleAnimationKeypath
|
|
11
24
|
import kotlinx.cinterop.ExperimentalForeignApi
|
|
12
25
|
import kotlinx.cinterop.get
|
|
13
26
|
import kotlinx.cinterop.memScoped
|
|
14
27
|
import org.jetbrains.skia.FilterBlurMode
|
|
15
28
|
import org.jetbrains.skia.MaskFilter
|
|
29
|
+
import platform.Foundation.NSBundle
|
|
30
|
+
import platform.UIKit.UIColor
|
|
16
31
|
import platform.UIKit.UIScreen
|
|
17
32
|
|
|
18
33
|
|
|
@@ -46,4 +61,91 @@ actual fun getScreenHeight(): Dp {
|
|
|
46
61
|
return getScreenDimensions().height.dp
|
|
47
62
|
}
|
|
48
63
|
|
|
49
|
-
actual fun getAndroidBuildVersion(): Int = 999
|
|
64
|
+
actual fun getAndroidBuildVersion(): Int = 999
|
|
65
|
+
|
|
66
|
+
@OptIn(ExperimentalForeignApi::class)
|
|
67
|
+
@Composable
|
|
68
|
+
actual fun LottieAnimation(
|
|
69
|
+
path: String,
|
|
70
|
+
tintColor: Color?,
|
|
71
|
+
bgColor: Color?,
|
|
72
|
+
modifier: Modifier
|
|
73
|
+
) {
|
|
74
|
+
var animation by remember { mutableStateOf<CompatibleAnimation?>(null) }
|
|
75
|
+
|
|
76
|
+
LaunchedEffect(Unit) {
|
|
77
|
+
animation = CompatibleAnimation(name = "compose-resources/composeResources/vn.momo.compose.resources/".plus(path), subdirectory = null, NSBundle.mainBundle)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
when (val value = animation) {
|
|
81
|
+
null -> {
|
|
82
|
+
Box(modifier)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
else -> {
|
|
86
|
+
val factory = remember {
|
|
87
|
+
{
|
|
88
|
+
CompatibleAnimationView(value).also {
|
|
89
|
+
it.translatesAutoresizingMaskIntoConstraints = true
|
|
90
|
+
it.setBackgroundColor(bgColor?.toUIColor() ?: UIColor.whiteColor)
|
|
91
|
+
it.setLoopAnimationCount(-1.0)
|
|
92
|
+
it.setAnimationSpeed(1.0)
|
|
93
|
+
|
|
94
|
+
if (tintColor != null) {
|
|
95
|
+
val uiColor = tintColor.toUIColor()
|
|
96
|
+
|
|
97
|
+
it.setColorValue(
|
|
98
|
+
uiColor,
|
|
99
|
+
CompatibleAnimationKeypath("**.Fill 1.Color")
|
|
100
|
+
)
|
|
101
|
+
it.setColorValue(
|
|
102
|
+
uiColor,
|
|
103
|
+
CompatibleAnimationKeypath("**.Fill.Color")
|
|
104
|
+
)
|
|
105
|
+
it.setColorValue(
|
|
106
|
+
uiColor,
|
|
107
|
+
CompatibleAnimationKeypath("**.Color")
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
it.setColorValue(
|
|
111
|
+
uiColor,
|
|
112
|
+
CompatibleAnimationKeypath("**.Stroke 1.Color")
|
|
113
|
+
)
|
|
114
|
+
it.setColorValue(
|
|
115
|
+
uiColor,
|
|
116
|
+
CompatibleAnimationKeypath("**.Stroke.Color")
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
it.play()
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
Box(modifier) {
|
|
128
|
+
UIKitView(
|
|
129
|
+
modifier = Modifier.fillMaxSize(),
|
|
130
|
+
factory = factory,
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
fun Color.toUIColor(): UIColor {
|
|
138
|
+
val argb = this.toArgb()
|
|
139
|
+
|
|
140
|
+
val a = ((argb shr 24) and 0xFF) / 255.0
|
|
141
|
+
val r = ((argb shr 16) and 0xFF) / 255.0
|
|
142
|
+
val g = ((argb shr 8) and 0xFF) / 255.0
|
|
143
|
+
val b = (argb and 0xFF) / 255.0
|
|
144
|
+
|
|
145
|
+
return UIColor(
|
|
146
|
+
red = r,
|
|
147
|
+
green = g,
|
|
148
|
+
blue = b,
|
|
149
|
+
alpha = a
|
|
150
|
+
)
|
|
151
|
+
}
|
package/ios/Button/Button.swift
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import Foundation
|
|
3
2
|
import SwiftUI
|
|
4
3
|
import Lottie
|
|
@@ -23,12 +22,85 @@ public enum ButtonSize {
|
|
|
23
22
|
case small
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
public struct ButtonStyleData {
|
|
26
|
+
let background: Color
|
|
27
|
+
let content: Color
|
|
28
|
+
let border: Color?
|
|
29
|
+
let borderWidth: CGFloat
|
|
30
|
+
|
|
31
|
+
public init(
|
|
32
|
+
background: Color,
|
|
33
|
+
content: Color,
|
|
34
|
+
border: Color? = nil,
|
|
35
|
+
borderWidth: CGFloat = 1
|
|
36
|
+
) {
|
|
37
|
+
self.background = background
|
|
38
|
+
self.content = content
|
|
39
|
+
self.border = border
|
|
40
|
+
self.borderWidth = borderWidth
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// MARK: - ButtonType Extension (Style mapping)
|
|
45
|
+
|
|
46
|
+
public extension ButtonType {
|
|
47
|
+
var style: ButtonStyleData {
|
|
48
|
+
switch self {
|
|
49
|
+
case .primary:
|
|
50
|
+
return ButtonStyleData(
|
|
51
|
+
background: Colors.primary,
|
|
52
|
+
content: Colors.black01
|
|
53
|
+
)
|
|
54
|
+
case .danger:
|
|
55
|
+
return ButtonStyleData(
|
|
56
|
+
background: Colors.error,
|
|
57
|
+
content: Colors.black01
|
|
58
|
+
)
|
|
59
|
+
case .tonal:
|
|
60
|
+
return ButtonStyleData(
|
|
61
|
+
background: Colors.primaryLight,
|
|
62
|
+
content: Colors.primary
|
|
63
|
+
)
|
|
64
|
+
case .text:
|
|
65
|
+
return ButtonStyleData(
|
|
66
|
+
background: .clear,
|
|
67
|
+
content: Colors.primary
|
|
68
|
+
)
|
|
69
|
+
case .secondary:
|
|
70
|
+
return ButtonStyleData(
|
|
71
|
+
background: Colors.black01,
|
|
72
|
+
content: Colors.black17,
|
|
73
|
+
border: Colors.black04
|
|
74
|
+
)
|
|
75
|
+
case .outline:
|
|
76
|
+
return ButtonStyleData(
|
|
77
|
+
background: Colors.card,
|
|
78
|
+
content: Colors.primary,
|
|
79
|
+
border: Colors.primary
|
|
80
|
+
)
|
|
81
|
+
case .disabled:
|
|
82
|
+
return ButtonStyleData(
|
|
83
|
+
background: Colors.black05,
|
|
84
|
+
content: Colors.black06
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// MARK: - Button View
|
|
27
91
|
|
|
28
92
|
public struct Button: View {
|
|
29
93
|
// MARK: Lifecycle
|
|
30
94
|
|
|
31
|
-
public init(
|
|
95
|
+
public init(
|
|
96
|
+
title: String = "",
|
|
97
|
+
action: @escaping () -> Void,
|
|
98
|
+
type: ButtonType = .primary,
|
|
99
|
+
size: ButtonSize = .large,
|
|
100
|
+
iconLeft: AnyView? = nil,
|
|
101
|
+
iconRight: AnyView? = nil,
|
|
102
|
+
loading: Bool = false
|
|
103
|
+
) {
|
|
32
104
|
self.title = title
|
|
33
105
|
self.action = action
|
|
34
106
|
self.type = type
|
|
@@ -38,117 +110,87 @@ public struct Button: View {
|
|
|
38
110
|
self.loading = loading
|
|
39
111
|
}
|
|
40
112
|
|
|
41
|
-
// MARK: Public
|
|
42
|
-
|
|
43
|
-
public var body: some View {
|
|
44
|
-
SwiftUI.Button(action: action) {
|
|
45
|
-
HStack {
|
|
46
|
-
if loading {
|
|
47
|
-
LottieView2(name: "lottie_circle_loader", loopMode: .loop)
|
|
48
|
-
.frame(width: iconSize, height: iconSize)
|
|
49
|
-
.colorMultiply(.white)
|
|
50
|
-
} else if let iconLeft = iconLeft {
|
|
51
|
-
iconLeft.frame(width: iconSize, height: iconSize)
|
|
52
|
-
}
|
|
53
|
-
renderTitle(title: title, type: type, size: size).lineLimit(1).truncationMode(.tail)
|
|
54
|
-
if let iconRight = iconRight {
|
|
55
|
-
iconRight.frame(width: iconSize, height: iconSize)
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
.buttonSize(size)
|
|
59
|
-
.buttonType(type)
|
|
60
|
-
.opacity(loading ? 0.75 : 1.0)
|
|
61
|
-
}
|
|
62
|
-
.disabled(type == .disabled || loading)
|
|
63
|
-
.clipShape(RoundedRectangle(cornerRadius: Radius.S))
|
|
64
|
-
}
|
|
65
|
-
|
|
66
113
|
// MARK: Internal
|
|
67
114
|
|
|
68
115
|
var title: String
|
|
69
|
-
var action: () -> Void
|
|
70
|
-
var type: ButtonType
|
|
71
|
-
var size: ButtonSize
|
|
116
|
+
var action: () -> Void
|
|
117
|
+
var type: ButtonType
|
|
118
|
+
var size: ButtonSize
|
|
72
119
|
var iconLeft: AnyView?
|
|
73
120
|
var iconRight: AnyView?
|
|
74
121
|
var loading: Bool
|
|
75
|
-
|
|
122
|
+
|
|
123
|
+
// MARK: Private Helpers
|
|
124
|
+
|
|
76
125
|
private var iconSize: CGFloat {
|
|
77
126
|
switch size {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
case .small: return 16
|
|
127
|
+
case .large: return 24
|
|
128
|
+
case .medium, .small: return 16
|
|
81
129
|
}
|
|
82
130
|
}
|
|
83
131
|
|
|
84
|
-
func
|
|
132
|
+
private func height(for size: ButtonSize) -> CGFloat {
|
|
85
133
|
switch size {
|
|
86
|
-
case .large:
|
|
87
|
-
|
|
88
|
-
case .
|
|
89
|
-
return Text(title).font(.system(size: 14, weight: Font.Weight.bold))
|
|
90
|
-
case .small:
|
|
91
|
-
return Text(title).font(.system(size: 12, weight: Font.Weight.bold))
|
|
134
|
+
case .large: return 48
|
|
135
|
+
case .medium: return 36
|
|
136
|
+
case .small: return 28
|
|
92
137
|
}
|
|
93
138
|
}
|
|
94
|
-
}
|
|
95
139
|
|
|
96
|
-
private
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
case .
|
|
100
|
-
|
|
101
|
-
.background(Colors.primary)
|
|
102
|
-
.foregroundColor(Colors.black01)
|
|
103
|
-
)
|
|
104
|
-
case .danger:
|
|
105
|
-
return AnyView(self
|
|
106
|
-
.background(Colors.error)
|
|
107
|
-
.foregroundColor(Colors.black01)
|
|
108
|
-
)
|
|
109
|
-
case .tonal:
|
|
110
|
-
return AnyView(self
|
|
111
|
-
.background(Colors.primaryLight)
|
|
112
|
-
.foregroundColor(Colors.primary)
|
|
113
|
-
)
|
|
114
|
-
case .text:
|
|
115
|
-
return AnyView(self
|
|
116
|
-
.foregroundColor(Colors.primary)
|
|
117
|
-
)
|
|
118
|
-
case .secondary:
|
|
119
|
-
return AnyView(self
|
|
120
|
-
.background(Colors.black01)
|
|
121
|
-
.foregroundColor(Colors.black17)
|
|
122
|
-
.overlay(RoundedRectangle(cornerRadius: Radius.S).stroke(Colors.black04, lineWidth: 1))
|
|
123
|
-
)
|
|
124
|
-
case .outline:
|
|
125
|
-
return AnyView(self
|
|
126
|
-
.background(Colors.card)
|
|
127
|
-
.foregroundColor(Colors.primary)
|
|
128
|
-
.overlay(RoundedRectangle(cornerRadius: Radius.S).stroke(Colors.primary, lineWidth: 1))
|
|
129
|
-
)
|
|
130
|
-
case .disabled:
|
|
131
|
-
return AnyView(self
|
|
132
|
-
.background(Colors.black05)
|
|
133
|
-
.foregroundColor(Colors.black06)
|
|
134
|
-
)
|
|
140
|
+
private func padding(for size: ButtonSize) -> CGFloat {
|
|
141
|
+
switch size {
|
|
142
|
+
case .large: return Spacing.L
|
|
143
|
+
case .medium: return Spacing.M
|
|
144
|
+
case .small: return Spacing.S
|
|
135
145
|
}
|
|
136
146
|
}
|
|
137
147
|
|
|
138
|
-
func
|
|
148
|
+
private func renderTitle(title: String, size: ButtonSize) -> Text {
|
|
139
149
|
switch size {
|
|
140
150
|
case .large:
|
|
141
|
-
return
|
|
142
|
-
.frame(maxWidth: .infinity, minHeight: 48, maxHeight: 48)
|
|
143
|
-
.padding([.leading, .trailing], Spacing.L)
|
|
151
|
+
return Text(title).font(.system(size: 16, weight: .bold))
|
|
144
152
|
case .medium:
|
|
145
|
-
return
|
|
146
|
-
.frame(minWidth: 80, maxWidth: .infinity, minHeight: 36, maxHeight: 36)
|
|
147
|
-
.padding([.leading, .trailing], Spacing.M)
|
|
153
|
+
return Text(title).font(.system(size: 14, weight: .bold))
|
|
148
154
|
case .small:
|
|
149
|
-
return
|
|
150
|
-
.frame(minWidth: 56, maxWidth: 76, minHeight: 28, maxHeight: 28)
|
|
151
|
-
.padding([.leading, .trailing], Spacing.S)
|
|
155
|
+
return Text(title).font(.system(size: 12, weight: .bold))
|
|
152
156
|
}
|
|
153
157
|
}
|
|
158
|
+
|
|
159
|
+
// MARK: View
|
|
160
|
+
|
|
161
|
+
public var body: some View {
|
|
162
|
+
let style = type.style
|
|
163
|
+
|
|
164
|
+
SwiftUI.Button(action: action) {
|
|
165
|
+
HStack(spacing: 8) {
|
|
166
|
+
if loading {
|
|
167
|
+
LottieView(name: "lottie_circle_loader", loopMode: .loop)
|
|
168
|
+
.frame(width: iconSize, height: iconSize)
|
|
169
|
+
.colorMultiply(style.content)
|
|
170
|
+
} else if let iconLeft = iconLeft {
|
|
171
|
+
iconLeft.frame(width: iconSize, height: iconSize)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
renderTitle(title: title, size: size)
|
|
175
|
+
.lineLimit(1)
|
|
176
|
+
.truncationMode(.tail)
|
|
177
|
+
|
|
178
|
+
if let iconRight = iconRight {
|
|
179
|
+
iconRight.frame(width: iconSize, height: iconSize)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
.frame(maxWidth: .infinity)
|
|
183
|
+
.padding(.horizontal, padding(for: size))
|
|
184
|
+
.frame(height: height(for: size))
|
|
185
|
+
.background(style.background)
|
|
186
|
+
.foregroundColor(style.content)
|
|
187
|
+
.overlay(
|
|
188
|
+
RoundedRectangle(cornerRadius: Radius.S)
|
|
189
|
+
.stroke(style.border ?? .clear, lineWidth: style.border != nil ? style.borderWidth : 0)
|
|
190
|
+
)
|
|
191
|
+
.clipShape(RoundedRectangle(cornerRadius: Radius.S))
|
|
192
|
+
.opacity(loading ? 0.75 : 1.0)
|
|
193
|
+
}
|
|
194
|
+
.disabled(type == .disabled || loading)
|
|
195
|
+
}
|
|
154
196
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import SwiftUI
|
|
10
10
|
import Lottie
|
|
11
11
|
|
|
12
|
-
struct
|
|
12
|
+
struct LottieView: UIViewRepresentable {
|
|
13
13
|
var name: String = ""
|
|
14
14
|
var url: String?
|
|
15
15
|
var loopMode: LottieLoopMode = .playOnce
|
|
@@ -19,7 +19,7 @@ struct LottieView2: UIViewRepresentable {
|
|
|
19
19
|
// Add static cache for animations
|
|
20
20
|
private static var animationCache = NSCache<NSString, LottieAnimation>()
|
|
21
21
|
|
|
22
|
-
func makeUIView(context: UIViewRepresentableContext<
|
|
22
|
+
func makeUIView(context: UIViewRepresentableContext<LottieView>) -> UIView {
|
|
23
23
|
let view = UIView(frame: .zero)
|
|
24
24
|
|
|
25
25
|
// Configure Lottie with optimized settings
|
|
@@ -82,5 +82,5 @@ struct LottieView2: UIViewRepresentable {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<
|
|
85
|
+
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieView>) {}
|
|
86
86
|
}
|
package/package.json
CHANGED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
package vn.momo.kits.components
|
|
2
|
-
|
|
3
|
-
import androidx.compose.runtime.Composable
|
|
4
|
-
import androidx.compose.runtime.getValue
|
|
5
|
-
import androidx.compose.ui.Modifier
|
|
6
|
-
import androidx.compose.ui.graphics.Color
|
|
7
|
-
import androidx.compose.ui.graphics.ColorFilter
|
|
8
|
-
import io.github.alexzhirkevich.compottie.Compottie
|
|
9
|
-
import io.github.alexzhirkevich.compottie.LottieCompositionSpec
|
|
10
|
-
import io.github.alexzhirkevich.compottie.animateLottieCompositionAsState
|
|
11
|
-
import io.github.alexzhirkevich.compottie.rememberLottieComposition
|
|
12
|
-
import io.github.alexzhirkevich.compottie.rememberLottiePainter
|
|
13
|
-
import vn.momo.uikits.resources.Res
|
|
14
|
-
|
|
15
|
-
@Composable
|
|
16
|
-
fun LottieView(
|
|
17
|
-
path: String,
|
|
18
|
-
tintColor: Color? = null,
|
|
19
|
-
modifier: Modifier = Modifier) {
|
|
20
|
-
val composition by rememberLottieComposition {
|
|
21
|
-
LottieCompositionSpec.JsonString(
|
|
22
|
-
Res.readBytes(path).decodeToString()
|
|
23
|
-
)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
val progress by animateLottieCompositionAsState(
|
|
27
|
-
composition = composition,
|
|
28
|
-
iterations = Compottie.IterateForever
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
Image(
|
|
32
|
-
source = rememberLottiePainter(
|
|
33
|
-
composition = composition,
|
|
34
|
-
progress = { progress }
|
|
35
|
-
),
|
|
36
|
-
colorFilter = tintColor?.let { ColorFilter.tint(it) },
|
|
37
|
-
modifier = modifier,
|
|
38
|
-
)
|
|
39
|
-
}
|