@momo-kits/native-kits 0.300.4-debug → 0.300.4

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