@momo-kits/native-kits 0.152.4-scale.4 → 0.152.5-klib.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.
Files changed (117) hide show
  1. package/README.md +175 -5
  2. package/ios/native-kits.podspec +18 -16
  3. package/package.json +2 -4
  4. package/CODE_OF_CONDUCT.md +0 -133
  5. package/CONTRIBUTING.md +0 -114
  6. package/LICENSE +0 -20
  7. package/build.gradle.kts +0 -32
  8. package/compose/MoMoComposeKits.podspec +0 -54
  9. package/compose/build.gradle.kts +0 -149
  10. package/compose/src/androidMain/AndroidManifest.xml +0 -2
  11. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -105
  12. package/compose/src/commonMain/composeResources/files/lottie_circle_loader.json +0 -1
  13. package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
  14. package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
  15. package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
  16. package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
  17. package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
  18. package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
  19. package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
  20. package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
  21. package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
  22. package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
  23. package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
  24. package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
  25. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
  26. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -57
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -201
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -222
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -48
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -308
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -715
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -214
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -404
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -78
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -27
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -334
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -345
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -90
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -133
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -543
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -23
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -69
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -143
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -179
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -111
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -395
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -164
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -234
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -226
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -227
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -241
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -235
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -364
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -50
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -34
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -85
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -33
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -338
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -95
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -64
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -89
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -91
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -86
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -91
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -208
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -172
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -199
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -237
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -191
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -306
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -13
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -189
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -293
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -2
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -35
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -2
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -59
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -68
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -49
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -232
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -111
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -94
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -159
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -302
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScaleSizeScope.kt +0 -22
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -483
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -169
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -217
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -180
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -251
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -80
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -306
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -31
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -385
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -125
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -38
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -62
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -88
  111. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -144
  112. package/gradle.properties +0 -19
  113. package/gradlew +0 -240
  114. package/gradlew.bat +0 -91
  115. package/ios/Theme.md +0 -18
  116. package/local.properties +0 -8
  117. package/settings.gradle.kts +0 -25
@@ -1,715 +0,0 @@
1
- package vn.momo.kits.application
2
-
3
- import androidx.compose.animation.animateContentSize
4
- import androidx.compose.foundation.ScrollState
5
- import androidx.compose.foundation.background
6
- import androidx.compose.foundation.border
7
- import androidx.compose.foundation.gestures.detectTapGestures
8
- import androidx.compose.foundation.interaction.FocusInteraction
9
- import androidx.compose.foundation.interaction.MutableInteractionSource
10
- import androidx.compose.foundation.layout.Arrangement
11
- import androidx.compose.foundation.layout.Box
12
- import androidx.compose.foundation.layout.Column
13
- import androidx.compose.foundation.layout.Row
14
- import androidx.compose.foundation.layout.WindowInsets
15
- import androidx.compose.foundation.layout.asPaddingValues
16
- import androidx.compose.foundation.layout.fillMaxSize
17
- import androidx.compose.foundation.layout.fillMaxWidth
18
- import androidx.compose.foundation.layout.height
19
- import androidx.compose.foundation.layout.ime
20
- import androidx.compose.foundation.layout.navigationBars
21
- import androidx.compose.foundation.layout.offset
22
- import androidx.compose.foundation.layout.padding
23
- import androidx.compose.foundation.layout.size
24
- import androidx.compose.foundation.layout.sizeIn
25
- import androidx.compose.foundation.rememberScrollState
26
- import androidx.compose.foundation.shape.CircleShape
27
- import androidx.compose.foundation.shape.RoundedCornerShape
28
- import androidx.compose.foundation.text.BasicTextField
29
- import androidx.compose.foundation.text.KeyboardActions
30
- import androidx.compose.foundation.text.KeyboardOptions
31
- import androidx.compose.foundation.verticalScroll
32
- import androidx.compose.runtime.Composable
33
- import androidx.compose.runtime.LaunchedEffect
34
- import androidx.compose.runtime.MutableState
35
- import androidx.compose.runtime.Stable
36
- import androidx.compose.runtime.State
37
- import androidx.compose.runtime.derivedStateOf
38
- import androidx.compose.runtime.getValue
39
- import androidx.compose.runtime.mutableStateOf
40
- import androidx.compose.runtime.produceState
41
- import androidx.compose.runtime.remember
42
- import androidx.compose.runtime.rememberUpdatedState
43
- import androidx.compose.runtime.snapshotFlow
44
- import androidx.compose.ui.Alignment
45
- import androidx.compose.ui.Modifier
46
- import androidx.compose.ui.composed
47
- import androidx.compose.ui.draw.clip
48
- import androidx.compose.ui.draw.drawBehind
49
- import androidx.compose.ui.draw.drawWithContent
50
- import androidx.compose.ui.geometry.Offset
51
- import androidx.compose.ui.graphics.Brush
52
- import androidx.compose.ui.graphics.Color
53
- import androidx.compose.ui.graphics.graphicsLayer
54
- import androidx.compose.ui.input.pointer.pointerInput
55
- import androidx.compose.ui.layout.Layout
56
- import androidx.compose.ui.layout.Measurable
57
- import androidx.compose.ui.layout.MeasurePolicy
58
- import androidx.compose.ui.layout.MeasureResult
59
- import androidx.compose.ui.layout.MeasureScope
60
- import androidx.compose.ui.layout.Placeable
61
- import androidx.compose.ui.layout.layoutId
62
- import androidx.compose.ui.platform.LocalDensity
63
- import androidx.compose.ui.platform.LocalFocusManager
64
- import androidx.compose.ui.platform.LocalSoftwareKeyboardController
65
- import androidx.compose.ui.text.style.TextOverflow
66
- import androidx.compose.ui.unit.Constraints
67
- import androidx.compose.ui.unit.Dp
68
- import androidx.compose.ui.unit.IntOffset
69
- import androidx.compose.ui.unit.LayoutDirection
70
- import androidx.compose.ui.unit.dp
71
- import androidx.compose.ui.unit.sp
72
- import kotlinx.coroutines.flow.collectLatest
73
- import kotlinx.coroutines.flow.mapNotNull
74
- import vn.momo.kits.components.Icon
75
- import vn.momo.kits.components.Text
76
- import vn.momo.kits.const.AppTheme
77
- import vn.momo.kits.const.Colors
78
- import vn.momo.kits.const.Radius
79
- import vn.momo.kits.const.Spacing
80
- import vn.momo.kits.const.Typography
81
- import vn.momo.kits.modifier.kitsAutomationId
82
- import vn.momo.kits.modifier.noFeedbackClickable
83
- import vn.momo.kits.modifier.setAutomationId
84
- import vn.momo.kits.modifier.shadow
85
- import vn.momo.kits.utils.getAppStatusBarHeight
86
- import kotlin.math.max
87
-
88
- @Composable
89
- fun LiteScreen(
90
- scrollable: Boolean = true,
91
- scrollState: ScrollState? = null,
92
- headerType: HeaderType = HeaderType.DEFAULT,
93
- verticalArrangement: Arrangement.Vertical = Arrangement.Top,
94
- horizontalAlignment: Alignment.Horizontal = Alignment.Start,
95
- backgroundColor: Color = AppTheme.current.colors.background.default,
96
- /* Begin of header props */
97
- title: String? = null,
98
- inputSearchProps: LiteInputSearchProps? = null,
99
- goBack: (() -> Unit)? = null,
100
- headerRight: @Composable() (() -> Unit)? = null,
101
- useAnimationSearch: Boolean = true,
102
- titlePosition: TitlePosition = TitlePosition.LEFT,
103
- headerRightData: HeaderRightData? = null,
104
- /* End of header props */
105
-
106
- screenContent: @Composable () -> Unit,
107
- ) {
108
- val content by rememberUpdatedState(screenContent)
109
-
110
- val finalScrollState = scrollState ?: rememberScrollState()
111
-
112
- Column(
113
- modifier = Modifier
114
- .fillMaxSize()
115
- .background(color = backgroundColor)
116
- .hideKeyboardOnTap(),
117
- verticalArrangement = verticalArrangement,
118
- horizontalAlignment = horizontalAlignment,
119
- ) {
120
- val contentModifier = remember(scrollable, finalScrollState) {
121
- var res = Modifier.weight(1f)
122
- if (scrollable) {
123
- res = res.verticalScroll(finalScrollState)
124
- }
125
- res
126
- }
127
-
128
- LiteScreenHeader(
129
- scrollState = finalScrollState,
130
- title = title,
131
- headerRight = headerRight,
132
- headerType = headerType,
133
- onGoBack = goBack,
134
- inputSearchProps = inputSearchProps,
135
- titlePosition = titlePosition,
136
- useAnimationSearch = useAnimationSearch,
137
- headerRightData = headerRightData,
138
- )
139
-
140
- Box(
141
- modifier = contentModifier,
142
- contentAlignment = Alignment.TopCenter,
143
- ) {
144
- content()
145
- }
146
- }
147
- }
148
-
149
- private object HeaderId {
150
- private const val PACKAGE_NAME = "vn.momo.compose.kits"
151
- const val BACK_ID = "${PACKAGE_NAME}.back"
152
- const val HEADER_RIGHT_ID = "${PACKAGE_NAME}.headerRight"
153
- const val INPUT_SEARCH_ID = "${PACKAGE_NAME}.inputSearch"
154
- const val TITLE_ID = "${PACKAGE_NAME}.title"
155
-
156
- val EXTENDED_HEADER_HEIGHT = 154.dp
157
- }
158
-
159
- @Composable
160
- private fun LiteScreenHeader(
161
- scrollState: ScrollState?,
162
- title: String? = null,
163
- tintColor: Color? = null,
164
- headerRightData: HeaderRightData? = null,
165
- headerType: HeaderType = HeaderType.DEFAULT,
166
- titlePosition: TitlePosition = TitlePosition.LEFT,
167
- useAnimationSearch: Boolean = true,
168
- onGoBack: (() -> Unit)? = null,
169
- inputSearchProps: LiteInputSearchProps? = null,
170
- headerRight: @Composable (() -> Unit)? = null,
171
- ) {
172
- val statusBarHeight = getAppStatusBarHeight()
173
- if (headerType == HeaderType.NONE) {
174
- Box(modifier = Modifier.height(statusBarHeight))
175
- return
176
- }
177
- val theme = AppTheme.current
178
- val density = LocalDensity.current
179
-
180
- val isHeaderExtend = remember(headerType) {
181
- headerType == HeaderType.EXTENDED
182
- }
183
- val backgroundHeight = remember(isHeaderExtend, statusBarHeight) {
184
- if (!isHeaderExtend) statusBarHeight + HEADER_HEIGHT.dp
185
- else HeaderId.EXTENDED_HEADER_HEIGHT
186
- }
187
- val listGradientColors = remember {
188
- listOf(
189
- Color(0xFFFDCADE),
190
- Color(0x00FDCADE),
191
- )
192
- }
193
-
194
- val headerColor = remember(tintColor, theme) {
195
- if (tintColor == Colors.black_01) HeaderColor(
196
- tintIconColor = tintColor,
197
- backgroundButton = Colors.black_20.copy(alpha = 0.6f),
198
- borderColor = Colors.black_01.copy(alpha = 0.2f)
199
- )
200
- else HeaderColor(
201
- tintIconColor = tintColor ?: theme.colors.text.default,
202
- backgroundButton = Colors.black_01.copy(alpha = 0.6f),
203
- borderColor = Colors.black_20.copy(alpha = 0.2f)
204
- )
205
- }
206
-
207
- val scrollPercentage = produceState(
208
- initialValue = 0f,
209
- key1 = useAnimationSearch,
210
- key2 = scrollState,
211
- key3 = backgroundHeight,
212
- ) {
213
- if (!useAnimationSearch) return@produceState
214
- scrollState ?: return@produceState
215
- val rangePx = with(density) { backgroundHeight.toPx() }
216
- snapshotFlow { scrollState.value }.collectLatest {
217
- value = (it / rangePx).coerceIn(0f, 1f)
218
- }
219
- }
220
-
221
- val titleStyle = remember {
222
- Typography.actionSBold.copy(
223
- fontSize = 15.sp,
224
- lineHeight = 22.sp,
225
- )
226
- }
227
-
228
- val titleModifier = remember {
229
- Modifier
230
- .kitsAutomationId("title_navigation_header")
231
- .layoutId(HeaderId.TITLE_ID)
232
- .graphicsLayer {
233
- alpha = if (isHeaderExtend && inputSearchProps != null && useAnimationSearch)
234
- (1 - scrollPercentage.value * 2).coerceIn(0f, 1f)
235
- else 1f
236
- }
237
- }
238
-
239
- val policy = remember(
240
- useAnimationSearch,
241
- isHeaderExtend,
242
- statusBarHeight,
243
- titlePosition,
244
- scrollPercentage,
245
- ) {
246
- LiteScreenHeaderPolicy(
247
- useAnimationSearch = useAnimationSearch,
248
- isHeaderExtend = isHeaderExtend,
249
- statusBarHeight = statusBarHeight,
250
- titlePosition = titlePosition,
251
- scrollPercentage = scrollPercentage,
252
- )
253
- }
254
-
255
- Layout(
256
- modifier = Modifier
257
- .animateContentSize()
258
- .drawBehind {
259
- val headerHeight = max(
260
- HeaderId.EXTENDED_HEADER_HEIGHT.toPx(),
261
- size.height,
262
- )
263
- drawRect(color = Colors.black_01)
264
- drawRect(
265
- brush = Brush.linearGradient(
266
- colors = listGradientColors,
267
- start = Offset.Zero,
268
- end = Offset(
269
- x = 0f,
270
- y = headerHeight * (1 - scrollPercentage.value),
271
- ),
272
- )
273
- )
274
- },
275
- content = {
276
- if (onGoBack != null) {
277
- Box(
278
- modifier = Modifier
279
- .size(28.dp)
280
- .layoutId(HeaderId.BACK_ID)
281
- .clip(CircleShape)
282
- .border(
283
- width = 0.2.dp,
284
- color = headerColor.borderColor,
285
- shape = CircleShape,
286
- )
287
- .background(color = headerColor.backgroundButton)
288
- .noFeedbackClickable(onClick = onGoBack)
289
- .setAutomationId("btn_navigation_back")
290
- .padding(Spacing.XS),
291
- ) {
292
- Icon(
293
- source = "arrow-back",
294
- color = headerColor.tintIconColor,
295
- size = 20.dp,
296
- )
297
- }
298
- }
299
-
300
- Box(
301
- modifier = Modifier
302
- .layoutId(HeaderId.HEADER_RIGHT_ID)
303
- ) {
304
- if (headerRight != null) {
305
- headerRight()
306
- } else {
307
- HeaderRight(
308
- headerRight = headerRightData,
309
- tintColor = tintColor,
310
- )
311
- }
312
- }
313
-
314
- if (inputSearchProps != null) {
315
- LiteInputSearch(
316
- modifier = Modifier.layoutId(HeaderId.INPUT_SEARCH_ID),
317
- inputSearchProps = inputSearchProps,
318
- )
319
- }
320
- if (title != null && (inputSearchProps == null || isHeaderExtend)) {
321
- Text(
322
- text = title,
323
- color = headerColor.tintIconColor,
324
- style = titleStyle,
325
- modifier = titleModifier,
326
- maxLines = 1,
327
- overflow = TextOverflow.Ellipsis
328
- )
329
- }
330
- },
331
- measurePolicy = policy,
332
- )
333
- }
334
-
335
- private class LiteScreenHeaderPolicy(
336
- private val useAnimationSearch: Boolean,
337
- private val isHeaderExtend: Boolean,
338
- private val statusBarHeight: Dp,
339
- private val scrollPercentage: State<Float>,
340
- private val titlePosition: TitlePosition,
341
- ) : MeasurePolicy {
342
-
343
- override fun MeasureScope.measure(
344
- measurables: List<Measurable>,
345
- constraints: Constraints
346
- ): MeasureResult {
347
- val spacing12 = Spacing.M.roundToPx()
348
- val statusBarPx = statusBarHeight.roundToPx()
349
- val scrollPercent = scrollPercentage.value
350
-
351
- val realConstraints = constraints.copy(
352
- minWidth = 0,
353
- minHeight = 0,
354
- maxWidth = (constraints.maxWidth - spacing12 * 2)
355
- .coerceAtLeast(0),
356
- )
357
- val backIconPlaceable =
358
- measurables.find { it.layoutId == HeaderId.BACK_ID }?.measure(realConstraints)
359
- val headerRightPlaceable =
360
- measurables.find { it.layoutId == HeaderId.HEADER_RIGHT_ID }?.measure(
361
- realConstraints.copy(
362
- maxWidth = realConstraints.maxWidth / 2,
363
- )
364
- )
365
- val inputSearchConstraints = if (isHeaderExtend) {
366
- val minWidth =
367
- if (useAnimationSearch) realConstraints.maxWidth - backIconPlaceable.safeWidth - headerRightPlaceable.safeWidth - spacing12 * 2
368
- else realConstraints.maxWidth
369
- realConstraints.copy(
370
- maxWidth = (realConstraints.maxWidth * (1 - scrollPercent)).toInt()
371
- .coerceAtLeast(minWidth)
372
- )
373
- } else realConstraints.copy(
374
- maxWidth = realConstraints.maxWidth - backIconPlaceable.safeWidth - headerRightPlaceable.safeWidth - spacing12 * 2
375
- )
376
- val inputSearchPlaceable = measurables.find { it.layoutId == HeaderId.INPUT_SEARCH_ID }
377
- ?.measure(inputSearchConstraints)
378
- val titlePlaceable = measurables.find { it.layoutId == HeaderId.TITLE_ID }?.measure(
379
- constraints = realConstraints.copy(
380
- maxWidth = realConstraints.maxWidth - backIconPlaceable.safeWidth - headerRightPlaceable.safeWidth - spacing12 * 2
381
- )
382
- )
383
-
384
- val firstRowMaxHeight = buildList {
385
- add(backIconPlaceable.safeHeight)
386
- add(headerRightPlaceable.safeHeight)
387
- if (!isHeaderExtend) {
388
- add(inputSearchPlaceable.safeHeight)
389
- }
390
- if (isHeaderExtend) {
391
- add(titlePlaceable.safeHeight)
392
- }
393
- }.max()
394
-
395
- var defaultHeight = statusBarPx + spacing12 + firstRowMaxHeight + spacing12
396
- if (isHeaderExtend) {
397
- defaultHeight += inputSearchPlaceable.safeHeight + spacing12
398
- }
399
- val height = when {
400
- !useAnimationSearch && !isHeaderExtend -> defaultHeight
401
- else -> (defaultHeight - scrollPercent * (defaultHeight - statusBarPx - HEADER_HEIGHT.dp.roundToPx())).toInt()
402
- }
403
-
404
- return layout(
405
- width = constraints.maxWidth,
406
- height = height,
407
- ) {
408
- val startX = spacing12
409
- val startY = statusBarPx + spacing12
410
- var curX = startX
411
- var curY = startY
412
-
413
- if (backIconPlaceable != null) {
414
- backIconPlaceable.place(
415
- x = startX,
416
- y = startY + backIconPlaceable.verticalCenterOffset(firstRowMaxHeight),
417
- )
418
- curX += backIconPlaceable.safeWidth + spacing12
419
- }
420
-
421
- headerRightPlaceable?.place(
422
- x = constraints.maxWidth - spacing12 - headerRightPlaceable.safeWidth,
423
- y = startY + headerRightPlaceable.verticalCenterOffset(firstRowMaxHeight),
424
- )
425
-
426
- val titleOffset = IntOffset(
427
- x = if (titlePosition == TitlePosition.LEFT) curX
428
- else titlePlaceable.horizontalCenterOffset(
429
- space = constraints.maxWidth,
430
- layoutDirection = layoutDirection,
431
- ),
432
- y = startY + titlePlaceable.verticalCenterOffset(firstRowMaxHeight),
433
- )
434
-
435
- titlePlaceable?.place(titleOffset)
436
-
437
- if (backIconPlaceable != null || headerRightPlaceable != null || titlePlaceable != null) {
438
- curY += firstRowMaxHeight + spacing12
439
- }
440
-
441
- val inputSearchOffset = if (isHeaderExtend) {
442
- IntOffset(
443
- x = startX + ((backIconPlaceable.safeWidth + spacing12) * (scrollPercent * 2f).coerceIn(
444
- 0f, 1f
445
- )).toInt(),
446
- y = (curY * (1 - scrollPercent)).toInt().coerceAtLeast(
447
- startY + inputSearchPlaceable.verticalCenterOffset(firstRowMaxHeight)
448
- ),
449
- )
450
- } else {
451
- IntOffset(
452
- x = curX,
453
- y = startY + inputSearchPlaceable.verticalCenterOffset(firstRowMaxHeight),
454
- )
455
- }
456
-
457
- inputSearchPlaceable?.place(inputSearchOffset)
458
- }
459
- }
460
-
461
- private val Placeable?.safeHeight
462
- get() = this?.height ?: 0
463
- private val Placeable?.safeWidth
464
- get() = this?.width ?: 0
465
-
466
- private fun Placeable?.verticalCenterOffset(space: Int): Int {
467
- if (this == null) return 0
468
- return Alignment.CenterVertically.align(safeHeight, space)
469
- }
470
-
471
- private fun Placeable?.horizontalCenterOffset(
472
- space: Int,
473
- layoutDirection: LayoutDirection
474
- ): Int {
475
- if (this == null) return 0
476
- return Alignment.CenterHorizontally.align(safeWidth, space, layoutDirection)
477
- }
478
- }
479
-
480
- private val EMPTY_FUNC = {}
481
-
482
- @Stable
483
- data class LiteInputSearchProps(
484
- val textFieldState: MutableState<String>,
485
- val onValueChange: (String) -> Unit,
486
- val onClear: () -> Unit = EMPTY_FUNC,
487
- val clearCondition: (() -> Boolean)? = null,
488
- val modifier: Modifier = Modifier,
489
- val onFocused: (() -> Unit) = EMPTY_FUNC,
490
- val keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
491
- val keyboardActions: KeyboardActions = KeyboardActions.Default,
492
- val onKeyboardAction: (() -> Unit) = EMPTY_FUNC,
493
-
494
- val btnText: String? = null,
495
- val isShowBtnText: Boolean = !btnText.isNullOrEmpty(),
496
- val onPressBtnText: (() -> Unit) = EMPTY_FUNC,
497
-
498
- val disabled: Boolean = false,
499
-
500
- val placeHolder: String? = null,
501
-
502
- val iconRightTextField: @Composable ((Modifier) -> Unit)? = null,
503
- )
504
-
505
- @Composable
506
- private fun LiteInputSearch(
507
- modifier: Modifier = Modifier,
508
- inputSearchProps: LiteInputSearchProps? = null,
509
- ) {
510
- inputSearchProps ?: return
511
- val theme = AppTheme.current
512
-
513
- val isFocused = remember { mutableStateOf(false) }
514
- val interactionSource = remember { MutableInteractionSource() }
515
- val textState by remember { inputSearchProps.textFieldState }
516
-
517
- val isShowBtnText by remember(inputSearchProps.isShowBtnText, inputSearchProps.btnText) {
518
- derivedStateOf {
519
- inputSearchProps.isShowBtnText && inputSearchProps.btnText.isNullOrEmpty()
520
- }
521
- }
522
- val inputFieldStyle = remember(theme) {
523
- Typography.bodyDefaultRegular.copy(
524
- color = theme.colors.text.default,
525
- )
526
- }
527
-
528
- LaunchedEffect(inputSearchProps.onFocused) {
529
- interactionSource.interactions.mapNotNull { it as? FocusInteraction }.collectLatest {
530
- val isFocus = it is FocusInteraction.Focus
531
- isFocused.value = isFocus
532
- if (isFocus) inputSearchProps.onFocused()
533
- }
534
- }
535
-
536
- Row(
537
- modifier = modifier,
538
- verticalAlignment = Alignment.CenterVertically,
539
- ) {
540
- val textFieldModifier = remember(inputSearchProps.modifier) {
541
- inputSearchProps.modifier
542
- .weight(1f)
543
- .sizeIn(minHeight = 36.dp)
544
- }
545
- BasicTextField(
546
- value = textState,
547
- onValueChange = inputSearchProps.onValueChange,
548
- enabled = !inputSearchProps.disabled,
549
- keyboardOptions = inputSearchProps.keyboardOptions,
550
- keyboardActions = inputSearchProps.keyboardActions,
551
- modifier = textFieldModifier,
552
- textStyle = inputFieldStyle,
553
- singleLine = true,
554
- interactionSource = interactionSource,
555
- decorationBox = { innerTextField ->
556
- val isShowClear by remember(inputSearchProps.clearCondition, isFocused.value) {
557
- derivedStateOf {
558
- inputSearchProps.clearCondition?.invoke() == true || (isFocused.value && textState.isNotEmpty())
559
- }
560
- }
561
- val placeHolder by produceState<String?>(null, inputSearchProps.placeHolder) {
562
- if (inputSearchProps.placeHolder.isNullOrEmpty()) return@produceState
563
- snapshotFlow { textState }.collectLatest {
564
- value = if (it.isEmpty()) inputSearchProps.placeHolder else null
565
- }
566
- }
567
- val iconRightModifier = remember {
568
- Modifier
569
- .padding(start = Spacing.L)
570
- .drawWithContent {
571
- val offsetX = -Spacing.S.toPx()
572
- drawLine(
573
- start = Offset(
574
- x = offsetX,
575
- y = 0f,
576
- ),
577
- end = Offset(
578
- x = offsetX,
579
- y = size.height,
580
- ),
581
- color = theme.colors.primary,
582
- strokeWidth = 1.dp.toPx(),
583
- )
584
- drawContent()
585
- }
586
- }
587
-
588
- Row(
589
- modifier = Modifier
590
- .background(
591
- color = theme.colors.background.surface,
592
- shape = RoundedCornerShape(Radius.XL),
593
- )
594
- .padding(
595
- horizontal = Spacing.M,
596
- vertical = Spacing.S,
597
- ),
598
- horizontalArrangement = Arrangement.Start,
599
- verticalAlignment = Alignment.CenterVertically,
600
- ) {
601
- Icon(
602
- source = "navigation_search",
603
- modifier = Modifier.padding(end = Spacing.XS),
604
- size = 24.dp,
605
- color = theme.colors.text.hint
606
- )
607
- Box(
608
- modifier = Modifier.weight(1f),
609
- contentAlignment = Alignment.CenterStart,
610
- ) {
611
- if (!placeHolder.isNullOrEmpty()) {
612
- Text(
613
- text = placeHolder ?: "",
614
- style = Typography.bodyDefaultRegular,
615
- maxLines = 1,
616
- color = theme.colors.text.hint,
617
- overflow = TextOverflow.Ellipsis
618
- )
619
- }
620
- innerTextField()
621
- }
622
-
623
- if (isShowClear) {
624
- Icon(
625
- source = "24_navigation_close_circle_full",
626
- size = 16.dp,
627
- color = theme.colors.text.hint,
628
- modifier = Modifier.padding(start = Spacing.XS)
629
- .noFeedbackClickable(onClick = inputSearchProps.onClear),
630
- )
631
- }
632
-
633
- inputSearchProps.iconRightTextField?.invoke(iconRightModifier)
634
- }
635
- }
636
- )
637
-
638
- if (isShowBtnText) {
639
- Text(
640
- text = inputSearchProps.btnText ?: "",
641
- style = Typography.actionDefaultBold,
642
- color = theme.colors.text.default,
643
- modifier = Modifier.padding(start = Spacing.L)
644
- .noFeedbackClickable(onClick = inputSearchProps.onPressBtnText)
645
- )
646
- }
647
- }
648
- }
649
-
650
- @Composable
651
- private fun footerOffset(
652
- useAvoidKeyboard: Boolean = true,
653
- ): State<IntOffset> {
654
- if (!useAvoidKeyboard) return remember { mutableStateOf(IntOffset.Zero) }
655
- val density = LocalDensity.current
656
- val navPaddingValue = WindowInsets.navigationBars.asPaddingValues()
657
- val keyboardSize = keyboardSizeState()
658
-
659
- return produceState(IntOffset.Zero, density, navPaddingValue) {
660
- val navSystemOffset = with(density) { navPaddingValue.calculateBottomPadding().roundToPx() }
661
- snapshotFlow { keyboardSize.value }.collectLatest {
662
- value = if (it == 0.dp) IntOffset.Zero
663
- else IntOffset(
664
- x = 0, y = with(density) {
665
- navSystemOffset - it.roundToPx()
666
- })
667
- }
668
- }
669
- }
670
-
671
- @Composable
672
- fun keyboardSizeState(): State<Dp> {
673
- val bottom = WindowInsets.ime.asPaddingValues()
674
- return rememberUpdatedState(bottom.calculateBottomPadding())
675
- }
676
-
677
- @Composable
678
- private fun Footer(
679
- useAvoidKeyboard: Boolean,
680
- footerContent: (@Composable () -> Unit)?,
681
- ) {
682
- footerContent ?: return
683
- val theme = AppTheme.current
684
- val navPaddingValue = WindowInsets.navigationBars.asPaddingValues()
685
- val offsetMove = footerOffset(useAvoidKeyboard)
686
-
687
- Box(
688
- modifier = Modifier.padding(navPaddingValue).fillMaxWidth().offset {
689
- offsetMove.value
690
- }.shadow(
691
- color = Colors.black_20.copy(alpha = 0.05f),
692
- blurRadius = 24f,
693
- offsetX = 0.dp,
694
- offsetY = (-4).dp
695
- ).background(theme.colors.background.surface).padding(
696
- start = Spacing.M,
697
- top = Spacing.M,
698
- end = Spacing.M,
699
- )
700
- ) {
701
- footerContent()
702
- }
703
- }
704
-
705
- fun Modifier.hideKeyboardOnTap() = composed {
706
- val focusManager = LocalFocusManager.current
707
- val keyboardManager = LocalSoftwareKeyboardController.current
708
-
709
- pointerInput(Unit) {
710
- detectTapGestures {
711
- keyboardManager?.hide()
712
- focusManager.clearFocus()
713
- }
714
- }
715
- }