@momo-kits/native-kits 0.113.2-beta.5 → 0.113.3-beta.10

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.
@@ -3,12 +3,9 @@ package vn.momo.kits.platform
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.res.Resources
5
5
  import android.graphics.BlurMaskFilter
6
- import androidx.compose.foundation.layout.WindowInsets
7
- import androidx.compose.foundation.layout.statusBars
8
6
  import androidx.compose.runtime.Composable
9
7
  import androidx.compose.ui.graphics.NativePaint
10
8
  import androidx.compose.ui.platform.LocalConfiguration
11
- import androidx.compose.ui.platform.LocalDensity
12
9
  import androidx.compose.ui.unit.Dp
13
10
  import androidx.compose.ui.unit.dp
14
11
 
@@ -31,6 +28,10 @@ actual fun NativePaint.setMaskFilter(blurRadius: Float) {
31
28
  @SuppressLint("InternalInsetResource")
32
29
  @Composable
33
30
  actual fun getStatusBarHeight(): Dp {
34
- val density = LocalDensity.current
35
- return with(density) { WindowInsets.statusBars.getTop(density).toDp() }
31
+ val resourceId = Resources.getSystem().getIdentifier(
32
+ "status_bar_height",
33
+ "dimen",
34
+ "android"
35
+ )
36
+ return if(resourceId > 0) Resources.getSystem().getDimensionPixelSize(resourceId).dp else 0.dp
36
37
  }
@@ -0,0 +1,58 @@
1
+ package vn.momo.kits.application
2
+
3
+ import androidx.compose.foundation.background
4
+ import androidx.compose.foundation.layout.Box
5
+ import androidx.compose.foundation.layout.fillMaxWidth
6
+ import androidx.compose.foundation.layout.height
7
+ import androidx.compose.foundation.layout.offset
8
+ import androidx.compose.foundation.layout.padding
9
+ import androidx.compose.foundation.layout.width
10
+ import androidx.compose.foundation.shape.RoundedCornerShape
11
+ import androidx.compose.runtime.Composable
12
+ import androidx.compose.ui.Modifier
13
+ import androidx.compose.ui.graphics.Color
14
+ import androidx.compose.ui.unit.dp
15
+ import androidx.compose.ui.zIndex
16
+ import vn.momo.kits.components.InputSearch
17
+ import vn.momo.kits.components.InputSearchProps
18
+ import vn.momo.kits.const.Radius
19
+ import vn.momo.kits.const.Spacing
20
+
21
+ @Composable
22
+ fun AnimationSearchInput(
23
+ animations: HeaderAnimations,
24
+ inputSearchProps: InputSearchProps
25
+ ) {
26
+ Box(
27
+ modifier = Modifier
28
+ .fillMaxWidth()
29
+ .offset(
30
+ x = animations.translateX.dp,
31
+ y = animations.translateY.dp
32
+ )
33
+ ) {
34
+ Box(
35
+ modifier = Modifier
36
+ .height(HEADER_HEIGHT.dp)
37
+ .padding(vertical = Spacing.S)
38
+ .zIndex(1f)
39
+ ) {
40
+ Box(
41
+ modifier = Modifier
42
+ .width(animations.width.dp)
43
+ .background(
44
+ color = animations.backgroundSearch,
45
+ shape = RoundedCornerShape(Radius.XL)
46
+ )
47
+ ) {
48
+ InputSearch(
49
+ inputSearchProps = inputSearchProps.copy(
50
+ showButtonText = false,
51
+ backgroundColor = Color.Transparent
52
+ )
53
+ )
54
+ }
55
+ }
56
+ }
57
+
58
+ }
@@ -1,6 +1,5 @@
1
1
  package vn.momo.kits.application
2
2
 
3
- import androidx.compose.animation.animateColorAsState
4
3
  import androidx.compose.foundation.background
5
4
  import androidx.compose.foundation.border
6
5
  import androidx.compose.foundation.clickable
@@ -24,10 +23,10 @@ import androidx.compose.runtime.setValue
24
23
  import androidx.compose.runtime.snapshotFlow
25
24
  import androidx.compose.ui.Alignment
26
25
  import androidx.compose.ui.Modifier
27
- import androidx.compose.ui.graphics.Color
28
26
  import androidx.compose.ui.graphics.graphicsLayer
29
27
  import androidx.compose.ui.semantics.semantics
30
28
  import androidx.compose.ui.semantics.testTag
29
+ import androidx.compose.ui.unit.Dp
31
30
  import androidx.compose.ui.unit.dp
32
31
  import androidx.compose.ui.zIndex
33
32
  import kotlinx.coroutines.flow.distinctUntilChanged
@@ -42,15 +41,6 @@ import vn.momo.kits.const.Spacing
42
41
  import vn.momo.kits.modifier.setAutomationId
43
42
  import vn.momo.kits.platform.getStatusBarHeight
44
43
 
45
- enum class TitlePosition {
46
- LEFT, CENTER,
47
- }
48
-
49
- data class AnimatedHeader(
50
- val type: HeaderType = HeaderType.DEFAULT,
51
- val composable: @Composable (scrollState: Int) -> Unit = {}
52
- )
53
-
54
44
  @Composable
55
45
  fun Header(
56
46
  headerType: HeaderType = HeaderType.DEFAULT,
@@ -58,16 +48,16 @@ fun Header(
58
48
  title: String,
59
49
  headerRight: @Composable (() -> Unit)? = null,
60
50
  goBack: (() -> Unit)? = null,
61
- opacity: Float? = null,
51
+ opacity: Float = 1f,
62
52
  animatedHeader: AnimatedHeader? = null,
63
53
  scrollState: Int = 0,
64
54
  inputSearchProps: InputSearchProps? = null,
55
+ headerRightWidth: Dp = 0.dp,
65
56
  useAnimationSearch: Boolean = false
66
57
  ) {
67
58
  val statusBarHeight = AppStatusBar.current ?: getStatusBarHeight()
68
59
  val tintIconColor = AppTheme.current.colors.text.default
69
60
  val backgroundButton = Colors.black_01.copy(alpha = 0.6f)
70
- val miniAppContext = ApplicationContext.current
71
61
 
72
62
  var colorFraction by remember { mutableStateOf(0f) }
73
63
 
@@ -77,24 +67,31 @@ fun Header(
77
67
  .distinctUntilChanged()
78
68
  .collect { fraction -> colorFraction = fraction }
79
69
  }
70
+ val animations = useHeaderSearchAnimation(
71
+ opacityAni = opacity,
72
+ scrollState = scrollState,
73
+ headerRightWidth = headerRightWidth,
74
+ isBack = goBack != null
75
+ )
80
76
 
81
- val backgroundSearch by animateColorAsState(
82
- targetValue = animateColor(
83
- Colors.black_01,
84
- AppTheme.current.colors.background.default,
85
- colorFraction
86
- )
77
+ val backgroundSearch = animateColor(
78
+ Colors.black_01,
79
+ AppTheme.current.colors.background.default,
80
+ colorFraction
87
81
  )
88
82
 
83
+ val searchAnimationEnable =
84
+ inputSearchProps != null && useAnimationSearch && headerType == HeaderType.EXTENDED
85
+
89
86
  if (headerType != HeaderType.NONE) {
90
87
  BoxWithConstraints(
91
- Modifier.height(statusBarHeight + 52.dp)
88
+ Modifier.height(statusBarHeight + HEADER_HEIGHT.dp)
92
89
  .fillMaxWidth()
93
90
  .zIndex(10f),
94
91
  contentAlignment = Alignment.BottomCenter
95
92
  ) {
96
93
  Row(
97
- modifier = Modifier.height(52.dp)
94
+ modifier = Modifier.height(HEADER_HEIGHT.dp)
98
95
  .fillMaxWidth()
99
96
  .padding(horizontal = Spacing.M),
100
97
  verticalAlignment = Alignment.CenterVertically,
@@ -132,9 +129,9 @@ fun Header(
132
129
  }
133
130
  }
134
131
 
135
- if (((useAnimationSearch && inputSearchProps != null) || inputSearchProps == null) && miniAppContext != null) {
132
+ if (useAnimationSearch || inputSearchProps == null) {
136
133
  Box(Modifier.graphicsLayer {
137
- alpha = if (useAnimationSearch) 1f - (opacity ?: 1f) else 1f
134
+ alpha = if (animatedHeader !== null) opacity else 1f
138
135
  }) {
139
136
  headerRight?.invoke()
140
137
  }
@@ -143,7 +140,7 @@ fun Header(
143
140
 
144
141
  // Title
145
142
  Row(
146
- modifier = Modifier.height(52.dp)
143
+ modifier = Modifier.height(HEADER_HEIGHT.dp)
147
144
  .fillMaxWidth()
148
145
  .padding(horizontal = Spacing.M),
149
146
  verticalAlignment = Alignment.CenterVertically,
@@ -156,14 +153,15 @@ fun Header(
156
153
  } else {
157
154
  Box(
158
155
  Modifier.weight(1f).graphicsLayer {
159
- alpha = if (useAnimationSearch && inputSearchProps != null) {
160
- 1f - (opacity ?: 1f)
156
+ alpha = if (useAnimationSearch) {
157
+ 1f - (opacity)
161
158
  } else {
162
- if (animatedHeader !== null) opacity ?: 1f else 1f
159
+ if (animatedHeader !== null) opacity else 1f
163
160
  }
164
161
  },
165
162
  contentAlignment = if (titlePosition == TitlePosition.LEFT) Alignment.TopStart else Alignment.Center
166
163
  ) {
164
+
167
165
  HeaderTitle(
168
166
  title = title,
169
167
  color = tintIconColor,
@@ -174,16 +172,13 @@ fun Header(
174
172
  }
175
173
  }
176
174
  }
175
+
176
+ if (searchAnimationEnable) {
177
+ AnimationSearchInput(
178
+ animations = animations,
179
+ inputSearchProps = inputSearchProps!!
180
+ )
181
+ }
177
182
  }
178
183
  }
179
- }
180
-
181
-
182
- fun animateColor(start: Color, stop: Color, fraction: Float): Color {
183
- return Color(
184
- red = start.red + fraction * (stop.red - start.red),
185
- green = start.green + fraction * (stop.green - start.green),
186
- blue = start.blue + fraction * (stop.blue - start.blue),
187
- alpha = start.alpha + fraction * (stop.alpha - start.alpha)
188
- )
189
- }
184
+ }
@@ -1,66 +1,68 @@
1
1
  package vn.momo.kits.application
2
2
 
3
+ import androidx.compose.animation.animateColorAsState
4
+ import androidx.compose.animation.core.animateFloatAsState
3
5
  import androidx.compose.foundation.layout.Box
4
6
  import androidx.compose.foundation.layout.fillMaxWidth
5
7
  import androidx.compose.runtime.Composable
8
+ import androidx.compose.runtime.getValue
6
9
  import androidx.compose.ui.Modifier
7
10
  import androidx.compose.ui.graphics.Color
8
- import androidx.compose.ui.unit.Dp
9
- import androidx.compose.ui.unit.dp
10
- import vn.momo.kits.components.InputSearchProps
11
11
  import vn.momo.kits.const.AppStatusBar
12
12
  import vn.momo.kits.const.AppTheme
13
- import vn.momo.kits.platform.getScreenDimensions
14
13
  import vn.momo.kits.platform.getStatusBarHeight
15
14
 
16
- private const val SCREEN_PADDING = 12
17
- private const val BACK_WIDTH = 28
15
+ enum class TitlePosition {
16
+ LEFT, CENTER,
17
+ }
18
+
19
+ data class AnimatedHeader(
20
+ val type: HeaderType = HeaderType.DEFAULT,
21
+ val composable: @Composable (scrollState: Int) -> Unit = {}
22
+ )
23
+
24
+ fun animateColor(start: Color, stop: Color, fraction: Float): Color {
25
+ return Color(
26
+ red = start.red + fraction * (stop.red - start.red),
27
+ green = start.green + fraction * (stop.green - start.green),
28
+ blue = start.blue + fraction * (stop.blue - start.blue),
29
+ alpha = start.alpha + fraction * (stop.alpha - start.alpha)
30
+ )
31
+ }
18
32
 
19
33
  @Composable
20
34
  fun HeaderBackground(
21
- isBack: Boolean = true,
22
35
  headerType: HeaderType = HeaderType.DEFAULT,
23
36
  scrollState: Int,
24
- inputSearchProps: InputSearchProps? = null,
25
- headerRightWidth: Dp = 0.dp,
26
- useAnimationSearch: Boolean = false
27
37
  ) {
28
38
  val statusBarHeight = AppStatusBar.current ?: getStatusBarHeight()
29
- val screenWidth = getScreenDimensions().width
30
- val leftPosition = if (isBack) (BACK_WIDTH + 20).dp else 20.dp
31
- val searchWidth = screenWidth - (SCREEN_PADDING * 2)
32
39
 
33
- val animations = useHeaderSearchAnimation(
34
- scrollState = scrollState,
35
- statusBarHeight = statusBarHeight,
36
- leftPosition = leftPosition,
37
- searchWidth = searchWidth,
38
- headerRightWidth = headerRightWidth
40
+ val opacityAni by animateFloatAsState(
41
+ targetValue = (1 - (scrollState / HEADER_HEIGHT*1f)).coerceIn(0f, 1f),
39
42
  )
40
43
 
41
- val backgroundColor = if (scrollState == 0) {
42
- Color.Transparent
43
- } else {
44
- AppTheme.current.colors.background.surface
45
- }
44
+ val backgroundColor by animateColorAsState(
45
+ targetValue = animateColor(
46
+ Color.Transparent,
47
+ AppTheme.current.colors.background.surface,
48
+ opacityAni
49
+ ),
50
+ )
46
51
 
47
52
  when (headerType) {
48
53
  HeaderType.DEFAULT -> {
49
- val height = statusBarHeight + 52.dp
50
54
  HeaderDefault(
51
- height = height,
52
- animations = animations,
53
- backgroundColor = backgroundColor
55
+ opacityAni = opacityAni,
56
+ statusBarHeight = statusBarHeight,
54
57
  )
55
58
  }
56
59
 
57
60
  HeaderType.EXTENDED -> {
58
- HeaderExtended(
59
- animations = animations,
60
- inputSearchProps = inputSearchProps,
61
- useAnimationSearch = useAnimationSearch,
62
- backgroundColor = backgroundColor
63
- )
61
+ HeaderExtended(
62
+ opacityAni = opacityAni,
63
+ statusBarHeight = statusBarHeight,
64
+ backgroundColor = backgroundColor,
65
+ )
64
66
  }
65
67
 
66
68
  else -> {
@@ -21,13 +21,13 @@ import vn.momo.kits.utils.bottomBorder
21
21
 
22
22
  @Composable
23
23
  fun HeaderDefault(
24
- height: Dp,
25
- backgroundColor: Color,
26
- animations: HeaderAnimations
24
+ opacityAni: Float,
25
+ statusBarHeight: Dp,
27
26
  ) {
27
+ val height = statusBarHeight + HEADER_HEIGHT.dp
28
28
  Box(
29
29
  modifier = Modifier.fillMaxWidth().height(height)
30
- .background(backgroundColor)
30
+ .background(AppTheme.current.colors.background.surface)
31
31
  .bottomBorder(
32
32
  strokeWidth = 1.dp,
33
33
  color = AppTheme.current.colors.border.default
@@ -37,7 +37,7 @@ fun HeaderDefault(
37
37
  modifier = Modifier
38
38
  .height(height)
39
39
  .fillMaxWidth()
40
- .alpha(animations.opacity)
40
+ .alpha(opacityAni)
41
41
  .background(
42
42
  Brush.linearGradient(
43
43
  colors = listOf(
@@ -4,12 +4,7 @@ import androidx.compose.foundation.background
4
4
  import androidx.compose.foundation.layout.Box
5
5
  import androidx.compose.foundation.layout.fillMaxWidth
6
6
  import androidx.compose.foundation.layout.height
7
- import androidx.compose.foundation.layout.offset
8
- import androidx.compose.foundation.layout.padding
9
- import androidx.compose.foundation.layout.width
10
- import androidx.compose.foundation.shape.RoundedCornerShape
11
7
  import androidx.compose.runtime.Composable
12
- import androidx.compose.ui.Alignment
13
8
  import androidx.compose.ui.Modifier
14
9
  import androidx.compose.ui.draw.alpha
15
10
  import androidx.compose.ui.geometry.Offset
@@ -17,86 +12,64 @@ import androidx.compose.ui.graphics.Brush
17
12
  import androidx.compose.ui.graphics.Color
18
13
  import androidx.compose.ui.layout.ContentScale
19
14
  import androidx.compose.ui.platform.LocalDensity
15
+ import androidx.compose.ui.unit.Dp
20
16
  import androidx.compose.ui.unit.dp
21
17
  import vn.momo.kits.components.Image
22
- import vn.momo.kits.components.InputSearch
23
- import vn.momo.kits.components.InputSearchProps
24
18
  import vn.momo.kits.components.Options
25
19
  import vn.momo.kits.const.AppTheme
26
- import vn.momo.kits.const.Radius
27
- import vn.momo.kits.const.Spacing
20
+ import vn.momo.kits.utils.bottomBorder
28
21
 
29
22
  @Composable
30
23
  fun HeaderExtended(
31
- animations: HeaderAnimations,
32
- inputSearchProps: InputSearchProps? = null,
33
- useAnimationSearch: Boolean = false,
34
- backgroundColor: Color = Color.Transparent
35
- ){
24
+ opacityAni: Float,
25
+ statusBarHeight: Dp,
26
+ backgroundColor: Color
27
+ ) {
36
28
  Box(
37
- modifier = Modifier.fillMaxWidth().height(animations.height.dp)
38
- ) {
39
- Box(
40
- modifier = Modifier
41
- .fillMaxWidth()
42
- .height(154.dp)
43
- .alpha(animations.opacity)
44
- .background(
45
- Brush.linearGradient(
46
- colors = listOf(
47
- Color(0xFFFDCADE),
48
- Color(0x00FDCADE)
49
- ),
50
- start = Offset(0f, 0f),
51
- end = Offset(0f, with(LocalDensity.current) { 154.dp.toPx() })
52
- )
29
+ modifier = Modifier
30
+ .fillMaxWidth()
31
+ .height(154.dp)
32
+ .alpha(opacityAni)
33
+ .background(
34
+ Brush.linearGradient(
35
+ colors = listOf(
36
+ Color(0xFFFDCADE),
37
+ Color(0x00FDCADE)
38
+ ),
39
+ start = Offset(0f, 0f),
40
+ end = Offset(0f, with(LocalDensity.current) { 154.dp.toPx() })
53
41
  )
54
- ) {
55
- if (AppTheme.current.assets.headerBackground != null) {
56
- Box(
57
- modifier = Modifier
58
- .fillMaxWidth()
59
- .background(backgroundColor)
60
- ) {
61
- Image(
62
- loading = false,
63
- source = AppTheme.current.assets.headerBackground!!,
64
- modifier = Modifier.fillMaxWidth().height(154.dp),
65
- options = Options(
66
- contentScale = ContentScale.FillWidth
67
- )
68
- )
69
- }
70
- }
71
- }
72
-
73
- if (inputSearchProps != null && useAnimationSearch) {
42
+ )
43
+ ) {
44
+ if (AppTheme.current.assets.headerBackground != null) {
74
45
  Box(
75
46
  modifier = Modifier
76
- .height(52.dp)
77
- .offset(
78
- x = animations.translateX.dp
79
- )
80
- .padding(vertical = Spacing.S)
81
- .align(Alignment.BottomStart)
47
+ .fillMaxWidth()
48
+ .background(backgroundColor)
82
49
  ) {
83
- Box(
84
- modifier = Modifier
85
- .width(animations.width.dp)
86
- .background(
87
- color = animations.backgroundSearch,
88
- shape = RoundedCornerShape(Radius.XL)
89
- )
90
- ) {
91
- InputSearch(
92
- inputSearchProps = inputSearchProps.copy(
93
- showButtonText = false,
94
- backgroundColor = Color.Transparent
95
- )
50
+ Image(
51
+ loading = false,
52
+ source = AppTheme.current.assets.headerBackground!!,
53
+ modifier = Modifier.fillMaxWidth().height(154.dp),
54
+ options = Options(
55
+ contentScale = ContentScale.FillWidth
96
56
  )
97
- }
57
+ )
98
58
  }
99
59
  }
100
60
  }
101
- }
102
61
 
62
+ Box(
63
+ modifier = Modifier
64
+ .fillMaxWidth()
65
+ .alpha(1f - opacityAni)
66
+ .height(statusBarHeight + HEADER_HEIGHT.dp)
67
+ .background(
68
+ AppTheme.current.colors.background.surface
69
+ )
70
+ .bottomBorder(
71
+ strokeWidth = 1.dp,
72
+ color = AppTheme.current.colors.border.default
73
+ )
74
+ )
75
+ }
@@ -78,13 +78,13 @@ object Spacing {
78
78
  }
79
79
 
80
80
  @Composable
81
- fun HeaderRight(
82
- data: HeaderRightData? = null,
81
+ internal fun HeaderRight(
82
+ headerRight: HeaderRightData? = null,
83
83
  ) {
84
84
  Row(
85
85
  verticalAlignment = Alignment.CenterVertically,
86
86
  ) {
87
- ToolkitHeaderRight(headerRight = data)
87
+ ToolkitHeaderRight(headerRight = headerRight)
88
88
  }
89
89
  }
90
90
 
@@ -115,11 +115,12 @@ fun ToolkitHeaderRight(headerRight: HeaderRightData? = null) {
115
115
  }
116
116
 
117
117
  fun onPressClose() {
118
- api?.request("dismiss", "", {_ -> })
118
+ api?.request("dismiss", "", { _ -> })
119
119
  }
120
120
 
121
121
  fun onPressMore() {
122
- api?.request("showTools",
122
+ api?.request(
123
+ "showTools",
123
124
  mapOf(
124
125
  "tools" to headerRight?.tools?.map { it.toMap() },
125
126
  "context" to mapOf(
@@ -127,7 +128,11 @@ fun ToolkitHeaderRight(headerRight: HeaderRightData? = null) {
127
128
  "code" to context?.appCode,
128
129
  "name" to context?.appName,
129
130
  "icon" to context?.appIcon,
130
- "description" to context?.description
131
+ "description" to context?.description,
132
+ "support" to context?.support,
133
+ "toolkitConfig" to context?.toolkitConfig,
134
+ "providerId" to context?.providerId,
135
+ "permissions" to context?.permissions
131
136
  )
132
137
  )
133
138
  ) { response ->
@@ -180,10 +185,18 @@ fun ToolkitHeaderRight(headerRight: HeaderRightData? = null) {
180
185
  group.items.any { it.showBadge }
181
186
  }
182
187
 
188
+ val toolkitWidth = if (context !== null) 65.dp else 28.dp
189
+
190
+ var isShowShortcut = headerRight?.useShortcut == true
191
+ //backup case context null of more icon
192
+ if (headerRight?.useMore == true && context == null) {
193
+ isShowShortcut = false
194
+ }
195
+
183
196
  Row(
184
197
  verticalAlignment = Alignment.CenterVertically
185
198
  ) {
186
- if (headerRight?.useShortcut == true) {
199
+ if (isShowShortcut) {
187
200
  NavigationButton(
188
201
  disabled = isLoading,
189
202
  icon = navButtonConfig.icon,
@@ -197,20 +210,26 @@ fun ToolkitHeaderRight(headerRight: HeaderRightData? = null) {
197
210
  modifier = Modifier
198
211
  .padding(start = Spacing.S)
199
212
  .border(0.2.dp, Color(0x33000000), shape = RoundedCornerShape(14.dp))
200
- .width(65.dp)
213
+ .width(toolkitWidth)
201
214
  .height(28.dp)
202
215
  .clip(shape = RoundedCornerShape(14.dp))
203
216
  .background(Color(0x99FFFFFF))
204
217
  ) {
205
- Icon(source = "help_center", size = 20.dp, modifier = Modifier.padding(4.dp).clickable {
206
- onPressHelpCenter()
207
- })
208
- Box(
209
- modifier = Modifier
210
- .width(0.5.dp)
211
- .height(12.dp)
212
- .background(Colors.black_20)
213
- )
218
+ if (context != null) {
219
+ Icon(
220
+ source = "help_center",
221
+ size = 20.dp,
222
+ modifier = Modifier.padding(4.dp).clickable {
223
+ onPressHelpCenter()
224
+ })
225
+ Box(
226
+ modifier = Modifier
227
+ .width(0.5.dp)
228
+ .height(12.dp)
229
+ .background(Colors.black_20)
230
+ )
231
+ }
232
+
214
233
  Icon(
215
234
  source = "16_navigation_close_circle",
216
235
  size = 20.dp,
@@ -8,7 +8,6 @@ import androidx.compose.ui.zIndex
8
8
  import vn.momo.kits.components.Text
9
9
  import vn.momo.kits.const.Typography
10
10
 
11
-
12
11
  @Composable
13
12
  fun HeaderTitle(
14
13
  title: String = "",
@@ -9,7 +9,7 @@ import androidx.compose.runtime.remember
9
9
  import androidx.compose.runtime.setValue
10
10
  import androidx.compose.runtime.staticCompositionLocalOf
11
11
  import androidx.compose.ui.unit.Dp
12
- import androidx.compose.ui.unit.dp
12
+ import kotlinx.serialization.Serializable
13
13
  import vn.momo.kits.components.TrustBannerData
14
14
  import vn.momo.kits.const.AppStatusBar
15
15
  import vn.momo.kits.const.AppTheme
@@ -49,6 +49,10 @@ data class MiniAppContext(
49
49
  val appId: String = "",
50
50
  val appCode: String = "",
51
51
  val description: Any? = null,
52
+ val support: Map<String, Any?> = emptyMap(),
53
+ val toolkitConfig: Map<String, Any?> = emptyMap(),
54
+ val providerId: String = "",
55
+ val permissions: List<Map<String, Any>>? = emptyList()
52
56
  )
53
57
 
54
58
  val PlatformApi = staticCompositionLocalOf<Any?> { null }
@@ -75,7 +79,7 @@ data class KitConfig(
75
79
  fun ApplicationContainer(
76
80
  theme: Theme = defaultTheme,
77
81
  composeApi: ComposeApi? = null,
78
- statusBarHeight: Dp? = null,
82
+ statusBarHeight: Dp? = AppStatusBar.current,
79
83
  applicationContext: MiniAppContext? = null,
80
84
  config: KitConfig? = null,
81
85
  content: @Composable () -> Unit,
@@ -99,8 +103,6 @@ fun ApplicationContainer(
99
103
 
100
104
  val appStatusBarHeight = statusBarHeight ?: getStatusBarHeight()
101
105
 
102
- println("appStatusBarHeight ${appStatusBarHeight.value}")
103
-
104
106
  disableOverscroll()
105
107
  CompositionLocalProvider(
106
108
  AppTheme provides appTheme,
@@ -47,6 +47,8 @@ enum class HeaderType {
47
47
  NONE
48
48
  }
49
49
 
50
+ const val HEADER_HEIGHT = 52
51
+
50
52
  @Composable
51
53
  fun Screen(
52
54
  backgroundColor: Color? = null,
@@ -62,12 +64,13 @@ fun Screen(
62
64
  onContentLayout: ((LayoutCoordinates) -> Unit)? = null,
63
65
  useAvoidKeyboard: Boolean = true,
64
66
  footer: @Composable (() -> Unit)? = null,
65
- headerRight: @Composable (() -> Unit)? = { HeaderRight() },
67
+ headerRight: @Composable (() -> Unit)? = null,
66
68
  fabProps: FabProps? = null,
67
69
  animatedHeader: AnimatedHeader? = null,
68
70
  layoutOffset: Dp = 56.dp,
69
71
  inputSearchProps: InputSearchProps? = null,
70
72
  useAnimationSearch: Boolean = false,
73
+ headerRightWidth: Dp = 0.dp,
71
74
  content: @Composable () -> Unit,
72
75
  ) {
73
76
  val statusBarHeight = AppStatusBar.current ?: getStatusBarHeight()
@@ -84,7 +87,7 @@ fun Screen(
84
87
  val bottomPadding = min(indicator, 21.dp)
85
88
 
86
89
  val opacity by animateFloatAsState(
87
- targetValue = ((scrollState.value / 114f)).coerceIn(0f, 1f)
90
+ targetValue = ((scrollState.value / HEADER_HEIGHT*1f)).coerceIn(0f, 1f),
88
91
  )
89
92
 
90
93
  val headerAnimated =
@@ -94,15 +97,13 @@ fun Screen(
94
97
 
95
98
  Box(
96
99
  Modifier.fillMaxSize()
97
- .background(AppTheme.current.colors.background.surface)
100
+ .background(backgroundColor ?: AppTheme.current.colors.background.default)
98
101
  ) {
99
102
 
100
103
  if (animatedHeader === null) {
101
104
  HeaderBackground(
102
105
  headerType = headerType,
103
- scrollState = scrollState.value,
104
- inputSearchProps = inputSearchProps,
105
- useAnimationSearch = useAnimationSearch
106
+ scrollState = scrollState.value
106
107
  )
107
108
  } else {
108
109
  Box(
@@ -111,31 +112,28 @@ fun Screen(
111
112
  .graphicsLayer { alpha = opacity }
112
113
  ) {
113
114
  HeaderBackground(
114
- isBack = isBack,
115
115
  headerType = headerType,
116
- scrollState = scrollState.value,
117
- inputSearchProps = inputSearchProps,
118
- useAnimationSearch = useAnimationSearch
116
+ scrollState = scrollState.value
119
117
  )
120
118
  }
121
119
  }
122
-
123
120
  Header(
124
121
  headerType = headerType,
125
122
  title = title,
126
123
  titlePosition = titlePosition,
127
124
  headerRight = headerRight,
125
+ headerRightWidth = headerRightWidth,
128
126
  goBack = goBack,
129
127
  opacity = opacity,
130
128
  animatedHeader = animatedHeader,
131
129
  inputSearchProps = inputSearchProps,
132
130
  scrollState = scrollState.value,
133
- useAnimationSearch = useAnimationSearch
131
+ useAnimationSearch = useAnimationSearch,
134
132
  )
135
133
 
136
134
  Column(
137
135
  modifier = Modifier.fillMaxSize()
138
- .padding(top = if (animatedHeader === null && headerType !== HeaderType.NONE) statusBarHeight + 52.dp else 0.dp)
136
+ .padding(top = if (animatedHeader === null && headerType !== HeaderType.NONE) statusBarHeight + HEADER_HEIGHT.dp else 0.dp)
139
137
  .pointerInput(Unit) {
140
138
  detectTapGestures(onTap = {
141
139
  keyboardController?.hide()
@@ -155,19 +153,15 @@ fun Screen(
155
153
  verticalArrangement = verticalArrangement,
156
154
  horizontalAlignment = horizontalAlignment
157
155
  ) {
158
- Box(
159
- modifier = Modifier.background(
160
- backgroundColor ?: AppTheme.current.colors.background.default
161
- )
162
- ) {
156
+ Box {
163
157
  headerAnimated()
164
158
  Column {
165
159
  if (animatedHeader !== null) {
166
- Spacer(modifier = Modifier.padding(top = statusBarHeight + 52.dp + layoutOffset))
160
+ Spacer(modifier = Modifier.padding(top = statusBarHeight + HEADER_HEIGHT.dp + layoutOffset))
167
161
  }
168
162
 
169
- if (useAnimationSearch && inputSearchProps != null) {
170
- Spacer(modifier = Modifier.padding(top = statusBarHeight))
163
+ if (useAnimationSearch && inputSearchProps != null && headerType == HeaderType.EXTENDED) {
164
+ Spacer(modifier = Modifier.padding(top = (HEADER_HEIGHT.dp + Spacing.M)))
171
165
  }
172
166
  content()
173
167
  }
@@ -3,59 +3,63 @@ package vn.momo.kits.application
3
3
  import androidx.compose.animation.animateColorAsState
4
4
  import androidx.compose.animation.core.animateFloatAsState
5
5
  import androidx.compose.runtime.Composable
6
+ import androidx.compose.runtime.getValue
6
7
  import androidx.compose.ui.graphics.Color
7
8
  import androidx.compose.ui.unit.Dp
9
+ import androidx.compose.ui.unit.dp
8
10
  import vn.momo.kits.const.AppTheme
9
11
  import vn.momo.kits.const.Colors
10
- import androidx.compose.runtime.getValue
12
+ import vn.momo.kits.platform.getScreenDimensions
11
13
 
12
14
  data class HeaderAnimations(
13
15
  val opacity: Float,
14
16
  val backgroundSearch: Color,
15
17
  val translateX: Float,
16
18
  val width: Float,
17
- val height: Float
19
+ val translateY: Float
18
20
  )
19
21
 
22
+ private const val SCREEN_PADDING = 12
23
+ private const val BACK_WIDTH = 28
24
+
20
25
  @Composable
21
26
  fun useHeaderSearchAnimation(
27
+ opacityAni: Float,
22
28
  scrollState: Int,
23
- statusBarHeight: Dp,
24
- leftPosition: Dp,
25
- searchWidth: Int,
26
- headerRightWidth: Dp
29
+ headerRightWidth: Dp,
30
+ isBack: Boolean
27
31
  ): HeaderAnimations {
28
- val opacityAni by animateFloatAsState(
29
- targetValue = (1 - (scrollState / 52f)).coerceIn(0f, 1f)
30
- )
32
+ val screenWidth = getScreenDimensions().width
33
+ val leftPosition = if (isBack) (BACK_WIDTH + 20).dp else 12.dp
34
+ val searchWidth = screenWidth - (SCREEN_PADDING * 2)
31
35
 
32
36
  val backgroundSearch by animateColorAsState(
33
37
  targetValue = animateColor(
34
- AppTheme.current.colors.background.default,
35
38
  Colors.black_01,
39
+ AppTheme.current.colors.background.default,
36
40
  opacityAni
37
- )
41
+ ),
38
42
  )
39
43
 
40
44
  val animatedTranslateX by animateFloatAsState(
41
- targetValue = (scrollState / 52f).coerceIn(0f, 1f) * (leftPosition.value - 12) + 12
45
+ targetValue = (scrollState / HEADER_HEIGHT*1f).coerceIn(0f, 1f) * (leftPosition.value - 12) + 12,
42
46
  )
43
47
 
44
48
  val animatedWidth by animateFloatAsState(
45
- targetValue = (scrollState / 52f).coerceIn(0f, 1f) *
46
- ((searchWidth - leftPosition.value - headerRightWidth.value + 12) - searchWidth) + searchWidth
49
+ targetValue = (scrollState / HEADER_HEIGHT*1f).coerceIn(
50
+ 0f,
51
+ 1f
52
+ ) * ((searchWidth - leftPosition.value - headerRightWidth.value + 12) - searchWidth) + searchWidth,
47
53
  )
48
54
 
49
- val animatedHeight by animateFloatAsState(
50
- targetValue = (1 - (scrollState / 52f).coerceIn(0f, 1f)) *
51
- (statusBarHeight.value + 94 - (statusBarHeight.value + 52)) + (statusBarHeight.value + 52)
55
+ val animatedTranslateY by animateFloatAsState(
56
+ targetValue = (1 - (scrollState / HEADER_HEIGHT* 1f).coerceIn(0f, 1f)) * HEADER_HEIGHT
52
57
  )
53
-
54
58
  return HeaderAnimations(
55
59
  opacity = opacityAni,
56
60
  backgroundSearch = backgroundSearch,
57
61
  translateX = animatedTranslateX,
58
62
  width = animatedWidth,
59
- height = animatedHeight
63
+ translateY = animatedTranslateY
60
64
  )
61
65
  }
@@ -35,6 +35,7 @@ import vn.momo.kits.const.AppTheme
35
35
  import vn.momo.kits.const.Radius
36
36
  import vn.momo.kits.const.Spacing
37
37
  import vn.momo.kits.const.Typography
38
+ import vn.momo.kits.modifier.setAutomationId
38
39
  import vn.momo.kits.utils.ifNotNull
39
40
 
40
41
  data class PopupAction(
@@ -95,12 +96,12 @@ fun PopupNotify(
95
96
  callback?.invoke()
96
97
  }
97
98
 
98
- Box(Modifier.semantics {
99
- testTag = "popup_notify"
100
- }, contentAlignment = Alignment.TopEnd) {
99
+ Box(Modifier.setAutomationId("popup_notify")
100
+ , contentAlignment = Alignment.TopEnd) {
101
101
  Column(
102
102
  modifier = Modifier
103
103
  .fillMaxWidth()
104
+ .padding(horizontal = 12.dp)
104
105
  .clip(RoundedCornerShape(Radius.L))
105
106
  .background(
106
107
  AppTheme.current.colors.background.surface,
@@ -115,7 +116,7 @@ fun PopupNotify(
115
116
  )
116
117
 
117
118
  Column(modifier = Modifier.padding(Spacing.XL)) {
118
- Text(style = Typography.titleXs, maxLines = 2, text = title)
119
+ Text(style = Typography.titleXs, maxLines = 2, text = title, modifier = Modifier.setAutomationId("title_popup_permission"))
119
120
  content(Modifier.padding(top = Spacing.S))
120
121
  if (error.isNotEmpty()) {
121
122
  Text(
@@ -142,7 +143,7 @@ fun PopupNotify(
142
143
  Modifier
143
144
  .width(22.dp)
144
145
  .height(22.dp)
145
- .offset(x = Spacing.S, y = -Spacing.S)
146
+ .offset(x = Spacing.M / 2, y = -Spacing.M)
146
147
  .background(
147
148
  color = AppTheme.current.colors.text.default,
148
149
  shape = RoundedCornerShape(Radius.M)
@@ -164,9 +165,7 @@ fun PopupNotify(
164
165
  source = "navigation_close",
165
166
  color = AppTheme.current.colors.background.surface,
166
167
  size = 16.dp,
167
- modifier = Modifier.semantics {
168
- testTag = "ic_popup_close"
169
- }
168
+ modifier = Modifier.setAutomationId("ic_popup_close")
170
169
  )
171
170
  }
172
171
  }
@@ -233,9 +232,7 @@ fun renderRow(
233
232
  },
234
233
  title = it.title,
235
234
  type = ButtonType.TEXT,
236
- modifier = Modifier.semantics {
237
- testTag = "btn_popup_cancel"
238
- }
235
+ modifier = Modifier.setAutomationId("btn_popup_cancel")
239
236
  )
240
237
  }
241
238
  Spacer(modifier = Modifier.width(Spacing.S))
@@ -248,9 +245,7 @@ fun renderRow(
248
245
  onAction(primary?.onPress)
249
246
  },
250
247
  title = primary?.title ?: "",
251
- modifier = Modifier.semantics {
252
- testTag = "btn_popup_allow"
253
- }
248
+ modifier = Modifier.setAutomationId("btn_popup_allow")
254
249
  )
255
250
  }
256
251
  }
@@ -271,9 +266,7 @@ fun renderColumn(
271
266
  onAction(primary?.onPress)
272
267
  },
273
268
  title = primary?.title ?: "",
274
- modifier = Modifier.semantics {
275
- testTag = "btn_popup_allow"
276
- }
269
+ modifier = Modifier.setAutomationId("btn_popup_allow")
277
270
  )
278
271
  secondary?.let {
279
272
  Spacer(modifier = Modifier.height(Spacing.S))
@@ -286,9 +279,7 @@ fun renderColumn(
286
279
  },
287
280
  title = it.title,
288
281
  type = ButtonType.TEXT,
289
- modifier = Modifier.semantics {
290
- testTag = "btn_popup_cancel"
291
- }
282
+ modifier = Modifier.setAutomationId("btn_popup_cancel")
292
283
  )
293
284
  }
294
285
  }
@@ -37,7 +37,7 @@ fun Text(
37
37
  modifier = modifier.setAutomationId(accessibilityId?:text, text),
38
38
  text = text,
39
39
  color = color ?: AppTheme.current.colors.text.default,
40
- style = style,
40
+ style = style.copy(fontWeight = null),
41
41
  textAlign = textAlign,
42
42
  fontSize = fontSize,
43
43
  fontFamily = font,
@@ -27,7 +27,7 @@ public struct ImageView: View {
27
27
  error = false
28
28
  }
29
29
  .resizable()
30
- .renderingMode(.template)
30
+ .renderingMode(.original)
31
31
  .placeholder {
32
32
  Rectangle().foregroundColor(.gray)
33
33
  VStack(alignment: .center, content: {
@@ -46,8 +46,10 @@ public struct PopupDisplay: View {
46
46
  .overlay(RoundedRectangle(cornerRadius: Radius.L).stroke(Colors.black01, lineWidth: 1))
47
47
  }.foregroundColor(Colors.black20)
48
48
  .background(Colors.black01.cornerRadius(15))
49
- .offset(x: 6, y: -6)
49
+ .padding(.trailing, -Spacing.M )
50
+ .padding(.top, -Spacing.M)
50
51
  .zIndex(1)
52
+ .accessibility(identifier: "ic_popup_close")
51
53
 
52
54
  VStack {
53
55
  if(!url.isEmpty) {
@@ -70,6 +72,7 @@ public struct PopupDisplay: View {
70
72
  .padding(.top, 16)
71
73
  .padding(.bottom, 8)
72
74
  .lineLimit(2)
75
+ .accessibility(identifier: "title_popup_permission")
73
76
 
74
77
  Text(description)
75
78
  .foregroundColor(.black)
@@ -77,7 +80,9 @@ public struct PopupDisplay: View {
77
80
  .opacity(0.6)
78
81
  .multilineTextAlignment(.leading)
79
82
  .padding(.bottom, 20)
80
- }.padding(.horizontal, 24)
83
+ }
84
+ .frame(maxWidth: .infinity, alignment: .leading)
85
+ .padding(.horizontal, 24)
81
86
 
82
87
  if buttonDirection == .row {
83
88
  if buttonType == .text {
@@ -97,7 +102,9 @@ public struct PopupDisplay: View {
97
102
  }
98
103
  }
99
104
  .background(Color.white.cornerRadius(15))
100
- .padding(.horizontal, 24)
105
+ .padding(.horizontal, 12)
106
+ .accessibilityElement(children: .ignore)
107
+ .accessibility(identifier: "popup_notify")
101
108
  }
102
109
 
103
110
  // MARK: Internal
@@ -150,8 +157,9 @@ public struct PopupDisplay: View {
150
157
 
151
158
  var RowButtons: some View {
152
159
  HStack {
153
- Button(title: closeButtonTitle, action: onPressCloseButton, type: .secondary, size: .medium)
160
+ Button(title: closeButtonTitle, action: onPressCloseButton, type: .secondary, size: .medium).accessibility(identifier: "btn_popup_cancel")
154
161
  Button(title: actionButtonTitle, action: onPressAction, size: .medium)
162
+ .accessibility(identifier: "btn_popup_allow")
155
163
  }
156
164
  .frame(maxWidth: .infinity, alignment: .center)
157
165
  .padding(.horizontal, 24)
@@ -164,8 +172,9 @@ public struct PopupDisplay: View {
164
172
  title: actionButtonTitle,
165
173
  action: onPressAction,
166
174
  size: .medium
167
- )
168
- Button(title: closeButtonTitle, action: onPressCloseButton, type: .secondary, size: .medium)
175
+ ).accessibility(identifier: "btn_popup_allow")
176
+ Button(title: closeButtonTitle, action: onPressCloseButton, type: .secondary, size: .medium).accessibility(identifier: "btn_popup_cancel")
177
+
169
178
  }
170
179
  .frame(maxWidth: .infinity, alignment: .trailing)
171
180
  .padding(.horizontal, 24)
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
- #Tue Apr 23 16:40:15 ICT 2024
8
- sdk.dir=/Users/hung.dao1/Library/Android/sdk
7
+ #Sun Mar 02 11:30:14 ICT 2025
8
+ sdk.dir=/Users/nguyenhoangson/Library/Android/sdk
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/native-kits",
3
- "version": "0.113.2-beta.5",
3
+ "version": "0.113.3-beta.10",
4
4
  "private": false,
5
5
  "dependencies": {},
6
6
  "devDependencies": {},