@momo-kits/native-kits 0.156.10-debug → 0.156.11-debug

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.
@@ -40,7 +40,7 @@ kotlin {
40
40
  }
41
41
 
42
42
  cocoapods {
43
- version = "0.156.10-debug"
43
+ version = "0.156.11-debug"
44
44
  summary = "IOS Shared module"
45
45
  homepage = "https://momo.vn"
46
46
  ios.deploymentTarget = "15.0"
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |spec|
2
2
  spec.name = 'compose'
3
- spec.version = '0.156.1-beta.2'
3
+ spec.version = '0.156.6-beta.16'
4
4
  spec.homepage = 'https://momo.vn'
5
5
  spec.source = { :http=> ''}
6
6
  spec.authors = ''
@@ -10,11 +10,8 @@ import androidx.compose.foundation.layout.Box
10
10
  import androidx.compose.runtime.Composable
11
11
  import androidx.compose.runtime.getValue
12
12
  import androidx.compose.ui.Modifier
13
- import androidx.compose.ui.draw.drawBehind
14
13
  import androidx.compose.ui.graphics.Color
15
14
  import androidx.compose.ui.graphics.NativePaint
16
- import androidx.compose.ui.graphics.Paint
17
- import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
18
15
  import androidx.compose.ui.graphics.toArgb
19
16
  import androidx.compose.ui.platform.LocalConfiguration
20
17
  import androidx.compose.ui.unit.Dp
@@ -78,6 +78,14 @@ data class MiniAppContext(
78
78
  }
79
79
  }
80
80
 
81
+ @Immutable
82
+ data class ComponentInformation(
83
+ val componentName: String? = null,
84
+ val componentId: String? = null,
85
+ val params: Map<String,Any?>? = null,
86
+ val action: String? = null
87
+ )
88
+
81
89
  var IsShowBaseLineDebug = false
82
90
 
83
91
  val ApplicationContext = staticCompositionLocalOf<MiniAppContext?> {
@@ -92,4 +100,8 @@ val AppLanguage = staticCompositionLocalOf<String?> {
92
100
  null
93
101
  }
94
102
 
103
+ val LocalComponentInformation = staticCompositionLocalOf<ComponentInformation?> {
104
+ null
105
+ }
106
+
95
107
  internal val ScaleSizeMaxRate = staticCompositionLocalOf<Float?> { null }
@@ -23,7 +23,7 @@ import vn.momo.kits.modifier.conditional
23
23
 
24
24
 
25
25
  @Composable
26
- fun Badge(label: String = "Label", backgroundColor: Color? = null) {
26
+ fun Badge(label: String = "Label", backgroundColor: Color? = null, modifier: Modifier? = null) {
27
27
  val primaryColors = listOf(
28
28
  Color(0xFFF0F0F0),
29
29
  Color(0xFFEB2F96),
@@ -63,21 +63,23 @@ fun Badge(label: String = "Label", backgroundColor: Color? = null) {
63
63
  }
64
64
  val scaleSize = scaleSize(16f)
65
65
 
66
- Box(
67
- modifier = Modifier
68
- .height(scaleSize.dp)
69
- .widthIn(min = scaleSize.dp)
70
- .background(color = badgeColor, shape = RoundedCornerShape(Radius.M))
71
- .border(width = 1.dp, shape = RoundedCornerShape(Radius.M), color = Colors.black_01)
72
- .conditional(IsShowBaseLineDebug) {
73
- border(1.dp, Colors.blue_03)
74
- }
75
- .padding(horizontal = Spacing.XS), contentAlignment = Alignment.Center
76
- ) {
77
- Text(
78
- text = formatTitle(label),
79
- color = Colors.black_01,
80
- style = Typography.actionXxsBold
81
- )
66
+ if (modifier != null) {
67
+ Box(
68
+ modifier = modifier
69
+ .height(scaleSize.dp)
70
+ .widthIn(min = scaleSize.dp)
71
+ .background(color = badgeColor, shape = RoundedCornerShape(Radius.M))
72
+ .border(width = 1.dp, shape = RoundedCornerShape(Radius.M), color = Colors.black_01)
73
+ .conditional(IsShowBaseLineDebug) {
74
+ border(1.dp, Colors.blue_03)
75
+ }
76
+ .padding(horizontal = Spacing.XS), contentAlignment = Alignment.Center
77
+ ) {
78
+ Text(
79
+ text = formatTitle(label),
80
+ color = Colors.black_01,
81
+ style = Typography.actionXxsBold
82
+ )
83
+ }
82
84
  }
83
85
  }
@@ -0,0 +1,198 @@
1
+ package vn.momo.kits.components
2
+
3
+ import androidx.compose.foundation.Canvas
4
+ import androidx.compose.foundation.background
5
+ import androidx.compose.foundation.border
6
+ import androidx.compose.foundation.layout.Box
7
+ import androidx.compose.foundation.layout.WindowInsets
8
+ import androidx.compose.foundation.layout.asPaddingValues
9
+ import androidx.compose.foundation.layout.fillMaxHeight
10
+ import androidx.compose.foundation.layout.fillMaxSize
11
+ import androidx.compose.foundation.layout.fillMaxWidth
12
+ import androidx.compose.foundation.layout.height
13
+ import androidx.compose.foundation.layout.navigationBars
14
+ import androidx.compose.foundation.layout.padding
15
+ import androidx.compose.foundation.layout.width
16
+ import androidx.compose.runtime.Composable
17
+ import androidx.compose.ui.Alignment
18
+ import androidx.compose.ui.Modifier
19
+ import androidx.compose.ui.geometry.Offset
20
+ import androidx.compose.ui.graphics.Color
21
+ import androidx.compose.ui.graphics.PathEffect
22
+ import androidx.compose.ui.graphics.StrokeCap
23
+ import androidx.compose.ui.unit.dp
24
+ import io.ktor.util.Platform
25
+ import vn.momo.kits.application.IsShowBaseLineDebug
26
+ import vn.momo.kits.const.Colors
27
+ import vn.momo.kits.modifier.conditional
28
+ import vn.momo.kits.platform.getPlatformName
29
+ import vn.momo.kits.platform.getStatusBarHeight
30
+
31
+ /**
32
+ * A debug overlay that draws danger/warning baseline guides on top of the screen.
33
+ *
34
+ * Highlights safe-area boundaries, header regions, and bottom navigation zones
35
+ * using colored solid or dotted lines and semi-transparent red zones.
36
+ *
37
+ * @param enabled When `false` the composable renders nothing. Pass `false` when
38
+ * your QC automation flag is active to suppress the overlay.
39
+ */
40
+ @Composable
41
+ fun BaselineView(enabled: Boolean = true) {
42
+ if (!enabled) return
43
+
44
+ val bottomInsetHeight = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
45
+ val topInset = if (getPlatformName() == "Android") getStatusBarHeight() - 14.dp else getStatusBarHeight()
46
+ val bottomInset = if (getPlatformName() == "iOS") minOf(bottomInsetHeight, 21.dp) else bottomInsetHeight
47
+
48
+ Box(modifier = Modifier.fillMaxSize()) {
49
+ // Danger zones
50
+ Box(
51
+ modifier = Modifier
52
+ .fillMaxWidth()
53
+ .height(topInset)
54
+ .background(Color.Red.copy(alpha = 0.15f))
55
+ )
56
+ Box(
57
+ modifier = Modifier
58
+ .width(12.dp)
59
+ .fillMaxHeight()
60
+ .padding(top = topInset, bottom = bottomInset)
61
+ .background(Color.Red.copy(alpha = 0.15f))
62
+ )
63
+ Box(
64
+ modifier = Modifier
65
+ .width(12.dp)
66
+ .fillMaxHeight()
67
+ .padding(top = topInset, bottom = bottomInset)
68
+ .background(Color.Red.copy(alpha = 0.15f))
69
+ .align(Alignment.TopEnd)
70
+ )
71
+ Box(
72
+ modifier = Modifier
73
+ .fillMaxWidth()
74
+ .height(bottomInset)
75
+ .background(Color.Red.copy(alpha = 0.15f))
76
+ .align(Alignment.BottomCenter)
77
+ )
78
+
79
+ // Danger lines
80
+ BaselineDottedLine(
81
+ Modifier
82
+ .padding(top = topInset)
83
+ .fillMaxWidth(),
84
+ isDotted = false,
85
+ color = Color(0xFFE400FF)
86
+ )
87
+ BaselineDottedLine(
88
+ Modifier
89
+ .padding(top = topInset + 52.dp)
90
+ .fillMaxWidth(),
91
+ color = Color(0xFFE400FF)
92
+ )
93
+ BaselineDottedLine(
94
+ Modifier
95
+ .padding(top = topInset + 104.dp)
96
+ .fillMaxWidth(),
97
+ color = Color(0xFFE400FF)
98
+ )
99
+ BaselineDottedLine(
100
+ Modifier
101
+ .padding(bottom = bottomInset + 64.dp)
102
+ .fillMaxWidth()
103
+ .align(Alignment.BottomCenter),
104
+ color = Color(0xFFE400FF)
105
+ )
106
+ BaselineDottedLine(
107
+ Modifier
108
+ .padding(bottom = bottomInset)
109
+ .fillMaxWidth()
110
+ .align(Alignment.BottomCenter),
111
+ isDotted = false,
112
+ color = Color(0xFFE400FF)
113
+ )
114
+ BaselineDottedLine(
115
+ Modifier
116
+ .padding(start = 12.dp)
117
+ .fillMaxHeight(),
118
+ orientation = BaselineOrientation.Vertical,
119
+ isDotted = false,
120
+ color = Color(0xFFE400FF)
121
+ )
122
+ BaselineDottedLine(
123
+ Modifier
124
+ .padding(end = 12.dp)
125
+ .fillMaxHeight()
126
+ .align(Alignment.BottomEnd),
127
+ orientation = BaselineOrientation.Vertical,
128
+ isDotted = false,
129
+ color = Color(0xFFE400FF)
130
+ )
131
+
132
+ // Warning lines
133
+ BaselineDottedLine(
134
+ Modifier
135
+ .padding(top = topInset + 26.dp)
136
+ .fillMaxWidth(),
137
+ color = Color(0xFFFF7A00)
138
+ )
139
+ BaselineDottedLine(
140
+ Modifier
141
+ .padding(bottom = bottomInset + 56.dp)
142
+ .fillMaxWidth()
143
+ .align(Alignment.BottomCenter),
144
+ color = Color(0xFFFFCC00)
145
+ )
146
+ BaselineDottedLine(
147
+ Modifier
148
+ .padding(bottom = bottomInset + 8.dp)
149
+ .fillMaxWidth()
150
+ .align(Alignment.BottomCenter),
151
+ color = Color(0xFFFFCC00)
152
+ )
153
+
154
+ // Header background warning lines
155
+ BaselineDottedLine(
156
+ Modifier
157
+ .padding(start = 40.dp, top = topInset)
158
+ .height(52.dp),
159
+ color = Color(0xFF00C520),
160
+ orientation = BaselineOrientation.Vertical
161
+ )
162
+ BaselineDottedLine(
163
+ Modifier
164
+ .padding(start = 48.dp, top = topInset)
165
+ .height(52.dp),
166
+ color = Color(0xFF00C520),
167
+ orientation = BaselineOrientation.Vertical
168
+ )
169
+ }
170
+ }
171
+
172
+ enum class BaselineOrientation { Horizontal, Vertical }
173
+
174
+ @Composable
175
+ fun BaselineDottedLine(
176
+ modifier: Modifier = Modifier,
177
+ color: Color = Color.Red,
178
+ orientation: BaselineOrientation = BaselineOrientation.Horizontal,
179
+ isDotted: Boolean = true
180
+ ) {
181
+ Canvas(modifier = modifier) {
182
+ val pathEffect = if (isDotted) PathEffect.dashPathEffect(floatArrayOf(8f, 8f)) else null
183
+ drawLine(
184
+ color = color,
185
+ start = if (orientation == BaselineOrientation.Horizontal) Offset(
186
+ 0f,
187
+ size.height / 2
188
+ ) else Offset(size.width / 2, 0f),
189
+ end = if (orientation == BaselineOrientation.Horizontal) Offset(
190
+ size.width,
191
+ size.height / 2
192
+ ) else Offset(size.width / 2, size.height),
193
+ strokeWidth = 1.dp.toPx(),
194
+ pathEffect = pathEffect,
195
+ cap = StrokeCap.Round,
196
+ )
197
+ }
198
+ }
@@ -11,17 +11,14 @@ import androidx.compose.foundation.interaction.collectIsPressedAsState
11
11
  import androidx.compose.foundation.layout.Arrangement
12
12
  import androidx.compose.foundation.layout.Box
13
13
  import androidx.compose.foundation.layout.Row
14
- import androidx.compose.foundation.layout.defaultMinSize
15
14
  import androidx.compose.foundation.layout.fillMaxWidth
16
15
  import androidx.compose.foundation.layout.height
17
16
  import androidx.compose.foundation.layout.padding
18
17
  import androidx.compose.foundation.layout.size
19
18
  import androidx.compose.foundation.shape.RoundedCornerShape
20
19
  import androidx.compose.runtime.Composable
21
- import androidx.compose.runtime.CompositionLocalProvider
22
20
  import androidx.compose.runtime.getValue
23
21
  import androidx.compose.runtime.remember
24
- import androidx.compose.runtime.staticCompositionLocalOf
25
22
  import androidx.compose.ui.Alignment
26
23
  import androidx.compose.ui.Modifier
27
24
  import androidx.compose.ui.draw.alpha
@@ -127,13 +124,12 @@ fun getTextColor(loading: Boolean, type: ButtonType): Color {
127
124
  }
128
125
 
129
126
  @Composable
130
- fun RenderTitle(size: Size, title: String = "", type: ButtonType) {
127
+ fun RenderTitle(size: Size, title: String = "", textColor: Color) {
131
128
  val style = remember(size) { getStyle(size) }
132
- val color = TextColor.current
133
129
  Text(
134
130
  style = style,
135
131
  text = title,
136
- color = color,
132
+ color = textColor,
137
133
  overflow = TextOverflow.Ellipsis,
138
134
  maxLines = 1
139
135
  )
@@ -145,21 +141,20 @@ fun RenderIcon(
145
141
  isIconLeft: Boolean,
146
142
  useTintColor: Boolean = true,
147
143
  icon: String = "",
148
- forceLoading: Boolean = false
144
+ forceLoading: Boolean = false,
145
+ bgColor: Color? = null,
146
+ textColor: Color? = null,
149
147
  ) {
150
- val bgColor = BackgroundColor.current
151
148
  val iconSize = remember(size) { getIconSize(size) }
152
149
  val margin = remember(size) { getIconSpace(size) }
153
- val color = if (useTintColor) TextColor.current else Color.Unspecified
154
-
155
- val showLoading = forceLoading
150
+ val color = if (useTintColor) textColor else Color.Unspecified
156
151
 
157
152
  val modifier = Modifier.padding(
158
153
  start = if (isIconLeft) 0.dp else margin,
159
154
  end = if (isIconLeft) margin else 0.dp
160
155
  )
161
156
 
162
- if (showLoading) {
157
+ if (forceLoading) {
163
158
  Box(modifier) {
164
159
  LottieAnimation(
165
160
  modifier = Modifier.size(iconSize),
@@ -195,9 +190,9 @@ fun getTypeStyle(
195
190
  type: ButtonType,
196
191
  color: Color? = AppTheme.current.colors.primary,
197
192
  size: Size,
193
+ bgColor: Color,
198
194
  ): Modifier {
199
195
  val theme = AppTheme.current
200
- val bgColor = BackgroundColor.current
201
196
  val radius = remember(size) { size.value.radius }
202
197
  val modifier = Modifier.background(bgColor)
203
198
 
@@ -304,45 +299,41 @@ fun Button(
304
299
  .clip(RoundedCornerShape(radius))
305
300
  }
306
301
 
307
- CompositionLocalProvider(
308
- IsLoading provides loading,
309
- BackgroundColor provides getButtonBackgroundColor(loading, type),
310
- TextColor provides getTextColor(loading, type)
302
+ val bgColor = getButtonBackgroundColor(loading, type)
303
+ val textColor = getTextColor(loading, type)
304
+
305
+ Row(
306
+ modifier = clickableModifier
307
+ .padding(horizontal = animatedPadding)
308
+ .clip(RoundedCornerShape(radius))
309
+ .then(getTypeStyle(type, size = size, bgColor = bgColor))
310
+ .conditional(IsShowBaseLineDebug) {
311
+ border(1.dp, Colors.blue_03)
312
+ }
313
+ .padding(horizontal = sizeSpecs.padding)
314
+ .height(sizeSpecs.height),
315
+ horizontalArrangement = Arrangement.Center,
316
+ verticalAlignment = Alignment.CenterVertically,
311
317
  ) {
312
- Row(
313
- modifier = clickableModifier
314
- .padding(horizontal = animatedPadding)
315
- .clip(RoundedCornerShape(radius))
316
- .then(getTypeStyle(type, size = size))
317
- .conditional(IsShowBaseLineDebug) {
318
- border(1.dp, Colors.blue_03)
319
- }
320
- .padding(horizontal = sizeSpecs.padding)
321
- .height(sizeSpecs.height),
322
- horizontalArrangement = Arrangement.Center,
323
- verticalAlignment = Alignment.CenterVertically,
324
- ) {
325
- RenderIcon(
326
- size = size,
327
- isIconLeft = true,
328
- useTintColor = useTintColor,
329
- icon = iconLeft,
330
- forceLoading = loading && loadingOnLeft
331
- )
332
- RenderTitle(size, title, type = type)
333
- RenderIcon(
334
- size = size,
335
- isIconLeft = false,
336
- useTintColor = useTintColor,
337
- icon = iconRight,
338
- forceLoading = loading && !loadingOnLeft
339
- )
340
- }
318
+ RenderIcon(
319
+ size = size,
320
+ isIconLeft = true,
321
+ useTintColor = useTintColor,
322
+ icon = iconLeft,
323
+ forceLoading = loading && loadingOnLeft,
324
+ bgColor = bgColor,
325
+ textColor = textColor
326
+ )
327
+ RenderTitle(size, title, textColor = textColor)
328
+ RenderIcon(
329
+ size = size,
330
+ isIconLeft = false,
331
+ useTintColor = useTintColor,
332
+ icon = iconRight,
333
+ forceLoading = loading && !loadingOnLeft,
334
+ bgColor = bgColor,
335
+ textColor = textColor
336
+ )
341
337
  }
342
338
  }
343
339
 
344
-
345
- private val IsLoading = staticCompositionLocalOf<Boolean> { false }
346
- private val BackgroundColor = staticCompositionLocalOf<Color> { Color.Transparent }
347
- private val TextColor = staticCompositionLocalOf<Color> { Color.Transparent }
348
-
@@ -43,11 +43,11 @@ import androidx.compose.ui.unit.TextUnit
43
43
  import androidx.compose.ui.unit.dp
44
44
  import androidx.compose.ui.unit.sp
45
45
  import androidx.compose.ui.zIndex
46
+ import vn.momo.kits.application.IsShowBaseLineDebug
46
47
  import vn.momo.kits.const.AppTheme
48
+ import vn.momo.kits.const.Colors
47
49
  import vn.momo.kits.const.Radius
48
50
  import vn.momo.kits.const.Spacing
49
- import vn.momo.kits.application.IsShowBaseLineDebug
50
- import vn.momo.kits.const.Colors
51
51
  import vn.momo.kits.const.Typography
52
52
  import vn.momo.kits.const.getFont
53
53
  import vn.momo.kits.const.scaleSize
@@ -141,7 +141,7 @@ fun getBorderColor(isFocused: Boolean, error: String, disabled: Boolean): Color
141
141
  }
142
142
 
143
143
  @Composable
144
- fun RenderRightIcon(loading: Boolean, icon: String, color: Color, onClick: () -> Unit) {
144
+ fun RenderRightIcon(loading: Boolean, icon: String, color: Color, onClick: () -> Unit, modifier: Modifier = Modifier) {
145
145
  if (loading) {
146
146
  Box {
147
147
  CircularProgressIndicator(
@@ -157,7 +157,7 @@ fun RenderRightIcon(loading: Boolean, icon: String, color: Color, onClick: () ->
157
157
  source = icon,
158
158
  color = color,
159
159
  size = 24.dp,
160
- modifier = Modifier.clickable(
160
+ modifier = modifier.clickable(
161
161
  onClick = onClick,
162
162
  interactionSource = remember { MutableInteractionSource() },
163
163
  indication = null
@@ -224,6 +224,7 @@ fun Input(
224
224
  fontWeight: InputFontWeight = InputFontWeight.REGULAR,
225
225
  keyboardType: KeyboardType = KeyboardType.Text,
226
226
  modifier: Modifier = Modifier,
227
+ inputModifier: Modifier = Modifier,
227
228
  ) {
228
229
  // Consolidated state management
229
230
  var inputState by remember { mutableStateOf(InputState()) }
@@ -253,10 +254,6 @@ fun Input(
253
254
  if (disabled) theme.colors.text.disable else floatingIconColor
254
255
  }
255
256
 
256
- val testId = remember(disabled, floatingValue) {
257
- if (disabled) "input_${floatingValue}_disabled" else "input_$floatingValue"
258
- }
259
-
260
257
  val fontSize = 14.sp
261
258
  val lineHeight = 24.sp
262
259
  val scaleFontSize = scaleSize(fontSize)
@@ -298,8 +295,7 @@ fun Input(
298
295
  Column(modifier = modifier
299
296
  .conditional(IsShowBaseLineDebug) {
300
297
  border(1.dp, Colors.blue_03)
301
- }
302
- .setAutomationId(testId)) {
298
+ }) {
303
299
  BasicTextField(
304
300
  enabled = !disabled,
305
301
  readOnly = readOnly,
@@ -308,7 +304,7 @@ fun Input(
308
304
  textStyle = textStyle,
309
305
  visualTransformation = visualTransformation,
310
306
  keyboardOptions = keyboardOptionsConfig,
311
- modifier = Modifier
307
+ modifier = inputModifier
312
308
  .height(scaleSize(size.values.height.value, 1.1f).dp)
313
309
  .onFocusChanged { focusState ->
314
310
  val wasFocused = inputState.isFocused
@@ -417,7 +413,7 @@ fun Input(
417
413
  },
418
414
  interactionSource = remember { MutableInteractionSource() },
419
415
  indication = null
420
- )
416
+ ).setAutomationId("ic_clear")
421
417
  )
422
418
  }
423
419
  }
@@ -430,7 +426,12 @@ fun Input(
430
426
  onRightIconPressed()
431
427
  inputState = inputState.copy(passHidden = !inputState.passHidden)
432
428
  }
433
- RenderRightIcon(loading, iconName, iconTintColor, togglePassword)
429
+ RenderRightIcon(
430
+ loading = loading,
431
+ icon = iconName,
432
+ color = iconTintColor,
433
+ onClick = togglePassword,
434
+ modifier = Modifier.setAutomationId("ic_show_hide"))
434
435
  }
435
436
  else -> {
436
437
  RenderRightIcon(loading, icon, iconTintColor, onRightIconPressed)
@@ -31,11 +31,11 @@ import androidx.compose.ui.text.TextStyle
31
31
  import androidx.compose.ui.text.input.KeyboardType
32
32
  import androidx.compose.ui.unit.Dp
33
33
  import androidx.compose.ui.unit.dp
34
+ import vn.momo.kits.application.IsShowBaseLineDebug
34
35
  import vn.momo.kits.const.AppTheme
35
36
  import vn.momo.kits.const.Colors
36
37
  import vn.momo.kits.const.Radius
37
38
  import vn.momo.kits.const.Spacing
38
- import vn.momo.kits.application.IsShowBaseLineDebug
39
39
  import vn.momo.kits.const.Typography
40
40
  import vn.momo.kits.const.scaleSize
41
41
  import vn.momo.kits.modifier.conditional
@@ -98,6 +98,7 @@ fun InputPhoneNumber(
98
98
  onBlur: () -> Unit = {},
99
99
  loading: Boolean = false,
100
100
  modifier: Modifier = Modifier,
101
+ inputModifier: Modifier = Modifier,
101
102
  ) {
102
103
  // Consolidated state management
103
104
  var inputState by remember { mutableStateOf(InputState()) }
@@ -113,23 +114,20 @@ fun InputPhoneNumber(
113
114
  }
114
115
 
115
116
  val (textColor, placeholderColor, iconTintColor) = colors
116
-
117
- val testId = "input_phone_number"
118
-
119
117
  val textStyle = scaleSize(size.values.textStyle.copy(color = textColor))
120
118
 
119
+
121
120
  Column(modifier = modifier
122
121
  .conditional(IsShowBaseLineDebug) {
123
122
  border(1.dp, Colors.blue_03)
124
- }
125
- .setAutomationId(testId)) {
123
+ }) {
126
124
  BasicTextField(
127
125
  singleLine = true,
128
126
  value = text.value,
129
127
  textStyle = textStyle,
130
128
  keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
131
129
  cursorBrush = SolidColor(AppTheme.current.colors.primary),
132
- modifier = Modifier
130
+ modifier = inputModifier
133
131
  .height(scaleSize(size.values.height.value, 1.1f).dp)
134
132
  .onFocusChanged { focusState ->
135
133
  val wasFocused = inputState.isFocused
@@ -218,6 +216,7 @@ fun InputPhoneNumber(
218
216
  indication = null
219
217
  )
220
218
  .padding(horizontal = Spacing.S)
219
+ .setAutomationId("ic_clear")
221
220
  )
222
221
  }
223
222
  }
@@ -180,10 +180,6 @@ val darkTheme = Theme(
180
180
 
181
181
  val AppTheme = staticCompositionLocalOf { defaultTheme }
182
182
 
183
- val AppThemeController = staticCompositionLocalOf<(Theme) -> Unit> {
184
- error("No AppTheme controller provided")
185
- }
186
-
187
183
  val AppStatusBar = staticCompositionLocalOf { 0.dp }
188
184
 
189
185
  val AppNavigationBar = staticCompositionLocalOf { 0.dp }
@@ -30,7 +30,6 @@ import vn.momo.kits.application.MiniAppContext
30
30
  import vn.momo.kits.const.AppNavigationBar
31
31
  import vn.momo.kits.const.AppStatusBar
32
32
  import vn.momo.kits.const.AppTheme
33
- import vn.momo.kits.const.AppThemeController
34
33
  import vn.momo.kits.const.Theme
35
34
  import vn.momo.kits.const.ThemeAssets
36
35
  import vn.momo.kits.const.defaultTheme
@@ -80,7 +79,6 @@ fun NavigationContainer(
80
79
  LocalNavigator provides navigator,
81
80
  LocalMaxApi provides maxApi,
82
81
  AppTheme provides theme.value,
83
- AppThemeController provides { theme.value = it },
84
82
  AppStatusBar provides statusBarHeight,
85
83
  AppNavigationBar provides navigationBarHeight,
86
84
  ApplicationContext provides mergedContext,
@@ -40,13 +40,15 @@ import vn.momo.kits.const.Colors
40
40
  import vn.momo.kits.const.Radius
41
41
  import vn.momo.kits.const.Spacing
42
42
  import vn.momo.kits.application.IsShowBaseLineDebug
43
+ import vn.momo.kits.components.BadgeDot
44
+ import vn.momo.kits.components.DotSize
43
45
  import vn.momo.kits.const.Typography
44
46
  import vn.momo.kits.modifier.conditional
45
47
  import vn.momo.kits.modifier.noFeedbackClickable
46
48
  import vn.momo.kits.platform.getScreenDimensions
47
49
 
48
50
  val floatingButtonWidth = 75.dp
49
- const val BOTTOM_TAB_BAR_HEIGHT = 56
51
+ const val BOTTOM_TAB_BAR_HEIGHT = 64
50
52
 
51
53
  @Composable
52
54
  fun BottomTabBar(
@@ -142,6 +144,18 @@ fun RowScope.renderTabBarItem(
142
144
 
143
145
  @Composable
144
146
  fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> Unit) {
147
+ fun isNumber(label: String): Boolean {
148
+ val numberRegex = "^\\d+$".toRegex()
149
+ return numberRegex.matches(label)
150
+ }
151
+ fun formatLabel(label: String): String? {
152
+ if (isNumber(label) && label.toInt() == 0) {
153
+ return null
154
+ }
155
+ return label
156
+ }
157
+ val badgeLabel = item.badgeLabel?.let { formatLabel(it) }
158
+
145
159
  Box(modifier = Modifier
146
160
  .fillMaxSize()
147
161
  .padding(horizontal = Spacing.XXS)
@@ -156,31 +170,34 @@ fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> Unit) {
156
170
  Column(
157
171
  modifier = Modifier
158
172
  .fillMaxSize()
159
- .padding(horizontal = Spacing.XXS)
173
+ .padding(horizontal = Spacing.S, vertical = Spacing.S)
160
174
  .noFeedbackClickable {
161
175
  onClick()
162
176
  },
163
177
  horizontalAlignment = Alignment.CenterHorizontally,
164
- verticalArrangement = Arrangement.Bottom
178
+ verticalArrangement = Arrangement.Center
165
179
  ) {
166
180
  Icon(
167
181
  source = item.icon,
168
- modifier = Modifier.weight(1f),
182
+ size = 28.dp,
183
+ modifier = Modifier.padding(Spacing.XS),
169
184
  color = if (selected) AppTheme.current.colors.primary else AppTheme.current.colors.text.hint)
170
185
  Text(
171
186
  text = item.label,
172
187
  color = if (selected) AppTheme.current.colors.primary else AppTheme.current.colors.text.hint,
173
- style = Typography.labelXsMedium,
188
+ style = if (selected) Typography.labelXsMedium else Typography.descriptionXsRegular,
174
189
  maxLines = 1,
175
190
  overflow = TextOverflow.Ellipsis
176
191
  )
177
192
  }
178
- if(item.badgeLabel != null){
179
- Box(modifier = Modifier
180
- .offset(x = 44.dp, y = (-32).dp)
181
- ){
182
- Badge(item.badgeLabel)
183
- }
193
+ if (badgeLabel != null) {
194
+ if (badgeLabel.isEmpty())
195
+ BadgeDot(
196
+ size = DotSize.Small,
197
+ modifier = Modifier.offset(x = 50.dp, y = (-48).dp)
198
+ )
199
+ else
200
+ Badge(badgeLabel, modifier = Modifier.offset(x = 44.dp, y = (-42).dp))
184
201
  }
185
202
  }
186
203
  }
@@ -190,7 +207,7 @@ fun FloatingButton(data: BottomTabFloatingButton) {
190
207
  Column(
191
208
  modifier = Modifier
192
209
  .width(floatingButtonWidth)
193
- .padding(horizontal = Spacing.XXS)
210
+ .padding(horizontal = Spacing.XXS, vertical = Spacing.S)
194
211
  .conditional(IsShowBaseLineDebug) {
195
212
  border(1.dp, Colors.blue_03)
196
213
  }
@@ -35,6 +35,7 @@ import vn.momo.kits.application.IsShowBaseLineDebug
35
35
  import vn.momo.kits.const.Spacing
36
36
  import vn.momo.kits.modifier.activeOpacityClickable
37
37
  import vn.momo.kits.modifier.conditional
38
+ import vn.momo.kits.modifier.setAutomationId
38
39
  import vn.momo.kits.navigation.LocalHeaderRightWidthPx
39
40
  import vn.momo.kits.navigation.LocalNavigator
40
41
  import vn.momo.kits.navigation.LocalOptions
@@ -119,14 +120,15 @@ fun Header(onBackHandler: (() -> Unit)? = null) {
119
120
  }
120
121
 
121
122
  @Composable
122
- fun BackButton(borderColor: Color, backgroundButton: Color, tintIconColor: Color, onBackHandler: () -> Unit){
123
+ private fun BackButton(borderColor: Color, backgroundButton: Color, tintIconColor: Color, onBackHandler: () -> Unit){
123
124
  Box(
124
125
  modifier = Modifier
125
126
  .size(28.dp)
126
127
  .background(backgroundButton, RoundedCornerShape(100))
127
128
  .border(width = 0.2.dp, color = borderColor, shape = RoundedCornerShape(100))
128
129
  .activeOpacityClickable(onClick = onBackHandler)
129
- .padding(Spacing.XS),
130
+ .padding(Spacing.XS)
131
+ .setAutomationId("btn_navigation_back"),
130
132
  contentAlignment = Alignment.Center
131
133
  ) {
132
134
  Icon(
@@ -10,6 +10,7 @@ import androidx.compose.ui.unit.sp
10
10
  import androidx.compose.ui.zIndex
11
11
  import vn.momo.kits.components.Text
12
12
  import vn.momo.kits.const.Typography
13
+ import vn.momo.kits.modifier.setAutomationId
13
14
 
14
15
  @Composable
15
16
  fun HeaderTitle(
@@ -17,7 +18,7 @@ fun HeaderTitle(
17
18
  color: Color? = null,
18
19
  ) {
19
20
  Text(
20
- modifier = Modifier.fillMaxWidth().zIndex(1f),
21
+ modifier = Modifier.fillMaxWidth().zIndex(1f).setAutomationId("title_navigation_header"),
21
22
  text = title,
22
23
  textAlign = TextAlign.Start,
23
24
  style = Typography.actionSBold.copy(
@@ -0,0 +1,15 @@
1
+ package vn.momo.kits.utils
2
+
3
+ import androidx.compose.runtime.Composable
4
+ import vn.momo.kits.application.LocalComponentInformation
5
+
6
+ @Composable
7
+ fun getComponentId(
8
+ componentName: String? = null,
9
+ accessibilityLabel: String? = null,
10
+ ): String {
11
+ val componentInformation = LocalComponentInformation.current
12
+ if (accessibilityLabel != null) return accessibilityLabel
13
+ if (componentInformation?.componentId != null) return componentInformation.componentId
14
+ return ""
15
+ }
@@ -12,11 +12,8 @@ import androidx.compose.runtime.mutableStateOf
12
12
  import androidx.compose.runtime.remember
13
13
  import androidx.compose.runtime.setValue
14
14
  import androidx.compose.ui.Modifier
15
- import androidx.compose.ui.draw.drawBehind
16
15
  import androidx.compose.ui.graphics.Color
17
16
  import androidx.compose.ui.graphics.NativePaint
18
- import androidx.compose.ui.graphics.Paint
19
- import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
20
17
  import androidx.compose.ui.graphics.toArgb
21
18
  import androidx.compose.ui.interop.UIKitView
22
19
  import androidx.compose.ui.unit.Dp
package/gradle.properties CHANGED
@@ -18,7 +18,7 @@ kotlin.apple.xcodeCompatibility.nowarn=true
18
18
  name="ComposeKits"
19
19
  group=vn.momo.kits
20
20
  artifact.id=kits
21
- version=0.156.10
21
+ version=0.156.11
22
22
 
23
23
  repo=GitLab
24
24
  url=https://gitlab.mservice.com.vn/api/v4/projects/5400/packages/maven
@@ -31,7 +31,11 @@ public struct ImageView: View {
31
31
  .placeholder {
32
32
  VStack(alignment: .center, content: {
33
33
  if error {
34
- Image(systemName: "photo.badge.exclamationmark").frame(width: 24, height: 24)
34
+ Image("media_fail")
35
+ .resizable()
36
+ .renderingMode(.template)
37
+ .foregroundColor(Colors.black08)
38
+ .frame(width: 24, height: 24)
35
39
  } else if placeholder != nil {
36
40
  placeholder
37
41
  }
@@ -174,7 +174,7 @@ public struct Input: View {
174
174
  onChangeText?("")
175
175
  }) {
176
176
  Icon(source: "24_navigation_close_circle_full", size: 16, color: Colors.black12)
177
- .padding(.leading, Spacing.S)
177
+ .padding(.leading, Spacing.S).accessibility(identifier: "ic_clear")
178
178
  }
179
179
  }
180
180
 
@@ -194,7 +194,7 @@ public struct Input: View {
194
194
  size: 24,
195
195
  color: rightIconColor
196
196
  )
197
- .padding(.leading, Spacing.S)
197
+ .padding(.leading, Spacing.S).accessibility(identifier: "ic_show_hide")
198
198
  }
199
199
  }
200
200
  } else if !rightIcon.isEmpty {
@@ -21,6 +21,7 @@ public struct InputPhoneNumber: View {
21
21
  public var onFocus: (() -> Void)?
22
22
  public var onBlur: (() -> Void)?
23
23
  public var onRightIconPressed: (() -> Void)?
24
+ public var accessibilityLabel: String?
24
25
 
25
26
  @State private var isFocused: Bool = false
26
27
 
@@ -38,7 +39,8 @@ public struct InputPhoneNumber: View {
38
39
  onChangeText: ((String) -> Void)? = nil,
39
40
  onFocus: (() -> Void)? = nil,
40
41
  onBlur: (() -> Void)? = nil,
41
- onRightIconPressed: (() -> Void)? = nil
42
+ onRightIconPressed: (() -> Void)? = nil,
43
+ accessibilityLabel: String? = nil
42
44
  ) {
43
45
  self._text = text
44
46
  self.placeholder = placeholder
@@ -53,6 +55,7 @@ public struct InputPhoneNumber: View {
53
55
  self.onFocus = onFocus
54
56
  self.onBlur = onBlur
55
57
  self.onRightIconPressed = onRightIconPressed
58
+ self.accessibilityLabel = accessibilityLabel
56
59
  }
57
60
 
58
61
  // MARK: - Body
@@ -86,7 +89,7 @@ public struct InputPhoneNumber: View {
86
89
  MomoText(placeholder, typography: size == .small ? .headerSSemibold : .headerMBold, color: Colors.black12)
87
90
  }
88
91
 
89
- TextField("", text: textBinding, onEditingChanged: { focused in
92
+ TextField(placeholder, text: textBinding, onEditingChanged: { focused in
90
93
  handleFocusChange(focused)
91
94
  })
92
95
  .keyboardType(.numberPad)
@@ -94,6 +97,7 @@ public struct InputPhoneNumber: View {
94
97
  .foregroundColor(Colors.black17)
95
98
  .applyPrimaryCursorColor()
96
99
  .lineLimit(1)
100
+ .accessibility(identifier: accessibilityLabel ?? "")
97
101
  }
98
102
 
99
103
  // Clear button
@@ -103,7 +107,7 @@ public struct InputPhoneNumber: View {
103
107
  onChangeText?("")
104
108
  }) {
105
109
  Icon(source: "24_navigation_close_circle_full", size: 16, color: Colors.black12)
106
- .padding(.leading, Spacing.S)
110
+ .padding(.leading, Spacing.S).accessibility(identifier: "ic_clear")
107
111
  }
108
112
  }
109
113
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/native-kits",
3
- "version": "0.156.10-debug",
3
+ "version": "0.156.11-debug",
4
4
  "private": false,
5
5
  "dependencies": {},
6
6
  "devDependencies": {},