@momo-kits/native-kits 0.162.2-debug → 0.162.2-lottie.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 (154) hide show
  1. package/.claude/settings.local.json +14 -0
  2. package/CLAUDE.md +78 -0
  3. package/ios/Application/Components.swift +1 -0
  4. package/ios/Application/FloatingButton.swift +1 -0
  5. package/ios/Badge/Badge.swift +1 -0
  6. package/ios/Badge/BadgeRibbon.swift +2 -0
  7. package/ios/Button/Button.swift +1 -1
  8. package/ios/Checkbox/Checkbox.swift +1 -0
  9. package/ios/Information/Information.swift +151 -0
  10. package/ios/Input/Input.swift +1 -0
  11. package/ios/Input/InputPhoneNumber.swift +1 -0
  12. package/ios/Input/InputSearch.swift +3 -0
  13. package/ios/Input/InputTextArea.swift +1 -0
  14. package/ios/OTPKeyboard/KeyboardButton.swift +1 -1
  15. package/ios/Popup/PopupDisplay.swift +6 -0
  16. package/ios/Popup/PopupInput.swift +2 -0
  17. package/ios/Template/TrustBanner/TrustBanner.swift +2 -0
  18. package/ios/Typography/Text.swift +1 -0
  19. package/ios/Typography/Typography.swift +14 -5
  20. package/package.json +1 -1
  21. package/build.gradle.kts +0 -11
  22. package/compose/build.gradle.kts +0 -186
  23. package/compose/build.gradle.kts.backup +0 -186
  24. package/compose/compose.podspec +0 -47
  25. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +0 -6
  26. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -123
  27. package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
  28. package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
  29. package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
  30. package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
  31. package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
  32. package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
  33. package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
  34. package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
  35. package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
  36. package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
  37. package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
  38. package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
  39. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
  40. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -57
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -109
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -201
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -222
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -48
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -305
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -921
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -121
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -403
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +0 -157
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -85
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -32
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -340
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +0 -198
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -357
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -123
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -94
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -136
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +0 -224
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -543
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -23
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -76
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -148
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -188
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -116
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -452
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -172
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -255
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -235
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -233
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -259
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -241
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -364
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +0 -108
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -56
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -41
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -92
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -40
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -352
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -103
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -338
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -70
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +0 -87
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +0 -17
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -96
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +0 -348
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +0 -256
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +0 -494
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +0 -131
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +0 -215
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -96
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -531
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -92
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -135
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -214
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +0 -590
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -177
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +0 -192
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -205
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -239
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -191
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -306
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -16
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -188
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -285
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -2
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -35
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -2
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -57
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -68
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +0 -14
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -50
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -253
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -133
  124. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -104
  125. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -145
  126. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -345
  127. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +0 -8
  128. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -556
  129. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -161
  130. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -243
  131. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
  132. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -187
  133. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -315
  134. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -80
  135. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -306
  136. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -34
  137. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -370
  138. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -131
  139. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +0 -167
  140. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -72
  141. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -66
  142. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
  143. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -70
  144. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
  145. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -105
  146. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +0 -33
  147. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -186
  148. package/gradle/libs.versions.toml +0 -67
  149. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  150. package/gradle/wrapper/gradle-wrapper.properties +0 -8
  151. package/gradle.properties +0 -26
  152. package/gradlew +0 -252
  153. package/gradlew.bat +0 -94
  154. package/settings.gradle.kts +0 -64
@@ -1,921 +0,0 @@
1
- package vn.momo.kits.application
2
-
3
- import androidx.annotation.FloatRange
4
- import androidx.compose.animation.animateContentSize
5
- import androidx.compose.foundation.ScrollState
6
- import androidx.compose.foundation.background
7
- import androidx.compose.foundation.border
8
- import androidx.compose.foundation.gestures.detectTapGestures
9
- import androidx.compose.foundation.interaction.FocusInteraction
10
- import androidx.compose.foundation.interaction.MutableInteractionSource
11
- import androidx.compose.foundation.layout.Arrangement
12
- import androidx.compose.foundation.layout.Box
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.ime
19
- import androidx.compose.foundation.layout.navigationBars
20
- import androidx.compose.foundation.layout.offset
21
- import androidx.compose.foundation.layout.padding
22
- import androidx.compose.foundation.layout.size
23
- import androidx.compose.foundation.rememberScrollState
24
- import androidx.compose.foundation.shape.CircleShape
25
- import androidx.compose.foundation.text.BasicTextField
26
- import androidx.compose.foundation.text.KeyboardActions
27
- import androidx.compose.foundation.text.KeyboardOptions
28
- import androidx.compose.foundation.verticalScroll
29
- import androidx.compose.runtime.Composable
30
- import androidx.compose.runtime.LaunchedEffect
31
- import androidx.compose.runtime.MutableState
32
- import androidx.compose.runtime.Stable
33
- import androidx.compose.runtime.State
34
- import androidx.compose.runtime.derivedStateOf
35
- import androidx.compose.runtime.getValue
36
- import androidx.compose.runtime.mutableStateOf
37
- import androidx.compose.runtime.produceState
38
- import androidx.compose.runtime.remember
39
- import androidx.compose.runtime.rememberUpdatedState
40
- import androidx.compose.runtime.snapshotFlow
41
- import androidx.compose.runtime.staticCompositionLocalOf
42
- import androidx.compose.ui.Alignment
43
- import androidx.compose.ui.Modifier
44
- import androidx.compose.ui.composed
45
- import androidx.compose.ui.draw.clip
46
- import androidx.compose.ui.draw.drawBehind
47
- import androidx.compose.ui.draw.drawWithContent
48
- import androidx.compose.ui.geometry.Offset
49
- import androidx.compose.ui.geometry.Rect
50
- import androidx.compose.ui.graphics.Brush
51
- import androidx.compose.ui.graphics.Color
52
- import androidx.compose.ui.graphics.SolidColor
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.TextStyle
66
- import androidx.compose.ui.text.style.TextOverflow
67
- import androidx.compose.ui.unit.Constraints
68
- import androidx.compose.ui.unit.Dp
69
- import androidx.compose.ui.unit.IntOffset
70
- import androidx.compose.ui.unit.LayoutDirection
71
- import androidx.compose.ui.unit.dp
72
- import androidx.compose.ui.unit.lerp
73
- import androidx.compose.ui.unit.sp
74
- import kotlinx.coroutines.flow.collectLatest
75
- import kotlinx.coroutines.flow.mapNotNull
76
- import vn.momo.kits.components.Icon
77
- import vn.momo.kits.components.Text
78
- import vn.momo.kits.const.AppTheme
79
- import vn.momo.kits.const.Colors
80
- import vn.momo.kits.const.Spacing
81
- import vn.momo.kits.const.Typography
82
- import vn.momo.kits.modifier.kitsAutomationId
83
- import vn.momo.kits.modifier.noFeedbackClickable
84
- import vn.momo.kits.modifier.setAutomationId
85
- import vn.momo.kits.modifier.shadow
86
- import vn.momo.kits.navigation.component.HEADER_HEIGHT
87
- import vn.momo.kits.utils.getAppStatusBarHeight
88
- import kotlin.math.max
89
- import kotlin.math.roundToInt
90
-
91
-
92
- data class AnimationOption(
93
- val targetBounds: Rect,
94
- val progress: State<Float>,
95
- @param:FloatRange(from = 0.0, to = 1.0)
96
- val opacityLimitFraction: Float = 0f,
97
- ) {
98
- val opacityCap: Float
99
- get() = 1f - opacityLimitFraction
100
-
101
- val animateOpacity: Float
102
- get() {
103
- if (progress.value < opacityLimitFraction) return 0f
104
- return androidx.compose.ui.util.lerp(
105
- 0f,
106
- 1f,
107
- (progress.value - opacityLimitFraction) / (opacityCap),
108
- )
109
- }
110
- }
111
-
112
- val LocalAnimationOption = staticCompositionLocalOf<AnimationOption?> { null }
113
-
114
- @Composable
115
- fun LiteScreen(
116
- scrollable: Boolean = true,
117
- scrollState: ScrollState? = null,
118
- headerType: HeaderType = HeaderType.DEFAULT,
119
- verticalArrangement: Arrangement.Vertical = Arrangement.Top,
120
- horizontalAlignment: Alignment.Horizontal = Alignment.Start,
121
- backgroundColor: Color = AppTheme.current.colors.background.default,
122
- /* Begin of header props */
123
- title: String? = null,
124
- inputSearchProps: LiteInputSearchProps? = null,
125
- goBack: (() -> Unit)? = null,
126
- headerRight: @Composable() (() -> Unit)? = null,
127
- useAnimationSearch: Boolean = true,
128
- titlePosition: TitlePosition = TitlePosition.LEFT,
129
- headerRightData: HeaderRightData? = null,
130
- headerTintColor: Color? = null,
131
- headerBackgroundColor: Color? = null,
132
- headerSpaceBetween: Dp? = null,
133
- /* End of header props */
134
-
135
- screenContent: @Composable () -> Unit,
136
- ) {
137
- val content by rememberUpdatedState(screenContent)
138
-
139
- val finalScrollState = scrollState ?: rememberScrollState()
140
-
141
- val contentModifier = remember(
142
- key1 = scrollable,
143
- key2 = finalScrollState,
144
- ) {
145
- var res: Modifier = Modifier
146
- .background(color = backgroundColor)
147
- if (scrollable) {
148
- res = res.verticalScroll(finalScrollState)
149
- }
150
- res
151
- }
152
-
153
- LiteScreenLayout(
154
- scrollState = finalScrollState,
155
- modifier = Modifier
156
- .fillMaxSize()
157
- .hideKeyboardOnTap(),
158
- contentModifier = contentModifier,
159
- verticalArrangement = verticalArrangement,
160
- horizontalAlignment = horizontalAlignment,
161
- title = title,
162
- headerRight = headerRight,
163
- headerType = headerType,
164
- onGoBack = goBack,
165
- inputSearchProps = inputSearchProps,
166
- titlePosition = titlePosition,
167
- useAnimationSearch = useAnimationSearch,
168
- headerRightData = headerRightData,
169
- tintColor = headerTintColor,
170
- headerBackgroundColor = headerBackgroundColor,
171
- headerSpaceBetween = headerSpaceBetween,
172
- ) {
173
- content()
174
- }
175
- }
176
-
177
- private object HeaderId {
178
- private const val PACKAGE_NAME = "vn.momo.compose.kits"
179
- const val CONTENT_ID = "${PACKAGE_NAME}.content"
180
- const val BACKGROUND_ID = "${PACKAGE_NAME}.background"
181
- const val BACK_ID = "${PACKAGE_NAME}.back"
182
- const val HEADER_RIGHT_ID = "${PACKAGE_NAME}.headerRight"
183
- const val INPUT_SEARCH_ID = "${PACKAGE_NAME}.inputSearch"
184
- const val TITLE_ID = "${PACKAGE_NAME}.title"
185
-
186
- val EXTENDED_HEADER_HEIGHT = 154.dp
187
- }
188
-
189
- @Composable
190
- private fun LiteScreenLayout(
191
- scrollState: ScrollState?,
192
- modifier: Modifier = Modifier,
193
- contentModifier: Modifier = Modifier,
194
- verticalArrangement: Arrangement.Vertical = Arrangement.Top,
195
- horizontalAlignment: Alignment.Horizontal = Alignment.Start,
196
- title: String? = null,
197
- tintColor: Color? = null,
198
- headerRightData: HeaderRightData? = null,
199
- headerType: HeaderType = HeaderType.DEFAULT,
200
- titlePosition: TitlePosition = TitlePosition.LEFT,
201
- useAnimationSearch: Boolean = true,
202
- onGoBack: (() -> Unit)? = null,
203
- headerBackgroundColor: Color? = null,
204
- headerSpaceBetween: Dp? = null,
205
- inputSearchProps: LiteInputSearchProps? = null,
206
- headerRight: @Composable (() -> Unit)? = null,
207
- content: @Composable () -> Unit,
208
- ) {
209
- val statusBarHeight = getAppStatusBarHeight()
210
- val isHeaderNone = remember(headerType) {
211
- headerType == HeaderType.NONE
212
- }
213
- val theme = AppTheme.current
214
- val density = LocalDensity.current
215
-
216
- val isHeaderExtend = remember(headerType) {
217
- headerType == HeaderType.EXTENDED
218
- }
219
- val backgroundHeight = remember(isHeaderNone, isHeaderExtend, statusBarHeight) {
220
- when {
221
- isHeaderNone -> statusBarHeight
222
- !isHeaderExtend -> statusBarHeight + HEADER_HEIGHT.dp
223
- else -> HeaderId.EXTENDED_HEADER_HEIGHT
224
- }
225
- }
226
- val listGradientColors = remember {
227
- listOf(
228
- Color(0xFFFDCADE),
229
- Color(0x00FDCADE),
230
- )
231
- }
232
-
233
- val headerColor = remember(tintColor, theme) {
234
- if (tintColor == Colors.black_01) HeaderColor(
235
- tintIconColor = tintColor,
236
- backgroundButton = Colors.black_20.copy(alpha = 0.6f),
237
- borderColor = Colors.black_01.copy(alpha = 0.2f)
238
- )
239
- else HeaderColor(
240
- tintIconColor = tintColor ?: theme.colors.text.default,
241
- backgroundButton = Colors.black_01.copy(alpha = 0.6f),
242
- borderColor = Colors.black_20.copy(alpha = 0.2f)
243
- )
244
- }
245
-
246
- val scrollPercentage = produceState(
247
- initialValue = 0f,
248
- key1 = useAnimationSearch,
249
- key2 = scrollState,
250
- key3 = backgroundHeight,
251
- ) {
252
- if (!useAnimationSearch) return@produceState
253
- scrollState ?: return@produceState
254
- val rangePx = with(density) { backgroundHeight.toPx() }
255
- snapshotFlow { scrollState.value }.collectLatest {
256
- value = (it / rangePx).coerceIn(0f, 1f)
257
- }
258
- }
259
-
260
- val titleStyle = remember {
261
- Typography.actionSBold.copy(
262
- fontSize = 15.sp,
263
- lineHeight = 22.sp,
264
- )
265
- }
266
-
267
- val animationOption = LocalAnimationOption.current
268
- val animationOpacityModifier = Modifier.graphicsLayer {
269
- alpha = animationOption?.animateOpacity ?: 1f
270
- }
271
- val titleModifier = Modifier
272
- .kitsAutomationId("title_navigation_header")
273
- .layoutId(HeaderId.TITLE_ID)
274
- .graphicsLayer {
275
- val titleAlpha = if (isHeaderExtend && inputSearchProps != null && useAnimationSearch)
276
- (1 - scrollPercentage.value * 2).coerceIn(0f, 1f)
277
- else 1f
278
- alpha = titleAlpha * (animationOption?.progress?.value ?: 1f)
279
- }
280
-
281
- val policy = remember(
282
- useAnimationSearch,
283
- isHeaderNone,
284
- isHeaderExtend,
285
- statusBarHeight,
286
- titlePosition,
287
- scrollPercentage,
288
- headerSpaceBetween,
289
- animationOption,
290
- verticalArrangement,
291
- horizontalAlignment,
292
- ) {
293
- LiteScreenLayoutPolicy(
294
- useAnimationSearch = useAnimationSearch,
295
- isHeaderNone = isHeaderNone,
296
- isHeaderExtend = isHeaderExtend,
297
- statusBarHeight = statusBarHeight,
298
- titlePosition = titlePosition,
299
- scrollPercentage = scrollPercentage,
300
- headerSpaceBetween = headerSpaceBetween,
301
- animationOption = animationOption,
302
- verticalArrangement = verticalArrangement,
303
- horizontalAlignment = horizontalAlignment,
304
- )
305
- }
306
-
307
- Layout(
308
- modifier = modifier
309
- .animateContentSize(),
310
- content = {
311
- Box(
312
- modifier = animationOpacityModifier
313
- .layoutId(HeaderId.CONTENT_ID)
314
- .then(contentModifier),
315
- contentAlignment = Alignment.TopCenter,
316
- ) {
317
- content()
318
- }
319
-
320
- if (!isHeaderNone) {
321
- Box(
322
- modifier = Modifier
323
- .layoutId(HeaderId.BACKGROUND_ID)
324
- .then(animationOpacityModifier)
325
- .drawBehind {
326
- val headerHeight = max(
327
- HeaderId.EXTENDED_HEADER_HEIGHT.toPx(),
328
- size.height,
329
- )
330
- headerBackgroundColor?.let {
331
- drawRect(color = it)
332
- } ?: run {
333
- drawRect(color = Colors.black_01)
334
- drawRect(
335
- brush = Brush.linearGradient(
336
- colors = listGradientColors,
337
- start = Offset.Zero,
338
- end = Offset(
339
- x = 0f,
340
- y = headerHeight * (1 - scrollPercentage.value),
341
- ),
342
- )
343
- )
344
- }
345
- },
346
- )
347
- }
348
-
349
- if (!isHeaderNone && onGoBack != null) {
350
- val coreModifier = Modifier
351
- .layoutId(HeaderId.BACK_ID)
352
- .then(animationOpacityModifier)
353
- .clip(CircleShape)
354
- .noFeedbackClickable(onClick = onGoBack)
355
- .setAutomationId("btn_navigation_back")
356
- inputSearchProps?.customBackIcon?.invoke(coreModifier) ?: Box(
357
- modifier = coreModifier
358
- .size(28.dp)
359
- .border(
360
- width = 0.2.dp,
361
- color = headerColor.borderColor,
362
- shape = CircleShape,
363
- )
364
- .background(color = headerColor.backgroundButton)
365
- .padding(Spacing.XS),
366
- ) {
367
- Icon(
368
- source = "arrow-back",
369
- color = headerColor.tintIconColor,
370
- size = 20.dp,
371
- )
372
- }
373
- }
374
-
375
- if (!isHeaderNone) {
376
- Box(
377
- modifier = Modifier
378
- .layoutId(HeaderId.HEADER_RIGHT_ID)
379
- .then(animationOpacityModifier)
380
- ) {
381
- if (headerRight != null) {
382
- headerRight()
383
- } else {
384
- HeaderRight(
385
- headerRight = headerRightData,
386
- tintColor = tintColor,
387
- )
388
- }
389
- }
390
- }
391
-
392
- if (!isHeaderNone && inputSearchProps != null) {
393
- LiteInputSearch(
394
- modifier = Modifier.layoutId(HeaderId.INPUT_SEARCH_ID),
395
- inputSearchProps = inputSearchProps,
396
- )
397
- }
398
- if (!isHeaderNone && title != null && (inputSearchProps == null || isHeaderExtend)) {
399
- Text(
400
- text = title,
401
- color = headerColor.tintIconColor,
402
- style = titleStyle,
403
- modifier = titleModifier,
404
- maxLines = 1,
405
- overflow = TextOverflow.Ellipsis
406
- )
407
- }
408
- },
409
- measurePolicy = policy,
410
- )
411
- }
412
-
413
- private class LiteScreenLayoutPolicy(
414
- private val useAnimationSearch: Boolean,
415
- private val isHeaderNone: Boolean,
416
- private val isHeaderExtend: Boolean,
417
- private val statusBarHeight: Dp,
418
- private val scrollPercentage: State<Float>,
419
- private val titlePosition: TitlePosition,
420
- private val headerSpaceBetween: Dp? = null,
421
- private val animationOption: AnimationOption?,
422
- private val verticalArrangement: Arrangement.Vertical,
423
- private val horizontalAlignment: Alignment.Horizontal,
424
- ) : MeasurePolicy {
425
-
426
- val searchStartPosition: IntOffset by lazy {
427
- if (animationOption == null) return@lazy IntOffset.Zero
428
- val offset = animationOption.targetBounds.topLeft
429
- IntOffset(
430
- x = offset.x.roundToInt(),
431
- y = offset.y.roundToInt(),
432
- )
433
- }
434
-
435
- override fun MeasureScope.measure(
436
- measurables: List<Measurable>,
437
- constraints: Constraints
438
- ): MeasureResult {
439
- val spacing12 = Spacing.M.roundToPx()
440
- val spaceBetween = headerSpaceBetween?.roundToPx() ?: spacing12
441
- val statusBarPx = statusBarHeight.roundToPx()
442
- val headerRowDefault = HEADER_HEIGHT.dp.roundToPx()
443
- val scrollPercent = scrollPercentage.value
444
-
445
- val contentMeasurable = measurables.find { it.layoutId == HeaderId.CONTENT_ID }
446
-
447
- val realConstraints = constraints.copy(
448
- minWidth = 0,
449
- minHeight = 0,
450
- maxWidth = (constraints.maxWidth - spacing12 * 2)
451
- .coerceAtLeast(0),
452
- )
453
- val backIconPlaceable =
454
- measurables.find { it.layoutId == HeaderId.BACK_ID }?.measure(realConstraints)
455
- val headerRightPlaceable =
456
- measurables.find { it.layoutId == HeaderId.HEADER_RIGHT_ID }?.measure(
457
- realConstraints.copy(
458
- maxWidth = realConstraints.maxWidth / 2,
459
- )
460
- )
461
- val baseInputSearchConstraints = if (isHeaderExtend) {
462
- val minWidth =
463
- if (useAnimationSearch) realConstraints.maxWidth - backIconPlaceable.safeWidth - headerRightPlaceable.safeWidth - spaceBetween * 2
464
- else realConstraints.maxWidth
465
- realConstraints.copy(
466
- maxWidth = (realConstraints.maxWidth * (1 - scrollPercent)).toInt()
467
- .coerceAtLeast(minWidth)
468
- )
469
- } else {
470
- var spaceConsumed = 0
471
- if (backIconPlaceable.safeWidth != 0) spaceConsumed += backIconPlaceable.safeWidth + spaceBetween
472
- if (headerRightPlaceable.safeWidth != 0) spaceConsumed += headerRightPlaceable.safeWidth + spaceBetween
473
- realConstraints.copy(
474
- maxWidth = realConstraints.maxWidth - spaceConsumed
475
- )
476
- }
477
- val inputSearchConstraints = baseInputSearchConstraints.withAnimationTargetBounds()
478
- val inputSearchPlaceable = measurables.find { it.layoutId == HeaderId.INPUT_SEARCH_ID }
479
- ?.measure(inputSearchConstraints)
480
- val titlePlaceable = measurables.find { it.layoutId == HeaderId.TITLE_ID }?.measure(
481
- constraints = realConstraints.copy(
482
- maxWidth = realConstraints.maxWidth - backIconPlaceable.safeWidth - headerRightPlaceable.safeWidth - spaceBetween * 2
483
- )
484
- )
485
-
486
- /**
487
- * replicate logic from [vn.momo.kits.navigation.component.HeaderBackground]
488
- * */
489
- var defaultHeight = statusBarPx + headerRowDefault
490
- if (isHeaderExtend) {
491
- defaultHeight = 154.dp.roundToPx()
492
- }
493
- val headerHeight = when {
494
- isHeaderNone -> statusBarPx
495
- !useAnimationSearch && !isHeaderExtend -> defaultHeight
496
- else -> {
497
- val collapsableSpace = defaultHeight - statusBarPx - headerRowDefault
498
- (defaultHeight - scrollPercent * collapsableSpace).toInt()
499
- }
500
- }
501
-
502
- val layoutWidth = constraints.maxWidth
503
- val layoutHeight = constraints.maxHeight
504
- val contentHeight = (layoutHeight - headerHeight).coerceAtLeast(0)
505
- val contentPlaceable = contentMeasurable?.measure(
506
- constraints.copy(
507
- minWidth = 0,
508
- minHeight = contentHeight,
509
- maxHeight = contentHeight,
510
- )
511
- )
512
- val backgroundPlaceable = measurables.find { it.layoutId == HeaderId.BACKGROUND_ID }
513
- ?.measure(
514
- Constraints.fixed(
515
- width = layoutWidth,
516
- height = headerHeight,
517
- )
518
- )
519
- val childrenHeights = intArrayOf(
520
- headerHeight,
521
- contentPlaceable.safeHeight,
522
- )
523
- val childrenY = IntArray(childrenHeights.size)
524
- with(verticalArrangement) {
525
- arrange(
526
- totalSize = layoutHeight,
527
- sizes = childrenHeights,
528
- outPositions = childrenY,
529
- )
530
- }
531
- val headerY = childrenY[0]
532
- val contentY = childrenY[1]
533
-
534
- return layout(
535
- width = layoutWidth,
536
- height = layoutHeight,
537
- ) {
538
- val progress = animationOption?.progress?.value ?: 1f
539
-
540
- val startX = spacing12
541
- val startY = headerY + statusBarPx
542
- var curX = startX
543
- var curY = startY
544
-
545
- contentPlaceable?.place(
546
- x = horizontalAlignment.align(
547
- size = contentPlaceable.width,
548
- space = layoutWidth,
549
- layoutDirection = layoutDirection,
550
- ),
551
- y = contentY,
552
- )
553
-
554
- backgroundPlaceable?.place(
555
- x = 0,
556
- y = headerY,
557
- )
558
-
559
- if (backIconPlaceable != null) {
560
- backIconPlaceable.place(
561
- x = startX,
562
- y = startY + backIconPlaceable.verticalCenterOffset(headerRowDefault),
563
- )
564
- curX += backIconPlaceable.safeWidth + spaceBetween
565
- }
566
-
567
- headerRightPlaceable?.place(
568
- x = layoutWidth - spacing12 - headerRightPlaceable.safeWidth,
569
- y = startY + headerRightPlaceable.verticalCenterOffset(headerRowDefault),
570
- )
571
-
572
- val titleOffset = IntOffset(
573
- x = if (titlePosition == TitlePosition.LEFT) curX
574
- else titlePlaceable.horizontalCenterOffset(
575
- space = layoutWidth,
576
- layoutDirection = layoutDirection,
577
- ),
578
- y = startY + titlePlaceable.verticalCenterOffset(headerRowDefault),
579
- )
580
-
581
- titlePlaceable?.place(titleOffset)
582
-
583
- if (backIconPlaceable != null || headerRightPlaceable != null || titlePlaceable != null) {
584
- curY += headerRowDefault
585
- }
586
-
587
- val inputSearchOffset = if (isHeaderExtend) {
588
- val baseY = curY + inputSearchPlaceable.verticalCenterOffset(headerRowDefault)
589
- val y = (baseY * (1 - scrollPercent)).toInt().coerceAtLeast(
590
- startY + inputSearchPlaceable.verticalCenterOffset(headerRowDefault)
591
- )
592
- IntOffset(
593
- x = startX + ((backIconPlaceable.safeWidth + spaceBetween) * (scrollPercent * 2f).coerceIn(
594
- 0f, 1f
595
- )).toInt(),
596
- y = y,
597
- )
598
- } else {
599
- IntOffset(
600
- x = curX,
601
- y = startY + inputSearchPlaceable.verticalCenterOffset(headerRowDefault),
602
- )
603
- }
604
- val finalPosition = lerp(
605
- searchStartPosition,
606
- inputSearchOffset,
607
- progress,
608
- )
609
-
610
- inputSearchPlaceable?.place(
611
- finalPosition
612
- )
613
- }
614
- }
615
-
616
- private val Placeable?.safeHeight
617
- get() = this?.height ?: 0
618
- private val Placeable?.safeWidth
619
- get() = this?.width ?: 0
620
-
621
- private fun Constraints.withAnimationTargetBounds(): Constraints {
622
- val option = animationOption ?: return this
623
- val progress = option.progress.value
624
- if (progress >= 1f) return this
625
-
626
- val targetWidth = option.targetBounds.width.roundToInt().coerceAtLeast(0)
627
- val targetHeight = option.targetBounds.height.roundToInt().coerceAtLeast(0)
628
- val animatedMinWidth = lerpPx(targetWidth, minWidth, progress)
629
- .coerceInBounds(maxWidth)
630
- val animatedMaxWidth = lerpMaxPx(targetWidth, maxWidth, progress)
631
- .coerceAtLeast(animatedMinWidth)
632
- val animatedMinHeight = lerpPx(targetHeight, minHeight, progress)
633
- .coerceInBounds(maxHeight)
634
- val animatedMaxHeight = lerpMaxPx(targetHeight, maxHeight, progress)
635
- .coerceAtLeast(animatedMinHeight)
636
-
637
- return copy(
638
- minWidth = animatedMinWidth,
639
- maxWidth = animatedMaxWidth,
640
- minHeight = animatedMinHeight,
641
- maxHeight = animatedMaxHeight,
642
- )
643
- }
644
-
645
- private fun lerpPx(
646
- start: Int,
647
- stop: Int,
648
- fraction: Float,
649
- ): Int = androidx.compose.ui.util.lerp(
650
- start.toFloat(),
651
- stop.toFloat(),
652
- fraction,
653
- ).roundToInt()
654
-
655
- private fun lerpMaxPx(
656
- start: Int,
657
- stop: Int,
658
- fraction: Float,
659
- ): Int {
660
- if (stop == Constraints.Infinity) return Constraints.Infinity
661
- return lerpPx(start, stop, fraction)
662
- }
663
-
664
- private fun Int.coerceInBounds(max: Int): Int {
665
- if (max == Constraints.Infinity) return coerceAtLeast(0)
666
- return coerceIn(0, max)
667
- }
668
-
669
- private fun Placeable?.verticalCenterOffset(space: Int): Int {
670
- if (this == null) return 0
671
- return Alignment.CenterVertically.align(safeHeight, space)
672
- }
673
-
674
- private fun Placeable?.horizontalCenterOffset(
675
- space: Int,
676
- layoutDirection: LayoutDirection
677
- ): Int {
678
- if (this == null) return 0
679
- return Alignment.CenterHorizontally.align(safeWidth, space, layoutDirection)
680
- }
681
- }
682
-
683
- private val EMPTY_FUNC = {}
684
-
685
- @Stable
686
- data class LiteInputSearchProps(
687
- val textFieldState: MutableState<String>,
688
- val onValueChange: (String) -> Unit,
689
- val onClear: () -> Unit = EMPTY_FUNC,
690
- val clearCondition: (() -> Boolean)? = null,
691
- val modifier: Modifier = Modifier,
692
- val onFocused: (() -> Unit) = EMPTY_FUNC,
693
- val keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
694
- val keyboardActions: KeyboardActions = KeyboardActions.Default,
695
- val onKeyboardAction: (() -> Unit) = EMPTY_FUNC,
696
-
697
- val btnText: String? = null,
698
- val isShowBtnText: Boolean = !btnText.isNullOrEmpty(),
699
- val onPressBtnText: (() -> Unit) = EMPTY_FUNC,
700
-
701
- val disabled: Boolean = false,
702
-
703
- val placeHolder: String? = null,
704
-
705
- val cursorBrush: Brush? = null,
706
- val customBackIcon: @Composable ((Modifier) -> Unit)? = null,
707
- val customSearchIcon: @Composable (() -> Unit)? = null,
708
- val customPlaceHolder: @Composable (() -> Unit)? = null,
709
- val customTextStyle: TextStyle? = null,
710
- val customIconClear: @Composable (() -> Unit)? = null,
711
- val iconRightTextField: @Composable ((Modifier) -> Unit)? = null,
712
- )
713
-
714
- @Composable
715
- private fun LiteInputSearch(
716
- modifier: Modifier = Modifier,
717
- inputSearchProps: LiteInputSearchProps? = null,
718
- ) {
719
- inputSearchProps ?: return
720
- val theme = AppTheme.current
721
-
722
- val isFocused = remember { mutableStateOf(false) }
723
- val interactionSource = remember { MutableInteractionSource() }
724
- val textState by remember { inputSearchProps.textFieldState }
725
-
726
- val isShowBtnText by remember(inputSearchProps.isShowBtnText, inputSearchProps.btnText) {
727
- derivedStateOf {
728
- inputSearchProps.isShowBtnText && !inputSearchProps.btnText.isNullOrEmpty()
729
- }
730
- }
731
- val inputFieldStyle = remember(theme) {
732
- Typography.bodyDefaultRegular.copy(
733
- color = theme.colors.text.default,
734
- )
735
- }
736
-
737
- LaunchedEffect(inputSearchProps.onFocused) {
738
- interactionSource.interactions.mapNotNull { it as? FocusInteraction }.collectLatest {
739
- val isFocus = it is FocusInteraction.Focus
740
- isFocused.value = isFocus
741
- if (isFocus) inputSearchProps.onFocused()
742
- }
743
- }
744
-
745
- Row(
746
- modifier = modifier,
747
- verticalAlignment = Alignment.CenterVertically,
748
- ) {
749
- val textFieldModifier = remember(inputSearchProps.modifier) {
750
- inputSearchProps.modifier
751
- .weight(1f)
752
- }
753
- BasicTextField(
754
- value = textState,
755
- onValueChange = inputSearchProps.onValueChange,
756
- enabled = !inputSearchProps.disabled,
757
- keyboardOptions = inputSearchProps.keyboardOptions,
758
- keyboardActions = inputSearchProps.keyboardActions,
759
- modifier = textFieldModifier,
760
- textStyle = inputSearchProps.customTextStyle ?: inputFieldStyle,
761
- singleLine = true,
762
- cursorBrush = inputSearchProps.cursorBrush ?: SolidColor(Color.Black),
763
- interactionSource = interactionSource,
764
- decorationBox = { innerTextField ->
765
- val isShowClear by remember(inputSearchProps.clearCondition, isFocused.value) {
766
- derivedStateOf {
767
- inputSearchProps.clearCondition?.invoke() == true || (isFocused.value && textState.isNotEmpty())
768
- }
769
- }
770
- val placeHolder by produceState<String?>(null, inputSearchProps.placeHolder) {
771
- if (inputSearchProps.placeHolder.isNullOrEmpty()) return@produceState
772
- snapshotFlow { textState }.collectLatest {
773
- value = if (it.isEmpty()) inputSearchProps.placeHolder else null
774
- }
775
- }
776
- val iconRightModifier = remember {
777
- Modifier
778
- .padding(start = Spacing.L)
779
- .drawWithContent {
780
- val offsetX = -Spacing.S.toPx()
781
- drawLine(
782
- start = Offset(
783
- x = offsetX,
784
- y = 0f,
785
- ),
786
- end = Offset(
787
- x = offsetX,
788
- y = size.height,
789
- ),
790
- color = theme.colors.primary,
791
- strokeWidth = 1.dp.toPx(),
792
- )
793
- drawContent()
794
- }
795
- }
796
-
797
- Row(
798
- modifier = Modifier
799
- .padding(
800
- horizontal = Spacing.M,
801
- vertical = Spacing.S,
802
- ),
803
- horizontalArrangement = Arrangement.Start,
804
- verticalAlignment = Alignment.CenterVertically,
805
- ) {
806
- inputSearchProps.customSearchIcon?.invoke() ?: Icon(
807
- source = "navigation_search",
808
- modifier = Modifier.padding(end = Spacing.XS),
809
- size = 24.dp,
810
- color = theme.colors.text.hint
811
- )
812
- Box(
813
- modifier = Modifier.weight(1f),
814
- contentAlignment = Alignment.CenterStart,
815
- ) {
816
- if (!placeHolder.isNullOrEmpty()) {
817
- inputSearchProps.customPlaceHolder?.invoke() ?: Text(
818
- text = placeHolder ?: "",
819
- style = inputSearchProps.customTextStyle
820
- ?: Typography.bodyDefaultRegular,
821
- maxLines = 1,
822
- color = theme.colors.text.hint,
823
- overflow = TextOverflow.Ellipsis
824
- )
825
- }
826
- innerTextField()
827
- }
828
-
829
- if (isShowClear) {
830
- inputSearchProps.customIconClear?.invoke() ?: Icon(
831
- source = "24_navigation_close_circle_full",
832
- size = 16.dp,
833
- color = theme.colors.text.hint,
834
- modifier = Modifier.padding(start = Spacing.XS)
835
- .noFeedbackClickable(onClick = inputSearchProps.onClear),
836
- )
837
- }
838
-
839
- inputSearchProps.iconRightTextField?.invoke(iconRightModifier)
840
- }
841
- }
842
- )
843
-
844
- if (isShowBtnText) {
845
- Text(
846
- text = inputSearchProps.btnText ?: "",
847
- style = Typography.actionDefaultBold,
848
- color = theme.colors.text.default,
849
- modifier = Modifier.padding(start = Spacing.L)
850
- .noFeedbackClickable(onClick = inputSearchProps.onPressBtnText)
851
- )
852
- }
853
- }
854
- }
855
-
856
- @Composable
857
- private fun footerOffset(
858
- useAvoidKeyboard: Boolean = true,
859
- ): State<IntOffset> {
860
- if (!useAvoidKeyboard) return remember { mutableStateOf(IntOffset.Zero) }
861
- val density = LocalDensity.current
862
- val navPaddingValue = WindowInsets.navigationBars.asPaddingValues()
863
- val keyboardSize = keyboardSizeState()
864
-
865
- return produceState(IntOffset.Zero, density, navPaddingValue) {
866
- val navSystemOffset = with(density) { navPaddingValue.calculateBottomPadding().roundToPx() }
867
- snapshotFlow { keyboardSize.value }.collectLatest {
868
- value = if (it == 0.dp) IntOffset.Zero
869
- else IntOffset(
870
- x = 0, y = with(density) {
871
- navSystemOffset - it.roundToPx()
872
- })
873
- }
874
- }
875
- }
876
-
877
- @Composable
878
- fun keyboardSizeState(): State<Dp> {
879
- val bottom = WindowInsets.ime.asPaddingValues()
880
- return rememberUpdatedState(bottom.calculateBottomPadding())
881
- }
882
-
883
- @Composable
884
- private fun Footer(
885
- useAvoidKeyboard: Boolean,
886
- footerContent: (@Composable () -> Unit)?,
887
- ) {
888
- footerContent ?: return
889
- val theme = AppTheme.current
890
- val navPaddingValue = WindowInsets.navigationBars.asPaddingValues()
891
- val offsetMove = footerOffset(useAvoidKeyboard)
892
-
893
- Box(
894
- modifier = Modifier.padding(navPaddingValue).fillMaxWidth().offset {
895
- offsetMove.value
896
- }.shadow(
897
- color = Colors.black_20.copy(alpha = 0.05f),
898
- blurRadius = 24f,
899
- offsetX = 0.dp,
900
- offsetY = (-4).dp
901
- ).background(theme.colors.background.surface).padding(
902
- start = Spacing.M,
903
- top = Spacing.M,
904
- end = Spacing.M,
905
- )
906
- ) {
907
- footerContent()
908
- }
909
- }
910
-
911
- fun Modifier.hideKeyboardOnTap() = composed {
912
- val focusManager = LocalFocusManager.current
913
- val keyboardManager = LocalSoftwareKeyboardController.current
914
-
915
- pointerInput(Unit) {
916
- detectTapGestures {
917
- keyboardManager?.hide()
918
- focusManager.clearFocus()
919
- }
920
- }
921
- }