@momo-kits/native-kits 0.167.1-duo.18-debug → 0.167.1-duo.19

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 (163) hide show
  1. package/CLAUDE.md +5 -3
  2. package/ios/Application/MaxApi.swift +12 -3
  3. package/ios/Colors+Radius+Spacing/Radius.swift +6 -0
  4. package/ios/Layout/GridContext.swift +67 -3
  5. package/ios/Navigation/BottomSheet.swift +30 -4
  6. package/ios/Navigation/BottomTab/BottomTab.swift +0 -2
  7. package/ios/Navigation/Component/SnackBar.swift +0 -1
  8. package/ios/Navigation/HostBackGestureBridge.swift +27 -6
  9. package/ios/Navigation/ModalScreen.swift +0 -1
  10. package/ios/Navigation/Navigation.swift +14 -0
  11. package/ios/Navigation/NavigationContainer.swift +26 -56
  12. package/ios/Navigation/Navigator.swift +102 -165
  13. package/ios/Navigation/StackScreen.swift +19 -2
  14. package/ios/Navigation/StackTransition.swift +400 -0
  15. package/ios/Navigation/SwipeBackHaptic.swift +33 -0
  16. package/package.json +1 -1
  17. package/build.gradle.kts +0 -11
  18. package/compose/build.gradle.kts +0 -204
  19. package/compose/build.gradle.kts.backup +0 -204
  20. package/compose/compose.podspec +0 -47
  21. package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +0 -19
  22. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +0 -6
  23. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +0 -12
  24. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +0 -7
  25. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -153
  26. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +0 -6
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -52
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -124
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -183
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -207
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -39
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -293
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -916
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +0 -269
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -110
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -378
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +0 -56
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +0 -157
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -81
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -32
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -338
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +0 -175
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -357
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -115
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -89
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -128
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +0 -209
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -542
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -49
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -86
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -125
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -231
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -189
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -478
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -114
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -265
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -235
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -223
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -265
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -185
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -343
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +0 -108
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -56
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -41
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -84
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -40
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -361
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -108
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -308
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -59
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +0 -87
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +0 -21
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -90
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +0 -330
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +0 -220
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +0 -473
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +0 -122
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +0 -207
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -92
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -560
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -86
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -125
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -207
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +0 -501
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -176
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +0 -182
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -193
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -236
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -180
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -325
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +0 -20
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +0 -18
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -16
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -194
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -283
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -54
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -50
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +0 -309
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -41
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -52
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -66
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +0 -104
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -48
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -338
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +0 -58
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -137
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -106
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -205
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -393
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +0 -8
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -713
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +0 -148
  123. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +0 -35
  124. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -218
  125. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -231
  126. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
  127. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +0 -98
  128. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +0 -312
  129. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -169
  130. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -447
  131. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -103
  132. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +0 -106
  133. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +0 -74
  134. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -379
  135. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -39
  136. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -363
  137. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +0 -192
  138. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -133
  139. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +0 -127
  140. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +0 -12
  141. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -91
  142. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +0 -9
  143. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -74
  144. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +0 -13
  145. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
  146. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -55
  147. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
  148. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -118
  149. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +0 -78
  150. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +0 -45
  151. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +0 -32
  152. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +0 -38
  153. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +0 -66
  154. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -217
  155. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +0 -94
  156. package/gradle/libs.versions.toml +0 -69
  157. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  158. package/gradle/wrapper/gradle-wrapper.properties +0 -8
  159. package/gradle.properties +0 -26
  160. package/gradlew +0 -252
  161. package/gradlew.bat +0 -94
  162. package/ios-demo/README.md +0 -120
  163. package/settings.gradle.kts +0 -64
@@ -1,542 +0,0 @@
1
- package vn.momo.kits.components
2
-
3
- import androidx.compose.animation.core.AnimationState
4
- import androidx.compose.animation.core.VectorConverter
5
- import androidx.compose.animation.core.animateTo
6
- import androidx.compose.animation.core.spring
7
- import androidx.compose.foundation.OverscrollEffect
8
- import androidx.compose.material.ExperimentalMaterialApi
9
- import androidx.compose.runtime.Stable
10
- import androidx.compose.runtime.State
11
- import androidx.compose.runtime.getValue
12
- import androidx.compose.runtime.mutableStateOf
13
- import androidx.compose.runtime.setValue
14
- import androidx.compose.ui.Modifier
15
- import androidx.compose.ui.geometry.Offset
16
- import androidx.compose.ui.geometry.Rect
17
- import androidx.compose.ui.geometry.Size
18
- import androidx.compose.ui.geometry.toRect
19
- import androidx.compose.ui.graphics.drawscope.ContentDrawScope
20
- import androidx.compose.ui.graphics.drawscope.clipRect
21
- import androidx.compose.ui.input.nestedscroll.NestedScrollSource
22
- import androidx.compose.ui.input.pointer.PointerEvent
23
- import androidx.compose.ui.input.pointer.PointerEventPass
24
- import androidx.compose.ui.input.pointer.PointerEventType
25
- import androidx.compose.ui.layout.Measurable
26
- import androidx.compose.ui.layout.MeasureResult
27
- import androidx.compose.ui.layout.MeasureScope
28
- import androidx.compose.ui.node.DelegatableNode
29
- import androidx.compose.ui.node.DrawModifierNode
30
- import androidx.compose.ui.node.LayoutAwareModifierNode
31
- import androidx.compose.ui.node.LayoutModifierNode
32
- import androidx.compose.ui.node.PointerInputModifierNode
33
- import androidx.compose.ui.unit.Constraints
34
- import androidx.compose.ui.unit.Density
35
- import androidx.compose.ui.unit.Dp
36
- import androidx.compose.ui.unit.IntOffset
37
- import androidx.compose.ui.unit.IntSize
38
- import androidx.compose.ui.unit.Velocity
39
- import androidx.compose.ui.unit.dp
40
- import androidx.compose.ui.unit.round
41
- import androidx.compose.ui.unit.toOffset
42
- import androidx.compose.ui.unit.toSize
43
- import kotlinx.coroutines.CoroutineScope
44
- import kotlinx.coroutines.cancel
45
- import kotlinx.coroutines.currentCoroutineContext
46
- import kotlinx.coroutines.isActive
47
- import kotlin.math.abs
48
- import kotlin.math.sign
49
-
50
- private enum class CupertinoScrollSource {
51
- DRAG, FLING
52
- }
53
-
54
- private enum class CupertinoOverscrollDirection {
55
- UNKNOWN, VERTICAL, HORIZONTAL
56
- }
57
-
58
- private enum class CupertinoSpringAnimationReason {
59
- FLING_FROM_OVERSCROLL, POSSIBLE_SPRING_IN_THE_END
60
- }
61
-
62
-
63
- private data class CupertinoOverscrollAvailableDelta(
64
-
65
- val availableDelta: Float,
66
-
67
-
68
- val newOverscrollValue: Float
69
- )
70
-
71
- data class PullToRefreshCustomState(
72
- val refreshingState: State<Boolean>,
73
- val onRefresh: () -> Unit,
74
- val threadHold: Dp = 140.dp,
75
- val refreshOffset: Dp = 100.dp,
76
- val sendHaptic: (() -> Unit)? = null,
77
- ) {
78
- val position = mutableStateOf(0f)
79
-
80
- fun updatePercentage(percentage: Float) {
81
- position.value = percentage.coerceIn(0f, 1f)
82
- }
83
- }
84
-
85
- /**
86
- * CupertinoOverscrollEffect
87
- *
88
- * @param density to be taken into consideration during computations;
89
- * Cupertino formulas use DPs, and scroll machinery uses raw values.
90
- *
91
- * @param applyClip Some consumers of overscroll effect apply clip by themselves and some don't,
92
- * thus this flag is needed to update our modifier chain and make the clipping correct in every case while avoiding redundancy
93
- */
94
- internal class CupertinoOverscrollEffect(
95
- val pullRefreshState: PullToRefreshCustomState? = null,
96
- private val density: Float,
97
- val applyClip: Boolean
98
- ) : OverscrollEffect {
99
- /*
100
- * Direction of scrolling for this overscroll effect, derived from arguments during
101
- * [applyToScroll] calls. Technically this effect supports both dimensions, but current API requires
102
- * that different stages of animations spawned by this effect for both dimensions
103
- * end at the same time, which is not the case:
104
- * Spring->Fling->Spring, Fling->Spring, Spring->Fling effects can have different timing per dimension
105
- * (see Notes of https://github.com/JetBrains/compose-multiplatform-core/pull/609),
106
- * which is not possible to express without changing API. Hence, this effect will be fixed to latest
107
- * received delta.
108
- */
109
- private var direction: CupertinoOverscrollDirection = CupertinoOverscrollDirection.UNKNOWN
110
-
111
- /*
112
- * Size of container is taking into consideration when computing rubber banding
113
- */
114
- private var scrollSize: Size = Size.Zero
115
-
116
- /*
117
- * Current offset in overscroll area
118
- * Negative for bottom-right
119
- * Positive for top-left
120
- * Zero if within the scrollable range
121
- * It will be mapped to the actual visible offset using the rubber banding rule inside
122
- * [Modifier.offset] within [effectModifier]
123
- */
124
- private var overscrollOffsetState = mutableStateOf(Offset.Zero)
125
- private var overscrollOffset: Offset
126
- get() = overscrollOffsetState.value
127
- set(value) {
128
- overscrollOffsetState.value = value
129
- drawCallScheduledByOffsetChange = true
130
- }
131
- private var drawCallScheduledByOffsetChange = true
132
-
133
- private var lastFlingUnconsumedDelta: Offset = Offset.Zero
134
- private val visibleOverscrollOffset: IntOffset
135
- get() = overscrollOffsetState.value.rubberBanded().round()
136
-
137
- override val isInProgress: Boolean
138
- get() =
139
- // If visible overscroll offset has at least one pixel
140
- // this effect is considered to be in progress
141
- visibleOverscrollOffset.toOffset().getDistance() > 0.5f
142
-
143
- @OptIn(ExperimentalMaterialApi::class)
144
- private val overscrollNode = CupertinoOverscrollNode(
145
- pullRefreshState = pullRefreshState,
146
- offset = { visibleOverscrollOffset },
147
- onNodeRemeasured = { scrollSize = it.toSize() },
148
- onDraw = ::onDraw,
149
- applyClip = applyClip
150
- )
151
- override val node: DelegatableNode get() = overscrollNode
152
-
153
- private fun onDraw() {
154
- // Fix an issue where scrolling was canceled but the overscroll effect was not completed.
155
- // Reset the overscroll effect when no ongoing animation or interaction is applied.
156
- if (!drawCallScheduledByOffsetChange && isInProgress && overscrollNode.pointersDown == 0) {
157
- overscrollOffsetState.value = Offset.Zero
158
- }
159
-
160
- drawCallScheduledByOffsetChange = false
161
- }
162
-
163
- private fun NestedScrollSource.toCupertinoScrollSource(): CupertinoScrollSource? =
164
- when (this) {
165
- NestedScrollSource.UserInput -> CupertinoScrollSource.DRAG
166
- NestedScrollSource.SideEffect -> CupertinoScrollSource.FLING
167
- else -> null
168
- }
169
-
170
- /*
171
- * Takes input scroll delta, current overscroll value, and scroll source, return [CupertinoOverscrollAvailableDelta]
172
- */
173
- @Stable
174
- private fun availableDelta(
175
- delta: Float,
176
- overscroll: Float,
177
- source: CupertinoScrollSource
178
- ): CupertinoOverscrollAvailableDelta {
179
- // if source is fling:
180
- // 1. no delta will be consumed
181
- // 2. overscroll will stay the same
182
- if (source == CupertinoScrollSource.FLING) {
183
- return CupertinoOverscrollAvailableDelta(delta, overscroll)
184
- }
185
-
186
- val newOverscroll = overscroll + delta
187
-
188
- return if (delta >= 0f && overscroll <= 0f) {
189
- if (newOverscroll > 0f) {
190
- CupertinoOverscrollAvailableDelta(newOverscroll, 0f)
191
- } else {
192
- CupertinoOverscrollAvailableDelta(0f, newOverscroll)
193
- }
194
- } else if (delta <= 0f && overscroll >= 0f) {
195
- if (newOverscroll < 0f) {
196
- CupertinoOverscrollAvailableDelta(newOverscroll, 0f)
197
- } else {
198
- CupertinoOverscrollAvailableDelta(0f, newOverscroll)
199
- }
200
- } else {
201
- CupertinoOverscrollAvailableDelta(0f, newOverscroll)
202
- }
203
- }
204
-
205
- /*
206
- * Returns the amount of scroll delta available after user performed scroll inside overscroll area
207
- * It will update [overscroll] resulting in visual change because of [Modifier.offset] depending on it
208
- */
209
- private fun availableDelta(delta: Offset, source: CupertinoScrollSource): Offset {
210
- val (x, overscrollX) = availableDelta(delta.x, overscrollOffset.x, source)
211
- val (y, overscrollY) = availableDelta(delta.y, overscrollOffset.y, source)
212
- overscrollOffset = Offset(overscrollX, overscrollY)
213
-
214
- return Offset(x, y)
215
- }
216
-
217
- /*
218
- * Semantics of this method match the [OverscrollEffect.applyToScroll] one,
219
- * The only difference is NestedScrollSource being remapped to CupertinoScrollSource to narrow
220
- * processed states invariant
221
- */
222
- private fun applyToScroll(
223
- delta: Offset,
224
- source: CupertinoScrollSource,
225
- performScroll: (Offset) -> Offset
226
- ): Offset {
227
- // Calculate how much delta is available after being consumed by scrolling inside overscroll area
228
- val deltaLeftForPerformScroll = availableDelta(delta, source)
229
-
230
- // Then pass remaining delta to scroll closure
231
- val deltaConsumedByPerformScroll = performScroll(deltaLeftForPerformScroll)
232
-
233
- // Delta which is left after `performScroll` was invoked with availableDelta
234
- val unconsumedDelta = deltaLeftForPerformScroll - deltaConsumedByPerformScroll
235
-
236
- return when (source) {
237
- CupertinoScrollSource.DRAG -> {
238
- // [unconsumedDelta] is going into overscroll again in case a user drags and hits the
239
- // overscroll->content->overscroll or content->overscroll scenario within single frame
240
- overscrollOffset += unconsumedDelta
241
- lastFlingUnconsumedDelta = Offset.Zero
242
- delta - unconsumedDelta
243
- }
244
-
245
- CupertinoScrollSource.FLING -> {
246
- // If unconsumedDelta is not Zero, [CupertinoOverscrollEffect] will cancel fling and
247
- // start spring animation instead
248
- lastFlingUnconsumedDelta = unconsumedDelta
249
- delta - unconsumedDelta
250
- }
251
- }
252
- }
253
-
254
- override fun applyToScroll(
255
- delta: Offset,
256
- source: NestedScrollSource,
257
- performScroll: (Offset) -> Offset
258
- ): Offset {
259
- springAnimationScope?.cancel()
260
- springAnimationScope = null
261
-
262
- direction = direction.combinedWith(delta.toCupertinoOverscrollDirection())
263
-
264
- return source.toCupertinoScrollSource()?.let {
265
- applyToScroll(delta, it, performScroll)
266
- } ?: performScroll(delta)
267
- }
268
-
269
- override suspend fun applyToFling(
270
- velocity: Velocity,
271
- performFling: suspend (Velocity) -> Velocity
272
- ) {
273
- val availableFlingVelocity = playInitialSpringAnimationIfNeeded(velocity)
274
- val velocityConsumedByFling = performFling(availableFlingVelocity)
275
- val postFlingVelocity = availableFlingVelocity - velocityConsumedByFling
276
-
277
- val unconsumedDelta = lastFlingUnconsumedDelta.toFloat()
278
- if (unconsumedDelta == 0f && overscrollOffset == Offset.Zero) {
279
- return
280
- }
281
- applyPullToRefresh()
282
-
283
- playSpringAnimation(
284
- unconsumedDelta,
285
- postFlingVelocity.toFloat(),
286
- CupertinoSpringAnimationReason.POSSIBLE_SPRING_IN_THE_END
287
- )
288
- }
289
-
290
- private fun applyPullToRefresh() {
291
- val (isRefresh, refresh, threshHold, _, _) = pullRefreshState ?: return
292
- val overscrollY = overscrollOffset.y
293
- if (isRefresh.value || overscrollY < threshHold.value * density) return
294
- refresh.invoke()
295
- }
296
-
297
- private fun Offset.toCupertinoOverscrollDirection(): CupertinoOverscrollDirection {
298
- val hasXPart = abs(x) > 0f
299
- val hasYPart = abs(y) > 0f
300
-
301
- return if (hasXPart xor hasYPart) {
302
- if (hasXPart) {
303
- CupertinoOverscrollDirection.HORIZONTAL
304
- } else {
305
- // hasYPart != hasXPart and hasXPart is false
306
- CupertinoOverscrollDirection.VERTICAL
307
- }
308
- } else {
309
- // hasXPart and hasYPart are equal
310
- CupertinoOverscrollDirection.UNKNOWN
311
- }
312
- }
313
-
314
- private fun CupertinoOverscrollDirection.combinedWith(other: CupertinoOverscrollDirection): CupertinoOverscrollDirection =
315
- when (this) {
316
- CupertinoOverscrollDirection.UNKNOWN -> when (other) {
317
- CupertinoOverscrollDirection.UNKNOWN -> CupertinoOverscrollDirection.UNKNOWN
318
- CupertinoOverscrollDirection.VERTICAL -> CupertinoOverscrollDirection.VERTICAL
319
- CupertinoOverscrollDirection.HORIZONTAL -> CupertinoOverscrollDirection.HORIZONTAL
320
- }
321
-
322
- CupertinoOverscrollDirection.VERTICAL -> when (other) {
323
- CupertinoOverscrollDirection.UNKNOWN, CupertinoOverscrollDirection.VERTICAL -> CupertinoOverscrollDirection.VERTICAL
324
- CupertinoOverscrollDirection.HORIZONTAL -> CupertinoOverscrollDirection.HORIZONTAL
325
- }
326
-
327
- CupertinoOverscrollDirection.HORIZONTAL -> when (other) {
328
- CupertinoOverscrollDirection.UNKNOWN, CupertinoOverscrollDirection.HORIZONTAL -> CupertinoOverscrollDirection.HORIZONTAL
329
- CupertinoOverscrollDirection.VERTICAL -> CupertinoOverscrollDirection.VERTICAL
330
- }
331
- }
332
-
333
- private fun Velocity.toFloat(): Float =
334
- toOffset().toFloat()
335
-
336
- private fun Float.toVelocity(): Velocity =
337
- toOffset().toVelocity()
338
-
339
- private fun Offset.toFloat(): Float =
340
- when (direction) {
341
- CupertinoOverscrollDirection.UNKNOWN -> 0f
342
- CupertinoOverscrollDirection.VERTICAL -> y
343
- CupertinoOverscrollDirection.HORIZONTAL -> x
344
- }
345
-
346
- private fun Float.toOffset(): Offset =
347
- when (direction) {
348
- CupertinoOverscrollDirection.UNKNOWN -> Offset.Zero
349
- CupertinoOverscrollDirection.VERTICAL -> Offset(0f, this)
350
- CupertinoOverscrollDirection.HORIZONTAL -> Offset(this, 0f)
351
- }
352
-
353
- private suspend fun playInitialSpringAnimationIfNeeded(initialVelocity: Velocity): Velocity {
354
- val velocity = initialVelocity.toFloat()
355
- val overscroll = overscrollOffset.toFloat()
356
-
357
- return if ((velocity <= 0f && overscroll > 0f) || (velocity >= 0f && overscroll < 0f)) {
358
- playSpringAnimation(
359
- unconsumedDelta = 0f,
360
- velocity,
361
- CupertinoSpringAnimationReason.FLING_FROM_OVERSCROLL
362
- ).toVelocity()
363
- } else {
364
- initialVelocity
365
- }
366
- }
367
-
368
- private var springAnimationScope: CoroutineScope? = null
369
-
370
- private suspend fun playSpringAnimation(
371
- unconsumedDelta: Float,
372
- initialVelocity: Float,
373
- reason: CupertinoSpringAnimationReason
374
- ): Float {
375
- val initialValue = overscrollOffset.toFloat() + unconsumedDelta
376
- val initialSign = sign(initialValue)
377
- var currentVelocity = initialVelocity
378
-
379
- // All input values are divided by density so all internal calculations are performed as if
380
- // they operated on DPs. Callback value is then scaled back to raw pixels.
381
- val visibilityThreshold = 0.5f / density
382
-
383
- val spec = when (reason) {
384
- CupertinoSpringAnimationReason.FLING_FROM_OVERSCROLL -> {
385
- spring(
386
- stiffness = 300f,
387
- visibilityThreshold = visibilityThreshold
388
- )
389
- }
390
-
391
- CupertinoSpringAnimationReason.POSSIBLE_SPRING_IN_THE_END -> {
392
- spring(
393
- stiffness = 120f,
394
- visibilityThreshold = visibilityThreshold
395
- )
396
- }
397
- }
398
-
399
- springAnimationScope?.cancel()
400
- springAnimationScope = CoroutineScope(currentCoroutineContext())
401
- springAnimationScope?.run {
402
- AnimationState(
403
- Float.VectorConverter,
404
- initialValue / density,
405
- initialVelocity / density
406
- ).animateTo(
407
- targetValue = 0f,
408
- animationSpec = spec
409
- ) {
410
- overscrollOffset = pullRefreshState?.let { (refreshing, _, _, refreshOffset) ->
411
- if (!refreshing.value) return@let null
412
- value.coerceAtLeast(refreshOffset.value * density).toOffset()
413
- } ?: (value * density).toOffset()
414
- currentVelocity = velocity * density
415
-
416
- // If it was flung from overscroll, cancel animation and return velocity
417
- if (reason == CupertinoSpringAnimationReason.FLING_FROM_OVERSCROLL &&
418
- initialSign != 0f &&
419
- sign(value) != initialSign
420
- ) {
421
- this.cancelAnimation()
422
- }
423
- }
424
- springAnimationScope = null
425
- }
426
-
427
- if (currentCoroutineContext().isActive) {
428
- // The spring is critically damped, so in case spring-fling-spring sequence is slightly
429
- // offset and velocity is of the opposite sign, it will end up with no animation
430
- overscrollOffset = Offset.Zero
431
- }
432
-
433
- if (reason == CupertinoSpringAnimationReason.POSSIBLE_SPRING_IN_THE_END) {
434
- currentVelocity = 0f
435
- }
436
-
437
- return currentVelocity
438
- }
439
-
440
- private fun Offset.rubberBanded(): Offset {
441
- if (scrollSize.width == 0f || scrollSize.height == 0f) {
442
- return Offset.Zero
443
- }
444
-
445
- val dpOffset = this / density
446
- val dpSize = scrollSize / density
447
-
448
- return Offset(
449
- rubberBandedValue(dpOffset.x, dpSize.width),
450
- rubberBandedValue(dpOffset.y, dpSize.height)
451
- ) * density
452
- }
453
-
454
- /*
455
- * Maps raw delta offset [value] on an axis within scroll container with [dimension]
456
- * to actual visible offset
457
- */
458
- private fun rubberBandedValue(value: Float, dimension: Float) =
459
- sign(value) * (1f - (1f / (abs(value) * RUBBER_BAND_COEFFICIENT / dimension + 1f))) * dimension
460
-
461
- companion object Companion {
462
- private const val RUBBER_BAND_COEFFICIENT = 0.55f
463
- }
464
- }
465
-
466
- private class CupertinoOverscrollNode @OptIn(ExperimentalMaterialApi::class) constructor(
467
- val pullRefreshState: PullToRefreshCustomState?,
468
- val offset: Density.() -> IntOffset,
469
- val onNodeRemeasured: (IntSize) -> Unit,
470
- val onDraw: () -> Unit,
471
- val applyClip: Boolean
472
- ) : Modifier.Node(),
473
- LayoutModifierNode,
474
- LayoutAwareModifierNode,
475
- DrawModifierNode,
476
- PointerInputModifierNode {
477
- override fun onRemeasured(size: IntSize) = onNodeRemeasured(size)
478
-
479
- var pointersDown by mutableStateOf(0)
480
-
481
- override fun onPointerEvent(
482
- pointerEvent: PointerEvent,
483
- pass: PointerEventPass,
484
- bounds: IntSize
485
- ) {
486
- if (pass == PointerEventPass.Final) {
487
- if (pointerEvent.type == PointerEventType.Press) {
488
- pointersDown++
489
- } else if (pointerEvent.type == PointerEventType.Release) {
490
- pointersDown--
491
- // assert(pointersDown >= 0) { "pointersDown cannot be negative" }
492
- }
493
- }
494
- }
495
-
496
- override fun onCancelPointerInput() {
497
- pointersDown = 0
498
- }
499
-
500
- override fun ContentDrawScope.draw() {
501
- onDraw()
502
- if (applyClip) {
503
- val bounds = Rect(-offset().toOffset(), size)
504
- val rect = size.toRect().intersect(bounds)
505
- clipRect(
506
- left = rect.left,
507
- top = rect.top,
508
- right = rect.right,
509
- bottom = rect.bottom,
510
- ) { this@draw.drawContent() }
511
- } else {
512
- this@draw.drawContent()
513
- }
514
- }
515
-
516
- override fun MeasureScope.measure(
517
- measurable: Measurable,
518
- constraints: Constraints
519
- ): MeasureResult {
520
- val placeable = measurable.measure(constraints)
521
- return layout(placeable.width, placeable.height) {
522
- var position = offset()
523
-
524
- with(pullRefreshState) {
525
- this ?: return@with
526
- if (refreshingState.value) {
527
- position = position.copy(
528
- y = position.y.coerceAtLeast(refreshOffset.roundToPx())
529
- )
530
- }
531
- updatePercentage(position.y / refreshOffset.toPx())
532
- }
533
- placeable.placeWithLayer(position)
534
- }
535
- }
536
- }
537
-
538
- private fun Velocity.toOffset(): Offset =
539
- Offset(x, y)
540
-
541
- private fun Offset.toVelocity(): Velocity =
542
- Velocity(x, y)
@@ -1,49 +0,0 @@
1
- package vn.momo.kits.components
2
-
3
- import androidx.compose.foundation.Canvas
4
- import androidx.compose.foundation.background
5
- import androidx.compose.foundation.layout.Box
6
- import androidx.compose.foundation.layout.fillMaxWidth
7
- import androidx.compose.foundation.layout.height
8
- import androidx.compose.foundation.layout.padding
9
- import androidx.compose.runtime.Composable
10
- import androidx.compose.ui.Modifier
11
- import androidx.compose.ui.geometry.Offset
12
- import androidx.compose.ui.graphics.PathEffect
13
- import androidx.compose.ui.graphics.StrokeCap
14
- import androidx.compose.ui.unit.dp
15
- import vn.momo.kits.const.AppTheme
16
- import vn.momo.kits.const.Spacing
17
-
18
- enum class DividerType { DEFAULT, DASH }
19
-
20
- @Composable
21
- fun Divider(useMargin: Boolean = false, type: DividerType = DividerType.DEFAULT) {
22
- val color = AppTheme.current.colors.border.default
23
- val margin = if (useMargin) Spacing.XS else 0.dp
24
- if (type == DividerType.DASH) {
25
- Canvas(
26
- modifier = Modifier
27
- .fillMaxWidth()
28
- .padding(vertical = margin)
29
- .height(1.dp),
30
- ) {
31
- drawLine(
32
- color = color,
33
- start = Offset(0f, size.height / 2f),
34
- end = Offset(size.width, size.height / 2f),
35
- strokeWidth = size.height,
36
- cap = StrokeCap.Round,
37
- pathEffect = PathEffect.dashPathEffect(floatArrayOf(4.dp.toPx(), 4.dp.toPx())),
38
- )
39
- }
40
- return
41
- }
42
- Box(
43
- modifier = Modifier
44
- .fillMaxWidth()
45
- .padding(vertical = margin)
46
- .height(1.dp)
47
- .background(color = color),
48
- )
49
- }
@@ -1,86 +0,0 @@
1
- package vn.momo.kits.components
2
-
3
- import androidx.compose.foundation.border
4
- import androidx.compose.foundation.layout.Box
5
- import androidx.compose.foundation.layout.size
6
- import androidx.compose.runtime.Composable
7
- import androidx.compose.runtime.remember
8
- import androidx.compose.ui.Modifier
9
- import androidx.compose.ui.graphics.Color
10
- import androidx.compose.ui.graphics.ColorFilter
11
- import androidx.compose.ui.layout.ContentScale
12
- import androidx.compose.ui.semantics.contentDescription
13
- import androidx.compose.ui.semantics.semantics
14
- import androidx.compose.ui.unit.Dp
15
- import androidx.compose.ui.unit.dp
16
- import coil3.compose.AsyncImage
17
- import coil3.compose.LocalPlatformContext
18
- import coil3.request.ImageRequest
19
- import vn.momo.kits.application.IsShowBaseLineDebug
20
- import vn.momo.kits.const.AppTheme
21
- import vn.momo.kits.const.Colors
22
- import vn.momo.kits.gifimage.GifImage
23
- import vn.momo.kits.modifier.conditional
24
- import vn.momo.kits.utils.Icons
25
- import vn.momo.kits.utils.noThemeIcons
26
-
27
- @Composable
28
- fun Icon(
29
- source: String,
30
- size: Dp = 24.dp,
31
- color: Color? = AppTheme.current.colors.text.default,
32
- modifier: Modifier = Modifier,
33
- ) {
34
- // decode image without downscaling it
35
- val context = LocalPlatformContext.current
36
-
37
- val iconUrl = remember(source) {
38
- if (source.contains("https")) {
39
- source
40
- } else {
41
- Icons[source] ?: ""
42
- }
43
- }
44
-
45
- val iconColor = remember(source, color) {
46
- if (noThemeIcons.contains(source)) null else color
47
- }
48
-
49
- val colorFilter = remember(iconColor) {
50
- iconColor?.let { ColorFilter.tint(it) }
51
- }
52
-
53
- val contentDesc = remember(iconUrl) { "img|$iconUrl" }
54
-
55
- Box(
56
- modifier = modifier
57
- .size(size)
58
- .conditional(IsShowBaseLineDebug) {
59
- border(1.dp, Colors.blue_03)
60
- }
61
- .semantics { contentDescription = contentDesc }
62
- ) {
63
- if (isGifUrl(iconUrl)) {
64
- GifImage(
65
- modifier = Modifier.matchParentSize(),
66
- url = iconUrl,
67
- contentDescription = null,
68
- contentScale = ContentScale.Fit,
69
- )
70
- } else {
71
- AsyncImage(
72
- modifier = Modifier.matchParentSize(),
73
- model = ImageRequest.Builder(context)
74
- .data(iconUrl)
75
- .size(
76
- coil3.size.Dimension.Undefined,
77
- coil3.size.Dimension.Undefined
78
- )
79
- .build(),
80
- contentDescription = null,
81
- contentScale = ContentScale.Fit,
82
- colorFilter = colorFilter,
83
- )
84
- }
85
- }
86
- }