@momo-kits/native-kits 0.120.0-beta.3 → 0.120.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +3 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +1 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +3 -4
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +37 -90
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +31 -34
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +3 -4
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +19 -30
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +1 -8
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +23 -4
- package/ios/Application/Components.swift +2 -2
- package/ios/Application/Screen.swift +1 -4
- package/ios/Button/Button.swift +7 -7
- package/ios/Checkbox/Checkbox.swift +1 -2
- package/ios/Input/Input.swift +61 -64
- package/ios/Popup/PopupDisplay.swift +20 -25
- package/ios/Popup/PopupPromotion.swift +16 -20
- package/ios/Typography/Text.swift +42 -15
- package/ios/Typography/Typography.swift +22 -1
- package/local.properties +2 -2
- package/package.json +1 -1
|
@@ -27,7 +27,6 @@ import androidx.compose.ui.graphics.Color
|
|
|
27
27
|
import androidx.compose.ui.graphics.graphicsLayer
|
|
28
28
|
import androidx.compose.ui.semantics.semantics
|
|
29
29
|
import androidx.compose.ui.semantics.testTag
|
|
30
|
-
import androidx.compose.ui.text.style.TextAlign
|
|
31
30
|
import androidx.compose.ui.unit.Dp
|
|
32
31
|
import androidx.compose.ui.unit.dp
|
|
33
32
|
import androidx.compose.ui.zIndex
|
|
@@ -168,11 +167,9 @@ fun Header(
|
|
|
168
167
|
HeaderTitle(
|
|
169
168
|
title = title,
|
|
170
169
|
color = tintIconColor,
|
|
171
|
-
modifier = Modifier
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
,
|
|
175
|
-
textAlign = if(titlePosition == TitlePosition.LEFT) TextAlign.Start else TextAlign.Center
|
|
170
|
+
modifier = Modifier.semantics {
|
|
171
|
+
testTag = "title_navigation_header"
|
|
172
|
+
}
|
|
176
173
|
)
|
|
177
174
|
}
|
|
178
175
|
}
|
|
@@ -4,7 +4,6 @@ import androidx.compose.runtime.Composable
|
|
|
4
4
|
import androidx.compose.ui.Modifier
|
|
5
5
|
import androidx.compose.ui.graphics.Color
|
|
6
6
|
import androidx.compose.ui.text.style.TextAlign
|
|
7
|
-
import androidx.compose.ui.text.style.TextOverflow
|
|
8
7
|
import androidx.compose.ui.unit.sp
|
|
9
8
|
import androidx.compose.ui.zIndex
|
|
10
9
|
import vn.momo.kits.components.Text
|
|
@@ -26,7 +25,6 @@ fun HeaderTitle(
|
|
|
26
25
|
lineHeight = 22.sp,
|
|
27
26
|
),
|
|
28
27
|
color = color,
|
|
29
|
-
maxLines = 1
|
|
30
|
-
overflow = TextOverflow.Ellipsis
|
|
28
|
+
maxLines = 1
|
|
31
29
|
)
|
|
32
30
|
}
|
|
@@ -200,8 +200,9 @@ fun Footer(footer: @Composable (() -> Unit)? = null, keyboardSize: Dp = 0.dp, bo
|
|
|
200
200
|
Box(
|
|
201
201
|
Modifier
|
|
202
202
|
.offset(y = -keyboardSize)
|
|
203
|
+
.padding(bottom = bottom)
|
|
203
204
|
.shadow(
|
|
204
|
-
Colors.black_20.copy(alpha = 0.
|
|
205
|
+
Colors.black_20.copy(alpha = 0.1f),
|
|
205
206
|
borderRadius = 0.dp,
|
|
206
207
|
10f,
|
|
207
208
|
offsetX = 0.dp,
|
|
@@ -211,9 +212,7 @@ fun Footer(footer: @Composable (() -> Unit)? = null, keyboardSize: Dp = 0.dp, bo
|
|
|
211
212
|
.background(AppTheme.current.colors.background.surface)
|
|
212
213
|
) {
|
|
213
214
|
Box(
|
|
214
|
-
Modifier.fillMaxWidth()
|
|
215
|
-
.padding(bottom = bottom)
|
|
216
|
-
.padding(vertical = Spacing.S, horizontal = Spacing.M)
|
|
215
|
+
Modifier.fillMaxWidth().padding(vertical = Spacing.S, horizontal = Spacing.M)
|
|
217
216
|
) {
|
|
218
217
|
footer?.invoke()
|
|
219
218
|
}
|
|
@@ -2,6 +2,10 @@ package vn.momo.kits.components
|
|
|
2
2
|
|
|
3
3
|
import androidx.compose.foundation.layout.Box
|
|
4
4
|
import androidx.compose.runtime.Composable
|
|
5
|
+
import androidx.compose.runtime.getValue
|
|
6
|
+
import androidx.compose.runtime.mutableStateOf
|
|
7
|
+
import androidx.compose.runtime.remember
|
|
8
|
+
import androidx.compose.runtime.setValue
|
|
5
9
|
import androidx.compose.ui.Alignment
|
|
6
10
|
import androidx.compose.ui.Modifier
|
|
7
11
|
import androidx.compose.ui.graphics.ColorFilter
|
|
@@ -10,6 +14,8 @@ import androidx.compose.ui.graphics.painter.Painter
|
|
|
10
14
|
import androidx.compose.ui.layout.ContentScale
|
|
11
15
|
import androidx.compose.ui.semantics.contentDescription
|
|
12
16
|
import androidx.compose.ui.semantics.semantics
|
|
17
|
+
import coil3.compose.AsyncImage
|
|
18
|
+
import vn.momo.kits.const.AppTheme
|
|
13
19
|
|
|
14
20
|
data class Options(
|
|
15
21
|
val alignment: Alignment = Alignment.TopStart,
|
|
@@ -22,47 +28,6 @@ enum class ImageState {
|
|
|
22
28
|
Loading, Success, Error
|
|
23
29
|
}
|
|
24
30
|
|
|
25
|
-
private val supportedDomains = setOf(
|
|
26
|
-
"static.momocdn.net",
|
|
27
|
-
"img.mservice.com.vn",
|
|
28
|
-
"cdn.mservice.com.vn",
|
|
29
|
-
"img.mservice.io",
|
|
30
|
-
"image.momocdn.net"
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
private val sizeChart = mapOf(
|
|
34
|
-
"XXS" to 64,
|
|
35
|
-
"XS" to 128,
|
|
36
|
-
"S" to 256,
|
|
37
|
-
"M" to 512,
|
|
38
|
-
"L" to 768,
|
|
39
|
-
"XL" to 1024,
|
|
40
|
-
"XXL" to 2048
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
private fun closestSizeForWidth(width: Float): String {
|
|
44
|
-
return sizeChart.minByOrNull { (_, value) ->
|
|
45
|
-
kotlin.math.abs(value - width)
|
|
46
|
-
}?.key ?: "M"
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
private fun getHostFromUrl(url: String): String? {
|
|
50
|
-
return try {
|
|
51
|
-
val noProtocol = url.substringAfter("//")
|
|
52
|
-
noProtocol.substringBefore("/").substringBefore(":")
|
|
53
|
-
} catch (_: Exception) {
|
|
54
|
-
null
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
private fun appendQueryParam(url: String, key: String, value: String): String {
|
|
59
|
-
val parts = url.split("#", limit = 2)
|
|
60
|
-
val base = parts[0]
|
|
61
|
-
val fragment = if (parts.size > 1) "#" + parts[1] else ""
|
|
62
|
-
val sep = if (base.contains("?")) "&" else "?"
|
|
63
|
-
return "$base$sep$key=$value$fragment"
|
|
64
|
-
}
|
|
65
|
-
|
|
66
31
|
@Composable
|
|
67
32
|
fun Image(
|
|
68
33
|
source: Any,
|
|
@@ -70,59 +35,41 @@ fun Image(
|
|
|
70
35
|
options: Options? = null,
|
|
71
36
|
loading: Boolean = true,
|
|
72
37
|
) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// var urlToLoad by remember(source) { mutableStateOf(source) }
|
|
76
|
-
// val density = LocalDensity.current
|
|
38
|
+
var imageOptions = Options()
|
|
39
|
+
var imageState by remember { mutableStateOf(ImageState.Loading) }
|
|
77
40
|
|
|
78
41
|
options?.let {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
42
|
+
imageOptions = Options(
|
|
43
|
+
alignment = options.alignment,
|
|
44
|
+
contentScale = options.contentScale,
|
|
45
|
+
colorFilter = options.colorFilter,
|
|
46
|
+
alpha = DefaultAlpha,
|
|
47
|
+
)
|
|
85
48
|
}
|
|
86
49
|
|
|
87
|
-
Box(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
// colorFilter = imageOptions.colorFilter,
|
|
111
|
-
// alpha = imageOptions.alpha,
|
|
112
|
-
// onLoading = { imageState = ImageState.Loading },
|
|
113
|
-
// onSuccess = { imageState = ImageState.Success },
|
|
114
|
-
// onError = { imageState = ImageState.Error },
|
|
115
|
-
// )
|
|
116
|
-
// when (imageState) {
|
|
117
|
-
// ImageState.Loading -> if (loading) Skeleton()
|
|
118
|
-
// ImageState.Error -> Icon(
|
|
119
|
-
// source = "media_fail",
|
|
120
|
-
// color = AppTheme.current.colors.text.disable,
|
|
121
|
-
// modifier = modifier
|
|
122
|
-
// )
|
|
123
|
-
//
|
|
124
|
-
// ImageState.Success -> {}
|
|
125
|
-
// }
|
|
50
|
+
Box(modifier = modifier.semantics { contentDescription = "img|$source" }) {
|
|
51
|
+
AsyncImage(
|
|
52
|
+
modifier = Modifier.matchParentSize(),
|
|
53
|
+
model = source,
|
|
54
|
+
contentDescription = null,
|
|
55
|
+
contentScale = imageOptions.contentScale,
|
|
56
|
+
alignment = imageOptions.alignment,
|
|
57
|
+
colorFilter = imageOptions.colorFilter,
|
|
58
|
+
alpha = imageOptions.alpha,
|
|
59
|
+
onLoading = { imageState = ImageState.Loading },
|
|
60
|
+
onSuccess = { imageState = ImageState.Success },
|
|
61
|
+
onError = { imageState = ImageState.Error },
|
|
62
|
+
)
|
|
63
|
+
when (imageState) {
|
|
64
|
+
ImageState.Loading -> if (loading) Skeleton()
|
|
65
|
+
ImageState.Error -> Icon(
|
|
66
|
+
source = "media_fail",
|
|
67
|
+
color = AppTheme.current.colors.text.disable,
|
|
68
|
+
modifier = modifier
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
ImageState.Success -> {}
|
|
72
|
+
}
|
|
126
73
|
}
|
|
127
74
|
|
|
128
75
|
}
|
|
@@ -57,7 +57,6 @@ fun PopupNotify(
|
|
|
57
57
|
secondary: PopupAction? = null,
|
|
58
58
|
buttonDirection: PopupActionDirection = PopupActionDirection.ROW,
|
|
59
59
|
onIconClose: () -> Unit,
|
|
60
|
-
isShowCloseIcon: Boolean = true,
|
|
61
60
|
) {
|
|
62
61
|
var isScroll by remember { mutableStateOf(false) }
|
|
63
62
|
val layoutResult = remember { mutableStateOf<TextLayoutResult?>(null) }
|
|
@@ -108,13 +107,13 @@ fun PopupNotify(
|
|
|
108
107
|
RoundedCornerShape(Radius.L)
|
|
109
108
|
)
|
|
110
109
|
) {
|
|
111
|
-
|
|
110
|
+
image.ifNotNull(
|
|
112
111
|
Image(
|
|
113
112
|
source = image,
|
|
114
113
|
modifier = Modifier.fillMaxWidth().aspectRatio(1.777f),
|
|
115
|
-
options = Options(alignment =
|
|
116
|
-
)
|
|
117
|
-
|
|
114
|
+
options = Options(alignment = Alignment.Center)
|
|
115
|
+
), null
|
|
116
|
+
)
|
|
118
117
|
|
|
119
118
|
Column(modifier = Modifier.padding(Spacing.XL)) {
|
|
120
119
|
Text(
|
|
@@ -126,7 +125,7 @@ fun PopupNotify(
|
|
|
126
125
|
content(Modifier.padding(top = Spacing.S))
|
|
127
126
|
if (error.isNotEmpty()) {
|
|
128
127
|
Text(
|
|
129
|
-
text =
|
|
128
|
+
text = error,
|
|
130
129
|
style = Typography.descriptionXsRegular,
|
|
131
130
|
color = AppTheme.current.colors.text.hint,
|
|
132
131
|
maxLines = 1,
|
|
@@ -145,36 +144,34 @@ fun PopupNotify(
|
|
|
145
144
|
}
|
|
146
145
|
}
|
|
147
146
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
.
|
|
159
|
-
width = 2.dp,
|
|
160
|
-
color = AppTheme.current.colors.background.surface,
|
|
161
|
-
shape = RoundedCornerShape(Radius.M)
|
|
162
|
-
)
|
|
163
|
-
.clip(RoundedCornerShape(100))
|
|
164
|
-
.clickable(
|
|
165
|
-
interactionSource = remember { MutableInteractionSource() },
|
|
166
|
-
indication = null,
|
|
167
|
-
onClick = { onClose { onIconClose() } }
|
|
168
|
-
),
|
|
169
|
-
contentAlignment = Alignment.Center
|
|
170
|
-
) {
|
|
171
|
-
Icon(
|
|
172
|
-
source = "navigation_close",
|
|
147
|
+
Box(
|
|
148
|
+
Modifier
|
|
149
|
+
.width(22.dp)
|
|
150
|
+
.height(22.dp)
|
|
151
|
+
.offset(x = -(1).dp, y = (-11).dp)
|
|
152
|
+
.background(
|
|
153
|
+
color = AppTheme.current.colors.text.default,
|
|
154
|
+
shape = RoundedCornerShape(Radius.M)
|
|
155
|
+
)
|
|
156
|
+
.border(
|
|
157
|
+
width = 2.dp,
|
|
173
158
|
color = AppTheme.current.colors.background.surface,
|
|
174
|
-
|
|
175
|
-
modifier = Modifier.setAutomationId("ic_popup_close")
|
|
159
|
+
shape = RoundedCornerShape(Radius.M)
|
|
176
160
|
)
|
|
177
|
-
|
|
161
|
+
.clip(RoundedCornerShape(100))
|
|
162
|
+
.clickable(
|
|
163
|
+
interactionSource = remember { MutableInteractionSource() },
|
|
164
|
+
indication = null,
|
|
165
|
+
onClick = { onClose { onIconClose() } }
|
|
166
|
+
),
|
|
167
|
+
contentAlignment = Alignment.Center
|
|
168
|
+
) {
|
|
169
|
+
Icon(
|
|
170
|
+
source = "navigation_close",
|
|
171
|
+
color = AppTheme.current.colors.background.surface,
|
|
172
|
+
size = 16.dp,
|
|
173
|
+
modifier = Modifier.setAutomationId("ic_popup_close")
|
|
174
|
+
)
|
|
178
175
|
}
|
|
179
176
|
}
|
|
180
177
|
}
|
|
@@ -24,7 +24,6 @@ import androidx.compose.ui.unit.dp
|
|
|
24
24
|
import vn.momo.kits.const.AppTheme
|
|
25
25
|
import vn.momo.kits.const.Radius
|
|
26
26
|
import vn.momo.kits.const.Spacing
|
|
27
|
-
import vn.momo.kits.utils.ifNotNull
|
|
28
27
|
|
|
29
28
|
@Composable
|
|
30
29
|
fun PopupPromotion(
|
|
@@ -33,7 +32,7 @@ fun PopupPromotion(
|
|
|
33
32
|
onClose: (() -> Unit) = {},
|
|
34
33
|
) {
|
|
35
34
|
Column(Modifier.semantics { testTag = "popup_notify" }) {
|
|
36
|
-
source.
|
|
35
|
+
if (source.isNotEmpty()) {
|
|
37
36
|
Image(
|
|
38
37
|
source = source,
|
|
39
38
|
modifier = Modifier.fillMaxWidth()
|
|
@@ -44,8 +43,8 @@ fun PopupPromotion(
|
|
|
44
43
|
onClick = onPress
|
|
45
44
|
),
|
|
46
45
|
options = Options(alignment = Alignment.Center, contentScale = ContentScale.Crop)
|
|
47
|
-
)
|
|
48
|
-
|
|
46
|
+
)
|
|
47
|
+
}
|
|
49
48
|
Box(
|
|
50
49
|
modifier = Modifier
|
|
51
50
|
.padding(top = Spacing.S)
|
|
@@ -17,7 +17,6 @@ import androidx.compose.ui.graphics.Color
|
|
|
17
17
|
import androidx.compose.ui.unit.dp
|
|
18
18
|
import vn.momo.kits.const.Colors
|
|
19
19
|
import vn.momo.kits.const.Spacing
|
|
20
|
-
import vn.momo.kits.modifier.setAutomationId
|
|
21
20
|
import vn.momo.kits.utils.ifTrue
|
|
22
21
|
|
|
23
22
|
@Composable
|
|
@@ -25,8 +24,6 @@ fun Switch(
|
|
|
25
24
|
value: Boolean = false,
|
|
26
25
|
onChange: (Boolean) -> Unit = {},
|
|
27
26
|
disabled: Boolean = false,
|
|
28
|
-
title: String? = "",
|
|
29
|
-
accessibilityId: String? = ""
|
|
30
27
|
) {
|
|
31
28
|
val circleBackgroundColor = value.ifTrue(Colors.black_01, Colors.black_03)
|
|
32
29
|
var backgroundColor = value.ifTrue(Colors.green_03, Colors.black_07)
|
|
@@ -35,35 +32,27 @@ fun Switch(
|
|
|
35
32
|
}
|
|
36
33
|
|
|
37
34
|
Box(
|
|
38
|
-
modifier = Modifier.
|
|
35
|
+
modifier = Modifier.then(
|
|
36
|
+
Modifier.width(38.dp).height(20.dp).border(
|
|
37
|
+
0.dp, Color.Unspecified,
|
|
38
|
+
RoundedCornerShape(20.dp)
|
|
39
|
+
).background(backgroundColor, RoundedCornerShape(20.dp)).clip(RoundedCornerShape(20.dp))
|
|
40
|
+
.clickable(
|
|
41
|
+
enabled = !disabled,
|
|
42
|
+
onClick = { onChange(!value) }
|
|
43
|
+
)
|
|
44
|
+
.padding(horizontal = Spacing.XS)
|
|
45
|
+
),
|
|
46
|
+
contentAlignment = value.ifTrue(Alignment.CenterEnd, Alignment.CenterStart)
|
|
39
47
|
) {
|
|
40
48
|
Box(
|
|
41
|
-
modifier = Modifier.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
enabled = !disabled,
|
|
49
|
-
onClick = { onChange(!value) }
|
|
50
|
-
)
|
|
51
|
-
.padding(horizontal = Spacing.XS)
|
|
52
|
-
),
|
|
53
|
-
contentAlignment = value.ifTrue(Alignment.CenterEnd, Alignment.CenterStart)
|
|
54
|
-
) {
|
|
55
|
-
Box(
|
|
56
|
-
modifier = Modifier.size(14.dp)
|
|
57
|
-
.border(0.dp, Color.Unspecified, RoundedCornerShape(percent = 100))
|
|
58
|
-
.background(circleBackgroundColor, RoundedCornerShape(percent = 100)),
|
|
59
|
-
contentAlignment = Alignment.Center
|
|
60
|
-
)
|
|
61
|
-
{
|
|
62
|
-
Box(
|
|
63
|
-
Modifier.size(6.dp)
|
|
64
|
-
.background(backgroundColor, RoundedCornerShape(percent = 100))
|
|
65
|
-
)
|
|
66
|
-
}
|
|
49
|
+
modifier = Modifier.size(14.dp)
|
|
50
|
+
.border(0.dp, Color.Unspecified, RoundedCornerShape(percent = 100))
|
|
51
|
+
.background(circleBackgroundColor, RoundedCornerShape(percent = 100)),
|
|
52
|
+
contentAlignment = Alignment.Center
|
|
53
|
+
)
|
|
54
|
+
{
|
|
55
|
+
Box(Modifier.size(6.dp).background(backgroundColor, RoundedCornerShape(percent = 100)))
|
|
67
56
|
}
|
|
68
57
|
}
|
|
69
58
|
}
|
|
@@ -8,7 +8,6 @@ import androidx.compose.ui.text.TextStyle
|
|
|
8
8
|
import androidx.compose.ui.text.style.TextAlign
|
|
9
9
|
import androidx.compose.ui.text.style.TextDecoration
|
|
10
10
|
import androidx.compose.ui.text.style.TextOverflow
|
|
11
|
-
import androidx.compose.ui.unit.TextUnit
|
|
12
11
|
import androidx.compose.ui.unit.sp
|
|
13
12
|
import vn.momo.kits.const.AppTheme
|
|
14
13
|
import vn.momo.kits.const.Typography
|
|
@@ -28,9 +27,6 @@ fun Text(
|
|
|
28
27
|
textDecoration: TextDecoration? = null,
|
|
29
28
|
onTextLayout: ((TextLayoutResult) -> Unit)? = null,
|
|
30
29
|
fontFamily: String = "SFProText",
|
|
31
|
-
minLines: Int = 1,
|
|
32
|
-
letterSpacing: TextUnit = TextUnit.Unspecified,
|
|
33
|
-
softWrap: Boolean = true,
|
|
34
30
|
accessibilityId: String? = null
|
|
35
31
|
) {
|
|
36
32
|
val fontSize = scaleSize(style.fontSize.value).sp
|
|
@@ -45,11 +41,8 @@ fun Text(
|
|
|
45
41
|
fontSize = fontSize,
|
|
46
42
|
fontFamily = font,
|
|
47
43
|
maxLines = maxLines,
|
|
48
|
-
softWrap = softWrap,
|
|
49
|
-
letterSpacing = letterSpacing,
|
|
50
|
-
minLines = minLines,
|
|
51
44
|
textDecoration = textDecoration,
|
|
52
45
|
onTextLayout = onTextLayout,
|
|
53
46
|
overflow = overflow
|
|
54
47
|
)
|
|
55
|
-
}
|
|
48
|
+
}
|
|
@@ -2,6 +2,7 @@ package vn.momo.kits.const
|
|
|
2
2
|
|
|
3
3
|
import androidx.compose.runtime.Composable
|
|
4
4
|
import androidx.compose.runtime.Immutable
|
|
5
|
+
import androidx.compose.ui.platform.LocalDensity
|
|
5
6
|
import androidx.compose.ui.text.TextStyle
|
|
6
7
|
import androidx.compose.ui.text.font.FontFamily
|
|
7
8
|
import androidx.compose.ui.text.font.FontWeight
|
|
@@ -12,17 +13,35 @@ import org.jetbrains.compose.resources.FontResource
|
|
|
12
13
|
import org.jetbrains.compose.resources.InternalResourceApi
|
|
13
14
|
import org.jetbrains.compose.resources.ResourceItem
|
|
14
15
|
import vn.momo.kits.platform.getScreenDimensions
|
|
16
|
+
import kotlin.math.max
|
|
17
|
+
import kotlin.math.min
|
|
15
18
|
|
|
16
19
|
const val DEFAULT_SCREEN_SIZE = 375f
|
|
20
|
+
const val MAX_FONT_SCALE = 1.5f
|
|
21
|
+
const val MAX_DEVICE_SCALE = 5
|
|
17
22
|
|
|
18
23
|
@Composable
|
|
19
24
|
fun scaleSize(size: Float): Float {
|
|
20
25
|
val deviceWidth = getScreenDimensions().width
|
|
21
|
-
val
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
val deviceScale = deviceWidth / DEFAULT_SCREEN_SIZE
|
|
27
|
+
|
|
28
|
+
val density = LocalDensity.current
|
|
29
|
+
val fontScale = density.fontScale
|
|
30
|
+
|
|
31
|
+
val defaultFontSize = size / fontScale //To ignore OS scale
|
|
32
|
+
var fontSizeScaleDevice = defaultFontSize
|
|
33
|
+
var fontSizeScaleOS = defaultFontSize
|
|
34
|
+
|
|
35
|
+
if (deviceScale > 1) {
|
|
36
|
+
fontSizeScaleDevice =
|
|
37
|
+
min(deviceScale * fontSizeScaleDevice, fontSizeScaleDevice + MAX_DEVICE_SCALE)
|
|
24
38
|
}
|
|
25
|
-
|
|
39
|
+
|
|
40
|
+
if (fontScale > 1) {
|
|
41
|
+
fontSizeScaleOS = min(fontScale * fontSizeScaleOS, fontSizeScaleOS * MAX_FONT_SCALE)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return max(fontSizeScaleDevice, fontSizeScaleOS)
|
|
26
45
|
}
|
|
27
46
|
|
|
28
47
|
@OptIn(InternalResourceApi::class)
|
|
@@ -214,8 +214,8 @@ public struct Header: View {
|
|
|
214
214
|
}
|
|
215
215
|
} else {
|
|
216
216
|
Text(title)
|
|
217
|
-
.font(Font.system(size: 17).weight(.bold))
|
|
218
|
-
.lineSpacing(22)
|
|
217
|
+
.font(Font.system(size: scaleSize(17)).weight(.bold))
|
|
218
|
+
.lineSpacing(scaleSize(22))
|
|
219
219
|
.foregroundColor(tintColor)
|
|
220
220
|
.frame(maxWidth: titlePosition == .center ? UIScreen.main.bounds.width - 252 : nil)
|
|
221
221
|
.frame(maxWidth: titlePosition == .center ? .infinity : UIScreen.main.bounds.width - 172, alignment: titlePosition == .center ? .center : .leading)
|
|
@@ -88,8 +88,7 @@ public struct Screen<Content: View>: View {
|
|
|
88
88
|
let screenHeight = height + topInset + bottomInset
|
|
89
89
|
|
|
90
90
|
ZStack {
|
|
91
|
-
(backgroundColor ?? Color.white)
|
|
92
|
-
.edgesIgnoringSafeArea(.top)
|
|
91
|
+
(backgroundColor ?? Color.white).edgesIgnoringSafeArea(.all)
|
|
93
92
|
|
|
94
93
|
VStack(spacing: 0) {
|
|
95
94
|
ZStack(alignment: .top) {
|
|
@@ -185,8 +184,6 @@ struct Footer: View {
|
|
|
185
184
|
VStack {
|
|
186
185
|
footerView
|
|
187
186
|
}
|
|
188
|
-
.padding(.vertical, Spacing.S)
|
|
189
|
-
.padding(.horizontal, Spacing.M)
|
|
190
187
|
.padding(.bottom, keyboardSize)
|
|
191
188
|
.background(Colors.black01)
|
|
192
189
|
}
|
package/ios/Button/Button.swift
CHANGED
|
@@ -42,7 +42,7 @@ public struct Button: View {
|
|
|
42
42
|
SwiftUI.Button(action: action) {
|
|
43
43
|
HStack {
|
|
44
44
|
iconLeft
|
|
45
|
-
renderTitle(title: title, type: type, size: size)
|
|
45
|
+
renderTitle(title: title, type: type, size: size)
|
|
46
46
|
iconRight
|
|
47
47
|
}
|
|
48
48
|
.buttonSize(size)
|
|
@@ -62,11 +62,11 @@ public struct Button: View {
|
|
|
62
62
|
func renderTitle(title: String, type: ButtonType, size: ButtonSize) -> Text {
|
|
63
63
|
switch size {
|
|
64
64
|
case .large:
|
|
65
|
-
return Text(title).font(.system(size: 16, weight: Font.Weight.bold))
|
|
65
|
+
return Text(title).font(.system(size: scaleSize(16), weight: Font.Weight.bold))
|
|
66
66
|
case .medium:
|
|
67
|
-
return Text(title).font(.system(size: 14, weight: Font.Weight.bold))
|
|
67
|
+
return Text(title).font(.system(size: scaleSize(14), weight: Font.Weight.bold))
|
|
68
68
|
case .small:
|
|
69
|
-
return Text(title).font(.system(size: 12, weight: Font.Weight.bold))
|
|
69
|
+
return Text(title).font(.system(size: scaleSize(12), weight: Font.Weight.bold))
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
}
|
|
@@ -123,15 +123,15 @@ private extension View {
|
|
|
123
123
|
case .large:
|
|
124
124
|
return self
|
|
125
125
|
.frame(maxWidth: .infinity, minHeight: 48, maxHeight: 48)
|
|
126
|
-
.padding([.leading, .trailing], Spacing.
|
|
126
|
+
.padding([.leading, .trailing], Spacing.XS)
|
|
127
127
|
case .medium:
|
|
128
128
|
return self
|
|
129
129
|
.frame(minWidth: 80, maxWidth: .infinity, minHeight: 36, maxHeight: 36)
|
|
130
|
-
.padding([.leading, .trailing], Spacing.
|
|
130
|
+
.padding([.leading, .trailing], Spacing.XS)
|
|
131
131
|
case .small:
|
|
132
132
|
return self
|
|
133
133
|
.frame(minWidth: 56, maxWidth: 76, minHeight: 28, maxHeight: 28)
|
|
134
|
-
.padding([.leading, .trailing], Spacing.
|
|
134
|
+
.padding([.leading, .trailing], Spacing.XS)
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
}
|
package/ios/Input/Input.swift
CHANGED
|
@@ -5,6 +5,7 @@ import SwiftUI
|
|
|
5
5
|
fileprivate struct FloatingComponent: View {
|
|
6
6
|
var floatingValue: String
|
|
7
7
|
var floatingIcon: AnyView?
|
|
8
|
+
var disabled: Bool = false
|
|
8
9
|
|
|
9
10
|
public init (_ floatingValue: String = "", floatingIcon: AnyView? = nil){
|
|
10
11
|
self.floatingValue = floatingValue
|
|
@@ -13,12 +14,9 @@ fileprivate struct FloatingComponent: View {
|
|
|
13
14
|
|
|
14
15
|
public var body: some View {
|
|
15
16
|
return HStack {
|
|
16
|
-
|
|
17
|
-
.
|
|
18
|
-
.
|
|
19
|
-
.font(.system(size: 12))
|
|
20
|
-
.padding(.leading, 8)
|
|
21
|
-
.padding(.trailing, 8)
|
|
17
|
+
MomoText(floatingValue, typography: .labelSMedium, color: self.disabled ? Colors.black12 : Colors.black08)
|
|
18
|
+
.padding(.leading, Spacing.XS)
|
|
19
|
+
.padding(.trailing, Spacing.XS)
|
|
22
20
|
floatingIcon
|
|
23
21
|
}
|
|
24
22
|
.background(Colors.black01)
|
|
@@ -113,84 +111,83 @@ public struct Input: View {
|
|
|
113
111
|
}
|
|
114
112
|
|
|
115
113
|
public var body: some View {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if !isSecure {
|
|
124
|
-
TextField("", text: $value, onEditingChanged: { isChange in
|
|
125
|
-
self.focused = isChange
|
|
126
|
-
})
|
|
127
|
-
.disabled(disabled)
|
|
128
|
-
.font(fontWeight == .bold ? .headline : .body)
|
|
129
|
-
} else {
|
|
130
|
-
DotTextField(value: $value)
|
|
131
|
-
.disabled(disabled)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if loading {
|
|
135
|
-
ActivityIndicator(isAnimating: .constant(true), style: .medium)
|
|
136
|
-
.frame(width: 20, height: 20)
|
|
137
|
-
} else if !value.isEmpty {
|
|
138
|
-
SwiftButton(action: onCancel){
|
|
139
|
-
Image(systemName: "x.circle.fill")
|
|
140
|
-
.foregroundColor(Colors.text)
|
|
141
|
-
.frame(width: 16, height: 16)
|
|
142
|
-
.padding(.leading, -4)
|
|
143
|
-
.padding(.trailing, 8)
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
rightIcon
|
|
114
|
+
VStack(alignment: .leading, spacing: 4) {
|
|
115
|
+
ZStack(alignment: .topLeading) {
|
|
116
|
+
HStack(alignment: .center) {
|
|
117
|
+
if let leadingIcon = leadingIcon {
|
|
118
|
+
Icon(source: leadingIcon, size: 16, color: leadingIconColor)
|
|
148
119
|
}
|
|
149
|
-
.padding(.horizontal, 12)
|
|
150
|
-
.frame(height: size == .small ? 40 : 48)
|
|
151
|
-
.background(disabled ? Colors.black01 : Color.white)
|
|
152
|
-
.overlay(RoundedRectangle(cornerRadius: 8).stroke(focused ? Colors.pink03 : Colors.border, lineWidth: 1))
|
|
153
120
|
|
|
154
|
-
if
|
|
155
|
-
|
|
156
|
-
.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
121
|
+
if !isSecure {
|
|
122
|
+
TextField("", text: $value ,onEditingChanged: { isChange in
|
|
123
|
+
self.focused = isChange
|
|
124
|
+
})
|
|
125
|
+
.disabled(disabled)
|
|
126
|
+
.frame(maxHeight: .infinity)
|
|
127
|
+
.multilineTextAlignment(.leading)
|
|
128
|
+
.font(.system(size: scaleSize(14)))
|
|
129
|
+
} else {
|
|
130
|
+
DotTextField(value: $value)
|
|
131
|
+
.disabled(disabled)
|
|
160
132
|
}
|
|
161
133
|
|
|
162
|
-
if
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
134
|
+
if loading {
|
|
135
|
+
ActivityIndicator(isAnimating: .constant(true), style: .medium)
|
|
136
|
+
.frame(width: 20, height: 20)
|
|
137
|
+
} else if !value.isEmpty {
|
|
138
|
+
SwiftButton(action: onCancel){
|
|
139
|
+
Image(systemName: "x.circle.fill")
|
|
140
|
+
.foregroundColor(Colors.text)
|
|
141
|
+
.frame(width: 16, height: 16)
|
|
142
|
+
.padding(.leading, -4)
|
|
143
|
+
.padding(.trailing, 8)
|
|
144
|
+
}
|
|
169
145
|
}
|
|
146
|
+
|
|
147
|
+
rightIcon
|
|
170
148
|
}
|
|
149
|
+
.padding(.horizontal, 12)
|
|
150
|
+
.frame(height: size == .small ? scaleSize(40) : scaleSize(48))
|
|
151
|
+
.background(disabled ? Colors.black01 : Color.white)
|
|
152
|
+
.overlay(RoundedRectangle(cornerRadius: 8).stroke(focused ? Colors.pink03 : Colors.border, lineWidth: 1))
|
|
171
153
|
|
|
172
|
-
if
|
|
173
|
-
Text(
|
|
174
|
-
.
|
|
175
|
-
.font(.caption)
|
|
176
|
-
} else if let hint = hintText {
|
|
177
|
-
Text(hint)
|
|
154
|
+
if value.isEmpty {
|
|
155
|
+
Text(placeholder + (required ? " *" : ""))
|
|
156
|
+
.font(.system(size: scaleSize(14)))
|
|
178
157
|
.foregroundColor(Colors.black09)
|
|
179
|
-
.
|
|
158
|
+
.padding(.horizontal, leadingIcon == nil ? 12 : 32)
|
|
159
|
+
.padding(.top, 12)
|
|
160
|
+
.allowsHitTesting(false)
|
|
180
161
|
}
|
|
162
|
+
|
|
163
|
+
if !floatingValue.isEmpty {
|
|
164
|
+
FloatingComponent(floatingValue, floatingIcon: floatingIconURL.map { url in
|
|
165
|
+
AnyView(
|
|
166
|
+
Icon(source: url, size: 16, color: floatingIconColor)
|
|
167
|
+
)
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if !errorMessage.isEmpty {
|
|
174
|
+
MomoText(errorMessage, typography: .descriptionDefaultRegular, color: Colors.red03)
|
|
175
|
+
} else if hintText != nil {
|
|
176
|
+
MomoText(errorMessage, typography: .descriptionDefaultRegular, color: Colors.black12)
|
|
181
177
|
}
|
|
182
178
|
}
|
|
179
|
+
}
|
|
183
180
|
}
|
|
184
181
|
|
|
185
182
|
|
|
186
183
|
struct ActivityIndicator: UIViewRepresentable {
|
|
187
184
|
@Binding var isAnimating: Bool
|
|
188
185
|
let style: UIActivityIndicatorView.Style
|
|
189
|
-
|
|
186
|
+
|
|
190
187
|
func makeUIView(context: UIViewRepresentableContext<ActivityIndicator>) -> UIActivityIndicatorView {
|
|
191
188
|
return UIActivityIndicatorView(style: style)
|
|
192
189
|
}
|
|
193
|
-
|
|
190
|
+
|
|
194
191
|
func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext<ActivityIndicator>) {
|
|
195
192
|
isAnimating ? uiView.startAnimating() : uiView.stopAnimating()
|
|
196
193
|
}
|
|
@@ -21,7 +21,7 @@ public enum PopupButtonType {
|
|
|
21
21
|
public struct PopupDisplay: View {
|
|
22
22
|
// MARK: Lifecycle
|
|
23
23
|
|
|
24
|
-
public init(isPresented: Binding<Bool>, title: String = "", description: String = "", url: String = "", buttonDirection: PopupButtonDirection = .column, buttonType: PopupButtonType = .button, actionButtonTitle: String = "", closeButtonTitle: String = "", onPressAction: @escaping () -> Void = {}, onPressCloseButton: @escaping () -> Void = {}, onClose: @escaping () -> Void = {}, errorCode: String = ""
|
|
24
|
+
public init(isPresented: Binding<Bool>, title: String = "", description: String = "", url: String = "", buttonDirection: PopupButtonDirection = .column, buttonType: PopupButtonType = .button, actionButtonTitle: String = "", closeButtonTitle: String = "", onPressAction: @escaping () -> Void = {}, onPressCloseButton: @escaping () -> Void = {}, onClose: @escaping () -> Void = {}, errorCode: String = "") {
|
|
25
25
|
self._isPresented = isPresented
|
|
26
26
|
self.title = title
|
|
27
27
|
self.description = description
|
|
@@ -34,7 +34,6 @@ public struct PopupDisplay: View {
|
|
|
34
34
|
self.closeButtonTitle = closeButtonTitle
|
|
35
35
|
self.actionButtonTitle = actionButtonTitle
|
|
36
36
|
self.errorCode = errorCode
|
|
37
|
-
self.isShowCloseIcon = isShowCloseIcon
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
// MARK: Public
|
|
@@ -49,19 +48,17 @@ public struct PopupDisplay: View {
|
|
|
49
48
|
|
|
50
49
|
VStack(spacing: 12) {
|
|
51
50
|
ZStack(alignment: .topTrailing) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
.accessibility(identifier: "ic_popup_close")
|
|
64
|
-
}
|
|
51
|
+
SwiftUI.Button(action: onClose) {
|
|
52
|
+
Image(systemName: "xmark.circle.fill")
|
|
53
|
+
.resizable()
|
|
54
|
+
.frame(width: 22, height: 22)
|
|
55
|
+
.overlay(RoundedRectangle(cornerRadius: Radius.L).stroke(Colors.black01, lineWidth: 2))
|
|
56
|
+
}.foregroundColor(Colors.black20)
|
|
57
|
+
.background(Colors.black01.cornerRadius(15))
|
|
58
|
+
.padding(.trailing, -11)
|
|
59
|
+
.padding(.top, -11)
|
|
60
|
+
.zIndex(1)
|
|
61
|
+
.accessibility(identifier: "ic_popup_close")
|
|
65
62
|
|
|
66
63
|
VStack {
|
|
67
64
|
if(!url.isEmpty) {
|
|
@@ -91,7 +88,8 @@ public struct PopupDisplay: View {
|
|
|
91
88
|
ScrollView(showsIndicators: false) {
|
|
92
89
|
Text(description)
|
|
93
90
|
.font(.paragraph)
|
|
94
|
-
.foregroundColor(
|
|
91
|
+
.foregroundColor(.black)
|
|
92
|
+
.opacity(0.6)
|
|
95
93
|
.multilineTextAlignment(.leading)
|
|
96
94
|
.background(GeometryReader { geo in
|
|
97
95
|
Color.clear
|
|
@@ -104,7 +102,8 @@ public struct PopupDisplay: View {
|
|
|
104
102
|
} else {
|
|
105
103
|
Text(description)
|
|
106
104
|
.font(.paragraph)
|
|
107
|
-
.foregroundColor(
|
|
105
|
+
.foregroundColor(.black)
|
|
106
|
+
.opacity(0.6)
|
|
108
107
|
.multilineTextAlignment(.leading)
|
|
109
108
|
.background(GeometryReader { geo in
|
|
110
109
|
Color.clear
|
|
@@ -117,7 +116,7 @@ public struct PopupDisplay: View {
|
|
|
117
116
|
}.padding(.bottom, 8)
|
|
118
117
|
|
|
119
118
|
if(!errorCode.isEmpty) {
|
|
120
|
-
Text(
|
|
119
|
+
Text(errorCode)
|
|
121
120
|
.foregroundColor(Colors.black12)
|
|
122
121
|
.font(.description_xs_regular)
|
|
123
122
|
.lineLimit(1)
|
|
@@ -165,7 +164,6 @@ public struct PopupDisplay: View {
|
|
|
165
164
|
var actionButtonTitle: String
|
|
166
165
|
var closeButtonTitle: String
|
|
167
166
|
var errorCode: String
|
|
168
|
-
var isShowCloseIcon: Bool
|
|
169
167
|
|
|
170
168
|
var RowTextButtons: some View {
|
|
171
169
|
HStack {
|
|
@@ -203,9 +201,7 @@ public struct PopupDisplay: View {
|
|
|
203
201
|
|
|
204
202
|
var RowButtons: some View {
|
|
205
203
|
HStack {
|
|
206
|
-
|
|
207
|
-
Button(title: closeButtonTitle, action: onPressCloseButton, type: .secondary, size: .medium).accessibility(identifier: "btn_popup_cancel")
|
|
208
|
-
}
|
|
204
|
+
Button(title: closeButtonTitle, action: onPressCloseButton, type: .secondary, size: .medium).accessibility(identifier: "btn_popup_cancel")
|
|
209
205
|
Button(title: actionButtonTitle, action: onPressAction, size: .medium)
|
|
210
206
|
.accessibility(identifier: "btn_popup_allow")
|
|
211
207
|
}
|
|
@@ -221,9 +217,8 @@ public struct PopupDisplay: View {
|
|
|
221
217
|
action: onPressAction,
|
|
222
218
|
size: .medium
|
|
223
219
|
).accessibility(identifier: "btn_popup_allow")
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
220
|
+
Button(title: closeButtonTitle, action: onPressCloseButton, type: .secondary, size: .medium).accessibility(identifier: "btn_popup_cancel")
|
|
221
|
+
|
|
227
222
|
}
|
|
228
223
|
.frame(maxWidth: .infinity, alignment: .trailing)
|
|
229
224
|
.padding(.horizontal, 24)
|
|
@@ -19,26 +19,22 @@ public struct PopupPromotion: View {
|
|
|
19
19
|
|
|
20
20
|
public var body: some View {
|
|
21
21
|
VStack(spacing: 8) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
.onTapGesture {
|
|
39
|
-
onAction?()
|
|
40
|
-
}
|
|
41
|
-
}
|
|
22
|
+
WebImage(url: URL(string: imageUrl)!)
|
|
23
|
+
.resizable()
|
|
24
|
+
.placeholder {
|
|
25
|
+
Color.gray
|
|
26
|
+
.aspectRatio(0.72, contentMode: .fit)
|
|
27
|
+
.frame(maxWidth: UIScreen.main.bounds.width - Spacing.XL * 2)
|
|
28
|
+
.clipped()
|
|
29
|
+
}
|
|
30
|
+
.scaledToFill()
|
|
31
|
+
.aspectRatio(0.72, contentMode: .fit)
|
|
32
|
+
.frame(maxWidth: UIScreen.main.bounds.width - Spacing.XL * 2, alignment: .center)
|
|
33
|
+
.clipped()
|
|
34
|
+
.onTapGesture {
|
|
35
|
+
onAction?()
|
|
36
|
+
}
|
|
37
|
+
|
|
42
38
|
buildCloseIcon()
|
|
43
39
|
}
|
|
44
40
|
.accessibility(identifier: "popup_promotion")
|
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
import SwiftUI
|
|
2
2
|
|
|
3
|
+
public func scaleSize(_ size: CGFloat) -> CGFloat {
|
|
4
|
+
let defaultScreenSize: CGFloat = 375
|
|
5
|
+
let maxFontScale: CGFloat = 1.5
|
|
6
|
+
let maxDeviceScale: CGFloat = 5
|
|
7
|
+
|
|
8
|
+
let deviceWidth = UIScreen.main.bounds.width
|
|
9
|
+
let deviceScale = deviceWidth / defaultScreenSize
|
|
10
|
+
|
|
11
|
+
let defaultFont = UIFont.systemFont(ofSize: UIFont.labelFontSize)
|
|
12
|
+
let scaledFont = UIFontMetrics.default.scaledFont(for: defaultFont)
|
|
13
|
+
let fontScale = scaledFont.pointSize / defaultFont.pointSize
|
|
14
|
+
|
|
15
|
+
var fontSizeDeviceScale = size
|
|
16
|
+
var fontSizeOSScale = size
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
if deviceScale > 1 {
|
|
20
|
+
fontSizeDeviceScale = min(fontSizeDeviceScale * deviceScale, fontSizeDeviceScale + maxDeviceScale)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if fontScale > 1 {
|
|
24
|
+
fontSizeOSScale = min(fontSizeOSScale * fontScale, fontSizeOSScale * maxFontScale)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return max(fontSizeDeviceScale, fontSizeOSScale)
|
|
28
|
+
}
|
|
29
|
+
|
|
3
30
|
public enum TypographyStyle {
|
|
4
31
|
case headlineDefaultBold
|
|
5
32
|
case headerMBold
|
|
@@ -63,13 +90,13 @@ public extension TypographyStyle {
|
|
|
63
90
|
var fontWeight: Font.Weight {
|
|
64
91
|
switch self {
|
|
65
92
|
case .headlineDefaultBold, .headerMBold, .headerDefaultBold,
|
|
66
|
-
|
|
93
|
+
.actionDefaultBold, .actionSBold, .actionXsBold, .actionXxsBold:
|
|
67
94
|
return .bold
|
|
68
95
|
case .headerSSemibold, .headerXsSemibold:
|
|
69
96
|
return .semibold
|
|
70
97
|
case .bodyDefaultRegular, .bodyDefaultRegularStrikethrough,
|
|
71
|
-
|
|
72
|
-
|
|
98
|
+
.descriptionDefaultRegular, .descriptionDefaultRegularStrikethrough,
|
|
99
|
+
.descriptionXsRegular, .descriptionXsRegularStrikethrough:
|
|
73
100
|
return .regular
|
|
74
101
|
case .labelDefaultMedium, .labelSMedium, .labelXsMedium:
|
|
75
102
|
return .medium
|
|
@@ -78,8 +105,8 @@ public extension TypographyStyle {
|
|
|
78
105
|
var isStrikethrough: Bool {
|
|
79
106
|
switch self {
|
|
80
107
|
case .bodyDefaultRegularStrikethrough,
|
|
81
|
-
|
|
82
|
-
|
|
108
|
+
.descriptionDefaultRegularStrikethrough,
|
|
109
|
+
.descriptionXsRegularStrikethrough:
|
|
83
110
|
return true
|
|
84
111
|
default:
|
|
85
112
|
return false
|
|
@@ -89,25 +116,25 @@ public extension TypographyStyle {
|
|
|
89
116
|
|
|
90
117
|
public struct MomoText: View {
|
|
91
118
|
private let content: String
|
|
92
|
-
private let
|
|
119
|
+
private let typography: TypographyStyle
|
|
93
120
|
private let color: Color
|
|
94
|
-
|
|
121
|
+
|
|
95
122
|
public init(
|
|
96
123
|
_ content: String,
|
|
97
|
-
|
|
124
|
+
typography: TypographyStyle = .bodyDefaultRegular,
|
|
98
125
|
color: Color = Colors.text
|
|
99
126
|
) {
|
|
100
127
|
self.content = content
|
|
101
|
-
self.
|
|
128
|
+
self.typography = typography
|
|
102
129
|
self.color = color
|
|
103
130
|
}
|
|
104
|
-
|
|
131
|
+
|
|
105
132
|
public var body: some View {
|
|
106
|
-
|
|
107
|
-
.font(.system(size:
|
|
133
|
+
let text = SwiftUI.Text(content)
|
|
134
|
+
.font(.system(size: scaleSize(typography.fontSize), weight: typography.fontWeight))
|
|
108
135
|
.foregroundColor(color)
|
|
109
|
-
.lineSpacing(
|
|
110
|
-
|
|
136
|
+
.lineSpacing(scaleSize(typography.lineHeight) - scaleSize(typography.fontSize))
|
|
137
|
+
|
|
111
138
|
return text
|
|
112
139
|
}
|
|
113
|
-
}
|
|
140
|
+
}
|
|
@@ -2,7 +2,28 @@ import SwiftUI
|
|
|
2
2
|
|
|
3
3
|
public extension Font {
|
|
4
4
|
static func appFont(size: CGFloat) -> Font {
|
|
5
|
-
|
|
5
|
+
let defaultScreenSize: CGFloat = 375
|
|
6
|
+
let maxFontScale: CGFloat = 1.2
|
|
7
|
+
let maxDeviceScale: CGFloat = 5
|
|
8
|
+
|
|
9
|
+
let deviceWidth = UIScreen.main.bounds.width
|
|
10
|
+
let deviceScale = deviceWidth / defaultScreenSize
|
|
11
|
+
|
|
12
|
+
let defaultFont = UIFont.systemFont(ofSize: UIFont.labelFontSize)
|
|
13
|
+
let scaledFont = UIFontMetrics.default.scaledFont(for: defaultFont)
|
|
14
|
+
let fontScale = scaledFont.pointSize / defaultFont.pointSize
|
|
15
|
+
|
|
16
|
+
var fontSize = size
|
|
17
|
+
|
|
18
|
+
if deviceScale > 1 {
|
|
19
|
+
fontSize = min(fontSize * deviceScale, fontSize + maxDeviceScale)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if fontScale > 1 {
|
|
23
|
+
fontSize = min(fontSize * fontScale, fontSize * maxFontScale)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return Font.system(size: fontSize)
|
|
6
27
|
}
|
|
7
28
|
|
|
8
29
|
// New supported typography styles
|
package/local.properties
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
# Location of the SDK. This is only used by Gradle.
|
|
5
5
|
# For customization when using a Version Control System, please read the
|
|
6
6
|
# header note.
|
|
7
|
-
#
|
|
8
|
-
sdk.dir=/Users/
|
|
7
|
+
#Tue Apr 08 11:13:31 ICT 2025
|
|
8
|
+
sdk.dir=/Users/sonnguyen/Library/Android/sdk
|