@momo-kits/native-kits 0.89.33 → 0.110.1-rc.1

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,6 +3,7 @@ plugins {
3
3
  alias(libs.plugins.jetbrains.kotlin.multiplatform)
4
4
  alias(libs.plugins.compose)
5
5
  alias(libs.plugins.kotlin.compose.compiler)
6
+ kotlin("plugin.serialization")
6
7
  id("maven-publish")
7
8
  }
8
9
 
@@ -39,6 +40,8 @@ kotlin {
39
40
  implementation(libs.coil.multiplatform.compose)
40
41
  implementation(libs.coil.multiplatform.core)
41
42
  implementation(libs.coil.multiplatform.network.ktor)
43
+ implementation("com.google.code.gson:gson:2.10")
44
+ implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
42
45
  }
43
46
  }
44
47
  val androidMain by getting {
@@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.Box
10
10
  import androidx.compose.foundation.layout.BoxWithConstraints
11
11
  import androidx.compose.foundation.layout.Row
12
12
  import androidx.compose.foundation.layout.Spacer
13
+ import androidx.compose.foundation.layout.fillMaxSize
13
14
  import androidx.compose.foundation.layout.fillMaxWidth
14
15
  import androidx.compose.foundation.layout.height
15
16
  import androidx.compose.foundation.layout.padding
@@ -50,7 +51,6 @@ import vn.momo.kits.const.AppTheme
50
51
  import vn.momo.kits.const.Colors
51
52
  import vn.momo.kits.const.Spacing
52
53
  import vn.momo.kits.const.Typography
53
- import vn.momo.kits.modifier.shadow
54
54
  import vn.momo.kits.platform.getStatusBarHeight
55
55
  import vn.momo.kits.utils.bottomBorder
56
56
 
@@ -67,7 +67,7 @@ data class AnimatedHeader(
67
67
  @Composable
68
68
  fun HeaderBackground(
69
69
  headerType: HeaderType = HeaderType.DEFAULT,
70
- useShadow: Boolean? = true,
70
+ headerBanner: String? = null,
71
71
  scrollState: Int
72
72
  ) {
73
73
  val statusBarHeight = AppStatusBar.current ?: getStatusBarHeight()
@@ -75,6 +75,14 @@ fun HeaderBackground(
75
75
  targetValue = (1 - (scrollState / 200f)).coerceIn(0f, 1f),
76
76
  )
77
77
 
78
+
79
+ val backgroundColor = if (scrollState == 0) {
80
+ Color.Transparent
81
+ } else {
82
+ AppTheme.current.colors.background.surface
83
+ }
84
+
85
+
78
86
  when (headerType) {
79
87
  HeaderType.DEFAULT -> {
80
88
  val height = statusBarHeight + 52.dp
@@ -84,6 +92,7 @@ fun HeaderBackground(
84
92
  strokeWidth = 1.dp,
85
93
  color = AppTheme.current.colors.border.default
86
94
  )
95
+ .background(backgroundColor)
87
96
  ) {
88
97
  Box(
89
98
  modifier = Modifier
@@ -103,7 +112,7 @@ fun HeaderBackground(
103
112
  )
104
113
  if (AppTheme.current.assets.headerBackground != null) {
105
114
  Image(
106
- source = AppTheme.current.assets.headerBackground!!,
115
+ source = headerBanner ?: AppTheme.current.assets.headerBackground!!,
107
116
  modifier = Modifier.fillMaxWidth().height(height),
108
117
  options = Options(
109
118
  contentScale = ContentScale.FillWidth
@@ -115,46 +124,43 @@ fun HeaderBackground(
115
124
  }
116
125
 
117
126
  HeaderType.EXTENDED -> {
118
- Box(
119
- modifier = Modifier.fillMaxWidth().height(154.dp)
120
- .alpha(opacityAni)
121
- .background(
122
- Brush.linearGradient(
123
- colors = listOf(
124
- Color(0xFFFDCADE),
125
- Color(0x00FDCADE)
126
- ),
127
- start = Offset(0f, 0f),
128
- end = Offset(0f, with(LocalDensity.current) { 154.dp.toPx() })
129
- )
130
- )
131
- ) {
132
- if (AppTheme.current.assets.headerBackground != null) {
133
- Image(
134
- source = AppTheme.current.assets.headerBackground!!,
135
- modifier = Modifier.fillMaxWidth().height(154.dp),
136
- options = Options(
137
- contentScale = ContentScale.FillWidth
127
+
128
+ Box(
129
+ modifier = Modifier.fillMaxWidth().height(154.dp)
130
+ .alpha(opacityAni)
131
+ .background(
132
+ Brush.linearGradient(
133
+ colors = listOf(
134
+ Color(0xFFFDCADE),
135
+ Color(0x00FDCADE)
136
+ ),
137
+ start = Offset(0f, 0f),
138
+ end = Offset(0f, with(LocalDensity.current) { 154.dp.toPx() })
139
+ )
138
140
  )
139
- )
141
+ ) {
142
+ if (AppTheme.current.assets.headerBackground != null) {
143
+ Box(
144
+ modifier = Modifier.fillMaxWidth()
145
+ .background(backgroundColor)
146
+ ) {
147
+ Image(
148
+ source = headerBanner ?: AppTheme.current.assets.headerBackground!!,
149
+ modifier = Modifier.fillMaxWidth().height(154.dp),
150
+ options = Options(
151
+ contentScale = ContentScale.FillWidth
152
+ )
153
+ )
154
+ }
140
155
  }
141
156
  }
142
157
  }
158
+
143
159
  else -> {
144
160
  Box(modifier = Modifier.fillMaxWidth())
145
161
  }
146
162
  }
147
- Box(
148
- modifier = Modifier.fillMaxWidth().height(statusBarHeight + 52.dp)
149
- .alpha(1 - opacityAni)
150
- .background(Colors.black_01)
151
- .then(
152
- if (useShadow == true) Modifier.shadow() else Modifier.bottomBorder(
153
- 1.dp,
154
- Colors.black_04
155
- )
156
- )
157
- )
163
+
158
164
  }
159
165
 
160
166
  @Composable
@@ -163,10 +169,9 @@ fun Header(
163
169
  titlePosition: TitlePosition,
164
170
  title: String,
165
171
  headerRight: @Composable (() -> Unit)? = null,
166
- goBack: (() -> Unit)?,
172
+ goBack: (() -> Unit)? = null,
167
173
  opacity: Float? = null,
168
174
  animatedHeader: AnimatedHeader? = null,
169
- useSearchInput: Boolean? = false,
170
175
  scrollState: Int = 0,
171
176
  inputSearchProps: InputSearchProps?
172
177
  ) {
@@ -206,7 +211,7 @@ fun Header(
206
211
  horizontalArrangement = Arrangement.SpaceBetween
207
212
  ) {
208
213
  Box {
209
- if (goBack !== null) {
214
+ if (goBack != null) {
210
215
  Box(
211
216
  modifier = Modifier
212
217
  .size(28.dp)
@@ -238,12 +243,11 @@ fun Header(
238
243
  }
239
244
  }
240
245
  }
241
- if (useSearchInput == false) {
242
- Box(Modifier.graphicsLayer {
243
- alpha = if (animatedHeader !== null) opacity ?: 1f else 1f
244
- }) {
245
- headerRight?.invoke()
246
- }
246
+
247
+ Box(Modifier.graphicsLayer {
248
+ alpha = if (animatedHeader !== null) opacity ?: 1f else 1f
249
+ }) {
250
+ headerRight?.invoke()
247
251
  }
248
252
  }
249
253
 
@@ -254,13 +258,12 @@ fun Header(
254
258
  .padding(horizontal = Spacing.M),
255
259
  verticalAlignment = Alignment.CenterVertically,
256
260
  ) {
257
- if (goBack !== null) {
261
+ if (goBack != null && titlePosition == TitlePosition.LEFT) {
258
262
  Spacer(Modifier.width(40.dp))
259
263
  }
260
- if (useSearchInput == true && inputSearchProps != null) {
264
+ if (inputSearchProps != null) {
261
265
  InputSearch(inputSearchProps = inputSearchProps.copy(backgroundColor = backgroundSearch))
262
- }
263
- else{
266
+ } else{
264
267
  Box(
265
268
  Modifier.weight(1f).graphicsLayer {
266
269
  alpha = if (animatedHeader !== null) opacity ?: 1f else 1f
@@ -292,7 +295,7 @@ fun HeaderTitle(
292
295
  modifier = Modifier.then(modifier).zIndex(1f),
293
296
  text = title,
294
297
  textAlign = textAlign,
295
- style = Typography.headerDefault,
298
+ style = Typography.actionS,
296
299
  color = color,
297
300
  maxLines = 1
298
301
  )
@@ -1,8 +1,5 @@
1
1
  package vn.momo.kits.application
2
2
 
3
- import androidx.compose.foundation.layout.WindowInsets
4
- import androidx.compose.foundation.layout.asPaddingValues
5
- import androidx.compose.foundation.layout.systemBars
6
3
  import androidx.compose.runtime.Composable
7
4
  import androidx.compose.runtime.CompositionLocalProvider
8
5
  import androidx.compose.runtime.staticCompositionLocalOf
@@ -17,6 +14,10 @@ val AppNavigator = staticCompositionLocalOf<Navigator> {
17
14
  error("no navigator provided!")
18
15
  }
19
16
 
17
+ val PlatformApi = staticCompositionLocalOf<ComposeApi> {
18
+ error("no api provided!")
19
+ }
20
+
20
21
  class Navigator {
21
22
  fun push(content: @Composable () -> Unit) {
22
23
  }
@@ -49,14 +50,23 @@ class Navigator {
49
50
  }
50
51
  }
51
52
 
53
+ interface ComposeApi {
54
+ fun request(funcName: String, params: String?): String
55
+ fun request(funcName: String, params: String?, onResponse: ((String) -> Unit)?): String
56
+ fun requestCallback(funcName: String, params: String?, onResponse: ((String) -> Unit)?)
57
+ fun removeCallback(id: String)
58
+ }
59
+
52
60
  @Composable
53
61
  fun ApplicationContainer(
54
62
  theme: Theme = defaultTheme,
63
+ composeApi: ComposeApi,
55
64
  content: @Composable () -> Unit,
56
65
  ) {
57
66
  disableOverscroll()
58
67
  CompositionLocalProvider(
59
68
  AppTheme provides theme,
69
+ PlatformApi provides composeApi,
60
70
  AppNavigator provides Navigator(),
61
71
  AppStatusBar provides getStatusBarHeight(),
62
72
  ) {
@@ -32,6 +32,8 @@ import androidx.compose.ui.unit.Dp
32
32
  import androidx.compose.ui.unit.dp
33
33
  import androidx.compose.ui.unit.min
34
34
  import androidx.compose.ui.zIndex
35
+ import kotlinx.serialization.Serializable
36
+ import kotlinx.serialization.json.Json
35
37
  import vn.momo.kits.components.InputSearchProps
36
38
  import vn.momo.kits.const.AppStatusBar
37
39
  import vn.momo.kits.const.AppTheme
@@ -47,6 +49,9 @@ enum class HeaderType {
47
49
  NONE
48
50
  }
49
51
 
52
+ @Serializable
53
+ data class ConfigData(val headerBanner: String? = null)
54
+
50
55
  @Composable
51
56
  fun Screen(
52
57
  backgroundColor: Color? = null,
@@ -56,7 +61,7 @@ fun Screen(
56
61
  horizontalAlignment: Alignment.Horizontal = Alignment.Start,
57
62
  title: String = "Stack",
58
63
  titlePosition: TitlePosition = TitlePosition.LEFT,
59
- goBack: (() -> Unit) = { },
64
+ goBack: (() -> Unit)? = null,
60
65
  scrollable: Boolean = true,
61
66
  scrollState: ScrollState = rememberScrollState(),
62
67
  onContentLayout: ((LayoutCoordinates) -> Unit)? = null,
@@ -66,8 +71,6 @@ fun Screen(
66
71
  fabProps: FabProps? = null,
67
72
  animatedHeader: AnimatedHeader? = null,
68
73
  layoutOffset: Dp = 56.dp,
69
- useShadow: Boolean? = true,
70
- useSearchInput: Boolean? = false,
71
74
  inputSearchProps: InputSearchProps? = null,
72
75
  content: @Composable () -> Unit,
73
76
  ) {
@@ -81,6 +84,11 @@ fun Screen(
81
84
  else -> 0.dp
82
85
  }
83
86
 
87
+ val platformApi = PlatformApi.current
88
+ val rawData = platformApi.request("GetConfig", "DESIGN_SYSTEM")
89
+ val json = Json { ignoreUnknownKeys = true }
90
+ val headerBanner = json.decodeFromString<ConfigData>(rawData).headerBanner
91
+
84
92
  val indicator = WindowInsets.systemBars.asPaddingValues().calculateBottomPadding()
85
93
  val bottomPadding = min(indicator, 21.dp)
86
94
 
@@ -97,16 +105,19 @@ fun Screen(
97
105
  ) {
98
106
 
99
107
  if (animatedHeader === null) {
100
- HeaderBackground(headerType = headerType, scrollState = scrollState.value)
108
+ HeaderBackground(
109
+ headerType = headerType,
110
+ scrollState = scrollState.value,
111
+ headerBanner = headerBanner
112
+ )
101
113
  } else {
102
114
  Box(
103
- Modifier.zIndex(5f)
115
+ Modifier.zIndex(2f)
104
116
  .background(Color.White.copy(alpha = opacity))
105
117
  .graphicsLayer { alpha = opacity }
106
118
  ) {
107
119
  HeaderBackground(
108
120
  headerType = headerType,
109
- useShadow = useShadow,
110
121
  scrollState = scrollState.value
111
122
  )
112
123
  }
@@ -120,7 +131,6 @@ fun Screen(
120
131
  goBack = goBack,
121
132
  opacity = opacity,
122
133
  animatedHeader = animatedHeader,
123
- useSearchInput = useSearchInput,
124
134
  inputSearchProps = inputSearchProps,
125
135
  scrollState = scrollState.value
126
136
  )
@@ -1,13 +1,17 @@
1
1
  package vn.momo.kits.components
2
2
 
3
+ import androidx.compose.foundation.layout.Box
3
4
  import androidx.compose.foundation.layout.size
4
5
  import androidx.compose.runtime.Composable
5
6
  import androidx.compose.ui.Modifier
6
7
  import androidx.compose.ui.graphics.Color
7
8
  import androidx.compose.ui.graphics.ColorFilter
8
9
  import androidx.compose.ui.layout.ContentScale
10
+ import androidx.compose.ui.semantics.contentDescription
11
+ import androidx.compose.ui.semantics.semantics
9
12
  import androidx.compose.ui.unit.Dp
10
13
  import androidx.compose.ui.unit.dp
14
+ import coil3.compose.AsyncImage
11
15
  import vn.momo.kits.const.AppTheme
12
16
  import vn.momo.kits.utils.Icons
13
17
 
@@ -23,14 +27,14 @@ fun Icon(
23
27
  } else {
24
28
  Icons[source] ?: ""
25
29
  }
26
- Image(
27
- modifier = modifier.size(size),
28
- source = icon,
29
- loading = false,
30
- options = Options(
30
+ Box(modifier = modifier.size(size).semantics {contentDescription = "img|$icon"}) {
31
+ AsyncImage(
32
+ modifier = Modifier.matchParentSize(),
33
+ model = icon,
34
+ contentDescription = null,
31
35
  contentScale = ContentScale.Fit,
32
- colorFilter = color?.let { ColorFilter.tint(it) }
33
- ),
34
- )
36
+ colorFilter = color?.let { ColorFilter.tint(it) },
37
+ )
38
+ }
35
39
  }
36
40
 
@@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.size
15
15
  import androidx.compose.foundation.layout.width
16
16
  import androidx.compose.foundation.shape.RoundedCornerShape
17
17
  import androidx.compose.foundation.text.BasicTextField
18
+ import androidx.compose.foundation.text.KeyboardActions
18
19
  import androidx.compose.foundation.text.KeyboardOptions
19
20
  import androidx.compose.material3.CircularProgressIndicator
20
21
  import androidx.compose.runtime.Composable
@@ -29,6 +30,7 @@ import androidx.compose.ui.focus.onFocusChanged
29
30
  import androidx.compose.ui.graphics.Color
30
31
  import androidx.compose.ui.text.TextStyle
31
32
  import androidx.compose.ui.text.input.KeyboardType
33
+ import androidx.compose.ui.text.style.TextOverflow
32
34
  import androidx.compose.ui.unit.dp
33
35
  import androidx.compose.ui.unit.sp
34
36
  import vn.momo.kits.const.AppTheme
@@ -44,6 +46,7 @@ fun RenderRightIconSearch(
44
46
  icon: String,
45
47
  color: Color,
46
48
  onClick: () -> Unit,
49
+ iconModifier: Modifier = Modifier
47
50
  ) {
48
51
  if (loading) {
49
52
  Box(Modifier.padding(horizontal = Spacing.S)) {
@@ -64,7 +67,7 @@ fun RenderRightIconSearch(
64
67
  source = icon,
65
68
  color = color,
66
69
  size = 24.dp,
67
- modifier = Modifier.padding(start = Spacing.S, end = 0.dp).clickable(
70
+ modifier = iconModifier.padding(start = Spacing.S, end = 0.dp).clickable(
68
71
  onClick = onClick,
69
72
  interactionSource = remember { MutableInteractionSource() },
70
73
  indication = null
@@ -90,6 +93,11 @@ data class InputSearchProps(
90
93
  val fontWeight: InputFontWeight = InputFontWeight.REGULAR,
91
94
  val keyboardType: KeyboardType = KeyboardType.Text,
92
95
  val backgroundColor: Color = Colors.black_01,
96
+ val clearCondition: (() -> Boolean)? = null,
97
+ val keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
98
+ val keyboardActions: KeyboardActions = KeyboardActions.Default,
99
+ val modifier: Modifier = Modifier,
100
+ val iconModifier: Modifier = Modifier,
93
101
  )
94
102
 
95
103
  @Composable
@@ -98,7 +106,6 @@ fun InputSearch(
98
106
  text = remember { mutableStateOf("") },
99
107
  iconColor = AppTheme.current.colors.text.default
100
108
  ),
101
-
102
109
  ) {
103
110
  var isFocused by remember { mutableStateOf(false) }
104
111
  var isBlurred = false
@@ -124,8 +131,9 @@ fun InputSearch(
124
131
  lineHeight = 20.sp,
125
132
  fontWeight = inputSearchProps.fontWeight.value
126
133
  ),
127
- keyboardOptions = KeyboardOptions.Default.copy(keyboardType = inputSearchProps.keyboardType),
128
- modifier = Modifier.height(36.dp).onFocusChanged {
134
+ keyboardOptions = inputSearchProps.keyboardOptions.copy(keyboardType = inputSearchProps.keyboardType),
135
+ keyboardActions = inputSearchProps.keyboardActions,
136
+ modifier = inputSearchProps.modifier.height(36.dp).onFocusChanged {
129
137
  isFocused = it.isFocused
130
138
  if (it.isFocused) {
131
139
  inputSearchProps.onFocus()
@@ -137,7 +145,12 @@ fun InputSearch(
137
145
  decorationBox = { innerTextField ->
138
146
  Row(verticalAlignment = Alignment.CenterVertically) {
139
147
  Row(
140
- modifier = Modifier.weight(inputSearchProps.showButtonText.ifTrue(8f, 1f))
148
+ modifier = Modifier.weight(
149
+ inputSearchProps.showButtonText.ifTrue(
150
+ 8f,
151
+ 1f
152
+ )
153
+ )
141
154
  .background(
142
155
  inputSearchProps.backgroundColor,
143
156
  RoundedCornerShape(Radius.XL)
@@ -152,7 +165,6 @@ fun InputSearch(
152
165
  ),
153
166
  verticalAlignment = Alignment.CenterVertically
154
167
  ) {
155
- //leading icon
156
168
  Icon(
157
169
  source = "navigation_search",
158
170
  modifier = Modifier.padding(end = Spacing.XS),
@@ -168,12 +180,14 @@ fun InputSearch(
168
180
  lineHeight = 24.sp,
169
181
  fontWeight = inputSearchProps.fontWeight.value
170
182
  ),
171
- color = placeholderColor
183
+ maxLines = 1,
184
+ color = placeholderColor,
185
+ overflow = TextOverflow.Ellipsis
172
186
  )
173
187
  }
174
188
  innerTextField()
175
189
  }
176
- if (isFocused && inputSearchProps.text.value.isNotEmpty()) {
190
+ if (inputSearchProps.clearCondition?.invoke() == true || (isFocused && inputSearchProps.text.value.isNotEmpty())) {
177
191
  Row {
178
192
  Spacer(Modifier.width(Spacing.XS))
179
193
  Icon(
@@ -192,7 +206,8 @@ fun InputSearch(
192
206
  inputSearchProps.loading,
193
207
  inputSearchProps.icon,
194
208
  iconTintColor,
195
- inputSearchProps.onRightIconPressed
209
+ inputSearchProps.onRightIconPressed,
210
+ iconModifier = inputSearchProps.iconModifier
196
211
  )
197
212
  }
198
213
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/native-kits",
3
- "version": "0.89.33",
3
+ "version": "0.110.1-rc.1",
4
4
  "private": false,
5
5
  "dependencies": {},
6
6
  "devDependencies": {},