@momo-kits/native-kits 0.120.0-beta.0 → 0.120.0-beta.20

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.
Files changed (25) hide show
  1. package/compose/build.gradle.kts +9 -11
  2. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +3 -6
  3. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +1 -0
  4. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +1 -3
  5. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +2 -1
  6. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +3 -4
  7. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +2 -66
  8. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -1
  9. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +31 -34
  10. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +25 -37
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +19 -30
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +6 -8
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +27 -5
  14. package/ios/Application/Components.swift +2 -2
  15. package/ios/Application/Screen.swift +1 -4
  16. package/ios/Button/Button.swift +7 -7
  17. package/ios/Checkbox/Checkbox.swift +1 -2
  18. package/ios/Input/Input.swift +61 -64
  19. package/ios/Popup/PopupDisplay.swift +20 -25
  20. package/ios/Popup/PopupPromotion.swift +22 -28
  21. package/ios/Typography/Text.swift +42 -15
  22. package/ios/Typography/Typography.swift +22 -1
  23. package/local.properties +2 -2
  24. package/package.json +1 -1
  25. package/publish.sh +19 -0
@@ -15,10 +15,11 @@ kotlin {
15
15
  publishLibraryVariants("release")
16
16
  }
17
17
 
18
- val isDebug = (project.findProperty("kotlin.native.cocoapods.platform") as? String)?.contains("iphoneos", ignoreCase = true) == false
19
- val targets = if(isDebug) listOf(iosX64(),iosSimulatorArm64()) else listOf(iosArm64())
20
-
21
- targets.forEach { iosTarget ->
18
+ listOf(
19
+ iosX64(),
20
+ iosArm64(),
21
+ iosSimulatorArm64(),
22
+ ).forEach { iosTarget ->
22
23
  iosTarget.binaries.framework {
23
24
  baseName = "lib"
24
25
  isStatic = true
@@ -49,13 +50,10 @@ kotlin {
49
50
  implementation(libs.jetbrains.coroutines.android)
50
51
  }
51
52
  }
52
- if(isDebug) {
53
- val iosX64Main by getting
54
- val iosSimulatorArm64Main by getting
55
- } else {
56
- val iosArm64Main by getting
57
- }
58
-
53
+ val iosX64Main by getting
54
+ val iosArm64Main by getting
55
+ val iosSimulatorArm64Main by getting
56
+
59
57
  iosMain.dependencies {
60
58
  implementation(libs.ktor.client.darwin)
61
59
  }
@@ -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
- .setAutomationId("title_navigation_header")
173
- .fillMaxWidth(fraction = if(titlePosition == TitlePosition.LEFT) 0.7f else 0.5f)
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
  }
@@ -17,6 +17,7 @@ enum class TitlePosition {
17
17
  }
18
18
 
19
19
  data class AnimatedHeader(
20
+ val type: HeaderType = HeaderType.DEFAULT,
20
21
  val composable: @Composable (scrollState: Int) -> Unit = {}
21
22
  )
22
23
 
@@ -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
  }
@@ -96,7 +96,8 @@ val AppLanguage = staticCompositionLocalOf<String?> {
96
96
  data class KitConfig(
97
97
  val trustBanner: TrustBannerData? = null,
98
98
  val headerBar: String? = null,
99
- val headerGradient: String? = null
99
+ val headerGradient: String? = null,
100
+ val isWhiteList: Boolean = false
100
101
  )
101
102
 
102
103
  @Composable
@@ -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.2f),
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
  }
@@ -14,8 +14,6 @@ import androidx.compose.ui.graphics.painter.Painter
14
14
  import androidx.compose.ui.layout.ContentScale
15
15
  import androidx.compose.ui.semantics.contentDescription
16
16
  import androidx.compose.ui.semantics.semantics
17
- import androidx.compose.ui.platform.LocalDensity
18
- import androidx.compose.ui.layout.onSizeChanged
19
17
  import coil3.compose.AsyncImage
20
18
  import vn.momo.kits.const.AppTheme
21
19
 
@@ -30,47 +28,6 @@ enum class ImageState {
30
28
  Loading, Success, Error
31
29
  }
32
30
 
33
- private val supportedDomains = setOf(
34
- "static.momocdn.net",
35
- "img.mservice.com.vn",
36
- "cdn.mservice.com.vn",
37
- "img.mservice.io",
38
- "image.momocdn.net"
39
- )
40
-
41
- private val sizeChart = mapOf(
42
- "XXS" to 64,
43
- "XS" to 128,
44
- "S" to 256,
45
- "M" to 512,
46
- "L" to 768,
47
- "XL" to 1024,
48
- "XXL" to 2048
49
- )
50
-
51
- private fun closestSizeForWidth(width: Float): String {
52
- return sizeChart.minByOrNull { (_, value) ->
53
- kotlin.math.abs(value - width)
54
- }?.key ?: "M"
55
- }
56
-
57
- private fun getHostFromUrl(url: String): String? {
58
- return try {
59
- val noProtocol = url.substringAfter("//")
60
- noProtocol.substringBefore("/").substringBefore(":")
61
- } catch (_: Exception) {
62
- null
63
- }
64
- }
65
-
66
- private fun appendQueryParam(url: String, key: String, value: String): String {
67
- val parts = url.split("#", limit = 2)
68
- val base = parts[0]
69
- val fragment = if (parts.size > 1) "#" + parts[1] else ""
70
- val sep = if (base.contains("?")) "&" else "?"
71
- return "$base$sep$key=$value$fragment"
72
- }
73
-
74
31
  @Composable
75
32
  fun Image(
76
33
  source: Any,
@@ -80,8 +37,6 @@ fun Image(
80
37
  ) {
81
38
  var imageOptions = Options()
82
39
  var imageState by remember { mutableStateOf(ImageState.Loading) }
83
- var urlToLoad by remember(source) { mutableStateOf(source) }
84
- val density = LocalDensity.current
85
40
 
86
41
  options?.let {
87
42
  imageOptions = Options(
@@ -92,29 +47,10 @@ fun Image(
92
47
  )
93
48
  }
94
49
 
95
- Box(
96
- modifier = modifier
97
- .semantics { contentDescription = "img|$source" }
98
- .onSizeChanged { size ->
99
- if (source is String) {
100
- val host = getHostFromUrl(source)
101
- if (host != null && supportedDomains.contains(host)) {
102
- val widthPx = size.width.toFloat()
103
- val pixelRatio = density.density
104
- val pixelFitForWidth = widthPx * pixelRatio
105
- val sizeKey = closestSizeForWidth(pixelFitForWidth)
106
- urlToLoad = appendQueryParam(source, "size", sizeKey)
107
- } else {
108
- urlToLoad = source
109
- }
110
- } else {
111
- urlToLoad = source
112
- }
113
- }
114
- ) {
50
+ Box(modifier = modifier.semantics { contentDescription = "img|$source" }) {
115
51
  AsyncImage(
116
52
  modifier = Modifier.matchParentSize(),
117
- model = urlToLoad,
53
+ model = source,
118
54
  contentDescription = null,
119
55
  contentScale = imageOptions.contentScale,
120
56
  alignment = imageOptions.alignment,
@@ -36,7 +36,6 @@ import androidx.compose.ui.text.input.KeyboardType
36
36
  import androidx.compose.ui.unit.dp
37
37
  import androidx.compose.ui.zIndex
38
38
  import vn.momo.kits.const.AppTheme
39
- import vn.momo.kits.const.Colors
40
39
  import vn.momo.kits.const.Radius
41
40
  import vn.momo.kits.const.Spacing
42
41
  import vn.momo.kits.const.Typography
@@ -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
- if(image.isNotEmpty()) {
110
+ image.ifNotNull(
112
111
  Image(
113
112
  source = image,
114
113
  modifier = Modifier.fillMaxWidth().aspectRatio(1.777f),
115
- options = Options(alignment = Alignment.Center)
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 = "Mã lỗi: " + error,
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
- if(isShowCloseIcon) {
149
- Box(
150
- Modifier
151
- .width(22.dp)
152
- .height(22.dp)
153
- .offset(x = -(1).dp, y = (-11).dp)
154
- .background(
155
- color = AppTheme.current.colors.text.default,
156
- shape = RoundedCornerShape(Radius.M)
157
- )
158
- .border(
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
- size = 16.dp,
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
  }
@@ -22,11 +22,8 @@ import androidx.compose.ui.semantics.semantics
22
22
  import androidx.compose.ui.semantics.testTag
23
23
  import androidx.compose.ui.unit.dp
24
24
  import vn.momo.kits.const.AppTheme
25
- import vn.momo.kits.const.Colors
26
25
  import vn.momo.kits.const.Radius
27
26
  import vn.momo.kits.const.Spacing
28
- import vn.momo.kits.modifier.setAutomationId
29
- import vn.momo.kits.utils.ifNotNull
30
27
 
31
28
  @Composable
32
29
  fun PopupPromotion(
@@ -35,7 +32,7 @@ fun PopupPromotion(
35
32
  onClose: (() -> Unit) = {},
36
33
  ) {
37
34
  Column(Modifier.semantics { testTag = "popup_notify" }) {
38
- source.ifNotNull(
35
+ if (source.isNotEmpty()) {
39
36
  Image(
40
37
  source = source,
41
38
  modifier = Modifier.fillMaxWidth()
@@ -46,20 +43,28 @@ fun PopupPromotion(
46
43
  onClick = onPress
47
44
  ),
48
45
  options = Options(alignment = Alignment.Center, contentScale = ContentScale.Crop)
49
- ), null
50
- )
46
+ )
47
+ }
51
48
  Box(
52
49
  modifier = Modifier
53
50
  .padding(top = Spacing.S)
54
- .fillMaxWidth()
55
- .width(40.dp)
56
- .height(40.dp),
51
+ .fillMaxWidth(),
57
52
  contentAlignment = Alignment.Center
58
53
  ) {
59
54
  Box(
60
- modifier = Modifier
61
- .width(40.dp)
62
- .height(40.dp)
55
+ Modifier
56
+ .width(20.dp)
57
+ .height(20.dp)
58
+ .background(
59
+ color = AppTheme.current.colors.text.default,
60
+ shape = RoundedCornerShape(Radius.M)
61
+ )
62
+ .border(
63
+ width = 2.dp,
64
+ color = AppTheme.current.colors.background.surface,
65
+ shape = RoundedCornerShape(Radius.M)
66
+ )
67
+ .clip(RoundedCornerShape(100))
63
68
  .clickable(
64
69
  interactionSource = remember { MutableInteractionSource() },
65
70
  indication = null,
@@ -67,32 +72,15 @@ fun PopupPromotion(
67
72
  ),
68
73
  contentAlignment = Alignment.Center
69
74
  ) {
70
- Box(
71
- Modifier
72
- .width(22.dp)
73
- .height(22.dp)
74
- .background(
75
- color = AppTheme.current.colors.text.default,
76
- shape = RoundedCornerShape(Radius.M)
77
- )
78
- .border(
79
- width = 2.dp,
80
- color = AppTheme.current.colors.background.surface,
81
- shape = RoundedCornerShape(Radius.M)
82
- )
83
- .clip(RoundedCornerShape(100))
84
- ,
85
- contentAlignment = Alignment.Center
86
- ) {
87
- Icon(
88
- source = "navigation_close",
89
- color = AppTheme.current.colors.background.surface,
90
- size = 16.dp,
91
- modifier = Modifier.setAutomationId("ic_popup_close")
92
- )
93
- }
75
+ Icon(
76
+ source = "navigation_close",
77
+ color = AppTheme.current.colors.background.surface,
78
+ size = 14.dp,
79
+ modifier = Modifier.semantics {
80
+ testTag = "ic_popup_close"
81
+ }
82
+ )
94
83
  }
95
-
96
84
  }
97
85
  }
98
86
  }
@@ -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.setAutomationId( accessibilityId ?: "toggle_${title ?: "unknown"}_${value}")
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.then(
42
- Modifier.width(38.dp).height(20.dp).border(
43
- 0.dp, Color.Unspecified,
44
- RoundedCornerShape(20.dp)
45
- ).background(backgroundColor, RoundedCornerShape(20.dp))
46
- .clip(RoundedCornerShape(20.dp))
47
- .clickable(
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
  }
@@ -1,5 +1,7 @@
1
1
  package vn.momo.kits.components
2
2
 
3
+ import androidx.compose.foundation.border
4
+ import androidx.compose.foundation.layout.Box
3
5
  import androidx.compose.runtime.Composable
4
6
  import androidx.compose.ui.Modifier
5
7
  import androidx.compose.ui.graphics.Color
@@ -8,9 +10,10 @@ import androidx.compose.ui.text.TextStyle
8
10
  import androidx.compose.ui.text.style.TextAlign
9
11
  import androidx.compose.ui.text.style.TextDecoration
10
12
  import androidx.compose.ui.text.style.TextOverflow
11
- import androidx.compose.ui.unit.TextUnit
13
+ import androidx.compose.ui.unit.dp
12
14
  import androidx.compose.ui.unit.sp
13
15
  import vn.momo.kits.const.AppTheme
16
+ import vn.momo.kits.const.Colors
14
17
  import vn.momo.kits.const.Typography
15
18
  import vn.momo.kits.const.getFontFamily
16
19
  import vn.momo.kits.const.scaleSize
@@ -28,9 +31,6 @@ fun Text(
28
31
  textDecoration: TextDecoration? = null,
29
32
  onTextLayout: ((TextLayoutResult) -> Unit)? = null,
30
33
  fontFamily: String = "SFProText",
31
- minLines: Int = 1,
32
- letterSpacing: TextUnit = TextUnit.Unspecified,
33
- softWrap: Boolean = true,
34
34
  accessibilityId: String? = null
35
35
  ) {
36
36
  val fontSize = scaleSize(style.fontSize.value).sp
@@ -45,11 +45,9 @@ fun Text(
45
45
  fontSize = fontSize,
46
46
  fontFamily = font,
47
47
  maxLines = maxLines,
48
- softWrap = softWrap,
49
- letterSpacing = letterSpacing,
50
- minLines = minLines,
51
48
  textDecoration = textDecoration,
52
49
  onTextLayout = onTextLayout,
53
50
  overflow = overflow
54
51
  )
55
- }
52
+
53
+ }
@@ -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
@@ -11,18 +12,39 @@ import org.jetbrains.compose.resources.Font
11
12
  import org.jetbrains.compose.resources.FontResource
12
13
  import org.jetbrains.compose.resources.InternalResourceApi
13
14
  import org.jetbrains.compose.resources.ResourceItem
15
+ import vn.momo.kits.application.AppConfig
16
+ import vn.momo.kits.application.KitConfig
14
17
  import vn.momo.kits.platform.getScreenDimensions
18
+ import kotlin.math.max
19
+ import kotlin.math.min
15
20
 
16
21
  const val DEFAULT_SCREEN_SIZE = 375f
22
+ const val MAX_FONT_SCALE = 1.5f
23
+ const val MAX_DEVICE_SCALE = 5
17
24
 
18
25
  @Composable
19
- fun scaleSize(size: Float): Float {
26
+ fun scaleSize(size: Float, config: KitConfig? = AppConfig.current): Float {
27
+ val isWhiteList = config?.isWhiteList ?: false
20
28
  val deviceWidth = getScreenDimensions().width
21
- val scaleRate = deviceWidth / DEFAULT_SCREEN_SIZE
22
- if (scaleRate > 1) {
23
- return if ((scaleRate * size) > (size + 5)) size + 5 else scaleRate * size
29
+ val deviceScale = deviceWidth / DEFAULT_SCREEN_SIZE
30
+
31
+ val density = LocalDensity.current
32
+ val fontScale = density.fontScale
33
+
34
+ val defaultFontSize = size / fontScale //To ignore OS scale
35
+ var fontSizeScaleDevice = defaultFontSize
36
+ var fontSizeScaleOS = defaultFontSize
37
+
38
+ if (deviceScale > 1) {
39
+ fontSizeScaleDevice =
40
+ min(deviceScale * fontSizeScaleDevice, fontSizeScaleDevice + MAX_DEVICE_SCALE)
24
41
  }
25
- return size
42
+
43
+ if (fontScale > 1) {
44
+ fontSizeScaleOS = min(fontScale * fontSizeScaleOS, fontSizeScaleOS * MAX_FONT_SCALE)
45
+ }
46
+
47
+ return if (isWhiteList) max(fontSizeScaleDevice, fontSizeScaleOS) else fontSizeScaleDevice
26
48
  }
27
49
 
28
50
  @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
  }
@@ -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).lineLimit(1).truncationMode(.tail)
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.L)
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.M)
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.S)
134
+ .padding([.leading, .trailing], Spacing.XS)
135
135
  }
136
136
  }
137
137
  }
@@ -53,8 +53,7 @@ public struct Checkbox: View{
53
53
 
54
54
  // Title
55
55
  if !title.isEmpty {
56
- Text(title)
57
- .font(.description1)
56
+ MomoText(title, typography: .bodyDefaultRegular)
58
57
  }
59
58
  }
60
59
  }
@@ -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
- Text(floatingValue)
17
- .font(.paragraph)
18
- .foregroundColor(Colors.black09)
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
- VStack(alignment: .leading, spacing: 4) {
117
- ZStack(alignment: .topLeading) {
118
- HStack(alignment: .center) {
119
- if let leadingIcon = leadingIcon {
120
- Icon(source: leadingIcon, size: 16, color: leadingIconColor)
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 value.isEmpty {
155
- Text(placeholder + (required ? " *" : ""))
156
- .foregroundColor(Colors.black09)
157
- .padding(.horizontal, leadingIcon == nil ? 12 : 32)
158
- .padding(.top, 12)
159
- .allowsHitTesting(false)
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 !floatingValue.isEmpty {
163
- FloatingComponent(floatingValue, floatingIcon: floatingIconURL.map { url in
164
- AnyView(
165
- Icon(source: url, size: 16, color: floatingIconColor)
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 !errorMessage.isEmpty {
173
- Text(errorMessage)
174
- .foregroundColor(.red)
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
- .font(.caption)
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 = "", isShowCloseIcon: Bool = true) {
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
- if(isShowCloseIcon) {
53
- SwiftUI.Button(action: onClose) {
54
- Image(systemName: "xmark.circle.fill")
55
- .resizable()
56
- .frame(width: 22, height: 22)
57
- .overlay(RoundedRectangle(cornerRadius: Radius.L).stroke(Colors.black01, lineWidth: 2))
58
- }.foregroundColor(Colors.black20)
59
- .background(Colors.black01.cornerRadius(15))
60
- .padding(.trailing, -11)
61
- .padding(.top, -11)
62
- .zIndex(1)
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(Colors.black17)
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(Colors.black17)
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("Mã lỗi: " + errorCode)
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
- if(!closeButtonTitle.isEmpty) {
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
- if(!closeButtonTitle.isEmpty) {
225
- Button(title: closeButtonTitle, action: onPressCloseButton, type: .secondary, size: .medium).accessibility(identifier: "btn_popup_cancel")
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,36 +19,31 @@ public struct PopupPromotion: View {
19
19
 
20
20
  public var body: some View {
21
21
  VStack(spacing: 8) {
22
- if(imageUrl.isEmpty) {
23
- Icon(source: "media_fail", size: UIScreen.main.bounds.width - Spacing.XL * 2)
24
- }
25
- else {
26
- WebImage(url: URL(string: imageUrl)!)
27
- .resizable()
28
- .placeholder {
29
- Color.gray
30
- .aspectRatio(0.72, contentMode: .fit)
31
- .frame(maxWidth: UIScreen.main.bounds.width - Spacing.XL * 2)
32
- .clipped()
33
- }
34
- .scaledToFill()
35
- .aspectRatio(0.72, contentMode: .fit)
36
- .frame(maxWidth: UIScreen.main.bounds.width - Spacing.XL * 2, alignment: .center)
37
- .clipped()
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
- .accessibility(identifier: "ic_popup_close")
44
39
  }
45
- .accessibilityElement(children: .ignore)
46
40
  .accessibility(identifier: "popup_promotion")
47
41
  }
48
42
 
49
43
  @ViewBuilder
50
44
  private func buildCloseIcon() -> some View {
51
- HStack(alignment: .center, spacing: 0) {
45
+ HStack {
46
+ Spacer()
52
47
  SwiftUI.Button(action: {
53
48
  onIconClose?()
54
49
  }) {
@@ -56,14 +51,13 @@ public struct PopupPromotion: View {
56
51
  Circle()
57
52
  .strokeBorder(Colors.black01, lineWidth: 2)
58
53
  .background(Circle().fill(Colors.black17))
59
- .frame(width: 22, height: 22)
54
+ .frame(width: 20, height: 20)
60
55
 
61
- Icon(source: "navigation_close", size: 16, color: Colors.black01)
56
+ Icon(source: "navigation_close", size: 14, color: Colors.black01)
62
57
  }
63
- .frame(width: 40, height: 40)
64
- .padding(8)
65
- .zIndex(1)
66
58
  }
59
+ .padding(8)
60
+ Spacer()
67
61
  }
68
62
  }
69
63
 
@@ -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
- .actionDefaultBold, .actionSBold, .actionXsBold, .actionXxsBold:
93
+ .actionDefaultBold, .actionSBold, .actionXsBold, .actionXxsBold:
67
94
  return .bold
68
95
  case .headerSSemibold, .headerXsSemibold:
69
96
  return .semibold
70
97
  case .bodyDefaultRegular, .bodyDefaultRegularStrikethrough,
71
- .descriptionDefaultRegular, .descriptionDefaultRegularStrikethrough,
72
- .descriptionXsRegular, .descriptionXsRegularStrikethrough:
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
- .descriptionDefaultRegularStrikethrough,
82
- .descriptionXsRegularStrikethrough:
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 style: TypographyStyle
119
+ private let typography: TypographyStyle
93
120
  private let color: Color
94
-
121
+
95
122
  public init(
96
123
  _ content: String,
97
- style: TypographyStyle = .bodyDefaultRegular,
124
+ typography: TypographyStyle = .bodyDefaultRegular,
98
125
  color: Color = Colors.text
99
126
  ) {
100
127
  self.content = content
101
- self.style = style
128
+ self.typography = typography
102
129
  self.color = color
103
130
  }
104
-
131
+
105
132
  public var body: some View {
106
- var text = SwiftUI.Text(content)
107
- .font(.system(size: style.fontSize, weight: style.fontWeight))
133
+ let text = SwiftUI.Text(content)
134
+ .font(.system(size: scaleSize(typography.fontSize), weight: typography.fontWeight))
108
135
  .foregroundColor(color)
109
- .lineSpacing(style.lineHeight - style.fontSize)
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
- return Font.system(size: size)
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
- #Wed Aug 21 14:20:12 ICT 2024
8
- sdk.dir=/Users/huynhdung/Library/Android/sdk
7
+ #Tue Apr 08 11:13:31 ICT 2025
8
+ sdk.dir=/Users/sonnguyen/Library/Android/sdk
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/native-kits",
3
- "version": "0.120.0-beta.0",
3
+ "version": "0.120.0-beta.20",
4
4
  "private": false,
5
5
  "dependencies": {},
6
6
  "devDependencies": {},
package/publish.sh ADDED
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+ rm -rf dist
3
+ mkdir dist
4
+
5
+ cp . ./dist
6
+
7
+ # GET VERSION from mck_package.json
8
+ VERSIONSTRING=( v$(jq .version package.json) )
9
+ VERSION=(${VERSIONSTRING//[\"]/})
10
+ echo VERSION: $VERSION
11
+
12
+ rsync -r --verbose --exclude 'build' --exclude 'dist' --exclude 'example' --exclude 'gradle' --exclude 'sample' ./* dist
13
+
14
+ cd dist
15
+ npm publish --tag beta --access=public
16
+ cd ..
17
+ rm -rf dist
18
+
19
+ #curl -X POST -H 'Content-Type: application/json' 'https://chat.googleapis.com/v1/spaces/AAAAbP8987c/messages?key=AIzaSyDdI0hCZtE6vySjMm-WEfRq3CPzqKqqsHI&token=UGSFRvk_oYb9uGsAgs31bVvMm6jDkmD8zihGm3eyaQA%3D&threadKey=JoaXTEYaNNkl' -d '{"text": "@momo-kits/native new version release: '*"$VERSION"*' https://www.npmjs.com/package/@momo-kits/native-kits"}'