@momo-kits/native-kits 0.167.1-duo.1-debug → 0.167.1-duo.10-debug
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.
- package/CLAUDE.md +32 -0
- package/compose/build.gradle.kts +15 -1
- package/compose/build.gradle.kts.backup +14 -0
- package/compose/compose.podspec +1 -1
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +2 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +6 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +20 -21
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +2 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +4 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +9 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +11 -12
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +4 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +10 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +6 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +53 -26
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +12 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +7 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +13 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +13 -5
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +3 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +14 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +19 -21
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +5 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +3 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +7 -15
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +13 -11
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +21 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +232 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +11 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +43 -4
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +8 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +4 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +143 -31
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +100 -66
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +22 -16
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +98 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +312 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +66 -18
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +23 -14
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +3 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +1 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +81 -24
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +1 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +185 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +2 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +3 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +13 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +4 -1
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +4 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +86 -0
- package/gradle.properties +1 -1
- package/ios/Application/ApplicationEnvironment.swift +17 -0
- package/ios/Application/ComposeApi.swift +2 -1
- package/ios/Application/{Components.swift → LegacyHeader.swift} +7 -65
- package/ios/Application/Screen.swift +3 -2
- package/ios/Avatar/Avatar.swift +4 -3
- package/ios/Badge/Badge.swift +6 -4
- package/ios/Badge/BadgeDot.swift +1 -1
- package/ios/Badge/BadgeRibbon.swift +0 -1
- package/ios/Button/Button.swift +15 -12
- package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +64 -18
- package/ios/Carousel/Carousel.swift +29 -12
- package/ios/Checkbox/Checkbox.swift +7 -3
- package/ios/Chip/Chip.swift +5 -4
- package/ios/Collapse/Collapse.swift +70 -19
- package/ios/Colors+Radius+Spacing/Colors.swift +10 -1
- package/ios/DateTimePicker/DateTimePicker.swift +54 -20
- package/ios/DateTimePicker/WheelPicker.swift +103 -105
- package/ios/Divider/Divider.swift +37 -4
- package/ios/Extensions/Color++.swift +38 -9
- package/ios/Extensions/OptionalEnvironmentObject.swift +12 -0
- package/ios/Extensions/SkeletonUICompat.swift +11 -0
- package/ios/Icon/Icon.swift +21 -23
- package/ios/Icon/Icons.swift +1329 -0
- package/ios/IconButton/IconButton.swift +15 -7
- package/ios/Image/Image.swift +7 -23
- package/ios/Information/Information.swift +27 -9
- package/ios/Input/ErrorView.swift +2 -2
- package/ios/Input/Input.swift +160 -147
- package/ios/Input/InputPhoneNumber.swift +12 -9
- package/ios/Input/InputSearch.swift +102 -75
- package/ios/Input/InputTextArea.swift +128 -145
- package/ios/InputDropDown/InputDropDown.swift +10 -7
- package/ios/InputMoney/InputMoney.swift +33 -11
- package/ios/InputOTP/InputOTP.swift +22 -12
- package/ios/Layout/Card.swift +50 -0
- package/ios/Layout/GridContext.swift +123 -3
- package/ios/Layout/Item.swift +5 -4
- package/ios/Layout/ScreenLayout.swift +312 -0
- package/ios/Layout/Section.swift +4 -100
- package/ios/Loader/Loader.swift +5 -9
- package/ios/Lottie/LottieView.swift +14 -2
- package/ios/{Application/Navigation/Overplay → Navigation}/BottomSheet.swift +97 -8
- package/ios/Navigation/BottomTab/BottomTab.swift +187 -0
- package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTabBar.swift +15 -7
- package/ios/Navigation/BottomTab/DuoBarCompression.swift +105 -0
- package/ios/Navigation/BottomTab/DuoTabBar.swift +304 -0
- package/ios/{Application → Navigation/Component}/FloatingButton.swift +14 -3
- package/ios/Navigation/Component/Header.swift +493 -0
- package/ios/Navigation/Component/HeaderBackground.swift +134 -0
- package/ios/Navigation/Component/HeaderJourney.swift +73 -0
- package/ios/Navigation/Component/HeaderLocation.swift +48 -0
- package/ios/Navigation/Component/HeaderRight.swift +386 -0
- package/ios/Navigation/Component/HeaderTitle.swift +36 -0
- package/ios/{Application/Navigation → Navigation/Component}/HeaderUser.swift +21 -24
- package/ios/Navigation/Component/SideBar.swift +176 -0
- package/ios/{Application/Navigation/Overplay → Navigation/Component}/SnackBar.swift +5 -5
- package/ios/Navigation/HostBackGestureBridge.swift +57 -0
- package/ios/{Application/Navigation/Overplay → Navigation}/ModalScreen.swift +5 -1
- package/ios/Navigation/Navigation.swift +190 -0
- package/ios/{Application/Navigation → Navigation}/NavigationContainer.swift +92 -81
- package/ios/{Application/Navigation → Navigation}/Navigator.swift +185 -36
- package/ios/Navigation/StackScreen.swift +782 -0
- package/ios/{Application/Navigation → Navigation}/Tracking/ScreenTracker.swift +12 -5
- package/ios/OTPKeyboard/OTPKeyboard.swift +8 -11
- package/ios/Pagination/PaginationScroll.swift +2 -2
- package/ios/Popup/PopupDisplay.swift +72 -136
- package/ios/Popup/PopupInput.swift +10 -13
- package/ios/Popup/PopupLayout.swift +140 -0
- package/ios/Popup/PopupNotify.swift +56 -73
- package/ios/Popup/PopupPromotion.swift +31 -47
- package/ios/PopupView/FullscreenPopup.swift +13 -19
- package/ios/PopupView/PopupView.swift +1 -1
- package/ios/PopupView/Utils++.swift +0 -8
- package/ios/ProgressInfo/ProgressInfo.swift +4 -2
- package/ios/Radio/Radio.swift +2 -1
- package/ios/Rating/Rating.swift +9 -1
- package/ios/Resources/dot_loading.json +558 -0
- package/ios/Resources/lottie_circle_loader.json +1 -0
- package/ios/ScrollIndicator/ScrollIndicator.swift +11 -10
- package/ios/Skeleton/Skeleton.swift +22 -27
- package/ios/Slider/Slider.swift +16 -18
- package/ios/Stepper/Stepper.swift +6 -1
- package/ios/Steps/Steps.swift +8 -6
- package/ios/SuggestAction/SuggestAction.swift +5 -0
- package/ios/Swipeable/SwipeCell.swift +10 -6
- package/ios/Switch/Switch.swift +22 -20
- package/ios/TabView/TabView.swift +102 -21
- package/ios/Tag/Tag.swift +15 -4
- package/ios/Template/Logo/Logo.swift +2 -1
- package/ios/Template/TrustBanner/TrustBanner.swift +106 -54
- package/ios/Title/Title.swift +6 -17
- package/ios/Tooltip/Tooltip.swift +29 -14
- package/ios/Typography/Text.swift +61 -19
- package/ios/Uploader/Uploader.swift +3 -1
- package/ios/native-kits.podspec +2 -1
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +1 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DuoTabDemo.swift +80 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +1 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IPhoneDuoDemo.swift +173 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +77 -1
- package/package.json +1 -1
- package/scripts/gen-ios-icons.mjs +36 -0
- package/.claude/settings.local.json +0 -185
- package/ios/Application/HeaderRight.swift +0 -306
- package/ios/Application/Navigation/BottomTab/BottomTab.swift +0 -104
- package/ios/Application/Navigation/HeaderBackProps.swift +0 -66
- package/ios/Application/Navigation/HeaderTitle.swift +0 -57
- package/ios/Application/Navigation/NavigationOptions.swift +0 -109
- package/ios/Application/Navigation/NavigatorManager.swift +0 -22
- package/ios/Application/Navigation/component/Header.swift +0 -178
- package/ios/Application/Navigation/component/HeaderBackground.swift +0 -83
- package/ios/Application/Navigation/component/HeaderColor.swift +0 -65
- package/ios/Application/Navigation/component/HeaderRightWidthKey.swift +0 -11
- package/ios/Application/Navigation/component/NavigationFooter.swift +0 -90
- package/ios/Application/Navigation/component/NavigationHeaderRight.swift +0 -196
- package/ios/Application/StackScreen.swift +0 -492
- package/local.properties +0 -8
- /package/ios/{Application/Navigation → Navigation}/BottomTab/CurvedContainer.swift +0 -0
|
@@ -4,6 +4,8 @@ import androidx.compose.foundation.background
|
|
|
4
4
|
import androidx.compose.foundation.border
|
|
5
5
|
import androidx.compose.foundation.clickable
|
|
6
6
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
7
|
+
import androidx.compose.foundation.rememberScrollState
|
|
8
|
+
import androidx.compose.foundation.verticalScroll
|
|
7
9
|
import androidx.compose.foundation.layout.*
|
|
8
10
|
import androidx.compose.foundation.lazy.LazyColumn
|
|
9
11
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
@@ -20,7 +22,7 @@ import vn.momo.kits.application.LocalLocalize
|
|
|
20
22
|
import vn.momo.kits.const.*
|
|
21
23
|
import vn.momo.kits.modifier.conditional
|
|
22
24
|
import vn.momo.kits.modifier.setAutomationId
|
|
23
|
-
import vn.momo.kits.
|
|
25
|
+
import vn.momo.kits.layout.getScreenLayout
|
|
24
26
|
import kotlin.math.min
|
|
25
27
|
|
|
26
28
|
data class PopupAction(
|
|
@@ -34,9 +36,21 @@ enum class PopupActionDirection {
|
|
|
34
36
|
AUTO
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
//
|
|
39
|
+
// Width of a system alert on the iPhone Duo inner display, in both orientations (RN Popup/utils.ts).
|
|
40
|
+
private val DUO_POPUP_MAX_WIDTH = 440.dp
|
|
41
|
+
|
|
42
|
+
// Card width cap plus the popup's own side padding; unbounded off iPhone Duo.
|
|
43
|
+
@Composable
|
|
44
|
+
internal fun popupMaxWidth(horizontalPadding: Dp): Dp =
|
|
45
|
+
if (getScreenLayout().isIPhoneDuoDevice) DUO_POPUP_MAX_WIDTH + horizontalPadding * 2 else Dp.Unspecified
|
|
46
|
+
|
|
47
|
+
// Room left for a popup on iPhone Duo, where landscape is too short for it; null elsewhere.
|
|
38
48
|
@Composable
|
|
39
|
-
internal fun
|
|
49
|
+
internal fun popupMaxHeight(): Dp? {
|
|
50
|
+
val layout = getScreenLayout()
|
|
51
|
+
if (!layout.isIPhoneDuoDevice) return null
|
|
52
|
+
return layout.height - layout.duoTopInset - layout.insetBottom - Spacing.XL * 2
|
|
53
|
+
}
|
|
40
54
|
|
|
41
55
|
private fun heightForLines(
|
|
42
56
|
layout: TextLayoutResult,
|
|
@@ -129,12 +143,15 @@ fun PopupNotify(
|
|
|
129
143
|
callback?.invoke()
|
|
130
144
|
}
|
|
131
145
|
|
|
146
|
+
val maxHeight = popupMaxHeight()
|
|
147
|
+
val bodyScrollState = rememberScrollState()
|
|
148
|
+
|
|
132
149
|
Box(
|
|
133
150
|
Modifier.setAutomationId("popup_notify"), contentAlignment = Alignment.TopEnd
|
|
134
151
|
) {
|
|
135
152
|
Column(
|
|
136
153
|
modifier = Modifier
|
|
137
|
-
.widthIn(max = popupMaxWidth())
|
|
154
|
+
.widthIn(max = popupMaxWidth(horizontalPadding = 12.dp))
|
|
138
155
|
.fillMaxWidth()
|
|
139
156
|
.padding(horizontal = 12.dp)
|
|
140
157
|
.clip(RoundedCornerShape(Radius.L))
|
|
@@ -145,31 +162,41 @@ fun PopupNotify(
|
|
|
145
162
|
.conditional(IsShowBaseLineDebug) {
|
|
146
163
|
border(1.dp, Colors.blue_03)
|
|
147
164
|
}
|
|
165
|
+
.conditional(maxHeight != null) {
|
|
166
|
+
heightIn(max = maxHeight ?: Dp.Unspecified)
|
|
167
|
+
}
|
|
148
168
|
) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
169
|
+
// scrolls when iPhone Duo landscape is too short; actions stay visible
|
|
170
|
+
Column(
|
|
171
|
+
Modifier.conditional(maxHeight != null) {
|
|
172
|
+
weight(1f, fill = false).verticalScroll(bodyScrollState)
|
|
173
|
+
}
|
|
174
|
+
) {
|
|
175
|
+
if(props.image.isNotEmpty()) {
|
|
176
|
+
Image(
|
|
177
|
+
source = props.image,
|
|
178
|
+
modifier = Modifier.fillMaxWidth().aspectRatio(1.777f),
|
|
179
|
+
options = Options(alignment = Alignment.Center)
|
|
180
|
+
)
|
|
181
|
+
}
|
|
156
182
|
|
|
157
|
-
|
|
158
|
-
Text(
|
|
159
|
-
style = Typography.headerDefaultBold,
|
|
160
|
-
maxLines = 2,
|
|
161
|
-
text = props.title,
|
|
162
|
-
modifier = Modifier.setAutomationId("title_popup_permission")
|
|
163
|
-
)
|
|
164
|
-
content(Modifier.padding(top = Spacing.S))
|
|
165
|
-
if (props.error.isNotEmpty()) {
|
|
183
|
+
Column(modifier = Modifier.padding(Spacing.XL)) {
|
|
166
184
|
Text(
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
modifier = Modifier.padding(top = Spacing.S)
|
|
185
|
+
style = Typography.headerDefaultBold,
|
|
186
|
+
maxLines = 2,
|
|
187
|
+
text = props.title,
|
|
188
|
+
modifier = Modifier.setAutomationId("title_popup_permission")
|
|
172
189
|
)
|
|
190
|
+
content(Modifier.padding(top = Spacing.S))
|
|
191
|
+
if (props.error.isNotEmpty()) {
|
|
192
|
+
Text(
|
|
193
|
+
text = LocalLocalize.current.translate("errorCode") + props.error,
|
|
194
|
+
style = Typography.descriptionXsRegular,
|
|
195
|
+
color = AppTheme.current.colors.text.hint,
|
|
196
|
+
maxLines = 1,
|
|
197
|
+
modifier = Modifier.padding(top = Spacing.S)
|
|
198
|
+
)
|
|
199
|
+
}
|
|
173
200
|
}
|
|
174
201
|
}
|
|
175
202
|
|
|
@@ -227,7 +254,7 @@ internal fun BuildAction(
|
|
|
227
254
|
val closeAction = remember { mutableStateOf("button_action") }
|
|
228
255
|
when (buttonDirection) {
|
|
229
256
|
PopupActionDirection.AUTO -> {
|
|
230
|
-
if (secondary != null && (secondary.title.length > 12 || primary?.title?.length
|
|
257
|
+
if (secondary != null && (secondary.title.length > 12 || (primary?.title?.length ?: 0) > 12)) {
|
|
231
258
|
RenderColumn(
|
|
232
259
|
secondary = secondary,
|
|
233
260
|
onAction = onAction,
|
|
@@ -22,14 +22,24 @@ import vn.momo.kits.modifier.conditional
|
|
|
22
22
|
import vn.momo.kits.modifier.setAutomationId
|
|
23
23
|
import vn.momo.kits.utils.ifNotNull
|
|
24
24
|
|
|
25
|
+
private const val ASPECT_RATIO = 0.72f
|
|
26
|
+
private val CLOSE_ROW_HEIGHT = Spacing.S + 40.dp
|
|
27
|
+
|
|
25
28
|
@Composable
|
|
26
29
|
fun PopupPromotion(
|
|
27
30
|
source: String = "",
|
|
28
31
|
onPress: (() -> Unit) = {},
|
|
29
32
|
onClose: (() -> Unit) = {},
|
|
30
33
|
) {
|
|
34
|
+
val maxHeight = popupMaxHeight()
|
|
35
|
+
var maxWidth = popupMaxWidth(horizontalPadding = Spacing.M)
|
|
36
|
+
if (maxHeight != null) {
|
|
37
|
+
// fit the portrait image by height when iPhone Duo leaves too little room
|
|
38
|
+
maxWidth = minOf(maxWidth, (maxHeight - CLOSE_ROW_HEIGHT) * ASPECT_RATIO + Spacing.M * 2)
|
|
39
|
+
}
|
|
40
|
+
|
|
31
41
|
Column(Modifier
|
|
32
|
-
.widthIn(max =
|
|
42
|
+
.widthIn(max = maxWidth)
|
|
33
43
|
.padding(horizontal = Spacing.M)
|
|
34
44
|
.conditional(IsShowBaseLineDebug) {
|
|
35
45
|
border(1.dp, Colors.blue_03)
|
|
@@ -39,7 +49,7 @@ fun PopupPromotion(
|
|
|
39
49
|
Image(
|
|
40
50
|
source = source,
|
|
41
51
|
modifier = Modifier.fillMaxWidth()
|
|
42
|
-
.aspectRatio(
|
|
52
|
+
.aspectRatio(ASPECT_RATIO)
|
|
43
53
|
.clickable(
|
|
44
54
|
interactionSource = remember { MutableInteractionSource() },
|
|
45
55
|
indication = null,
|
|
@@ -5,13 +5,10 @@ import androidx.compose.foundation.border
|
|
|
5
5
|
import androidx.compose.foundation.layout.*
|
|
6
6
|
import androidx.compose.foundation.shape.CircleShape
|
|
7
7
|
import androidx.compose.runtime.Composable
|
|
8
|
-
import androidx.compose.runtime.mutableFloatStateOf
|
|
9
8
|
import androidx.compose.runtime.remember
|
|
10
9
|
import androidx.compose.ui.Alignment
|
|
11
10
|
import androidx.compose.ui.Modifier
|
|
12
11
|
import androidx.compose.ui.draw.clip
|
|
13
|
-
import androidx.compose.ui.layout.onGloballyPositioned
|
|
14
|
-
import androidx.compose.ui.platform.LocalDensity
|
|
15
12
|
import androidx.compose.ui.text.style.TextAlign
|
|
16
13
|
import androidx.compose.ui.unit.dp
|
|
17
14
|
import vn.momo.kits.application.IsShowBaseLineDebug
|
|
@@ -180,7 +177,6 @@ private fun ProgressInfoVertical(
|
|
|
180
177
|
) {
|
|
181
178
|
val theme = AppTheme.current
|
|
182
179
|
val lineColor = remember(theme) { theme.colors.primary.copy(alpha = 0.2f) }
|
|
183
|
-
val density = LocalDensity.current
|
|
184
180
|
|
|
185
181
|
Column(
|
|
186
182
|
modifier = modifier
|
|
@@ -189,16 +185,18 @@ private fun ProgressInfoVertical(
|
|
|
189
185
|
) {
|
|
190
186
|
steps.forEachIndexed { index, item ->
|
|
191
187
|
val isLast = index == steps.lastIndex
|
|
192
|
-
val rowHeightPx = remember { mutableFloatStateOf(0f) }
|
|
193
|
-
val rowHeightDp = with(density) { rowHeightPx.value.toDp() }
|
|
194
188
|
|
|
189
|
+
// Intrinsic height lets the connector fill the row in one pass; measuring the row and
|
|
190
|
+
// feeding it back lagged a frame and never let the line shrink with its text.
|
|
195
191
|
Row(
|
|
196
192
|
modifier = Modifier
|
|
197
193
|
.fillMaxWidth()
|
|
198
|
-
.
|
|
194
|
+
.height(IntrinsicSize.Min),
|
|
199
195
|
) {
|
|
200
196
|
Column(
|
|
201
|
-
modifier = Modifier
|
|
197
|
+
modifier = Modifier
|
|
198
|
+
.fillMaxHeight()
|
|
199
|
+
.heightIn(min = if (isLast) 40.dp else 48.dp),
|
|
202
200
|
horizontalAlignment = Alignment.CenterHorizontally,
|
|
203
201
|
) {
|
|
204
202
|
ProgressInfoStepIcon(
|
|
@@ -211,7 +209,7 @@ private fun ProgressInfoVertical(
|
|
|
211
209
|
Spacer(
|
|
212
210
|
modifier = Modifier
|
|
213
211
|
.width(2.dp)
|
|
214
|
-
.
|
|
212
|
+
.weight(1f)
|
|
215
213
|
.background(lineColor),
|
|
216
214
|
)
|
|
217
215
|
}
|
|
@@ -40,7 +40,7 @@ fun Radio(
|
|
|
40
40
|
verticalAlignment = Alignment.CenterVertically,
|
|
41
41
|
) {
|
|
42
42
|
Box(
|
|
43
|
-
modifier =
|
|
43
|
+
modifier = Modifier
|
|
44
44
|
.padding(end = if (label.isEmpty()) 0.dp else Spacing.XS)
|
|
45
45
|
.border(borderWidth, borderColor, RoundedCornerShape(Radius.M))
|
|
46
46
|
.conditional(IsShowBaseLineDebug) {
|
|
@@ -43,9 +43,11 @@ private fun getValueForPosition(
|
|
|
43
43
|
step: Float,
|
|
44
44
|
): Float {
|
|
45
45
|
val availableSpace = containerWidth - thumbWidth
|
|
46
|
-
if (availableSpace <= 0f) return min
|
|
47
|
-
val relStepUnit = step / (max - min)
|
|
46
|
+
if (availableSpace <= 0f || max <= min) return min
|
|
48
47
|
var relPosition = (positionInView - thumbWidth / 2f) / availableSpace
|
|
48
|
+
// A zero step would make the snap math NaN, and NaN.roundToInt() throws.
|
|
49
|
+
if (step <= 0f) return clamp(min + relPosition * (max - min), min, max)
|
|
50
|
+
val relStepUnit = step / (max - min)
|
|
49
51
|
val relOffset = relPosition % relStepUnit
|
|
50
52
|
relPosition -= relOffset
|
|
51
53
|
if (relOffset / relStepUnit >= 0.5f) relPosition += relStepUnit
|
|
@@ -131,6 +133,11 @@ fun Slider(
|
|
|
131
133
|
var draggingLow by remember { mutableStateOf(true) }
|
|
132
134
|
var isDragging by remember { mutableStateOf(false) }
|
|
133
135
|
|
|
136
|
+
// pointerInput is not keyed on the callbacks, so read their latest values.
|
|
137
|
+
val currentOnValueChanged by rememberUpdatedState(onValueChanged)
|
|
138
|
+
val currentOnTouchStart by rememberUpdatedState(onSliderTouchStart)
|
|
139
|
+
val currentOnTouchEnd by rememberUpdatedState(onSliderTouchEnd)
|
|
140
|
+
|
|
134
141
|
val thumbColor = if (disabled) theme.colors.text.disable else theme.colors.primary
|
|
135
142
|
val trackActiveColor = if (disabled) theme.colors.text.disable else theme.colors.primary
|
|
136
143
|
val trackInactiveColor = theme.colors.background.default
|
|
@@ -167,7 +174,7 @@ fun Slider(
|
|
|
167
174
|
|
|
168
175
|
draggingLow = disableRange || isLowCloser(touchX, lowCenterX, highCenterX)
|
|
169
176
|
isDragging = true
|
|
170
|
-
|
|
177
|
+
currentOnTouchStart(lowValue, highValue)
|
|
171
178
|
|
|
172
179
|
// Apply initial press
|
|
173
180
|
applyDrag(touchX, trackWidthPx, thumbSizePx,
|
|
@@ -175,7 +182,7 @@ fun Slider(
|
|
|
175
182
|
lowValue, highValue) { nl, nh ->
|
|
176
183
|
lowValue = nl
|
|
177
184
|
highValue = nh
|
|
178
|
-
|
|
185
|
+
currentOnValueChanged(nl, nh, true)
|
|
179
186
|
}
|
|
180
187
|
|
|
181
188
|
// Track move / release
|
|
@@ -186,7 +193,7 @@ fun Slider(
|
|
|
186
193
|
|
|
187
194
|
if (!moveChange.pressed) {
|
|
188
195
|
isDragging = false
|
|
189
|
-
|
|
196
|
+
currentOnTouchEnd(lowValue, highValue)
|
|
190
197
|
break
|
|
191
198
|
}
|
|
192
199
|
|
|
@@ -195,7 +202,7 @@ fun Slider(
|
|
|
195
202
|
lowValue, highValue) { nl, nh ->
|
|
196
203
|
lowValue = nl
|
|
197
204
|
highValue = nh
|
|
198
|
-
|
|
205
|
+
currentOnValueChanged(nl, nh, true)
|
|
199
206
|
}
|
|
200
207
|
}
|
|
201
208
|
}
|
|
@@ -8,8 +8,10 @@ import androidx.compose.foundation.gestures.detectHorizontalDragGestures
|
|
|
8
8
|
import androidx.compose.foundation.layout.*
|
|
9
9
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
10
10
|
import androidx.compose.runtime.Composable
|
|
11
|
+
import androidx.compose.runtime.getValue
|
|
11
12
|
import androidx.compose.runtime.remember
|
|
12
13
|
import androidx.compose.runtime.rememberCoroutineScope
|
|
14
|
+
import androidx.compose.runtime.rememberUpdatedState
|
|
13
15
|
import androidx.compose.ui.Alignment
|
|
14
16
|
import androidx.compose.ui.Modifier
|
|
15
17
|
import androidx.compose.ui.draw.clip
|
|
@@ -62,6 +64,12 @@ fun Swipe(
|
|
|
62
64
|
|
|
63
65
|
val offsetX = remember { Animatable(0f) }
|
|
64
66
|
|
|
67
|
+
// pointerInput is keyed on the action counts only, so read the latest callbacks.
|
|
68
|
+
val currentOnOpen by rememberUpdatedState(onSwipeableOpen)
|
|
69
|
+
val currentOnClose by rememberUpdatedState(onSwipeableClose)
|
|
70
|
+
val currentOnLeftOpen by rememberUpdatedState(onSwipeableLeftOpen)
|
|
71
|
+
val currentOnRightOpen by rememberUpdatedState(onSwipeableRightOpen)
|
|
72
|
+
|
|
65
73
|
val shape = remember(itemRadius) {
|
|
66
74
|
if (itemRadius > 0.dp) RoundedCornerShape(itemRadius) else RoundedCornerShape(0.dp)
|
|
67
75
|
}
|
|
@@ -71,17 +79,17 @@ fun Swipe(
|
|
|
71
79
|
val current = offsetX.value
|
|
72
80
|
val target = when {
|
|
73
81
|
current > leftWidthPx * 0.5f && left.isNotEmpty() -> {
|
|
74
|
-
|
|
75
|
-
|
|
82
|
+
currentOnLeftOpen()
|
|
83
|
+
currentOnOpen()
|
|
76
84
|
leftWidthPx
|
|
77
85
|
}
|
|
78
86
|
current < -rightWidthPx * 0.5f && right.isNotEmpty() -> {
|
|
79
|
-
|
|
80
|
-
|
|
87
|
+
currentOnRightOpen()
|
|
88
|
+
currentOnOpen()
|
|
81
89
|
-rightWidthPx
|
|
82
90
|
}
|
|
83
91
|
else -> {
|
|
84
|
-
|
|
92
|
+
currentOnClose()
|
|
85
93
|
0f
|
|
86
94
|
}
|
|
87
95
|
}
|
|
@@ -43,9 +43,10 @@ fun Switch(
|
|
|
43
43
|
|
|
44
44
|
val (circleBackgroundColor, backgroundColor) = colors
|
|
45
45
|
|
|
46
|
-
//
|
|
46
|
+
// Both params default to "", so blank must fall back too or the id is always empty.
|
|
47
47
|
val automationIdValue = remember(accessibilityId, title, value) {
|
|
48
|
-
accessibilityId
|
|
48
|
+
accessibilityId?.takeIf { it.isNotEmpty() }
|
|
49
|
+
?: "toggle_${title?.takeIf { it.isNotEmpty() } ?: "unknown"}_$value"
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
// Memoize alignment calculation
|
|
@@ -10,9 +10,10 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|
|
10
10
|
import androidx.compose.runtime.*
|
|
11
11
|
import androidx.compose.ui.Alignment
|
|
12
12
|
import androidx.compose.ui.Modifier
|
|
13
|
+
import androidx.compose.ui.draw.clipToBounds
|
|
14
|
+
import androidx.compose.ui.geometry.Offset
|
|
13
15
|
import androidx.compose.ui.graphics.Color
|
|
14
16
|
import androidx.compose.ui.graphics.drawscope.withTransform
|
|
15
|
-
import androidx.compose.ui.graphics.drawscope.DrawScope
|
|
16
17
|
import androidx.compose.ui.graphics.Path
|
|
17
18
|
import androidx.compose.ui.layout.onGloballyPositioned
|
|
18
19
|
import androidx.compose.ui.layout.positionInParent
|
|
@@ -73,6 +74,8 @@ fun TabView(
|
|
|
73
74
|
} else null
|
|
74
75
|
|
|
75
76
|
val coroutineScope = rememberCoroutineScope()
|
|
77
|
+
// The collector below outlives recompositions; read the latest callback, not the first one.
|
|
78
|
+
val currentOnPageSelected by rememberUpdatedState(onPageSelected)
|
|
76
79
|
|
|
77
80
|
// Keep selectedIndex in sync when user swipes the pager
|
|
78
81
|
if (pagerState != null) {
|
|
@@ -80,7 +83,7 @@ fun TabView(
|
|
|
80
83
|
snapshotFlow { pagerState.currentPage }.collect { page ->
|
|
81
84
|
if (!lazyRendered.contains(page)) lazyRendered.add(page)
|
|
82
85
|
selectedIndex = page
|
|
83
|
-
|
|
86
|
+
currentOnPageSelected(page)
|
|
84
87
|
}
|
|
85
88
|
}
|
|
86
89
|
}
|
|
@@ -362,7 +365,7 @@ private fun CardTabBar(
|
|
|
362
365
|
}
|
|
363
366
|
}
|
|
364
367
|
|
|
365
|
-
val active = tabs
|
|
368
|
+
val active = tabs.getOrNull(selectedIndex) ?: return@BoxWithConstraints
|
|
366
369
|
Row(
|
|
367
370
|
modifier = Modifier
|
|
368
371
|
// Pinned to the strip's BOTTOM edge so the two always end on the same
|
|
@@ -415,7 +418,8 @@ private val CARD_TAB_HEIGHT = 40.dp
|
|
|
415
418
|
private val CARD_TAB_ACTIVE_HEIGHT = 44.dp
|
|
416
419
|
@Composable
|
|
417
420
|
private fun CardTabEar(width: Dp, mirrored: Boolean, color: Color, nudge: Dp) {
|
|
418
|
-
|
|
421
|
+
// Clipped like react-kits' Svg, whose 28-wide path is cut at a 22pt `pathWidth`.
|
|
422
|
+
Canvas(modifier = Modifier.offset(x = nudge).width(width).height(CARD_TAB_ACTIVE_HEIGHT).clipToBounds()) {
|
|
419
423
|
val path = Path().apply {
|
|
420
424
|
moveTo(0f, 0f)
|
|
421
425
|
lineTo(0.60765f, 0f)
|
|
@@ -425,8 +429,12 @@ private fun CardTabEar(width: Dp, mirrored: Boolean, color: Color, nudge: Dp) {
|
|
|
425
429
|
lineTo(0f, 44f)
|
|
426
430
|
close()
|
|
427
431
|
}
|
|
428
|
-
|
|
429
|
-
|
|
432
|
+
// Path coordinates are dp (react-kits viewBox), but DrawScope draws in px.
|
|
433
|
+
val pxPerDp = density
|
|
434
|
+
withTransform({
|
|
435
|
+
if (mirrored) scale(-1f, 1f)
|
|
436
|
+
scale(pxPerDp, pxPerDp, pivot = Offset.Zero)
|
|
437
|
+
}) { drawPath(path = path, color = color) }
|
|
430
438
|
}
|
|
431
439
|
}
|
|
432
440
|
|
|
@@ -30,6 +30,22 @@ enum class TagColor(val backgroundColor: Color, val textColor: Color) {
|
|
|
30
30
|
Grey(Colors.black_04, Colors.black_12),
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// RN only accepts the `_03` palette as a custom tag color.
|
|
34
|
+
private val tagCustomColors = listOf(
|
|
35
|
+
Colors.black_03,
|
|
36
|
+
Colors.pink_03,
|
|
37
|
+
Colors.violet_03,
|
|
38
|
+
Colors.indigo_03,
|
|
39
|
+
Colors.blue_03,
|
|
40
|
+
Colors.mint_03,
|
|
41
|
+
Colors.green_03,
|
|
42
|
+
Colors.lime_03,
|
|
43
|
+
Colors.yellow_03,
|
|
44
|
+
Colors.gold_03,
|
|
45
|
+
Colors.orange_03,
|
|
46
|
+
Colors.red_03,
|
|
47
|
+
)
|
|
48
|
+
|
|
33
49
|
@Composable
|
|
34
50
|
fun Tag(
|
|
35
51
|
label: String = "Label",
|
|
@@ -38,27 +54,9 @@ fun Tag(
|
|
|
38
54
|
size: TagSize = TagSize.Large,
|
|
39
55
|
customColor: Color? = null,
|
|
40
56
|
) {
|
|
41
|
-
val
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
Color(0xFF962AF0),
|
|
45
|
-
Color(0xFF4E4BFF),
|
|
46
|
-
Color(0xFF007AFF),
|
|
47
|
-
Color(0xFF13C2C2),
|
|
48
|
-
Color(0xFF34C759),
|
|
49
|
-
Color(0xFFA0D911),
|
|
50
|
-
Color(0xFFFFCC00),
|
|
51
|
-
Color(0xFFFA8C16),
|
|
52
|
-
Color(0xFFFA541C),
|
|
53
|
-
Color(0xFFF5222D)
|
|
54
|
-
)
|
|
55
|
-
var tagColor = color.backgroundColor
|
|
56
|
-
var labelColor = color.textColor
|
|
57
|
-
|
|
58
|
-
if (primaryColors.contains(customColor)) {
|
|
59
|
-
tagColor = color.backgroundColor
|
|
60
|
-
labelColor = color.textColor
|
|
61
|
-
}
|
|
57
|
+
val custom = customColor?.takeIf { it in tagCustomColors }
|
|
58
|
+
val tagColor = custom ?: color.backgroundColor
|
|
59
|
+
val labelColor = if (custom != null) Colors.black_01 else color.textColor
|
|
62
60
|
|
|
63
61
|
Box(
|
|
64
62
|
modifier = Modifier
|
|
@@ -87,8 +87,8 @@ fun Text(
|
|
|
87
87
|
color ?: theme.colors.text.default
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
//
|
|
91
|
-
val optimizedStyle = remember(style) {
|
|
90
|
+
// lineHeight is a key: it is scaled, so it changes with the font scale even when style doesn't.
|
|
91
|
+
val optimizedStyle = remember(style, lineHeight) {
|
|
92
92
|
style.copy(
|
|
93
93
|
fontWeight = null,
|
|
94
94
|
lineHeight = if (lineHeight.value.isFinite() && lineHeight.value > 0) lineHeight else style.lineHeight
|
|
@@ -21,6 +21,7 @@ import androidx.compose.ui.unit.*
|
|
|
21
21
|
import androidx.compose.ui.window.Popup
|
|
22
22
|
import androidx.compose.ui.window.PopupPositionProvider
|
|
23
23
|
import androidx.compose.ui.window.PopupProperties
|
|
24
|
+
import kotlinx.coroutines.flow.drop
|
|
24
25
|
import vn.momo.kits.application.IsShowBaseLineDebug
|
|
25
26
|
import vn.momo.kits.const.*
|
|
26
27
|
import vn.momo.kits.modifier.activeOpacityClickable
|
|
@@ -147,8 +148,10 @@ fun Tooltip(
|
|
|
147
148
|
val density = LocalDensity.current
|
|
148
149
|
val offsetPx = with(density) { (TOOLTIP_OFFSET + ARROW_SIZE).roundToPx() }
|
|
149
150
|
|
|
150
|
-
|
|
151
|
-
|
|
151
|
+
val currentOnVisibleChange by rememberUpdatedState(onVisibleChange)
|
|
152
|
+
LaunchedEffect(state) {
|
|
153
|
+
// changes only, like RN: the initial value is not reported
|
|
154
|
+
snapshotFlow { state.isVisible }.drop(1).collect { currentOnVisibleChange?.invoke(it) }
|
|
152
155
|
}
|
|
153
156
|
|
|
154
157
|
Box(modifier = modifier) {
|
package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt
CHANGED
|
@@ -128,11 +128,13 @@ fun getHours(hourMode: Int): List<String> {
|
|
|
128
128
|
* Get a list of minutes
|
|
129
129
|
*/
|
|
130
130
|
fun getMinutes(interval: Int): List<String> {
|
|
131
|
+
// A non-positive interval would never reach 60.
|
|
132
|
+
val step = interval.coerceAtLeast(1)
|
|
131
133
|
val minutes = mutableListOf<String>()
|
|
132
134
|
var min = 0
|
|
133
135
|
while (min < 60) {
|
|
134
136
|
minutes.add(paddingNum(min))
|
|
135
|
-
min +=
|
|
137
|
+
min += step
|
|
136
138
|
}
|
|
137
139
|
return minutes
|
|
138
140
|
}
|
|
@@ -154,22 +154,14 @@ internal fun WheelPickerItem(
|
|
|
154
154
|
.fillMaxWidth()
|
|
155
155
|
.height(itemHeight)
|
|
156
156
|
.graphicsLayer {
|
|
157
|
-
|
|
158
|
-
val
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
val absoluteOffset =
|
|
164
|
-
(index - firstVisibleIndex) * itemHeightValue - (visibleItemOffset / itemHeightValue)
|
|
165
|
-
val relativePosition = absoluteOffset / viewportHeight
|
|
166
|
-
|
|
167
|
-
val distance = abs(2 - relativePosition)
|
|
168
|
-
|
|
157
|
+
// Rows away from the centre slot (two below the first visible row); RN fades 1 → 0.8 → 0.4.
|
|
158
|
+
val distance = abs(
|
|
159
|
+
index - listState.firstVisibleItemIndex -
|
|
160
|
+
listState.firstVisibleItemScrollOffset / itemHeight.toPx() - 2
|
|
161
|
+
)
|
|
169
162
|
alpha = when {
|
|
170
|
-
distance <=
|
|
171
|
-
distance <=
|
|
172
|
-
distance <= 3 -> 0.4f
|
|
163
|
+
distance <= 1f -> 1f - 0.2f * distance
|
|
164
|
+
distance <= 2f -> 0.8f - 0.4f * (distance - 1f)
|
|
173
165
|
else -> 0.4f
|
|
174
166
|
}
|
|
175
167
|
},
|
|
@@ -4,6 +4,7 @@ import androidx.compose.foundation.background
|
|
|
4
4
|
import androidx.compose.foundation.layout.Arrangement
|
|
5
5
|
import androidx.compose.foundation.layout.Box
|
|
6
6
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
|
7
|
+
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
|
7
8
|
import androidx.compose.foundation.layout.FlowRow
|
|
8
9
|
import androidx.compose.foundation.layout.PaddingValues
|
|
9
10
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
@@ -14,6 +15,7 @@ import androidx.compose.runtime.CompositionLocalProvider
|
|
|
14
15
|
import androidx.compose.ui.Modifier
|
|
15
16
|
import androidx.compose.ui.draw.clip
|
|
16
17
|
import androidx.compose.ui.draw.shadow
|
|
18
|
+
import androidx.compose.ui.graphics.Color
|
|
17
19
|
import androidx.compose.ui.unit.dp
|
|
18
20
|
import vn.momo.kits.components.Image
|
|
19
21
|
import vn.momo.kits.const.AppTheme
|
|
@@ -21,30 +23,30 @@ import vn.momo.kits.const.Radius
|
|
|
21
23
|
import vn.momo.kits.const.Spacing
|
|
22
24
|
|
|
23
25
|
@Composable
|
|
24
|
-
|
|
26
|
+
@OptIn(ExperimentalLayoutApi::class)
|
|
27
|
+
fun Card(
|
|
25
28
|
useShadow: Boolean = false,
|
|
26
29
|
backgroundImage: String? = null,
|
|
30
|
+
backgroundColor: Color? = null,
|
|
27
31
|
contentPadding: PaddingValues = PaddingValues(Spacing.M),
|
|
28
32
|
modifier: Modifier = Modifier,
|
|
29
33
|
content: @Composable () -> Unit,
|
|
30
34
|
) {
|
|
31
|
-
val columns =
|
|
32
|
-
val
|
|
35
|
+
val columns = gridColumns()
|
|
36
|
+
val gap = Spacing.S
|
|
33
37
|
val shape = RoundedCornerShape(Radius.M)
|
|
34
|
-
var surface = modifier.fillMaxWidth().padding(horizontal =
|
|
35
|
-
if (useShadow) surface = surface.shadow(
|
|
36
|
-
surface = surface.clip(shape)
|
|
37
|
-
if (backgroundImage == null) surface = surface.background(AppTheme.current.colors.background.surface)
|
|
38
|
+
var surface = modifier.fillMaxWidth().padding(horizontal = Spacing.M)
|
|
39
|
+
if (useShadow) surface = surface.shadow(6.dp, shape)
|
|
40
|
+
surface = surface.clip(shape).background(backgroundColor ?: AppTheme.current.colors.background.surface)
|
|
38
41
|
Box(modifier = surface) {
|
|
39
42
|
if (backgroundImage != null) {
|
|
40
43
|
Image(source = backgroundImage, modifier = Modifier.matchParentSize())
|
|
41
44
|
}
|
|
42
45
|
BoxWithConstraints(modifier = Modifier.fillMaxWidth().padding(contentPadding)) {
|
|
43
|
-
|
|
44
|
-
CompositionLocalProvider(LocalGridContext provides GridContext(columns, gutter, sizePerSpan)) {
|
|
46
|
+
CompositionLocalProvider(LocalGridContext provides gridContext(constraints.maxWidth, columns, gap)) {
|
|
45
47
|
FlowRow(
|
|
46
|
-
horizontalArrangement = Arrangement.spacedBy(
|
|
47
|
-
verticalArrangement = Arrangement.spacedBy(
|
|
48
|
+
horizontalArrangement = Arrangement.spacedBy(gap),
|
|
49
|
+
verticalArrangement = Arrangement.spacedBy(gap),
|
|
48
50
|
) { content() }
|
|
49
51
|
}
|
|
50
52
|
}
|
|
@@ -6,33 +6,45 @@ import androidx.compose.foundation.layout.width
|
|
|
6
6
|
import androidx.compose.runtime.Composable
|
|
7
7
|
import androidx.compose.runtime.compositionLocalOf
|
|
8
8
|
import androidx.compose.ui.Modifier
|
|
9
|
+
import androidx.compose.ui.platform.LocalDensity
|
|
9
10
|
import androidx.compose.ui.unit.Dp
|
|
10
11
|
import androidx.compose.ui.unit.dp
|
|
12
|
+
import kotlin.math.floor
|
|
11
13
|
|
|
12
14
|
internal data class GridContext(
|
|
13
15
|
val numberOfColumns: Int = 12,
|
|
14
|
-
val
|
|
15
|
-
val
|
|
16
|
+
val gutterPx: Int = 0,
|
|
17
|
+
val columnPx: Float = 0f,
|
|
18
|
+
val density: Float = 1f,
|
|
16
19
|
) {
|
|
17
|
-
fun sizeForSpan(span: Int): Dp =
|
|
20
|
+
fun sizeForSpan(span: Int): Dp = (floor(columnPx * span + gutterPx * (span - 1)) / density).dp
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
internal val LocalGridContext = compositionLocalOf<GridContext?> { null }
|
|
21
24
|
|
|
22
25
|
@Composable
|
|
23
|
-
internal fun
|
|
24
|
-
|
|
26
|
+
internal fun gridColumns(): Int = if (getScreenLayout().contentWidth > 600.dp) 24 else 12
|
|
27
|
+
|
|
28
|
+
@Composable
|
|
29
|
+
internal fun gridContext(maxWidthPx: Int, columns: Int, gap: Dp): GridContext {
|
|
30
|
+
val density = LocalDensity.current
|
|
31
|
+
val gutterPx = with(density) { gap.roundToPx() }
|
|
32
|
+
return GridContext(columns, gutterPx, (maxWidthPx - gutterPx * (columns - 1)).toFloat() / columns, density.density)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@Composable
|
|
36
|
+
fun Item(
|
|
37
|
+
widthSpan: Int? = null,
|
|
25
38
|
heightSpan: Int = 0,
|
|
26
39
|
modifier: Modifier = Modifier,
|
|
40
|
+
contentModifier: Modifier = Modifier,
|
|
27
41
|
content: @Composable () -> Unit,
|
|
28
42
|
) {
|
|
29
43
|
val grid = LocalGridContext.current
|
|
30
44
|
var m = modifier
|
|
31
45
|
if (grid != null) {
|
|
32
|
-
m = m.width(grid.sizeForSpan(widthSpan))
|
|
46
|
+
m = m.width(grid.sizeForSpan(widthSpan ?: grid.numberOfColumns))
|
|
33
47
|
if (heightSpan > 0) m = m.height(grid.sizeForSpan(heightSpan))
|
|
34
48
|
}
|
|
35
|
-
|
|
36
|
-
// on top of each other — matches the React Item's flex-column content.
|
|
37
|
-
Column(modifier = m) { content() }
|
|
49
|
+
Column(modifier = m.then(contentModifier)) { content() }
|
|
38
50
|
}
|