@momo-kits/native-kits 0.117.1-beta.2 → 0.117.1-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.
@@ -23,6 +23,7 @@ import androidx.compose.runtime.setValue
23
23
  import androidx.compose.runtime.snapshotFlow
24
24
  import androidx.compose.ui.Alignment
25
25
  import androidx.compose.ui.Modifier
26
+ import androidx.compose.ui.graphics.Color
26
27
  import androidx.compose.ui.graphics.graphicsLayer
27
28
  import androidx.compose.ui.semantics.semantics
28
29
  import androidx.compose.ui.semantics.testTag
@@ -53,11 +54,13 @@ fun Header(
53
54
  scrollState: Int = 0,
54
55
  inputSearchProps: InputSearchProps? = null,
55
56
  headerRightWidth: Dp = 0.dp,
56
- useAnimationSearch: Boolean = false
57
+ useAnimationSearch: Boolean = false,
58
+ tintColor: Color? = null
57
59
  ) {
58
60
  val statusBarHeight = AppStatusBar.current ?: getStatusBarHeight()
59
- val tintIconColor = AppTheme.current.colors.text.default
60
- val backgroundButton = Colors.black_01.copy(alpha = 0.6f)
61
+ val tintIconColor = tintColor ?: AppTheme.current.colors.text.default
62
+ val backgroundButton = if (tintColor == Colors.black_01) Colors.black_20.copy(alpha = 0.6f) else Colors.black_01.copy(alpha = 0.6f)
63
+ val borderColor = if (tintColor == Colors.black_01) Colors.black_01.copy(alpha = 0.2f) else Colors.black_20.copy(alpha = 0.2f)
61
64
 
62
65
  var colorFraction by remember { mutableStateOf(0f) }
63
66
 
@@ -105,7 +108,7 @@ fun Header(
105
108
  .then(
106
109
  Modifier.border(
107
110
  0.2.dp,
108
- Colors.black_20.copy(alpha = 0.2f),
111
+ borderColor,
109
112
  RoundedCornerShape(100)
110
113
  )
111
114
  )
@@ -122,7 +125,7 @@ fun Header(
122
125
  ) {
123
126
  Icon(
124
127
  source = "arrow-back",
125
- color = Colors.black_20,
128
+ color = tintIconColor,
126
129
  size = 20.dp,
127
130
  )
128
131
  }
@@ -34,6 +34,7 @@ fun animateColor(start: Color, stop: Color, fraction: Float): Color {
34
34
  fun HeaderBackground(
35
35
  headerType: HeaderType = HeaderType.DEFAULT,
36
36
  scrollState: Int,
37
+ headerTransparent: Boolean = false,
37
38
  ) {
38
39
  val statusBarHeight = AppStatusBar.current ?: getStatusBarHeight()
39
40
 
@@ -42,11 +43,15 @@ fun HeaderBackground(
42
43
  )
43
44
 
44
45
  val backgroundColor by animateColorAsState(
45
- targetValue = animateColor(
46
- Color.Transparent,
47
- AppTheme.current.colors.background.surface,
48
- opacityAni
49
- ),
46
+ targetValue = if (headerTransparent) {
47
+ Color.Transparent
48
+ } else {
49
+ animateColor(
50
+ Color.Transparent,
51
+ AppTheme.current.colors.background.surface,
52
+ opacityAni
53
+ )
54
+ }
50
55
  )
51
56
 
52
57
  when (headerType) {
@@ -54,6 +59,8 @@ fun HeaderBackground(
54
59
  HeaderDefault(
55
60
  opacityAni = opacityAni,
56
61
  statusBarHeight = statusBarHeight,
62
+ backgroundColor = backgroundColor,
63
+ headerTransparent = headerTransparent
57
64
  )
58
65
  }
59
66
 
@@ -23,14 +23,16 @@ import vn.momo.kits.utils.bottomBorder
23
23
  fun HeaderDefault(
24
24
  opacityAni: Float,
25
25
  statusBarHeight: Dp,
26
+ backgroundColor: Color?,
27
+ headerTransparent: Boolean = false
26
28
  ) {
27
29
  val height = statusBarHeight + HEADER_HEIGHT.dp
28
30
  Box(
29
31
  modifier = Modifier.fillMaxWidth().height(height)
30
- .background(AppTheme.current.colors.background.surface)
32
+ .background(backgroundColor ?: AppTheme.current.colors.background.surface)
31
33
  .bottomBorder(
32
34
  strokeWidth = 1.dp,
33
- color = AppTheme.current.colors.border.default
35
+ color = backgroundColor?: AppTheme.current.colors.border.default
34
36
  )
35
37
  ) {
36
38
  Box(
@@ -39,14 +41,24 @@ fun HeaderDefault(
39
41
  .fillMaxWidth()
40
42
  .alpha(opacityAni)
41
43
  .background(
42
- Brush.linearGradient(
43
- colors = listOf(
44
- Color(0xFFFDCADE),
45
- Color(0x00FDCADE)
46
- ),
47
- start = Offset(0f, 0f),
48
- end = Offset(0f, with(LocalDensity.current) { 154.dp.toPx() })
49
- )
44
+ if (headerTransparent)
45
+ Brush.linearGradient(
46
+ colors = listOf(
47
+ Color.Transparent,
48
+ Color.Transparent,
49
+ ),
50
+ start = Offset(0f, 0f),
51
+ end = Offset(0f, with(LocalDensity.current) { 154.dp.toPx() })
52
+ )
53
+ else
54
+ Brush.linearGradient(
55
+ colors = listOf(
56
+ Color(0xFFFDCADE),
57
+ Color(0x00FDCADE)
58
+ ),
59
+ start = Offset(0f, 0f),
60
+ end = Offset(0f, with(LocalDensity.current) { 154.dp.toPx() })
61
+ )
50
62
  )
51
63
  )
52
64
  if (AppTheme.current.assets.headerBackground != null) {
@@ -30,6 +30,7 @@ import vn.momo.kits.const.Colors
30
30
  import kotlinx.serialization.json.Json
31
31
  import kotlinx.serialization.json.jsonObject
32
32
  import kotlinx.serialization.json.jsonPrimitive
33
+ import vn.momo.kits.const.AppTheme
33
34
 
34
35
  data class HeaderRightData(
35
36
  val useShortcut: Boolean = false,
@@ -80,11 +81,12 @@ object Spacing {
80
81
  @Composable
81
82
  fun HeaderRight(
82
83
  headerRight: HeaderRightData? = null,
84
+ tintColor: Color? = null,
83
85
  ) {
84
86
  Row(
85
87
  verticalAlignment = Alignment.CenterVertically,
86
88
  ) {
87
- ToolkitHeaderRight(headerRight = headerRight)
89
+ ToolkitHeaderRight(headerRight = headerRight, tintColor = tintColor)
88
90
  }
89
91
  }
90
92
 
@@ -113,7 +115,7 @@ private fun getNavigationButtonConfig(
113
115
  }
114
116
 
115
117
  @Composable
116
- private fun ToolkitHeaderRight(headerRight: HeaderRightData? = null) {
118
+ private fun ToolkitHeaderRight(headerRight: HeaderRightData? = null, tintColor: Color? = null) {
117
119
  var isFavorite by remember { mutableStateOf(false) }
118
120
  val isLoading by remember { mutableStateOf(false) }
119
121
  val api = PlatformApi.current as? ComposeApi
@@ -191,6 +193,10 @@ private fun ToolkitHeaderRight(headerRight: HeaderRightData? = null) {
191
193
  isShowShortcut = false
192
194
  }
193
195
 
196
+ val tintIconColor = tintColor ?: AppTheme.current.colors.text.default
197
+ val backgroundButton = if (tintColor == Colors.black_01) Colors.black_20.copy(alpha = 0.6f) else Colors.black_01.copy(alpha = 0.6f)
198
+ val borderColor = if (tintColor == Colors.black_01) Colors.black_01.copy(alpha = 0.2f) else Colors.black_20.copy(alpha = 0.2f)
199
+
194
200
  Row(
195
201
  verticalAlignment = Alignment.CenterVertically
196
202
  ) {
@@ -199,7 +205,8 @@ private fun ToolkitHeaderRight(headerRight: HeaderRightData? = null) {
199
205
  disabled = isLoading,
200
206
  icon = navButtonConfig.icon,
201
207
  showBadge = showBadge,
202
- onClick = navButtonConfig.onPress
208
+ onClick = navButtonConfig.onPress,
209
+ tintColor = tintIconColor
203
210
  )
204
211
  }
205
212
  Row(
@@ -207,16 +214,17 @@ private fun ToolkitHeaderRight(headerRight: HeaderRightData? = null) {
207
214
  horizontalArrangement = Arrangement.Center,
208
215
  modifier = Modifier
209
216
  .padding(start = Spacing.S)
210
- .border(0.2.dp, Color(0x33000000), shape = RoundedCornerShape(14.dp))
217
+ .border(0.2.dp, borderColor, shape = RoundedCornerShape(14.dp))
211
218
  .width(toolkitWidth)
212
219
  .height(28.dp)
213
220
  .clip(shape = RoundedCornerShape(14.dp))
214
- .background(Color(0x99FFFFFF))
221
+ .background(backgroundButton)
215
222
  ) {
216
223
  if (context != null) {
217
224
  Icon(
218
225
  source = "help_center",
219
226
  size = 20.dp,
227
+ color = tintIconColor,
220
228
  modifier = Modifier.padding(4.dp).clickable {
221
229
  onPressHelpCenter()
222
230
  })
@@ -224,13 +232,14 @@ private fun ToolkitHeaderRight(headerRight: HeaderRightData? = null) {
224
232
  modifier = Modifier
225
233
  .width(0.5.dp)
226
234
  .height(12.dp)
227
- .background(Colors.black_20)
235
+ .background(tintColor ?: Colors.black_20)
228
236
  )
229
237
  }
230
238
 
231
239
  Icon(
232
240
  source = "16_navigation_close_circle",
233
241
  size = 20.dp,
242
+ color = tintIconColor,
234
243
  modifier = Modifier.padding(4.dp).clickable { onPressClose() }
235
244
  )
236
245
  }
@@ -242,8 +251,12 @@ fun NavigationButton(
242
251
  disabled: Boolean,
243
252
  icon: String,
244
253
  showBadge: Boolean? = false,
245
- onClick: () -> Unit
254
+ onClick: () -> Unit,
255
+ tintColor: Color?
246
256
  ) {
257
+ val backgroundButton = if (tintColor == Colors.black_01) Colors.black_20.copy(alpha = 0.6f) else Colors.black_01.copy(alpha = 0.6f)
258
+ val borderColor = if (tintColor == Colors.black_01) Colors.black_01.copy(alpha = 0.2f) else Colors.black_20.copy(alpha = 0.2f)
259
+
247
260
  Box(
248
261
  modifier = Modifier
249
262
  .size(28.dp)
@@ -253,8 +266,8 @@ fun NavigationButton(
253
266
  modifier = Modifier
254
267
  .matchParentSize()
255
268
  .clip(CircleShape)
256
- .background(Color(0x99FFFFFF))
257
- .border(0.2.dp, Color(0x33000000), CircleShape)
269
+ .background(backgroundButton)
270
+ .border(0.2.dp, borderColor, CircleShape)
258
271
  )
259
272
  Box(
260
273
  modifier = Modifier.matchParentSize(),
@@ -262,7 +275,8 @@ fun NavigationButton(
262
275
  ) {
263
276
  Icon(
264
277
  source = icon,
265
- size = 20.dp
278
+ size = 20.dp,
279
+ color = tintColor
266
280
  )
267
281
  }
268
282
 
@@ -52,6 +52,8 @@ const val HEADER_HEIGHT = 52
52
52
  @Composable
53
53
  fun Screen(
54
54
  backgroundColor: Color? = null,
55
+ tintColor: Color? = null,
56
+ headerTransparent: Boolean = false,
55
57
  isBack: Boolean = true,
56
58
  headerType: HeaderType = HeaderType.DEFAULT,
57
59
  verticalArrangement: Arrangement.Vertical = Arrangement.Top,
@@ -103,7 +105,8 @@ fun Screen(
103
105
  if (animatedHeader === null) {
104
106
  HeaderBackground(
105
107
  headerType = headerType,
106
- scrollState = scrollState.value
108
+ scrollState = scrollState.value,
109
+ headerTransparent = headerTransparent
107
110
  )
108
111
  } else {
109
112
  Box(
@@ -129,6 +132,7 @@ fun Screen(
129
132
  inputSearchProps = inputSearchProps,
130
133
  scrollState = scrollState.value,
131
134
  useAnimationSearch = useAnimationSearch,
135
+ tintColor = tintColor
132
136
  )
133
137
 
134
138
  Column(
@@ -8,6 +8,7 @@ import androidx.compose.animation.core.rememberInfiniteTransition
8
8
  import androidx.compose.animation.core.tween
9
9
  import androidx.compose.foundation.background
10
10
  import androidx.compose.foundation.border
11
+ import androidx.compose.foundation.clickable
11
12
  import androidx.compose.foundation.layout.Arrangement
12
13
  import androidx.compose.foundation.layout.Box
13
14
  import androidx.compose.foundation.layout.Column
@@ -35,6 +36,7 @@ import androidx.compose.ui.text.input.KeyboardType
35
36
  import androidx.compose.ui.unit.dp
36
37
  import androidx.compose.ui.zIndex
37
38
  import vn.momo.kits.const.AppTheme
39
+ import vn.momo.kits.const.Colors
38
40
  import vn.momo.kits.const.Radius
39
41
  import vn.momo.kits.const.Spacing
40
42
  import vn.momo.kits.const.Typography
@@ -87,7 +89,7 @@ fun InputOTP(
87
89
  dataType: String = "number",
88
90
  modifier: Modifier = Modifier
89
91
  ) {
90
- val maxLength = 10
92
+ val maxLength = length ?: 10
91
93
  var isFocused by remember { mutableStateOf(false) }
92
94
  var isBlurred = false
93
95
 
@@ -100,6 +102,10 @@ fun InputOTP(
100
102
  }
101
103
  }
102
104
 
105
+ fun onClearText(){
106
+ handleChangeText("")
107
+ }
108
+
103
109
 
104
110
  Column {
105
111
  BasicTextField(
@@ -193,6 +199,22 @@ fun InputOTP(
193
199
  }
194
200
  }
195
201
  }
202
+ Box(
203
+ modifier = Modifier.align(Alignment.Center).zIndex(10f)
204
+ ) {
205
+ Row(
206
+ horizontalArrangement = Arrangement.End,
207
+ modifier = Modifier.fillMaxWidth()
208
+ ) {
209
+ Icon(
210
+ "24_navigation_close_circle_full",
211
+ size = 12.dp,
212
+ modifier = Modifier.padding(end = 12.dp).clickable {
213
+ onClearText()
214
+ }
215
+ )
216
+ }
217
+ }
196
218
  }
197
219
  }
198
220
  )
@@ -17,6 +17,7 @@ import androidx.compose.runtime.remember
17
17
  import androidx.compose.ui.Alignment
18
18
  import androidx.compose.ui.Modifier
19
19
  import androidx.compose.ui.draw.clip
20
+ import androidx.compose.ui.layout.ContentScale
20
21
  import androidx.compose.ui.semantics.semantics
21
22
  import androidx.compose.ui.semantics.testTag
22
23
  import androidx.compose.ui.unit.dp
@@ -41,6 +42,7 @@ fun PopupPromotion(
41
42
  indication = null,
42
43
  onClick = onPress
43
44
  ),
45
+ options = Options(alignment = Alignment.Center, contentScale = ContentScale.Fit)
44
46
  )
45
47
  }
46
48
  Box(
@@ -0,0 +1,208 @@
1
+ package vn.momo.kits.components
2
+
3
+ import androidx.compose.foundation.background
4
+ import androidx.compose.foundation.clickable
5
+ import androidx.compose.foundation.layout.Arrangement
6
+ import androidx.compose.foundation.layout.Box
7
+ import androidx.compose.foundation.layout.Column
8
+ import androidx.compose.foundation.layout.IntrinsicSize
9
+ import androidx.compose.foundation.layout.Row
10
+ import androidx.compose.foundation.layout.Spacer
11
+ import androidx.compose.foundation.layout.fillMaxHeight
12
+ import androidx.compose.foundation.layout.fillMaxWidth
13
+ import androidx.compose.foundation.layout.height
14
+ import androidx.compose.foundation.layout.padding
15
+ import androidx.compose.foundation.layout.size
16
+ import androidx.compose.foundation.layout.width
17
+ import androidx.compose.foundation.shape.RoundedCornerShape
18
+ import androidx.compose.runtime.Composable
19
+ import androidx.compose.ui.Alignment
20
+ import androidx.compose.ui.Modifier
21
+ import androidx.compose.ui.graphics.Color
22
+ import androidx.compose.ui.text.TextStyle
23
+ import androidx.compose.ui.text.font.FontWeight
24
+ import androidx.compose.ui.unit.dp
25
+ import androidx.compose.ui.unit.sp
26
+ import vn.momo.kits.const.AppTheme
27
+ import vn.momo.kits.const.Colors
28
+ import vn.momo.kits.const.Spacing
29
+ import vn.momo.kits.const.Typography
30
+
31
+ enum class TitleType { Card, Section }
32
+ enum class TitleSize { Small, Medium, Large }
33
+ enum class IconAlign { Top, Center, Bottom }
34
+ enum class ButtonSize { Small, Large }
35
+
36
+ @Composable
37
+ fun Title(
38
+ title: String,
39
+ type: TitleType = TitleType.Section,
40
+ size: TitleSize = TitleSize.Medium,
41
+ icon: String? = null,
42
+ iconColor: Color? = null,
43
+ iconAlign: IconAlign = IconAlign.Top,
44
+ showRightAction: Boolean = false,
45
+ showTrailingAction: Boolean = false,
46
+ badgeLabel: String? = null,
47
+ buttonTitle: String? = null,
48
+ buttonSize: ButtonSize = ButtonSize.Small,
49
+ onPressRightAction: () -> Unit = {},
50
+ onPressTrailingAction: () -> Unit = {},
51
+ textOnly: Boolean = false,
52
+ description: String? = null,
53
+ modifier: Modifier = Modifier
54
+ ) {
55
+ val theme = AppTheme.current
56
+ val textStyle: TextStyle = when (type) {
57
+ TitleType.Card -> when (size) {
58
+ TitleSize.Small -> TextStyle(fontSize = 14.sp, lineHeight = 20.sp)
59
+ TitleSize.Medium -> TextStyle(fontSize = 16.sp, lineHeight = 22.sp)
60
+ TitleSize.Large -> TextStyle(fontSize = 18.sp, lineHeight = 26.sp)
61
+ }
62
+
63
+ TitleType.Section -> when (size) {
64
+ TitleSize.Small -> TextStyle(
65
+ fontSize = 16.sp,
66
+ lineHeight = 24.sp,
67
+ fontWeight = FontWeight.Bold
68
+ )
69
+
70
+ TitleSize.Medium -> TextStyle(
71
+ fontSize = 18.sp,
72
+ lineHeight = 26.sp,
73
+ fontWeight = FontWeight.Bold
74
+ )
75
+
76
+ TitleSize.Large -> TextStyle(
77
+ fontSize = 20.sp,
78
+ lineHeight = 28.sp,
79
+ fontWeight = FontWeight.Bold
80
+ )
81
+ }
82
+ }
83
+
84
+ if (textOnly) {
85
+ Text(
86
+ text = title,
87
+ style = textStyle,
88
+ modifier = modifier
89
+ )
90
+ return
91
+ }
92
+
93
+ Row(
94
+ modifier = modifier
95
+ .fillMaxWidth()
96
+ .height(IntrinsicSize.Min)
97
+ .padding(8.dp),
98
+ verticalAlignment = Alignment.CenterVertically
99
+ ) {
100
+ icon?.let {
101
+ Column(
102
+ modifier = Modifier.fillMaxHeight().padding(end = Spacing.S),
103
+ verticalArrangement = when (iconAlign) {
104
+ IconAlign.Top -> Arrangement.Top
105
+ IconAlign.Center -> Arrangement.Center
106
+ IconAlign.Bottom -> Arrangement.Bottom
107
+ },
108
+ ) {
109
+ // Icon
110
+ Icon(
111
+ source = it,
112
+ color = iconColor ?: theme.colors.text.default,
113
+ modifier = Modifier.size(24.dp)
114
+ )
115
+ }
116
+ }
117
+
118
+ Column(
119
+ modifier = Modifier.weight(1f)
120
+ ) {
121
+ Row(
122
+ verticalAlignment = Alignment.CenterVertically
123
+ ) {
124
+ Row(
125
+ verticalAlignment = Alignment.CenterVertically,
126
+ modifier = if (showTrailingAction) Modifier else Modifier.weight(1f),
127
+ ) {
128
+ Text(
129
+ text = title,
130
+ style = textStyle,
131
+ maxLines = if (showTrailingAction || badgeLabel != null) 1 else 2,
132
+ )
133
+
134
+ // Badge
135
+ badgeLabel?.let {
136
+ Spacer(modifier = Modifier.width(4.dp))
137
+ Badge(
138
+ label = it
139
+ )
140
+ }
141
+
142
+ }
143
+
144
+ // Trailing action (icon)
145
+ if (showTrailingAction && !showRightAction) {
146
+ Spacer(modifier = Modifier.width(8.dp))
147
+ Box(
148
+ modifier = Modifier
149
+ .size(24.dp)
150
+ .background(
151
+ color = if (type == TitleType.Section) Colors.black_06.copy(0.6f) else Colors.black_06.copy(
152
+ 0.3f
153
+ ),
154
+ shape = RoundedCornerShape(12.dp)
155
+ )
156
+ .clickable { onPressTrailingAction() },
157
+ contentAlignment = Alignment.Center
158
+ ) {
159
+ Icon(
160
+ source = "arrow_chevron_right_small",
161
+ size = 18.dp
162
+ )
163
+ }
164
+ }
165
+ }
166
+
167
+ // Description
168
+ description?.let {
169
+ Spacer(modifier = Modifier.height(4.dp))
170
+ Text(
171
+ text = it,
172
+ style = Typography.descriptionDefaultRegular,
173
+ color = theme.colors.text.secondary
174
+ )
175
+ }
176
+ }
177
+
178
+ // Right action (button or icon)
179
+ if (showRightAction && !showTrailingAction) {
180
+ Spacer(modifier = Modifier.width(8.dp))
181
+ if (buttonTitle != null) {
182
+ Text(
183
+ text = buttonTitle,
184
+ style = if (buttonSize === ButtonSize.Small) Typography.actionXsBold else Typography.actionSBold,
185
+ color = theme.colors.primary,
186
+ modifier = Modifier.clickable { onPressRightAction() }
187
+ )
188
+ } else {
189
+ Box(
190
+ modifier = Modifier
191
+ .size(24.dp)
192
+ .background(
193
+ color = Colors.pink_03.copy(alpha = 0.1f),
194
+ shape = RoundedCornerShape(12.dp)
195
+ )
196
+ .clickable { onPressRightAction() },
197
+ contentAlignment = Alignment.Center
198
+ ) {
199
+ Icon(
200
+ source = "arrow_chevron_right_small",
201
+ modifier = Modifier.size(22.dp),
202
+ color = theme.colors.primary
203
+ )
204
+ }
205
+ }
206
+ }
207
+ }
208
+ }
@@ -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,31 @@ 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.min
15
17
 
16
18
  const val DEFAULT_SCREEN_SIZE = 375f
19
+ const val MAX_FONT_SCALE = 1.5f
20
+ const val MAX_DEVICE_SCALE = 5
17
21
 
18
22
  @Composable
19
23
  fun scaleSize(size: Float): Float {
20
24
  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
25
+ val deviceScale = deviceWidth / DEFAULT_SCREEN_SIZE
26
+
27
+ val density = LocalDensity.current
28
+ val fontScale = density.fontScale
29
+
30
+ var fontSize = size / fontScale //To ignore OS scale
31
+
32
+ if (deviceScale > 1) {
33
+ fontSize = min(deviceScale * fontSize, fontSize + MAX_DEVICE_SCALE)
24
34
  }
25
- return size
35
+
36
+ if (fontScale > 1) {
37
+ fontSize = min(fontScale * fontSize, fontSize * MAX_FONT_SCALE)
38
+ }
39
+
40
+ return fontSize
26
41
  }
27
42
 
28
43
  @OptIn(InternalResourceApi::class)
@@ -85,7 +85,9 @@ public struct HeaderBackground: View {
85
85
  endPoint: .bottom))
86
86
  .opacity(opacity)
87
87
  .frame(height: height))
88
- .overlay(Rectangle()
88
+ .overlay(
89
+ isTransparent ? nil :
90
+ Rectangle()
89
91
  .fill(Colors.black04)
90
92
  .frame(height: 1)
91
93
  .frame(maxWidth: .infinity)
@@ -154,9 +156,8 @@ public struct Header: View {
154
156
 
155
157
  public var body: some View {
156
158
 
157
- // let tintIconColor = Colors.black17
158
- let backgroundButton = tintColor == Colors.black01 ? Color.white.opacity(0.2) : Colors.black01.opacity(0.6)
159
- let borderColor: Color = tintColor == Colors.black01 ? Color.black.opacity(0.6) : Color.black.opacity(0.2)
159
+ let backgroundButtonColor: Color = tintColor == Colors.black01 ? Colors.black20.opacity(0.6) : Colors.black01.opacity(0.6)
160
+ let borderColor: Color = tintColor == Colors.black01 ? Colors.black01.opacity(0.2) : Colors.black20.opacity(0.2)
160
161
 
161
162
  if headerType != .none {
162
163
  VStack(spacing: 0) {
@@ -166,7 +167,7 @@ public struct Header: View {
166
167
  SwiftUI.Button(action: goBack) {
167
168
  Circle()
168
169
  .stroke(borderColor, lineWidth: 0.2)
169
- .background(Circle().fill(backgroundButton))
170
+ .background(Circle().fill(backgroundButtonColor))
170
171
  .frame(width: 28, height: 28)
171
172
  .overlay(
172
173
  Icon(source: "arrow-back", size: 20, color: tintColor)
@@ -213,8 +214,9 @@ public struct Header: View {
213
214
  }
214
215
  } else {
215
216
  Text(title)
216
- .font(Font.system(size: 17).weight(.bold)) .lineSpacing(22)
217
- .foregroundColor(Colors.black17)
217
+ .font(Font.system(size: 17).weight(.bold))
218
+ .lineSpacing(22)
219
+ .foregroundColor(tintColor)
218
220
  .frame(maxWidth: titlePosition == .center ? UIScreen.main.bounds.width - 252 : nil)
219
221
  .frame(maxWidth: titlePosition == .center ? .infinity : UIScreen.main.bounds.width - 172, alignment: titlePosition == .center ? .center : .leading)
220
222
  .lineLimit(1)
@@ -75,20 +75,23 @@ struct NavigationButtonConfig {
75
75
 
76
76
  // MARK: - Components
77
77
  public struct HeaderRight: View {
78
- public init(headerRight: HeaderRightData? = nil) {
78
+ public init(headerRight: HeaderRightData? = nil, tintColor: Color? = nil) {
79
79
  self.headerRight = headerRight
80
+ self.tintColor = tintColor
80
81
  }
81
82
  var headerRight: HeaderRightData?
83
+ var tintColor: Color? = nil
82
84
 
83
85
  public var body: some View {
84
86
  HStack(alignment: .center) {
85
- ToolkitHeaderRight(headerRight: headerRight)
87
+ ToolkitHeaderRight(headerRight: headerRight, tintColor: tintColor)
86
88
  }
87
89
  }
88
90
  }
89
91
 
90
92
  struct ToolkitHeaderRight: View {
91
93
  var headerRight: HeaderRightData?
94
+ var tintColor: Color?
92
95
  @State private var isFavorite: Bool = false
93
96
  @State private var isLoading: Bool = false
94
97
  @EnvironmentObject private var environment: ApplicationEnvironment
@@ -176,44 +179,47 @@ struct ToolkitHeaderRight: View {
176
179
  }
177
180
 
178
181
  var body: some View {
182
+ let backgroundButtonColor: Color = tintColor == Colors.black01 ? Colors.black20.opacity(0.6) : Colors.black01.opacity(0.6)
183
+ let borderColor: Color = tintColor == Colors.black01 ? Colors.black01.opacity(0.2) : Colors.black20.opacity(0.2)
179
184
  let navButtonConfig = getNavigationButtonConfig()
180
185
  let showBadge = headerRight?.tools.contains { group in
181
186
  group.items.contains { $0.showBadge }
182
187
  } ?? false
183
188
 
184
- HStack(alignment: .center) {
189
+ HStack(alignment: .center, spacing: 0) {
185
190
  if headerRight?.useShortcut == true {
186
191
  NavigationButton(
187
192
  disabled: isLoading,
188
193
  icon: navButtonConfig.icon,
189
194
  showBadge: showBadge,
190
- onClick: navButtonConfig.onPress
195
+ onClick: navButtonConfig.onPress,
196
+ tintColor: tintColor
191
197
  )
192
198
  }
193
199
 
194
200
  HStack(alignment: .center, spacing: 0) {
195
- Icon(source: "help_center", size: 20)
201
+ Icon(source: "help_center", size: 20, color: tintColor ?? Colors.black17)
196
202
  .padding(4)
197
203
  .onTapGesture {
198
204
  onPressHelpCenter()
199
205
  }
200
206
 
201
207
  Rectangle()
202
- .fill(Colors.black20)
208
+ .fill(tintColor ?? Colors.black20)
203
209
  .frame(width: 0.5, height: 12)
204
210
 
205
- Icon(source: "16_navigation_close_circle", size: 20, color: Colors.black17)
211
+ Icon(source: "16_navigation_close_circle", size: 20, color: tintColor ?? Colors.black17)
206
212
  .padding(4)
207
213
  .onTapGesture {
208
214
  onPressClose()
209
215
  }
210
216
  }
211
217
  .frame(width: 65, height: 28)
212
- .background(Color.white.opacity(0.6))
218
+ .background(backgroundButtonColor)
213
219
  .cornerRadius(14)
214
220
  .overlay(
215
221
  RoundedRectangle(cornerRadius: 14)
216
- .stroke(Color.black.opacity(0.2), lineWidth: 0.2)
222
+ .stroke(borderColor, lineWidth: 0.2)
217
223
  )
218
224
  .padding(.leading, Spacing.S)
219
225
  }
@@ -225,17 +231,21 @@ struct NavigationButton: View {
225
231
  var icon: String
226
232
  var showBadge: Bool
227
233
  var onClick: () -> Void
234
+ var tintColor: Color?
228
235
 
229
236
  var body: some View {
237
+ let backgroundButtonColor: Color = tintColor == Colors.black01 ? Colors.black20.opacity(0.6) : Colors.black01.opacity(0.6)
238
+ let borderColor: Color = tintColor == Colors.black01 ? Colors.black01.opacity(0.2) : Colors.black20.opacity(0.2)
239
+
230
240
  ZStack {
231
241
  Circle()
232
- .fill(Color.white.opacity(0.6))
242
+ .fill(backgroundButtonColor)
233
243
  .overlay(
234
244
  Circle()
235
- .stroke(Color.black.opacity(0.2), lineWidth: 0.2)
245
+ .stroke(borderColor, lineWidth: 0.2)
236
246
  )
237
247
 
238
- Icon(source: icon, size: 16)
248
+ Icon(source: icon, size: 16, color: tintColor)
239
249
 
240
250
  if showBadge {
241
251
  BadgeDot(size: .small)
@@ -76,6 +76,9 @@ public struct Screen<Content: View>: View {
76
76
  @State private var scrollOffset: CGFloat = 0
77
77
 
78
78
  public var body: some View {
79
+
80
+ let headerRightInstance = {HeaderRight(tintColor: tintColor)}
81
+
79
82
  GeometryReader { geometry in
80
83
  let topInset = geometry.safeAreaInsets.top
81
84
  let keyboardHeight: CGFloat = 0
@@ -101,7 +104,7 @@ public struct Screen<Content: View>: View {
101
104
  headerType: headerType,
102
105
  titlePosition: titlePosition,
103
106
  title: title,
104
- headerRight: headerRight,
107
+ headerRight: headerRight ?? headerRightInstance,
105
108
  goBack: goBack,
106
109
  opacity: min(scrollOffset / 114, 1),
107
110
  animatedHeader: animatedHeader,
@@ -27,7 +27,7 @@ public struct ImageView: View {
27
27
  error = false
28
28
  }
29
29
  .resizable()
30
- .renderingMode(.original)
30
+ .renderingMode(color != nil ? .template : .original)
31
31
  .placeholder {
32
32
  Rectangle().foregroundColor(.gray)
33
33
  VStack(alignment: .center, content: {
@@ -49,7 +49,7 @@ public struct ImageView: View {
49
49
  .scaledToFit()
50
50
  .transition(.fade)
51
51
  .aspectRatio(aspectRatio, contentMode: contentMode)
52
- .modifier(ColorMultiplyModifier(color: color))
52
+ .foregroundColor(color)
53
53
  }
54
54
 
55
55
  // MARK: Internal
@@ -44,7 +44,7 @@ public struct PopupDisplay: View {
44
44
  Image(systemName: "xmark.circle.fill")
45
45
  .resizable()
46
46
  .frame(width: 22, height: 22)
47
- .overlay(RoundedRectangle(cornerRadius: Radius.L).stroke(Colors.black01, lineWidth: 1))
47
+ .overlay(RoundedRectangle(cornerRadius: Radius.L).stroke(Colors.black01, lineWidth: 2))
48
48
  }.foregroundColor(Colors.black20)
49
49
  .background(Colors.black01.cornerRadius(15))
50
50
  .padding(.trailing, -11)
@@ -61,16 +61,16 @@ public struct PopupDisplay: View {
61
61
  .fill(Color.gray.opacity(0.3))
62
62
  .frame(maxWidth: .infinity, maxHeight: 184)
63
63
  }
64
- .scaledToFill()
65
- .frame(maxWidth: .infinity)
66
- .clipShape(RoundedCorner(radius: 15, corners: [.topLeft, .topRight]))
67
64
  .aspectRatio(2, contentMode: .fit)
65
+ .frame(maxWidth: .infinity, alignment: .center)
66
+ .clipShape(RoundedCorner(radius: 15, corners: [.topLeft, .topRight]))
67
+ .clipped()
68
68
  }
69
69
  VStack(alignment: .leading) {
70
70
  Text(title)
71
71
  .foregroundColor(.black)
72
72
  .font(.header_default_semibold)
73
- .padding(.top, 16)
73
+ .padding(.top, 24)
74
74
  .padding(.bottom, 8)
75
75
  .lineLimit(2)
76
76
  .accessibility(identifier: "title_popup_permission")
@@ -87,11 +87,12 @@ public struct PopupDisplay: View {
87
87
  .foregroundColor(Colors.black12)
88
88
  .font(.description_xs_regular)
89
89
  .lineLimit(1)
90
+ .padding(.bottom, 8)
90
91
  }
91
92
  }
92
93
  .frame(maxWidth: .infinity, alignment: .leading)
93
94
  .padding(.horizontal, 24)
94
- .padding(.bottom, 24)
95
+ .padding(.bottom, 16)
95
96
 
96
97
  if buttonDirection == .row {
97
98
  if buttonType == .text {
@@ -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 = 10
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.117.1-beta.2",
3
+ "version": "0.117.1-beta.20",
4
4
  "private": false,
5
5
  "dependencies": {},
6
6
  "devDependencies": {},