@momo-kits/native-kits 0.151.2-test.11 → 0.151.2-test.12
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 +23 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +3 -3
- 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 +10 -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 +57 -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) {
|
|
@@ -4,15 +4,20 @@ import android.annotation.SuppressLint
|
|
|
4
4
|
import android.content.res.Resources
|
|
5
5
|
import android.graphics.BlurMaskFilter
|
|
6
6
|
import android.os.Build
|
|
7
|
+
import androidx.compose.ui.graphics.Color
|
|
7
8
|
import androidx.compose.runtime.Composable
|
|
9
|
+
import androidx.compose.runtime.getValue
|
|
10
|
+
import androidx.compose.ui.Modifier
|
|
8
11
|
import androidx.compose.ui.graphics.NativePaint
|
|
9
12
|
import androidx.compose.ui.platform.LocalConfiguration
|
|
10
|
-
import androidx.compose.ui.platform.LocalView
|
|
11
13
|
import androidx.compose.ui.unit.Dp
|
|
12
14
|
import androidx.compose.ui.unit.dp
|
|
13
|
-
import
|
|
15
|
+
import com.airbnb.lottie.compose.LottieCompositionSpec
|
|
16
|
+
import com.airbnb.lottie.compose.LottieConstants
|
|
17
|
+
import com.airbnb.lottie.compose.rememberLottieComposition
|
|
14
18
|
import vn.momo.kits.const.AppNavigationBar
|
|
15
19
|
import vn.momo.kits.const.AppStatusBar
|
|
20
|
+
import vn.momo.kits.utils.readJson
|
|
16
21
|
import androidx.activity.compose.BackHandler as AndroidBackHandler
|
|
17
22
|
|
|
18
23
|
actual fun getPlatformName(): String = "Android"
|
|
@@ -50,4 +55,19 @@ actual fun getScreenHeight(): Dp {
|
|
|
50
55
|
return getScreenDimensions().height.dp + if (getAndroidBuildVersion() >= 35) 0.dp else AppStatusBar.current + AppNavigationBar.current
|
|
51
56
|
}
|
|
52
57
|
|
|
53
|
-
actual fun getAndroidBuildVersion(): Int = Build.VERSION.SDK_INT
|
|
58
|
+
actual fun getAndroidBuildVersion(): Int = Build.VERSION.SDK_INT
|
|
59
|
+
|
|
60
|
+
@Composable
|
|
61
|
+
actual fun LottieAnimation(
|
|
62
|
+
path: String,
|
|
63
|
+
tintColor: Color?,
|
|
64
|
+
modifier: Modifier
|
|
65
|
+
) {
|
|
66
|
+
val composition by rememberLottieComposition(LottieCompositionSpec.JsonString(readJson(path)))
|
|
67
|
+
|
|
68
|
+
com.airbnb.lottie.compose.LottieAnimation(
|
|
69
|
+
composition = composition,
|
|
70
|
+
modifier = modifier,
|
|
71
|
+
iterations = LottieConstants.IterateForever,
|
|
72
|
+
)
|
|
73
|
+
}
|
|
@@ -28,6 +28,7 @@ import vn.momo.kits.const.Radius
|
|
|
28
28
|
import vn.momo.kits.const.Spacing
|
|
29
29
|
import vn.momo.kits.const.Typography
|
|
30
30
|
import vn.momo.kits.modifier.activeOpacityClickable
|
|
31
|
+
import vn.momo.kits.platform.LottieAnimation
|
|
31
32
|
|
|
32
33
|
enum class ButtonType {
|
|
33
34
|
PRIMARY,
|
|
@@ -145,10 +146,9 @@ fun RenderLeading(
|
|
|
145
146
|
) {
|
|
146
147
|
if (loading) {
|
|
147
148
|
Box(Modifier.padding(end = marginRight)) {
|
|
148
|
-
|
|
149
|
-
path = "files/lottie_circle_loader.json",
|
|
150
|
-
tintColor = color,
|
|
149
|
+
LottieAnimation(
|
|
151
150
|
modifier = Modifier.size(iconSize),
|
|
151
|
+
path = "files/lottie_circle_loader"
|
|
152
152
|
)
|
|
153
153
|
}
|
|
154
154
|
} else {
|
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,11 @@ 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
|
+
modifier: Modifier = Modifier
|
|
37
|
+
)
|
|
@@ -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,31 @@
|
|
|
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.interop.UIKitView
|
|
9
18
|
import androidx.compose.ui.unit.Dp
|
|
10
19
|
import androidx.compose.ui.unit.dp
|
|
20
|
+
import cocoapods.lottie_ios.CompatibleAnimation
|
|
21
|
+
import cocoapods.lottie_ios.CompatibleAnimationView
|
|
11
22
|
import kotlinx.cinterop.ExperimentalForeignApi
|
|
12
23
|
import kotlinx.cinterop.get
|
|
13
24
|
import kotlinx.cinterop.memScoped
|
|
14
25
|
import org.jetbrains.skia.FilterBlurMode
|
|
15
26
|
import org.jetbrains.skia.MaskFilter
|
|
27
|
+
import platform.Foundation.NSBundle
|
|
28
|
+
import platform.UIKit.UIColor
|
|
16
29
|
import platform.UIKit.UIScreen
|
|
17
30
|
|
|
18
31
|
|
|
@@ -46,4 +59,46 @@ actual fun getScreenHeight(): Dp {
|
|
|
46
59
|
return getScreenDimensions().height.dp
|
|
47
60
|
}
|
|
48
61
|
|
|
49
|
-
actual fun getAndroidBuildVersion(): Int = 999
|
|
62
|
+
actual fun getAndroidBuildVersion(): Int = 999
|
|
63
|
+
|
|
64
|
+
@OptIn(ExperimentalForeignApi::class)
|
|
65
|
+
@Composable
|
|
66
|
+
actual fun LottieAnimation(
|
|
67
|
+
path: String,
|
|
68
|
+
tintColor: Color?,
|
|
69
|
+
modifier: Modifier
|
|
70
|
+
) {
|
|
71
|
+
var animation by remember { mutableStateOf<CompatibleAnimation?>(null) }
|
|
72
|
+
|
|
73
|
+
LaunchedEffect(Unit) {
|
|
74
|
+
animation = CompatibleAnimation(name = "compose-resources/composeResources/vn.momo.compose.resources/".plus(path), subdirectory = null, NSBundle.mainBundle)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
when (val value = animation) {
|
|
78
|
+
null -> {
|
|
79
|
+
Box(modifier)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
else -> {
|
|
83
|
+
val factory = remember {
|
|
84
|
+
{
|
|
85
|
+
CompatibleAnimationView(value).also {
|
|
86
|
+
it.translatesAutoresizingMaskIntoConstraints = true
|
|
87
|
+
it.setBackgroundColor(UIColor.whiteColor)
|
|
88
|
+
it.setLoopAnimationCount(-1.0)
|
|
89
|
+
it.setAnimationSpeed(1.0)
|
|
90
|
+
|
|
91
|
+
it.play()
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
Box(modifier) {
|
|
97
|
+
UIKitView(
|
|
98
|
+
modifier = Modifier.fillMaxSize(),
|
|
99
|
+
factory = factory,
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
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
|
-
}
|