@momo-kits/native-kits 0.113.1-beta.4 → 0.113.1-beta.5

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.
@@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.Row
12
12
  import androidx.compose.foundation.layout.Spacer
13
13
  import androidx.compose.foundation.layout.fillMaxWidth
14
14
  import androidx.compose.foundation.layout.height
15
- import androidx.compose.foundation.layout.offset
16
15
  import androidx.compose.foundation.layout.padding
17
16
  import androidx.compose.foundation.layout.size
18
17
  import androidx.compose.foundation.layout.width
@@ -36,7 +35,6 @@ import androidx.compose.ui.platform.LocalDensity
36
35
  import androidx.compose.ui.semantics.semantics
37
36
  import androidx.compose.ui.semantics.testTag
38
37
  import androidx.compose.ui.text.style.TextAlign
39
- import androidx.compose.ui.unit.Dp
40
38
  import androidx.compose.ui.unit.dp
41
39
  import androidx.compose.ui.zIndex
42
40
  import kotlinx.coroutines.flow.distinctUntilChanged
@@ -50,11 +48,9 @@ import vn.momo.kits.components.Text
50
48
  import vn.momo.kits.const.AppStatusBar
51
49
  import vn.momo.kits.const.AppTheme
52
50
  import vn.momo.kits.const.Colors
53
- import vn.momo.kits.const.Radius
54
51
  import vn.momo.kits.const.Spacing
55
52
  import vn.momo.kits.const.Typography
56
53
  import vn.momo.kits.modifier.setAutomationId
57
- import vn.momo.kits.platform.getScreenDimensions
58
54
  import vn.momo.kits.platform.getStatusBarHeight
59
55
  import vn.momo.kits.utils.bottomBorder
60
56
 
@@ -62,8 +58,6 @@ enum class TitlePosition {
62
58
  LEFT, CENTER,
63
59
  }
64
60
 
65
- private const val SCREEN_PADDING = 12
66
- private const val BACK_WIDTH = 28
67
61
 
68
62
  data class AnimatedHeader(
69
63
  val type: HeaderType = HeaderType.DEFAULT,
@@ -72,55 +66,20 @@ data class AnimatedHeader(
72
66
 
73
67
  @Composable
74
68
  fun HeaderBackground(
75
- isBack: Boolean = true,
76
69
  headerType: HeaderType = HeaderType.DEFAULT,
77
- scrollState: Int,
78
- inputSearchProps: InputSearchProps? = null,
79
- headerRightWidth: Dp = 0.dp,
80
- useAnimationSearch: Boolean = false
70
+ scrollState: Int
81
71
  ) {
82
72
  val statusBarHeight = AppStatusBar.current ?: getStatusBarHeight()
83
- val screenWidth = getScreenDimensions().width
84
- val leftPosition = if (isBack) (BACK_WIDTH + 20).dp else 20.dp
85
- val searchWidth = screenWidth - (SCREEN_PADDING * 2)
86
-
87
73
  val opacityAni by animateFloatAsState(
88
- targetValue = (1 - (scrollState / 52f)).coerceIn(0f, 1f),
89
- )
90
-
91
- val backgroundColor by animateColorAsState(
92
- targetValue = animateColor(
93
- Color.Transparent,
94
- AppTheme.current.colors.background.surface,
95
- opacityAni
96
- ),
97
- )
98
-
99
- val backgroundSearch by animateColorAsState(
100
- targetValue = animateColor(
101
- AppTheme.current.colors.background.default,
102
- Colors.black_01,
103
- opacityAni
104
- ),
105
- )
106
-
107
- val animatedTranslateX by animateFloatAsState(
108
- targetValue = (scrollState / 52f).coerceIn(0f, 1f) * (leftPosition.value - 12) + 12,
74
+ targetValue = (1 - (scrollState / 200f)).coerceIn(0f, 1f),
109
75
  )
110
76
 
111
- val animatedWidth by animateFloatAsState(
112
- targetValue = (scrollState / 52f).coerceIn(
113
- 0f,
114
- 1f
115
- ) * ((searchWidth - leftPosition.value - headerRightWidth.value + 12) - searchWidth) + searchWidth,
116
- )
77
+ val backgroundColor = if (scrollState == 0) {
78
+ Color.Transparent
79
+ } else {
80
+ AppTheme.current.colors.background.surface
81
+ }
117
82
 
118
- val animatedHeight by animateFloatAsState(
119
- targetValue = (1 - (scrollState / 52f).coerceIn(
120
- 0f,
121
- 1f
122
- )) * (statusBarHeight.value + 94 - (statusBarHeight.value + 52)) + (statusBarHeight.value + 52),
123
- )
124
83
 
125
84
  when (headerType) {
126
85
  HeaderType.DEFAULT -> {
@@ -160,71 +119,38 @@ fun HeaderBackground(
160
119
  )
161
120
  }
162
121
  }
122
+
163
123
  }
164
124
 
165
125
  HeaderType.EXTENDED -> {
126
+
166
127
  Box(
167
- modifier = Modifier.fillMaxWidth().height(animatedHeight.dp)
168
- ) {
169
- Box(
170
- modifier = Modifier
171
- .fillMaxWidth()
172
- .height(154.dp)
173
- .alpha(opacityAni)
174
- .background(
175
- Brush.linearGradient(
176
- colors = listOf(
177
- Color(0xFFFDCADE),
178
- Color(0x00FDCADE)
179
- ),
180
- start = Offset(0f, 0f),
181
- end = Offset(0f, with(LocalDensity.current) { 154.dp.toPx() })
182
- )
128
+ modifier = Modifier.fillMaxWidth().height(154.dp)
129
+ .alpha(opacityAni)
130
+ .background(
131
+ Brush.linearGradient(
132
+ colors = listOf(
133
+ Color(0xFFFDCADE),
134
+ Color(0x00FDCADE)
135
+ ),
136
+ start = Offset(0f, 0f),
137
+ end = Offset(0f, with(LocalDensity.current) { 154.dp.toPx() })
183
138
  )
184
- ) {
185
- if (AppTheme.current.assets.headerBackground != null) {
186
- Box(
187
- modifier = Modifier
188
- .fillMaxWidth()
189
- .background(backgroundColor)
190
- ) {
191
- Image(
192
- loading = false,
193
- source = AppTheme.current.assets.headerBackground!!,
194
- modifier = Modifier.fillMaxWidth().height(154.dp),
195
- options = Options(
196
- contentScale = ContentScale.FillWidth
197
- )
198
- )
199
- }
200
- }
201
- }
202
-
203
- if (inputSearchProps != null && useAnimationSearch) {
139
+ )
140
+ ) {
141
+ if (AppTheme.current.assets.headerBackground != null) {
204
142
  Box(
205
- modifier = Modifier
206
- .height(52.dp)
207
- .offset(
208
- x = animatedTranslateX.dp
209
- )
210
- .padding(vertical = Spacing.S)
211
- .align(Alignment.BottomStart)
143
+ modifier = Modifier.fillMaxWidth()
144
+ .background(backgroundColor)
212
145
  ) {
213
- Box(
214
- modifier = Modifier
215
- .width(animatedWidth.dp)
216
- .background(
217
- color = backgroundSearch,
218
- shape = RoundedCornerShape(Radius.XL)
219
- )
220
- ) {
221
- InputSearch(
222
- inputSearchProps = inputSearchProps.copy(
223
- showButtonText = false,
224
- backgroundColor = Color.Transparent
225
- )
146
+ Image(
147
+ loading = false,
148
+ source = AppTheme.current.assets.headerBackground!!,
149
+ modifier = Modifier.fillMaxWidth().height(154.dp),
150
+ options = Options(
151
+ contentScale = ContentScale.FillWidth
226
152
  )
227
- }
153
+ )
228
154
  }
229
155
  }
230
156
  }
@@ -234,6 +160,7 @@ fun HeaderBackground(
234
160
  Box(modifier = Modifier.fillMaxWidth())
235
161
  }
236
162
  }
163
+
237
164
  }
238
165
 
239
166
  @Composable
@@ -246,13 +173,11 @@ fun Header(
246
173
  opacity: Float? = null,
247
174
  animatedHeader: AnimatedHeader? = null,
248
175
  scrollState: Int = 0,
249
- inputSearchProps: InputSearchProps? = null,
250
- useAnimationSearch: Boolean = false
176
+ inputSearchProps: InputSearchProps?
251
177
  ) {
252
178
  val statusBarHeight = AppStatusBar.current ?: getStatusBarHeight()
253
179
  val tintIconColor = AppTheme.current.colors.text.default
254
180
  val backgroundButton = Colors.black_01.copy(alpha = 0.6f)
255
- val miniAppContext = ApplicationContext.current
256
181
 
257
182
  var colorFraction by remember { mutableStateOf(0f) }
258
183
 
@@ -263,6 +188,14 @@ fun Header(
263
188
  .collect { fraction -> colorFraction = fraction }
264
189
  }
265
190
 
191
+ val backgroundSearch by animateColorAsState(
192
+ targetValue = animateColor(
193
+ Colors.black_01,
194
+ AppTheme.current.colors.background.default,
195
+ colorFraction
196
+ )
197
+ )
198
+
266
199
  if (headerType != HeaderType.NONE) {
267
200
  BoxWithConstraints(
268
201
  Modifier.height(statusBarHeight + 52.dp)
@@ -309,12 +242,10 @@ fun Header(
309
242
  }
310
243
  }
311
244
 
312
- if (inputSearchProps == null && miniAppContext != null) {
313
- Box(Modifier.graphicsLayer {
314
- alpha = if (animatedHeader !== null) opacity ?: 1f else 1f
315
- }) {
316
- headerRight?.invoke()
317
- }
245
+ Box(Modifier.graphicsLayer {
246
+ alpha = if (animatedHeader !== null) opacity ?: 1f else 1f
247
+ }) {
248
+ headerRight?.invoke()
318
249
  }
319
250
  }
320
251
 
@@ -327,14 +258,13 @@ fun Header(
327
258
  ) {
328
259
  if (goBack != null && titlePosition == TitlePosition.LEFT) {
329
260
  Spacer(Modifier.width(40.dp))
261
+ }
262
+ if (inputSearchProps != null) {
263
+ InputSearch(inputSearchProps = inputSearchProps.copy(backgroundColor = backgroundSearch))
330
264
  } else {
331
265
  Box(
332
266
  Modifier.weight(1f).graphicsLayer {
333
- alpha = if (useAnimationSearch && inputSearchProps != null) {
334
- 1f - (opacity ?: 1f)
335
- } else {
336
- if (animatedHeader !== null) opacity ?: 1f else 1f
337
- }
267
+ alpha = if (animatedHeader !== null) opacity ?: 1f else 1f
338
268
  },
339
269
  contentAlignment = if (titlePosition == TitlePosition.LEFT) Alignment.TopStart else Alignment.Center
340
270
  ) {
@@ -376,4 +306,4 @@ fun animateColor(start: Color, stop: Color, fraction: Float): Color {
376
306
  blue = start.blue + fraction * (stop.blue - start.blue),
377
307
  alpha = start.alpha + fraction * (stop.alpha - start.alpha)
378
308
  )
379
- }
309
+ }
@@ -6,10 +6,14 @@ import androidx.compose.runtime.LaunchedEffect
6
6
  import androidx.compose.runtime.getValue
7
7
  import androidx.compose.runtime.mutableStateOf
8
8
  import androidx.compose.runtime.remember
9
+ import androidx.compose.runtime.rememberCoroutineScope
9
10
  import androidx.compose.runtime.setValue
10
11
  import androidx.compose.runtime.staticCompositionLocalOf
11
12
  import androidx.compose.ui.unit.Dp
12
- import vn.momo.kits.components.TrustBannerData
13
+ import kotlinx.coroutines.launch
14
+ import kotlinx.serialization.json.Json
15
+ import kotlinx.serialization.json.jsonObject
16
+ import kotlinx.serialization.json.jsonPrimitive
13
17
  import vn.momo.kits.const.AppStatusBar
14
18
  import vn.momo.kits.const.AppTheme
15
19
  import vn.momo.kits.const.Theme
@@ -18,94 +22,93 @@ import vn.momo.kits.const.defaultTheme
18
22
  import vn.momo.kits.platform.disableOverscroll
19
23
  import vn.momo.kits.platform.getStatusBarHeight
20
24
 
25
+ val PlatformApi = staticCompositionLocalOf<Any?> { null }
26
+
21
27
  interface ComposeApi {
22
- fun request(funcName: String, params: Any?): String
23
- fun request(funcName: String, params: Any?, onResponse: ((String) -> Unit)?): String
24
- fun requestCallback(funcName: String, params: Any?, onResponse: ((String) -> Unit)?)
28
+ fun request(funcName: String, params: String?): String
29
+ fun request(funcName: String, params: String?, onResponse: ((String) -> Unit)?): String
30
+ fun requestCallback(funcName: String, params: String?, onResponse: ((String) -> Unit)?)
25
31
  fun removeCallback(id: String)
26
32
  }
27
33
 
34
+ val AppNavigator = staticCompositionLocalOf<Navigator> {
35
+ error("no navigator provided!")
36
+ }
37
+
28
38
  class Navigator {
29
- fun push(content: @Composable () -> Unit) {}
30
- fun present(content: @Composable () -> Unit) {}
31
- fun reset(content: @Composable () -> Unit) {}
32
- fun pop() {}
33
- fun replace(content: @Composable () -> Unit) {}
34
- fun popToTop() {}
35
- fun showModal(
36
- onClose: () -> Unit = {},
37
- canBackgroundClose: Boolean = true,
38
- content: @Composable () -> Unit,
39
- ) {
39
+ fun push(content: @Composable () -> Unit) {
40
40
  }
41
41
 
42
- fun showBottomSheet(content: @Composable () -> Unit) {}
43
- }
42
+ fun present(content: @Composable () -> Unit) {
44
43
 
45
- data class MiniAppContext(
46
- val appIcon: String = "",
47
- val appName: Any? = null,
48
- val appId: String = "",
49
- val appCode: String = "",
50
- val description: Any? = null,
51
- )
44
+ }
52
45
 
53
- val PlatformApi = staticCompositionLocalOf<Any?> { null }
46
+ fun reset(content: @Composable () -> Unit) {
54
47
 
55
- val AppNavigator = staticCompositionLocalOf<Navigator> {
56
- error("no navigator provided!")
57
- }
48
+ }
58
49
 
59
- val ApplicationContext = staticCompositionLocalOf<MiniAppContext?> {
60
- null
61
- }
50
+ fun pop() {
51
+ }
62
52
 
63
- val AppConfig = staticCompositionLocalOf<KitConfig?> {
64
- null
65
- }
53
+ fun replace(content: @Composable () -> Unit) {
54
+ }
66
55
 
67
- data class KitConfig(
68
- val trustBanner: TrustBannerData? = null,
69
- val headerBar: String? = null,
70
- val headerGradient: String? = null
71
- )
56
+ fun popToTop() {
57
+ }
58
+
59
+ fun showModal(
60
+ onClose: () -> Unit = {},
61
+ canBackgroundClose: Boolean = true,
62
+ content: @Composable () -> Unit,
63
+ ) {
64
+ }
65
+
66
+ fun showBottomSheet(content: @Composable () -> Unit) {
67
+ }
68
+ }
72
69
 
73
70
  @Composable
74
71
  fun ApplicationContainer(
75
72
  theme: Theme = defaultTheme,
76
73
  composeApi: ComposeApi? = null,
77
- statusBarHeight: Dp? = null,
78
- applicationContext: MiniAppContext? = null,
79
- config: KitConfig? = null,
80
- content: @Composable () -> Unit,
74
+ statusBarHeight: Dp? = AppStatusBar.current ?: getStatusBarHeight(),
75
+ content: @Composable () -> Unit
81
76
  ) {
82
77
  var appTheme by remember { mutableStateOf(theme) }
78
+ val coroutineScope = rememberCoroutineScope()
83
79
 
84
80
  LaunchedEffect(Unit) {
85
81
  try {
86
- val headerBar = config?.headerBar
87
- if (headerBar != null && appTheme.assets.headerBackground == null) {
88
- appTheme = appTheme.copy(
89
- assets = ThemeAssets(
90
- headerBackground = headerBar
91
- )
92
- )
82
+ composeApi?.request("getConfig", "DESIGN_SYSTEM") {
83
+ val json = Json { ignoreUnknownKeys = true }
84
+ val response = json.parseToJsonElement(it).jsonObject["response"]
85
+ val headerBar = response
86
+ ?.jsonObject
87
+ ?.get("headerBar")
88
+ ?.jsonPrimitive
89
+ ?.content
90
+ if (headerBar != null && appTheme.assets.headerBackground == null) {
91
+ coroutineScope.launch {
92
+ appTheme = appTheme.copy(
93
+ assets = ThemeAssets(
94
+ headerBackground = headerBar
95
+ )
96
+ )
97
+ }
98
+ }
93
99
  }
94
- } catch (e: Exception) {
95
- print("@@ == NavigationContainer get config error $e")
100
+ }
101
+ catch (e: Exception) {
102
+ print("@@ == NavigationContainer fetch config error $e")
96
103
  }
97
104
  }
98
105
 
99
- val appStatusBarHeight = statusBarHeight ?: getStatusBarHeight()
100
-
101
106
  disableOverscroll()
102
107
  CompositionLocalProvider(
103
108
  AppTheme provides appTheme,
104
109
  PlatformApi provides composeApi,
105
110
  AppNavigator provides Navigator(),
106
- AppStatusBar provides appStatusBarHeight,
107
- ApplicationContext provides applicationContext,
108
- AppConfig provides config
111
+ AppStatusBar provides statusBarHeight,
109
112
  ) {
110
113
  content()
111
114
  }
@@ -62,12 +62,11 @@ fun Screen(
62
62
  onContentLayout: ((LayoutCoordinates) -> Unit)? = null,
63
63
  useAvoidKeyboard: Boolean = true,
64
64
  footer: @Composable (() -> Unit)? = null,
65
- headerRight: @Composable (() -> Unit)? = { HeaderRight() },
65
+ headerRight: @Composable (() -> Unit)? = null,
66
66
  fabProps: FabProps? = null,
67
67
  animatedHeader: AnimatedHeader? = null,
68
68
  layoutOffset: Dp = 56.dp,
69
69
  inputSearchProps: InputSearchProps? = null,
70
- useAnimationSearch: Boolean = false,
71
70
  content: @Composable () -> Unit,
72
71
  ) {
73
72
  val statusBarHeight = AppStatusBar.current ?: getStatusBarHeight()
@@ -84,7 +83,7 @@ fun Screen(
84
83
  val bottomPadding = min(indicator, 21.dp)
85
84
 
86
85
  val opacity by animateFloatAsState(
87
- targetValue = ((scrollState.value / 52f)).coerceIn(0f, 1f),
86
+ targetValue = ((scrollState.value / 114f)).coerceIn(0f, 1f)
88
87
  )
89
88
 
90
89
  val headerAnimated =
@@ -94,15 +93,13 @@ fun Screen(
94
93
 
95
94
  Box(
96
95
  Modifier.fillMaxSize()
97
- .background(backgroundColor ?: AppTheme.current.colors.background.surface)
96
+ .background(backgroundColor ?: AppTheme.current.colors.background.default)
98
97
  ) {
99
98
 
100
99
  if (animatedHeader === null) {
101
100
  HeaderBackground(
102
101
  headerType = headerType,
103
102
  scrollState = scrollState.value,
104
- inputSearchProps= inputSearchProps,
105
- useAnimationSearch = useAnimationSearch
106
103
  )
107
104
  } else {
108
105
  Box(
@@ -111,11 +108,8 @@ fun Screen(
111
108
  .graphicsLayer { alpha = opacity }
112
109
  ) {
113
110
  HeaderBackground(
114
- isBack = isBack,
115
111
  headerType = headerType,
116
112
  scrollState = scrollState.value,
117
- inputSearchProps= inputSearchProps,
118
- useAnimationSearch = useAnimationSearch
119
113
  )
120
114
  }
121
115
  }
@@ -129,8 +123,7 @@ fun Screen(
129
123
  opacity = opacity,
130
124
  animatedHeader = animatedHeader,
131
125
  inputSearchProps = inputSearchProps,
132
- scrollState = scrollState.value,
133
- useAnimationSearch = useAnimationSearch
126
+ scrollState = scrollState.value
134
127
  )
135
128
 
136
129
  Column(
@@ -161,10 +154,6 @@ fun Screen(
161
154
  if (animatedHeader !== null) {
162
155
  Spacer(modifier = Modifier.padding(top = statusBarHeight + 52.dp + layoutOffset))
163
156
  }
164
-
165
- if (useAnimationSearch && inputSearchProps != null) {
166
- Spacer(modifier = Modifier.padding(top = statusBarHeight))
167
- }
168
157
  content()
169
158
  }
170
159
  }
@@ -17,9 +17,9 @@ enum class DotSize(val size: Int) {
17
17
  }
18
18
 
19
19
  @Composable
20
- fun BadgeDot(size: DotSize = DotSize.Large, modifier: Modifier = Modifier) {
20
+ fun BadgeDot(size: DotSize = DotSize.Large) {
21
21
  Box(
22
- modifier = modifier
22
+ modifier = Modifier
23
23
  .size(size.size.dp)
24
24
  .border(width = 1.dp, color = Colors.black_01, shape = RoundedCornerShape(Radius.S))
25
25
  .background(color = Colors.red_03, shape = RoundedCornerShape(Radius.S))
@@ -75,7 +75,6 @@ fun OTPCaret() {
75
75
 
76
76
  @Composable
77
77
  fun InputOTP(
78
- value: String = "",
79
78
  length: Int? = null,
80
79
  errorMessage: String = "",
81
80
  errorSpacing: Boolean = false,
@@ -86,9 +85,9 @@ fun InputOTP(
86
85
  onBlur: () -> Unit = {},
87
86
  dataType: String = "number",
88
87
  accessibilityLabel: String? = null,
89
- modifier: Modifier = Modifier
90
88
  ) {
91
89
  val MAX_LENGTH = 10
90
+ var value by remember { mutableStateOf("") }
92
91
  var isFocused by remember { mutableStateOf(false) }
93
92
  var isBlurred = false
94
93
 
@@ -97,6 +96,7 @@ fun InputOTP(
97
96
  (dataType != "number" || !text.any { !it.isDigit() }) &&
98
97
  (text.length < value.length || value.length < text.length)
99
98
  ) {
99
+ value = text
100
100
  onChangeText(text)
101
101
  }
102
102
  }
@@ -108,7 +108,7 @@ fun InputOTP(
108
108
  onValueChange = handleChangeText,
109
109
  singleLine = true,
110
110
  keyboardOptions = KeyboardOptions.Default.copy(keyboardType = if (dataType == "number") KeyboardType.Number else KeyboardType.Ascii),
111
- modifier = modifier.height(56.dp).onFocusChanged {
111
+ modifier = Modifier.height(56.dp).onFocusChanged {
112
112
  isFocused = it.isFocused
113
113
  if (it.isFocused) {
114
114
  onFocus()
@@ -31,7 +31,6 @@ import androidx.compose.ui.graphics.Color
31
31
  import androidx.compose.ui.text.TextStyle
32
32
  import androidx.compose.ui.text.input.KeyboardType
33
33
  import androidx.compose.ui.text.style.TextOverflow
34
- import androidx.compose.ui.unit.Dp
35
34
  import androidx.compose.ui.unit.dp
36
35
  import androidx.compose.ui.unit.sp
37
36
  import vn.momo.kits.const.AppTheme
@@ -99,8 +98,7 @@ data class InputSearchProps(
99
98
  val keyboardActions: KeyboardActions = KeyboardActions.Default,
100
99
  val modifier: Modifier = Modifier,
101
100
  val iconModifier: Modifier = Modifier,
102
- val onClearPress: () -> Unit = {},
103
- val leftPosition: Dp? = null,
101
+ val onClearPress: () -> Unit = {}
104
102
  )
105
103
 
106
104
  @Composable
@@ -115,7 +115,7 @@ fun PopupNotify(
115
115
  )
116
116
 
117
117
  Column(modifier = Modifier.padding(Spacing.XL)) {
118
- Text(style = Typography.titleXs, maxLines = 1, text = title)
118
+ Text(style = Typography.titleXs, maxLines = 2, text = title)
119
119
  content(Modifier.padding(top = Spacing.S))
120
120
  if (error.isNotEmpty()) {
121
121
  Text(