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

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 (135) hide show
  1. package/package.json +1 -1
  2. package/build.gradle.kts +0 -11
  3. package/compose/build.gradle.kts +0 -198
  4. package/compose/build.gradle.kts.backup +0 -198
  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 -56
  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 -314
  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 -205
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -399
  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 -35
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -228
  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 -399
  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/SideBar.kt +0 -192
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -133
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +0 -127
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +0 -12
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -91
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +0 -9
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -74
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +0 -13
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -55
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -118
  121. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +0 -42
  122. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +0 -45
  123. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +0 -32
  124. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +0 -38
  125. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +0 -66
  126. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -217
  127. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +0 -94
  128. package/gradle/libs.versions.toml +0 -69
  129. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  130. package/gradle/wrapper/gradle-wrapper.properties +0 -8
  131. package/gradle.properties +0 -26
  132. package/gradlew +0 -252
  133. package/gradlew.bat +0 -94
  134. package/ios-demo/README.md +0 -120
  135. package/settings.gradle.kts +0 -64
@@ -1,314 +0,0 @@
1
- package vn.momo.kits.layout
2
-
3
- import androidx.compose.foundation.layout.WindowInsets
4
- import androidx.compose.foundation.layout.systemBars
5
- import androidx.compose.runtime.Composable
6
- import androidx.compose.runtime.Immutable
7
- import androidx.compose.runtime.compositionLocalOf
8
- import androidx.compose.runtime.remember
9
- import androidx.compose.ui.platform.LocalDensity
10
- import androidx.compose.ui.platform.LocalLayoutDirection
11
- import androidx.compose.ui.platform.LocalWindowInfo
12
- import androidx.compose.ui.unit.Dp
13
- import androidx.compose.ui.unit.dp
14
- import vn.momo.kits.const.Spacing
15
- import vn.momo.kits.navigation.component.HEADER_HEIGHT
16
- import vn.momo.kits.platform.ReservedRegion
17
- import vn.momo.kits.platform.getScreenDimensions
18
- import vn.momo.kits.platform.isPhoneIdiom
19
- import vn.momo.kits.platform.rememberReservedRegions
20
- import vn.momo.kits.utils.getAppStatusBarHeight
21
- import vn.momo.kits.utils.horizontalSafeAreaInsets
22
-
23
- enum class SideBarEdge { LEFT, RIGHT }
24
-
25
- @Immutable
26
- data class SideBarLayout(
27
- val edge: SideBarEdge,
28
- val width: Dp,
29
- // Preferred window y of the first control; the column is still kept above footer and bottom bars.
30
- val top: Dp,
31
- // Room kept free above the window bottom, clear of a camera at the bottom end of the strip.
32
- val bottom: Dp,
33
- )
34
-
35
- // Room the header row keeps from each side for the status bar clock and battery clusters.
36
- @Immutable
37
- data class StatusBarClearance(val left: Dp, val right: Dp)
38
-
39
- @Immutable
40
- data class ScreenLayout(
41
- val width: Dp,
42
- val height: Dp,
43
- val isIPhoneDuo: Boolean,
44
- // Raw top / bottom safe area; overlays and the Duo floating tab bar keep clear of them.
45
- val insetTop: Dp,
46
- val insetBottom: Dp,
47
- val insetLeft: Dp,
48
- val insetRight: Dp,
49
- val contentWidth: Dp,
50
- val headerHeight: Dp,
51
- // 0 when the status bar is off the top edge, so the bar row fills the header and keeps the title centered.
52
- val headerStatusBarHeight: Dp,
53
- // iPhone Duo moves back and toolkit into a column in the side safe area; null while they stay in the bar.
54
- val sideBar: SideBarLayout? = null,
55
- // Port of RN `device.ts` isIPhoneDuo: the screen stays on the outer display, so Split View still matches.
56
- val isIPhoneDuoDevice: Boolean = isIPhoneDuo,
57
- // Portrait iPhone Duo puts the status bar in the side strip, about twice as tall as the strip is wide.
58
- // Stored so it survives consumeHorizontalInsets().
59
- val duoTopInset: Dp = insetTop,
60
- // iPhone Duo inner display in portrait: the header row shares the status bar band, between its clusters.
61
- val statusBarClearance: StatusBarClearance? = null,
62
- ) {
63
- val headerBarHeight: Dp get() = headerHeight - headerStatusBarHeight
64
- }
65
-
66
- // Measured by momo-kits RN (Layout/utils.ts) on the iOS 27.1 iPhone Duo simulator; keep both in sync.
67
- internal object DuoMetrics {
68
- // Inner display is ~1.42, outer ~1.45; every slab iPhone is 1.77 or taller.
69
- const val MAX_ASPECT_RATIO = 1.6f
70
- val controlSize = 48.dp
71
- val itemPitch = 50.dp
72
- // Portrait: below the camera/clock cluster in the strip (ends near y 151).
73
- val firstItemTop = 170.dp
74
- // Inner display in landscape: below its status cluster (ends near y 98).
75
- val firstItemTopLandscape = 120.dp
76
- // Window height from which the inner display is in use (outer is 466pt tall in landscape, inner 669pt).
77
- val innerMinHeight = 600.dp
78
- // Outer display in portrait: the header is 4pt taller than the iPhone floor and its title
79
- // lines up with the camera punchout (y 24–66 at the top of the strip).
80
- val outerHeaderHeight = 76.dp
81
- val outerCameraCenterY = 45.dp
82
- // Outer display in landscape has no status bar in the strip: controls keep this far from
83
- // both ends unless a reserved region (the camera at either end) asks for more.
84
- val edgeInset = 24.dp
85
- // Inner display in portrait: the clock and connectivity cluster sits at the top right corner
86
- // (x 550–643 of 669, clock digits centered on y 47); its reserved region is taller than the glyphs.
87
- val innerStatusBarCenterY = 47.dp
88
- val innerStatusBarWidth = 120.dp
89
- // Host app's iOS 26 tab bar on the inner display in portrait: a 62pt glass pill sized to its tabs,
90
- // QR as a 62pt platter beside it, ≥ 20pt from the sides, 13pt into the home indicator inset.
91
- val floatingTabBarHeight = 62.dp
92
- val floatingTabBarMargin = 20.dp
93
- val floatingTabBarInsetOverlap = 13.dp
94
- }
95
-
96
- // Widest short side of any single-display iPhone.
97
- private val IPHONE_MAX_SHORT_SIDE = 440.dp
98
-
99
- private val IPHONE_HEADER_MIN_HEIGHT = 72.dp
100
-
101
- // A smaller top inset means the status bar is not on the top edge.
102
- private val STATUS_BAR_MIN_INSET = 20.dp
103
-
104
- internal val LocalScreenLayout = compositionLocalOf<ScreenLayout?> { null }
105
-
106
- // Port of RN `useScreenLayout`; follows fold/unfold and rotation through the window size and safe area.
107
- @Composable
108
- fun getScreenLayout(): ScreenLayout =
109
- LocalScreenLayout.current ?: calculateScreenLayout(getAppStatusBarHeight())
110
-
111
- @Composable
112
- internal fun calculateScreenLayout(topInset: Dp, parent: ScreenLayout? = null): ScreenLayout {
113
- val isPhone = isPhoneIdiom()
114
- if (parent != null) {
115
- // the parent's row center reproduces its own centered geometry
116
- val titleCenterY = parent.headerStatusBarHeight + parent.headerBarHeight / 2
117
- val statusBarClearance = parent.statusBarClearance?.takeIf { topInset >= STATUS_BAR_MIN_INSET }
118
- val (headerHeight, headerStatusBarHeight) = if (statusBarClearance != null) {
119
- statusBarRowGeometry()
120
- } else {
121
- headerGeometry(topInset, isPhone, parent.width, parent.height, parent.sideBar != null, titleCenterY)
122
- }
123
- return parent.copy(
124
- headerHeight = headerHeight,
125
- headerStatusBarHeight = headerStatusBarHeight,
126
- statusBarClearance = statusBarClearance,
127
- )
128
- }
129
-
130
- val density = LocalDensity.current
131
- val layoutDirection = LocalLayoutDirection.current
132
- val size = LocalWindowInfo.current.containerSize
133
- val insets = horizontalSafeAreaInsets
134
- val leftPx = insets.getLeft(density, layoutDirection)
135
- val rightPx = insets.getRight(density, layoutDirection)
136
- val bottomPx = WindowInsets.systemBars.getBottom(density)
137
- val screen = getScreenDimensions()
138
- val reservedRegions = rememberReservedRegions()
139
-
140
- return remember(size, leftPx, rightPx, bottomPx, topInset, screen, density, reservedRegions) {
141
- with(density) {
142
- val width = size.width.toDp()
143
- val height = size.height.toDp()
144
- screenLayoutOf(
145
- width = width,
146
- height = height,
147
- insetLeft = leftPx.toDp(),
148
- insetRight = rightPx.toDp(),
149
- topInset = topInset,
150
- bottomInset = bottomPx.toDp(),
151
- isPhone = isPhone,
152
- asymmetricInsets = leftPx != rightPx,
153
- isDuoDevice = isPhone && maxOf(
154
- minOf(screen.width, screen.height).dp,
155
- minOf(width, height),
156
- ) > IPHONE_MAX_SHORT_SIDE,
157
- reservedRegions = reservedRegions,
158
- )
159
- }
160
- }
161
- }
162
-
163
- // Content inside the safe-area padding: the insets are spent and the side bar belongs to the screen.
164
- internal fun ScreenLayout.consumeHorizontalInsets(): ScreenLayout =
165
- copy(width = contentWidth, insetLeft = 0.dp, insetRight = 0.dp, sideBar = null)
166
-
167
- // titleCenterY: window y the title row centers on for the outer display in landscape.
168
- private fun headerGeometry(
169
- topInset: Dp,
170
- isPhone: Boolean,
171
- width: Dp,
172
- height: Dp,
173
- hasSideBar: Boolean,
174
- titleCenterY: Dp,
175
- ): Pair<Dp, Dp> {
176
- val headerHeight = topInset + HEADER_HEIGHT.dp
177
- if (!isPhone) return headerHeight to topInset
178
- if (hasSideBar && topInset < STATUS_BAR_MIN_INSET) {
179
- if (height > width && width < DuoMetrics.innerMinHeight) {
180
- // outer display: the row starts below the top so the title centers on the camera
181
- return DuoMetrics.outerHeaderHeight to DuoMetrics.outerCameraCenterY * 2 - DuoMetrics.outerHeaderHeight
182
- }
183
- if (width > height && height < DuoMetrics.innerMinHeight) {
184
- // reaches 8pt below a side bar control centered on the row
185
- val centered = maxOf(IPHONE_HEADER_MIN_HEIGHT, titleCenterY + DuoMetrics.controlSize / 2 + Spacing.S)
186
- return centered to maxOf(0.dp, titleCenterY * 2 - centered)
187
- }
188
- }
189
- return maxOf(headerHeight, IPHONE_HEADER_MIN_HEIGHT) to if (topInset < STATUS_BAR_MIN_INSET) 0.dp else topInset
190
- }
191
-
192
- // Centered on the clock digits; the cluster's region center sits above them.
193
- private fun statusBarRowGeometry(): Pair<Dp, Dp> = DuoMetrics.innerStatusBarCenterY * 2 to 0.dp
194
-
195
- private data class StripOcclusion(val top: Dp, val topCenter: Dp, val bottom: Dp)
196
-
197
- // How far system regions reach into a side strip from its top and from its bottom end
198
- // (e.g. the camera, which sits at either end depending on the rotation), and the center y
199
- // of the region reaching furthest from the top.
200
- private fun stripOcclusion(regions: List<ReservedRegion>, stripLeft: Dp, stripRight: Dp, windowHeight: Dp): StripOcclusion {
201
- var top = 0.dp
202
- var topCenter = 0.dp
203
- var bottom = 0.dp
204
- regions.forEach { region ->
205
- if (region.x >= stripRight || region.x + region.width <= stripLeft) return@forEach
206
- val centerY = region.y + region.height / 2
207
- if (centerY < windowHeight / 2) {
208
- if (region.y + region.height > top) {
209
- top = region.y + region.height
210
- topCenter = centerY
211
- }
212
- } else {
213
- bottom = maxOf(bottom, windowHeight - region.y)
214
- }
215
- }
216
- return StripOcclusion(top, topCenter, bottom)
217
- }
218
-
219
- // How far the clock and battery clusters of a status bar on the top edge reach in from each side;
220
- // null without reserved regions there.
221
- private fun statusBarClusters(regions: List<ReservedRegion>, windowWidth: Dp, topInset: Dp): StatusBarClearance? {
222
- val midX = windowWidth / 2
223
- val clusters = regions.filter {
224
- it.y < topInset && it.y + it.height <= topInset && (it.x + it.width <= midX || it.x >= midX)
225
- }
226
- if (clusters.isEmpty()) return null
227
- var left = 0.dp
228
- var right = 0.dp
229
- clusters.forEach {
230
- if (it.x < midX) left = maxOf(left, it.x + it.width) else right = maxOf(right, windowWidth - it.x)
231
- }
232
- return StatusBarClearance(left, right)
233
- }
234
-
235
- // Slab iPhones report equal side insets in landscape; iPhone Duo moves the status bar to one side.
236
- private fun isIPhoneDuoWindow(width: Dp, height: Dp, asymmetricInsets: Boolean): Boolean {
237
- val shortSide = minOf(width, height)
238
- if (shortSide <= 0.dp) return asymmetricInsets
239
- return maxOf(width, height) / shortSide < DuoMetrics.MAX_ASPECT_RATIO || asymmetricInsets
240
- }
241
-
242
- private fun screenLayoutOf(
243
- width: Dp,
244
- height: Dp,
245
- insetLeft: Dp,
246
- insetRight: Dp,
247
- topInset: Dp,
248
- bottomInset: Dp,
249
- isPhone: Boolean,
250
- asymmetricInsets: Boolean,
251
- isDuoDevice: Boolean,
252
- reservedRegions: List<ReservedRegion>,
253
- ): ScreenLayout {
254
- val isIPhoneDuo = isPhone && isIPhoneDuoWindow(width, height, asymmetricInsets)
255
- val isInnerPortrait = height > width && width >= DuoMetrics.innerMinHeight
256
- val statusBarClearance = if (isIPhoneDuo && isInnerPortrait && topInset >= STATUS_BAR_MIN_INSET) {
257
- statusBarClusters(reservedRegions, width, topInset)
258
- ?: StatusBarClearance(left = 0.dp, right = DuoMetrics.innerStatusBarWidth + Spacing.S)
259
- } else {
260
- null
261
- }
262
-
263
- var sideBar: SideBarLayout? = null
264
- var titleCenterY = 0.dp
265
- val sideInset = maxOf(insetLeft, insetRight)
266
- // narrower strips cannot host a control 12pt from the inner edge
267
- if (isIPhoneDuo && sideInset >= DuoMetrics.controlSize + Spacing.M) {
268
- val edge = if (insetRight >= insetLeft) SideBarEdge.RIGHT else SideBarEdge.LEFT
269
- val stripLeft = if (edge == SideBarEdge.RIGHT) width - sideInset else 0.dp
270
- // clear of the camera and of the status bar cluster, which grows for calls and Live
271
- // Activities; region frames already include the system's margin
272
- val occlusion = stripOcclusion(reservedRegions, stripLeft, stripLeft + sideInset, height)
273
- // UIKit reports no usable status bar frame here (a 2pt top strip), so the spot is inferred like RN
274
- val top = when {
275
- statusBarClearance != null -> statusBarRowGeometry().first + Spacing.M
276
- topInset >= STATUS_BAR_MIN_INSET -> topInset + HEADER_HEIGHT.dp + Spacing.M
277
- width <= height -> DuoMetrics.firstItemTop
278
- height >= DuoMetrics.innerMinHeight -> DuoMetrics.firstItemTopLandscape
279
- // outer display in landscape: no status bar in the strip
280
- else -> DuoMetrics.edgeInset
281
- }
282
- // outer display in landscape: the title row centers on the camera when it sits at the
283
- // top end, otherwise on the back button
284
- titleCenterY = if (occlusion.top > top) occlusion.topCenter else top + DuoMetrics.controlSize / 2
285
- sideBar = SideBarLayout(
286
- edge = edge,
287
- width = sideInset,
288
- top = maxOf(top, occlusion.top),
289
- bottom = maxOf(DuoMetrics.edgeInset, occlusion.bottom),
290
- )
291
- }
292
- val (headerHeight, headerStatusBarHeight) = if (statusBarClearance != null) {
293
- statusBarRowGeometry()
294
- } else {
295
- headerGeometry(topInset, isPhone, width, height, sideBar != null, titleCenterY)
296
- }
297
-
298
- return ScreenLayout(
299
- width = width,
300
- height = height,
301
- isIPhoneDuo = isIPhoneDuo,
302
- insetTop = topInset,
303
- insetBottom = bottomInset,
304
- insetLeft = insetLeft,
305
- insetRight = insetRight,
306
- contentWidth = width - insetLeft - insetRight,
307
- headerHeight = headerHeight,
308
- headerStatusBarHeight = headerStatusBarHeight,
309
- sideBar = sideBar,
310
- isIPhoneDuoDevice = isDuoDevice,
311
- duoTopInset = maxOf(topInset, if (height > width) sideInset * 2 else 0.dp),
312
- statusBarClearance = statusBarClearance,
313
- )
314
- }
@@ -1,41 +0,0 @@
1
- package vn.momo.kits.layout
2
-
3
- import androidx.compose.foundation.layout.Arrangement
4
- import androidx.compose.foundation.layout.Box
5
- import androidx.compose.foundation.layout.BoxWithConstraints
6
- import androidx.compose.foundation.layout.ExperimentalLayoutApi
7
- import androidx.compose.foundation.layout.FlowRow
8
- import androidx.compose.foundation.layout.fillMaxWidth
9
- import androidx.compose.foundation.layout.padding
10
- import androidx.compose.runtime.Composable
11
- import androidx.compose.runtime.CompositionLocalProvider
12
- import androidx.compose.ui.Modifier
13
- import androidx.compose.ui.unit.dp
14
- import vn.momo.kits.components.Image
15
- import vn.momo.kits.const.Spacing
16
-
17
- @Composable
18
- @OptIn(ExperimentalLayoutApi::class)
19
- fun Section(
20
- useMargin: Boolean = true,
21
- backgroundImage: String? = null,
22
- modifier: Modifier = Modifier,
23
- content: @Composable () -> Unit,
24
- ) {
25
- val columns = gridColumns()
26
- val gap = Spacing.M
27
- val margin = if (useMargin) Spacing.M else 0.dp
28
- Box(modifier = Modifier.fillMaxWidth().padding(horizontal = margin).then(modifier)) {
29
- if (backgroundImage != null) {
30
- Image(source = backgroundImage, modifier = Modifier.matchParentSize())
31
- }
32
- BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
33
- CompositionLocalProvider(LocalGridContext provides gridContext(constraints.maxWidth, columns, gap)) {
34
- FlowRow(
35
- horizontalArrangement = Arrangement.spacedBy(gap),
36
- verticalArrangement = Arrangement.spacedBy(gap),
37
- ) { content() }
38
- }
39
- }
40
- }
41
- }
@@ -1,52 +0,0 @@
1
- package vn.momo.kits.modifier
2
-
3
- import androidx.compose.ui.Modifier
4
- import androidx.compose.ui.node.ModifierNodeElement
5
- import androidx.compose.ui.node.SemanticsModifierNode
6
- import androidx.compose.ui.semantics.SemanticsPropertyReceiver
7
- import androidx.compose.ui.semantics.contentDescription
8
- import androidx.compose.ui.semantics.semantics
9
- import androidx.compose.ui.semantics.testTag
10
- import vn.momo.kits.platform.getPlatformName
11
-
12
- fun Modifier.setAutomationId(accessibilityId: String, mergeDescendants: Boolean = false): Modifier {
13
- return if (getPlatformName() == "Android") {
14
- semantics {
15
- contentDescription = accessibilityId
16
- }
17
- } else {
18
- semantics(mergeDescendants = mergeDescendants) {
19
- testTag = accessibilityId
20
- }
21
- }}
22
-
23
- fun Modifier.kitsAutomationId(accessibilityId: String, label: String? = null) =
24
- this then KitsAutomationIdElement(accessibilityId, label)
25
-
26
- private data class KitsAutomationIdElement(
27
- private val accessibilityId: String,
28
- private val label: String? = null,
29
- ) : ModifierNodeElement<KitsAutomationIdNode>() {
30
- override fun create(): KitsAutomationIdNode {
31
- return KitsAutomationIdNode(accessibilityId, label)
32
- }
33
-
34
- override fun update(node: KitsAutomationIdNode) {
35
- node.accessibilityId = accessibilityId
36
- node.label = label
37
- }
38
- }
39
-
40
- private class KitsAutomationIdNode(
41
- var accessibilityId: String,
42
- var label: String?,
43
- ) : Modifier.Node(), SemanticsModifierNode {
44
- override fun SemanticsPropertyReceiver.applySemantics() {
45
- if (getPlatformName() == "Android") {
46
- contentDescription = accessibilityId
47
- } else {
48
- testTag = accessibilityId
49
- contentDescription = label ?: accessibilityId
50
- }
51
- }
52
- }
@@ -1,66 +0,0 @@
1
- package vn.momo.kits.modifier
2
-
3
- import androidx.compose.animation.core.animateFloatAsState
4
- import androidx.compose.foundation.clickable
5
- import androidx.compose.foundation.gestures.detectTapGestures
6
- import androidx.compose.foundation.interaction.MutableInteractionSource
7
- import androidx.compose.foundation.interaction.collectIsPressedAsState
8
- import androidx.compose.runtime.getValue
9
- import androidx.compose.runtime.remember
10
- import androidx.compose.ui.Modifier
11
- import androidx.compose.ui.composed
12
- import androidx.compose.ui.graphics.graphicsLayer
13
- import androidx.compose.ui.input.pointer.pointerInput
14
- import androidx.compose.ui.platform.LocalFocusManager
15
- import androidx.compose.ui.platform.LocalSoftwareKeyboardController
16
- import androidx.compose.ui.semantics.Role
17
-
18
- fun Modifier.noFeedbackClickable(
19
- enabled: Boolean = true, onClickLabel: String? = null, role: Role? = null, onClick: () -> Unit
20
- ): Modifier = composed {
21
- clickable(
22
- interactionSource = remember { MutableInteractionSource() },
23
- indication = null,
24
- enabled,
25
- onClickLabel,
26
- role,
27
- onClick
28
- )
29
- }
30
-
31
- fun Modifier.activeOpacityClickable(
32
- activeOpacity: Float = 0.2f,
33
- enabled: Boolean = true,
34
- onClickLabel: String? = null,
35
- role: Role? = null,
36
- onClick: () -> Unit
37
- ): Modifier = composed {
38
- val interactionSource = remember { MutableInteractionSource() }
39
- val isPressed by interactionSource.collectIsPressedAsState()
40
- val animatedAlpha by animateFloatAsState(
41
- targetValue = if (isPressed) activeOpacity else 1f,
42
- label = "ActiveOpacity"
43
- )
44
-
45
- this
46
- .graphicsLayer { alpha = animatedAlpha }
47
- .clickable(
48
- interactionSource = interactionSource,
49
- indication = null,
50
- enabled,
51
- onClickLabel,
52
- role,
53
- onClick
54
- )
55
- }
56
- fun Modifier.hideKeyboardOnTap() = composed {
57
- val focusManager = LocalFocusManager.current
58
- val keyboardManager = LocalSoftwareKeyboardController.current
59
-
60
- pointerInput(Unit) {
61
- detectTapGestures {
62
- keyboardManager?.hide()
63
- focusManager.clearFocus()
64
- }
65
- }
66
- }
@@ -1,11 +0,0 @@
1
- package vn.momo.kits.modifier
2
-
3
- import androidx.compose.ui.Modifier
4
-
5
- fun Modifier.conditional(condition: Boolean, modifier: Modifier.() -> Modifier): Modifier {
6
- return if (condition) {
7
- then(modifier(Modifier))
8
- } else {
9
- this
10
- }
11
- }
@@ -1,104 +0,0 @@
1
- package vn.momo.kits.modifier
2
-
3
- import androidx.compose.animation.core.LinearEasing
4
- import androidx.compose.animation.core.RepeatMode
5
- import androidx.compose.animation.core.animateFloat
6
- import androidx.compose.animation.core.infiniteRepeatable
7
- import androidx.compose.animation.core.rememberInfiniteTransition
8
- import androidx.compose.animation.core.tween
9
- import androidx.compose.runtime.Composable
10
- import androidx.compose.runtime.getValue
11
- import androidx.compose.ui.Modifier
12
- import androidx.compose.ui.draw.drawWithContent
13
- import androidx.compose.ui.geometry.Offset
14
- import androidx.compose.ui.graphics.Color
15
- import androidx.compose.ui.graphics.drawscope.clipRect
16
- import androidx.compose.ui.text.TextStyle
17
- import androidx.compose.ui.text.drawText
18
- import androidx.compose.ui.text.font.FontWeight
19
- import androidx.compose.ui.text.rememberTextMeasurer
20
- import androidx.compose.ui.unit.sp
21
- import vn.momo.kits.application.IsShowBaseLineDebug
22
- import vn.momo.kits.const.Colors
23
-
24
- @Suppress("ExperimentalAnnotationRetention")
25
- @RequiresOptIn(
26
- message = "This is internal API of native-kits, become internal in next sprint",
27
- level = RequiresOptIn.Level.WARNING,
28
- )
29
- @Retention(AnnotationRetention.BINARY)
30
- @Target(
31
- AnnotationTarget.CLASS,
32
- AnnotationTarget.FUNCTION,
33
- AnnotationTarget.PROPERTY,
34
- )
35
- annotation class InternalApi
36
-
37
- @Composable
38
- fun DeprecatedModifier(text: String = "Deprecated component"): Modifier = debugMarker(
39
- label = text,
40
- background = Colors.red_04.copy(alpha = 0.7f),
41
- textColor = Colors.red_01,
42
- animationLabel = "deprecatedMarquee",
43
- )
44
-
45
- @Composable
46
- private fun debugMarker(
47
- label: String,
48
- background: Color,
49
- textColor: Color,
50
- animationLabel: String,
51
- ): Modifier {
52
- if (!IsShowBaseLineDebug) return Modifier
53
-
54
- val textMeasurer = rememberTextMeasurer()
55
- val marquee = rememberInfiniteTransition(label = animationLabel)
56
- val progress by marquee.animateFloat(
57
- initialValue = 0f,
58
- targetValue = 1f,
59
- animationSpec = infiniteRepeatable(
60
- animation = tween(durationMillis = 4000, easing = LinearEasing),
61
- repeatMode = RepeatMode.Restart,
62
- ),
63
- label = "${animationLabel}Progress",
64
- )
65
-
66
- return Modifier.drawWithContent {
67
- drawContent()
68
- drawRect(color = background)
69
-
70
- if (size.width <= 0f || size.height <= 0f) return@drawWithContent
71
-
72
- val fontPx = (size.width * 0.1f).coerceIn(12.sp.toPx(), 20.sp.toPx())
73
- val style = TextStyle(
74
- color = textColor,
75
- fontSize = fontPx.toSp(),
76
- fontWeight = FontWeight.Bold,
77
- )
78
-
79
- val intrinsic = textMeasurer.measure(
80
- text = label,
81
- style = style,
82
- maxLines = 1,
83
- softWrap = false,
84
- )
85
- val textW = intrinsic.size.width
86
- val textH = intrinsic.size.height
87
- val y = (size.height - textH) / 2f
88
-
89
- if (textW <= size.width) {
90
- drawText(
91
- textLayoutResult = intrinsic,
92
- topLeft = Offset((size.width - textW) / 2f, y),
93
- )
94
- } else {
95
- clipRect(right = size.width, bottom = size.height) {
96
- val gap = textW * 0.5f
97
- val travel = textW + gap
98
- val x = -progress * travel
99
- drawText(textLayoutResult = intrinsic, topLeft = Offset(x, y))
100
- drawText(textLayoutResult = intrinsic, topLeft = Offset(x + travel, y))
101
- }
102
- }
103
- }
104
- }
@@ -1,48 +0,0 @@
1
- package vn.momo.kits.modifier
2
-
3
- import androidx.compose.ui.Modifier
4
- import androidx.compose.ui.draw.drawBehind
5
- import androidx.compose.ui.graphics.Color
6
- import androidx.compose.ui.graphics.Paint
7
- import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
8
- import androidx.compose.ui.unit.Dp
9
- import androidx.compose.ui.unit.dp
10
- import vn.momo.kits.platform.setColor
11
- import vn.momo.kits.platform.setMaskFilter
12
- fun Modifier.shadow(
13
- color: Color = Color.Black,
14
- borderRadius: Dp = 0.dp,
15
- blurRadius: Float = 0f,
16
- offsetY: Dp = 0.dp,
17
- offsetX: Dp = 0.dp,
18
- spread: Dp = 0f.dp,
19
- modifier: Modifier = Modifier
20
- ) = this.then(
21
- modifier.drawBehind {
22
- this.drawIntoCanvas {
23
- val paint = Paint()
24
- val frameworkPaint = paint.asFrameworkPaint()
25
- val spreadPixel = spread.toPx()
26
- val leftPixel = (0f - spreadPixel) + offsetX.toPx()
27
- val topPixel = (0f - spreadPixel) + offsetY.toPx()
28
- val rightPixel = (this.size.width + spreadPixel)
29
- val bottomPixel = (0f + spreadPixel) - offsetY.toPx()
30
-
31
- if (blurRadius != 0f) {
32
- frameworkPaint.setMaskFilter(blurRadius)
33
- }
34
-
35
- frameworkPaint.setColor(color)
36
-
37
- it.drawRoundRect(
38
- left = leftPixel,
39
- top = topPixel,
40
- right = rightPixel,
41
- bottom = bottomPixel,
42
- radiusX = borderRadius.toPx(),
43
- radiusY = borderRadius.toPx(),
44
- paint
45
- )
46
- }
47
- }
48
- )
@@ -1,51 +0,0 @@
1
- package vn.momo.kits.modifier
2
-
3
- import androidx.compose.runtime.Composable
4
- import androidx.compose.runtime.State
5
- import androidx.compose.ui.Modifier
6
- import androidx.compose.ui.layout.Measurable
7
- import androidx.compose.ui.layout.MeasureResult
8
- import androidx.compose.ui.layout.MeasureScope
9
- import androidx.compose.ui.node.LayoutModifierNode
10
- import androidx.compose.ui.node.ModifierNodeElement
11
- import androidx.compose.ui.unit.Constraints
12
- import androidx.compose.ui.unit.IntOffset
13
-
14
- @Composable
15
- fun Modifier.dynamicSize(
16
- widthState: State<Int>? = null,
17
- heightState: State<Int>? = null,
18
- ) = this then DynamicSizeElement(widthState, heightState)
19
-
20
- private data class DynamicSizeElement(
21
- val widthState: State<Int>?,
22
- val heightState: State<Int>?,
23
- ) : ModifierNodeElement<DynamicSizeNode>() {
24
- override fun create(): DynamicSizeNode {
25
- return DynamicSizeNode(widthState, heightState)
26
- }
27
-
28
- override fun update(node: DynamicSizeNode) {
29
- node.widthState = widthState
30
- node.heightState = heightState
31
- }
32
-
33
- }
34
-
35
- private class DynamicSizeNode(
36
- var widthState: State<Int>?,
37
- var heightState: State<Int>?,
38
- ) : Modifier.Node(), LayoutModifierNode {
39
- override fun MeasureScope.measure(
40
- measurable: Measurable,
41
- constraints: Constraints
42
- ): MeasureResult {
43
- val p = measurable.measure(constraints)
44
- return layout(
45
- width = widthState?.value ?: p.width,
46
- height = heightState?.value ?: p.height,
47
- ) {
48
- p.place(IntOffset.Zero)
49
- }
50
- }
51
- }