@momo-kits/native-kits 0.168.1-beta.25-debug → 0.168.1-beta.25
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/package.json +1 -1
- package/build.gradle.kts +0 -11
- package/compose/build.gradle.kts +0 -198
- package/compose/build.gradle.kts.backup +0 -198
- package/compose/compose.podspec +0 -47
- package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +0 -20
- package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +0 -6
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +0 -12
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +0 -7
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -153
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReduceMotion.android.kt +0 -17
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +0 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -110
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/DesignSystemKits.kt +0 -62
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -1071
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +0 -269
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +0 -56
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +0 -157
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -81
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -32
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -338
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +0 -175
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -357
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -115
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -89
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -128
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +0 -209
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -542
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -49
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -86
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -125
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -231
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -189
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -478
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -114
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -265
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -235
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -223
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -265
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -185
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -343
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +0 -108
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -56
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -41
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -84
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -40
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -370
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -108
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -308
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -59
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +0 -87
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +0 -21
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -90
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +0 -330
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +0 -220
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +0 -473
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +0 -122
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +0 -207
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -92
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -560
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -86
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -125
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -207
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +0 -501
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -176
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +0 -182
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -193
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -236
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -180
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -325
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +0 -17
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -16
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -194
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -287
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -54
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -50
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +0 -321
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -41
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -52
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -66
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +0 -104
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -48
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -499
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +0 -58
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -148
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -116
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -205
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -422
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +0 -8
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -732
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +0 -148
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +0 -35
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -232
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -237
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +0 -97
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoLiquidSelection.kt +0 -380
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +0 -436
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -169
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -455
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -105
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +0 -106
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +0 -74
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -399
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -39
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -363
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +0 -192
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -133
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +0 -127
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +0 -12
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -91
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +0 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -74
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReduceMotion.kt +0 -7
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +0 -20
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -55
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -118
- package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +0 -42
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +0 -45
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +0 -32
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +0 -38
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +0 -66
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -218
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReduceMotion.ios.kt +0 -48
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +0 -138
- package/gradle/libs.versions.toml +0 -69
- package/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/gradle/wrapper/gradle-wrapper.properties +0 -8
- package/gradle.properties +0 -26
- package/gradlew +0 -252
- package/gradlew.bat +0 -94
- package/ios-demo/README.md +0 -120
- package/settings.gradle.kts +0 -64
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
package vn.momo.kits.navigation.bottomtab
|
|
2
|
-
|
|
3
|
-
import androidx.compose.foundation.Canvas
|
|
4
|
-
import androidx.compose.foundation.layout.Box
|
|
5
|
-
import androidx.compose.foundation.layout.height
|
|
6
|
-
import androidx.compose.foundation.layout.offset
|
|
7
|
-
import androidx.compose.foundation.layout.width
|
|
8
|
-
import androidx.compose.runtime.Composable
|
|
9
|
-
import androidx.compose.ui.Alignment
|
|
10
|
-
import androidx.compose.ui.Modifier
|
|
11
|
-
import androidx.compose.ui.graphics.Path
|
|
12
|
-
import androidx.compose.ui.platform.LocalDensity
|
|
13
|
-
import androidx.compose.ui.unit.Dp
|
|
14
|
-
import androidx.compose.ui.unit.IntOffset
|
|
15
|
-
import androidx.compose.ui.unit.dp
|
|
16
|
-
import androidx.compose.ui.zIndex
|
|
17
|
-
import vn.momo.kits.const.AppTheme
|
|
18
|
-
|
|
19
|
-
@Composable
|
|
20
|
-
internal fun CurvedContainer(
|
|
21
|
-
circleSize: Dp = 48.dp,
|
|
22
|
-
tabBarItemIconSize: Dp = 28.dp,
|
|
23
|
-
circleOverSize: Dp = 2.dp,
|
|
24
|
-
width: Dp = 71.dp,
|
|
25
|
-
height: Dp = 16.dp
|
|
26
|
-
) {
|
|
27
|
-
val theme = AppTheme.current
|
|
28
|
-
val density = LocalDensity.current
|
|
29
|
-
val widthPx = with(density) { width.toPx()}
|
|
30
|
-
|
|
31
|
-
val circleSizePx = with(density) { circleSize.toPx() }
|
|
32
|
-
val tabBarItemIconSizePx = with(density) { tabBarItemIconSize.toPx() }
|
|
33
|
-
val circleOverSizePx = with(density) { circleOverSize.toPx() }
|
|
34
|
-
|
|
35
|
-
val circlePositionTop = circleSizePx - tabBarItemIconSizePx - 10f
|
|
36
|
-
val circleOverTop = circlePositionTop + circleOverSizePx
|
|
37
|
-
val circleR = circleSizePx / 2
|
|
38
|
-
val circleOverR = circleR + circleOverSizePx
|
|
39
|
-
|
|
40
|
-
val centerX = widthPx / 2
|
|
41
|
-
|
|
42
|
-
val curvedPath = getPath(
|
|
43
|
-
circleOverTop = circleOverTop,
|
|
44
|
-
centerX = centerX,
|
|
45
|
-
circleOverR = circleOverR,
|
|
46
|
-
widthPx = widthPx
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
Box(
|
|
50
|
-
contentAlignment = Alignment.BottomCenter
|
|
51
|
-
){
|
|
52
|
-
Canvas(
|
|
53
|
-
modifier = Modifier
|
|
54
|
-
.width(width)
|
|
55
|
-
.height(height)
|
|
56
|
-
.offset{ IntOffset(x = 0, y = 24) }
|
|
57
|
-
.zIndex(-1f)
|
|
58
|
-
) {
|
|
59
|
-
drawPath(path = curvedPath, color = theme.colors.background.surface)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
private fun getPath(circleOverTop: Float, centerX: Float, circleOverR: Float, widthPx: Float): Path{
|
|
65
|
-
return Path().apply {
|
|
66
|
-
moveTo(0f, circleOverTop)
|
|
67
|
-
lineTo(centerX - circleOverR - 40f, circleOverTop)
|
|
68
|
-
|
|
69
|
-
cubicTo(
|
|
70
|
-
centerX - circleOverR, circleOverTop,
|
|
71
|
-
centerX - circleOverR, 0f,
|
|
72
|
-
centerX, 0f
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
cubicTo(
|
|
76
|
-
centerX + circleOverR, 0f,
|
|
77
|
-
centerX + circleOverR, circleOverTop,
|
|
78
|
-
centerX + circleOverR + 40f, circleOverTop
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
lineTo(widthPx, circleOverTop)
|
|
82
|
-
lineTo(widthPx, 60f)
|
|
83
|
-
lineTo(0f, 60f)
|
|
84
|
-
close()
|
|
85
|
-
}
|
|
86
|
-
}
|
package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
package vn.momo.kits.navigation.bottomtab
|
|
2
|
-
|
|
3
|
-
import androidx.compose.runtime.Composable
|
|
4
|
-
import androidx.compose.runtime.Stable
|
|
5
|
-
import androidx.compose.runtime.compositionLocalOf
|
|
6
|
-
import androidx.compose.runtime.getValue
|
|
7
|
-
import androidx.compose.runtime.mutableStateOf
|
|
8
|
-
import androidx.compose.runtime.remember
|
|
9
|
-
import androidx.compose.runtime.setValue
|
|
10
|
-
import androidx.compose.ui.unit.Dp
|
|
11
|
-
import vn.momo.kits.const.Spacing
|
|
12
|
-
import vn.momo.kits.layout.DuoMetrics
|
|
13
|
-
import vn.momo.kits.layout.ScreenLayout
|
|
14
|
-
|
|
15
|
-
// iPhone Duo: which side bar bar starts compressed when the strip cannot hold both whole.
|
|
16
|
-
enum class DuoBarCompressionBehavior { TOOLBAR, TAB_BAR }
|
|
17
|
-
|
|
18
|
-
private enum class DuoBar { TOOLBAR, TAB_BAR }
|
|
19
|
-
|
|
20
|
-
@Stable
|
|
21
|
-
internal class DuoBarCompression(
|
|
22
|
-
val toolbarCompressed: Boolean = false,
|
|
23
|
-
val tabBarCompressed: Boolean = false,
|
|
24
|
-
// the open rail needs the toolbar button slot too: only Back stays
|
|
25
|
-
val toolbarHidden: Boolean = false,
|
|
26
|
-
val expandToolbar: () -> Unit = {},
|
|
27
|
-
val expandTabBar: () -> Unit = {},
|
|
28
|
-
// a rail opened from the compressed state closes once a tab is picked
|
|
29
|
-
val onTabSelected: () -> Unit = {},
|
|
30
|
-
// side bar columns report their full height so the tab bar knows what is left
|
|
31
|
-
val setToolbarHeight: (Dp) -> Unit = {},
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
internal val LocalDuoBarCompression = compositionLocalOf { DuoBarCompression() }
|
|
35
|
-
|
|
36
|
-
// space kept between the top and the bottom groups (HIG Vertical controls)
|
|
37
|
-
internal val DUO_GROUP_GAP = Spacing.M
|
|
38
|
-
// Back, then the right toolkit: help + home capsule
|
|
39
|
-
private val DEFAULT_TOOLBAR_HEIGHT = DuoMetrics.controlSize + Spacing.M + DuoMetrics.itemPitch * 2
|
|
40
|
-
// Back, then the single button the toolbar folds into
|
|
41
|
-
private val COMPRESSED_TOOLBAR_HEIGHT = DuoMetrics.controlSize * 2 + Spacing.M
|
|
42
|
-
|
|
43
|
-
// Port of RN DuoBarCompression (HIG, Designing for iPhone Duo: Vertical controls):
|
|
44
|
-
// - both fit: both stay whole
|
|
45
|
-
// - one fits whole beside the other compressed: one is compressed, tapping it swaps them
|
|
46
|
-
// - otherwise both rest compressed; tapping one opens it for a moment (the rail closes
|
|
47
|
-
// again once a tab is picked)
|
|
48
|
-
@Composable
|
|
49
|
-
internal fun rememberDuoBarCompression(
|
|
50
|
-
behavior: DuoBarCompressionBehavior,
|
|
51
|
-
tabCount: Int,
|
|
52
|
-
hasQr: Boolean,
|
|
53
|
-
layout: ScreenLayout,
|
|
54
|
-
): DuoBarCompression {
|
|
55
|
-
var toolbarHeight by remember { mutableStateOf(DEFAULT_TOOLBAR_HEIGHT) }
|
|
56
|
-
var expanded by remember(behavior) {
|
|
57
|
-
mutableStateOf(if (behavior == DuoBarCompressionBehavior.TOOLBAR) DuoBar.TAB_BAR else DuoBar.TOOLBAR)
|
|
58
|
-
}
|
|
59
|
-
var opened by remember { mutableStateOf<DuoBar?>(null) }
|
|
60
|
-
val setToolbarHeight = remember { { height: Dp -> toolbarHeight = height } }
|
|
61
|
-
val sideBar = layout.sideBar
|
|
62
|
-
|
|
63
|
-
return remember(sideBar, layout.height, tabCount, hasQr, toolbarHeight, expanded, opened) {
|
|
64
|
-
if (sideBar == null) return@remember DuoBarCompression()
|
|
65
|
-
val room = layout.height - sideBar.bottom - sideBar.top - DUO_GROUP_GAP
|
|
66
|
-
val tabBar = duoRailHeight(tabCount, hasQr)
|
|
67
|
-
val compressedTabBar = duoRailHeight(1, hasQr, compressed = true)
|
|
68
|
-
fun fits(toolbar: Dp, tabs: Dp) = toolbar + tabs <= room
|
|
69
|
-
|
|
70
|
-
when {
|
|
71
|
-
fits(toolbarHeight, tabBar) -> DuoBarCompression(setToolbarHeight = setToolbarHeight)
|
|
72
|
-
fits(COMPRESSED_TOOLBAR_HEIGHT, tabBar) && fits(toolbarHeight, compressedTabBar) -> DuoBarCompression(
|
|
73
|
-
toolbarCompressed = expanded == DuoBar.TAB_BAR,
|
|
74
|
-
tabBarCompressed = expanded == DuoBar.TOOLBAR,
|
|
75
|
-
expandToolbar = { expanded = DuoBar.TOOLBAR },
|
|
76
|
-
expandTabBar = { expanded = DuoBar.TAB_BAR },
|
|
77
|
-
setToolbarHeight = setToolbarHeight,
|
|
78
|
-
)
|
|
79
|
-
// an opened rail always shows, even when only the group gap is short: it closes once a tab is picked
|
|
80
|
-
opened == DuoBar.TAB_BAR -> DuoBarCompression(
|
|
81
|
-
toolbarCompressed = true,
|
|
82
|
-
toolbarHidden = true,
|
|
83
|
-
expandToolbar = { opened = DuoBar.TOOLBAR },
|
|
84
|
-
expandTabBar = { opened = DuoBar.TAB_BAR },
|
|
85
|
-
onTabSelected = { opened = null },
|
|
86
|
-
setToolbarHeight = setToolbarHeight,
|
|
87
|
-
)
|
|
88
|
-
else -> DuoBarCompression(
|
|
89
|
-
toolbarCompressed = opened != DuoBar.TOOLBAR,
|
|
90
|
-
tabBarCompressed = true,
|
|
91
|
-
expandToolbar = { opened = DuoBar.TOOLBAR },
|
|
92
|
-
expandTabBar = { opened = DuoBar.TAB_BAR },
|
|
93
|
-
setToolbarHeight = setToolbarHeight,
|
|
94
|
-
)
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoLiquidSelection.kt
DELETED
|
@@ -1,380 +0,0 @@
|
|
|
1
|
-
package vn.momo.kits.navigation.bottomtab
|
|
2
|
-
|
|
3
|
-
import androidx.compose.animation.core.Animatable
|
|
4
|
-
import androidx.compose.animation.core.SpringSpec
|
|
5
|
-
import androidx.compose.animation.core.spring
|
|
6
|
-
import androidx.compose.animation.core.tween
|
|
7
|
-
import androidx.compose.foundation.gestures.awaitEachGesture
|
|
8
|
-
import androidx.compose.foundation.gestures.awaitFirstDown
|
|
9
|
-
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
10
|
-
import androidx.compose.foundation.interaction.PressInteraction
|
|
11
|
-
import androidx.compose.runtime.Composable
|
|
12
|
-
import androidx.compose.runtime.LaunchedEffect
|
|
13
|
-
import androidx.compose.runtime.Stable
|
|
14
|
-
import androidx.compose.runtime.getValue
|
|
15
|
-
import androidx.compose.runtime.mutableFloatStateOf
|
|
16
|
-
import androidx.compose.runtime.mutableIntStateOf
|
|
17
|
-
import androidx.compose.runtime.mutableStateOf
|
|
18
|
-
import androidx.compose.runtime.remember
|
|
19
|
-
import androidx.compose.runtime.rememberCoroutineScope
|
|
20
|
-
import androidx.compose.runtime.rememberUpdatedState
|
|
21
|
-
import androidx.compose.runtime.setValue
|
|
22
|
-
import androidx.compose.runtime.snapshotFlow
|
|
23
|
-
import androidx.compose.ui.Modifier
|
|
24
|
-
import androidx.compose.ui.geometry.Offset
|
|
25
|
-
import androidx.compose.ui.graphics.graphicsLayer
|
|
26
|
-
import androidx.compose.ui.input.pointer.pointerInput
|
|
27
|
-
import androidx.compose.ui.input.pointer.util.VelocityTracker
|
|
28
|
-
import androidx.compose.ui.platform.LocalDensity
|
|
29
|
-
import androidx.compose.ui.unit.Dp
|
|
30
|
-
import androidx.compose.ui.unit.dp
|
|
31
|
-
import kotlinx.coroutines.CoroutineScope
|
|
32
|
-
import kotlinx.coroutines.Job
|
|
33
|
-
import kotlinx.coroutines.delay
|
|
34
|
-
import kotlinx.coroutines.launch
|
|
35
|
-
import vn.momo.kits.platform.rememberReduceMotion
|
|
36
|
-
import kotlin.math.abs
|
|
37
|
-
import kotlin.math.min
|
|
38
|
-
import kotlin.math.roundToInt
|
|
39
|
-
|
|
40
|
-
// iOS 26 tab bar selection motion without the glass material, port of RN useLiquidSelection.ts.
|
|
41
|
-
// Constants mirror RN; springs are RN (mass, stiffness, damping) converted to mass 1.
|
|
42
|
-
private val SPRING = spring<Float>(dampingRatio = 0.68f, stiffness = 275f)
|
|
43
|
-
// the lens trails the finger a little while dragged (RN damping 24, stiffness 600, mass 0.6)
|
|
44
|
-
private val DRAG_SPRING = spring<Float>(dampingRatio = 0.63f, stiffness = 1000f)
|
|
45
|
-
private val REDUCED_TWEEN = tween<Float>(durationMillis = 200)
|
|
46
|
-
// while pressed the selection bubble grows into a lens past the bar's edges (80x54 to about 96x70)
|
|
47
|
-
private const val LENS_SCALE_X = 1.2f
|
|
48
|
-
private const val LENS_SCALE_Y = 1.3f
|
|
49
|
-
// a moving lens stretches along its path and thins across it, eased per frame
|
|
50
|
-
private const val STRETCH_PER_POINT = 0.012f
|
|
51
|
-
private const val MAX_STRETCH = 0.25f
|
|
52
|
-
private const val STRETCH_EASING = 0.25f
|
|
53
|
-
private const val REST_STRETCH = 0.002f
|
|
54
|
-
// a tap is shorter than the lift: the lens finishes rising, then settles, one smooth pulse
|
|
55
|
-
private const val TAP_LIFT_MILLIS = 120
|
|
56
|
-
private const val BAR_SWELL = 0.04f
|
|
57
|
-
private const val ICON_POP = 1.15f
|
|
58
|
-
private const val ICON_POP_MILLIS = 120
|
|
59
|
-
// below it the tab's clickable handles the press as a tap
|
|
60
|
-
private val DRAG_SLOP = 10.dp
|
|
61
|
-
// UIScrollView rubber band constant
|
|
62
|
-
private const val RUBBER_BAND = 0.55f
|
|
63
|
-
// a cancelled press or a vetoed switch: the lens returns to the selected tab after this
|
|
64
|
-
private const val RESYNC_DELAY_MILLIS = 100L
|
|
65
|
-
|
|
66
|
-
internal enum class LiquidAxis { X, Y }
|
|
67
|
-
|
|
68
|
-
// resistance past the first and last tab, the further the finger goes the less the lens follows
|
|
69
|
-
private fun rubberBand(value: Float, max: Float, dimension: Float): Float {
|
|
70
|
-
fun band(overshoot: Float) = (1 - 1 / (overshoot * RUBBER_BAND / dimension + 1)) * dimension
|
|
71
|
-
return when {
|
|
72
|
-
value < 0 -> -band(-value)
|
|
73
|
-
value > max -> max + band(value - max)
|
|
74
|
-
else -> value
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
@Stable
|
|
79
|
-
internal class DuoLiquidSelection(
|
|
80
|
-
private val scope: CoroutineScope,
|
|
81
|
-
private val axis: LiquidAxis,
|
|
82
|
-
private val density: Float,
|
|
83
|
-
) {
|
|
84
|
-
var count = 0
|
|
85
|
-
// px between two neighbouring tab centers / to the first tab's center inside the drag modifier
|
|
86
|
-
var pitch = 0f
|
|
87
|
-
var firstCenter = 0f
|
|
88
|
-
var reduceMotion = false
|
|
89
|
-
var onSelect: (Int) -> Unit = {}
|
|
90
|
-
var onTick: () -> Unit = {}
|
|
91
|
-
|
|
92
|
-
val offset = Animatable(0f)
|
|
93
|
-
val lift = Animatable(0f)
|
|
94
|
-
var stretch by mutableFloatStateOf(0f)
|
|
95
|
-
private set
|
|
96
|
-
var selectedIndex by mutableIntStateOf(0)
|
|
97
|
-
private set
|
|
98
|
-
|
|
99
|
-
// where the lens is heading, so a recomposition doesn't restart a spring already going there
|
|
100
|
-
private var destination = 0f
|
|
101
|
-
private var dragging = false
|
|
102
|
-
private var hovered = 0
|
|
103
|
-
private var measured = false
|
|
104
|
-
private var resyncJob: Job? = null
|
|
105
|
-
|
|
106
|
-
private fun move(target: Float, velocity: Float? = null, spec: SpringSpec<Float> = SPRING) {
|
|
107
|
-
if (destination == target && velocity == null) return
|
|
108
|
-
destination = target
|
|
109
|
-
scope.launch {
|
|
110
|
-
if (reduceMotion) offset.animateTo(target, REDUCED_TWEEN)
|
|
111
|
-
else offset.animateTo(target, spec, initialVelocity = velocity ?: offset.velocity)
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
private fun animateLift(target: Float) {
|
|
116
|
-
scope.launch { if (reduceMotion) lift.snapTo(0f) else lift.animateTo(target, SPRING) }
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
suspend fun sync(index: Int) {
|
|
120
|
-
selectedIndex = index
|
|
121
|
-
val target = index * pitch
|
|
122
|
-
// snap on first layout so the lens doesn't slide in from the leading edge
|
|
123
|
-
if (measured) {
|
|
124
|
-
move(target)
|
|
125
|
-
} else {
|
|
126
|
-
destination = target
|
|
127
|
-
offset.snapTo(target)
|
|
128
|
-
}
|
|
129
|
-
measured = pitch > 0
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
suspend fun trackStretch() {
|
|
133
|
-
var previous = offset.value
|
|
134
|
-
snapshotFlow { offset.value }.collect { current ->
|
|
135
|
-
if (reduceMotion) {
|
|
136
|
-
stretch = 0f
|
|
137
|
-
} else {
|
|
138
|
-
val target = min(abs(current - previous) / density * STRETCH_PER_POINT, MAX_STRETCH)
|
|
139
|
-
// eased both ways: a slow frame (the spring turning at its overshoot) must not snap the shape
|
|
140
|
-
val next = stretch + (target - stretch) * STRETCH_EASING
|
|
141
|
-
stretch = if (next < REST_STRETCH) 0f else next
|
|
142
|
-
}
|
|
143
|
-
previous = current
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// returns the lens to the selected tab unless a switch to where it is heading happened
|
|
148
|
-
private fun resync() {
|
|
149
|
-
resyncJob?.cancel()
|
|
150
|
-
resyncJob = scope.launch {
|
|
151
|
-
delay(RESYNC_DELAY_MILLIS)
|
|
152
|
-
val target = selectedIndex * pitch
|
|
153
|
-
if (!dragging && destination != target) move(target)
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
fun pressIn(index: Int) {
|
|
158
|
-
resyncJob?.cancel()
|
|
159
|
-
// the lens heads to the pressed tab, not only the selected one
|
|
160
|
-
move(index * pitch)
|
|
161
|
-
animateLift(1f)
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// false when a drag took the press over
|
|
165
|
-
fun pressOut(): Boolean {
|
|
166
|
-
if (dragging) return false
|
|
167
|
-
if (!reduceMotion) {
|
|
168
|
-
scope.launch {
|
|
169
|
-
lift.animateTo(1f, tween(TAP_LIFT_MILLIS))
|
|
170
|
-
lift.animateTo(0f, SPRING)
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
// the lens keeps heading to the pressed tab: onClick switches to it, a cancelled press brings it back
|
|
174
|
-
resync()
|
|
175
|
-
return true
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
private fun position(point: Offset) = (if (axis == LiquidAxis.X) point.x else point.y) - firstCenter
|
|
179
|
-
|
|
180
|
-
private fun nearest(value: Float): Int =
|
|
181
|
-
if (pitch > 0) (value / pitch).roundToInt().coerceIn(0, (count - 1).coerceAtLeast(0)) else 0
|
|
182
|
-
|
|
183
|
-
private fun dragStart(point: Offset) {
|
|
184
|
-
dragging = true
|
|
185
|
-
resyncJob?.cancel()
|
|
186
|
-
hovered = nearest(position(point))
|
|
187
|
-
animateLift(1f)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
private fun dragUpdate(point: Offset) {
|
|
191
|
-
val value = position(point)
|
|
192
|
-
val target = nearest(value)
|
|
193
|
-
if (target != hovered) {
|
|
194
|
-
hovered = target
|
|
195
|
-
onTick()
|
|
196
|
-
}
|
|
197
|
-
val follow = rubberBand(value, (count - 1) * pitch, pitch)
|
|
198
|
-
if (reduceMotion) {
|
|
199
|
-
destination = follow
|
|
200
|
-
scope.launch { offset.snapTo(follow) }
|
|
201
|
-
} else {
|
|
202
|
-
move(follow, spec = DRAG_SPRING)
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
private fun dragEnd(point: Offset?, velocity: Float) {
|
|
207
|
-
dragging = false
|
|
208
|
-
animateLift(0f)
|
|
209
|
-
if (point == null) {
|
|
210
|
-
move(selectedIndex * pitch, velocity)
|
|
211
|
-
return
|
|
212
|
-
}
|
|
213
|
-
val target = nearest(position(point))
|
|
214
|
-
move(target * pitch, velocity)
|
|
215
|
-
onSelect(target)
|
|
216
|
-
resync()
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// The drag consumes the pointer once past the slop, which cancels the tab's clickable,
|
|
220
|
-
// so a press is either a tap or a drag.
|
|
221
|
-
val dragGesture: Modifier = Modifier.pointerInput(Unit) {
|
|
222
|
-
val slop = DRAG_SLOP.toPx()
|
|
223
|
-
awaitEachGesture {
|
|
224
|
-
val down = awaitFirstDown(requireUnconsumed = false)
|
|
225
|
-
val tracker = VelocityTracker()
|
|
226
|
-
tracker.addPosition(down.uptimeMillis, down.position)
|
|
227
|
-
var active = false
|
|
228
|
-
var last = down.position
|
|
229
|
-
while (true) {
|
|
230
|
-
val change = awaitPointerEvent().changes.firstOrNull { it.id == down.id }
|
|
231
|
-
if (change == null) {
|
|
232
|
-
if (active) dragEnd(null, 0f)
|
|
233
|
-
break
|
|
234
|
-
}
|
|
235
|
-
tracker.addPosition(change.uptimeMillis, change.position)
|
|
236
|
-
if (!change.pressed) {
|
|
237
|
-
if (active) {
|
|
238
|
-
change.consume()
|
|
239
|
-
val velocity = tracker.calculateVelocity()
|
|
240
|
-
dragEnd(change.position, if (axis == LiquidAxis.X) velocity.x else velocity.y)
|
|
241
|
-
}
|
|
242
|
-
break
|
|
243
|
-
}
|
|
244
|
-
last = change.position
|
|
245
|
-
if (!active && (last - down.position).getDistance() > slop) {
|
|
246
|
-
active = true
|
|
247
|
-
dragStart(last)
|
|
248
|
-
}
|
|
249
|
-
if (active) {
|
|
250
|
-
change.consume()
|
|
251
|
-
dragUpdate(last)
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
val bubbleTransform: Modifier = Modifier.graphicsLayer {
|
|
258
|
-
val along = 1 + stretch
|
|
259
|
-
val across = 1 - stretch * 0.5f
|
|
260
|
-
val lensX = 1 + lift.value * (LENS_SCALE_X - 1)
|
|
261
|
-
val lensY = 1 + lift.value * (LENS_SCALE_Y - 1)
|
|
262
|
-
if (axis == LiquidAxis.X) {
|
|
263
|
-
translationX = offset.value
|
|
264
|
-
scaleX = lensX * along
|
|
265
|
-
scaleY = lensY * across
|
|
266
|
-
} else {
|
|
267
|
-
translationY = offset.value
|
|
268
|
-
scaleX = lensX * across
|
|
269
|
-
scaleY = lensY * along
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
val barTransform: Modifier = Modifier.graphicsLayer {
|
|
274
|
-
scaleX = 1 + lift.value * BAR_SWELL
|
|
275
|
-
scaleY = scaleX
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// active tint of a tab follows the lens over it
|
|
279
|
-
fun activeAlpha(index: Int): Float {
|
|
280
|
-
if (pitch <= 0) return if (selectedIndex == index) 1f else 0f
|
|
281
|
-
return (1 - abs(offset.value - index * pitch) / pitch).coerceIn(0f, 1f)
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
@Composable
|
|
286
|
-
internal fun rememberDuoLiquidSelection(
|
|
287
|
-
count: Int,
|
|
288
|
-
pitch: Dp,
|
|
289
|
-
firstCenter: Dp,
|
|
290
|
-
axis: LiquidAxis,
|
|
291
|
-
index: Int,
|
|
292
|
-
onSelect: (Int) -> Unit,
|
|
293
|
-
onTick: () -> Unit,
|
|
294
|
-
): DuoLiquidSelection {
|
|
295
|
-
val scope = rememberCoroutineScope()
|
|
296
|
-
val density = LocalDensity.current
|
|
297
|
-
val selection = remember(axis) { DuoLiquidSelection(scope, axis, density.density) }
|
|
298
|
-
val reduceMotion = rememberReduceMotion()
|
|
299
|
-
val pitchPx = with(density) { pitch.toPx() }
|
|
300
|
-
selection.count = count
|
|
301
|
-
selection.pitch = pitchPx
|
|
302
|
-
selection.firstCenter = with(density) { firstCenter.toPx() }
|
|
303
|
-
selection.reduceMotion = reduceMotion
|
|
304
|
-
selection.onSelect = onSelect
|
|
305
|
-
selection.onTick = onTick
|
|
306
|
-
|
|
307
|
-
LaunchedEffect(selection, index, pitchPx) { selection.sync(index) }
|
|
308
|
-
LaunchedEffect(selection) { selection.trackStretch() }
|
|
309
|
-
return selection
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// Press tracking for one tab: lifts the lens over it and, like the system tab bar, still picks a
|
|
313
|
-
// held tab when released. Returns the long-press handler for the tab's combinedClickable.
|
|
314
|
-
@Composable
|
|
315
|
-
internal fun DuoLiquidSelection.rememberPressTracking(
|
|
316
|
-
index: Int,
|
|
317
|
-
interactionSource: MutableInteractionSource,
|
|
318
|
-
onSelect: () -> Unit,
|
|
319
|
-
onLongPress: (() -> Unit)?,
|
|
320
|
-
): () -> Unit {
|
|
321
|
-
val longPressed = remember { mutableStateOf(false) }
|
|
322
|
-
val currentOnSelect by rememberUpdatedState(onSelect)
|
|
323
|
-
val currentOnLongPress by rememberUpdatedState(onLongPress)
|
|
324
|
-
LaunchedEffect(interactionSource, index) {
|
|
325
|
-
interactionSource.interactions.collect {
|
|
326
|
-
when (it) {
|
|
327
|
-
is PressInteraction.Press -> {
|
|
328
|
-
longPressed.value = false
|
|
329
|
-
pressIn(index)
|
|
330
|
-
}
|
|
331
|
-
is PressInteraction.Release -> if (pressOut() && longPressed.value) currentOnSelect()
|
|
332
|
-
is PressInteraction.Cancel -> pressOut()
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
return remember {
|
|
337
|
-
{
|
|
338
|
-
longPressed.value = true
|
|
339
|
-
currentOnLongPress?.invoke()
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// The tab the lens lands on pops.
|
|
345
|
-
@Composable
|
|
346
|
-
internal fun DuoLiquidSelection.rememberIconPop(index: Int): Animatable<Float, *> {
|
|
347
|
-
val pop = remember { Animatable(1f) }
|
|
348
|
-
val isSelected = selectedIndex == index
|
|
349
|
-
val initial = remember { mutableStateOf(true) }
|
|
350
|
-
LaunchedEffect(isSelected) {
|
|
351
|
-
if (initial.value) {
|
|
352
|
-
initial.value = false
|
|
353
|
-
return@LaunchedEffect
|
|
354
|
-
}
|
|
355
|
-
if (isSelected && !reduceMotion) {
|
|
356
|
-
pop.animateTo(ICON_POP, tween(ICON_POP_MILLIS))
|
|
357
|
-
pop.animateTo(1f, SPRING)
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
return pop
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// Single button version of the lift, e.g. the compressed rail.
|
|
364
|
-
@Composable
|
|
365
|
-
internal fun Modifier.pressLift(interactionSource: MutableInteractionSource): Modifier {
|
|
366
|
-
val lift = remember { Animatable(0f) }
|
|
367
|
-
val reduceMotion by rememberUpdatedState(rememberReduceMotion())
|
|
368
|
-
LaunchedEffect(interactionSource) {
|
|
369
|
-
interactionSource.interactions.collect {
|
|
370
|
-
when (it) {
|
|
371
|
-
is PressInteraction.Press -> if (!reduceMotion) launch { lift.animateTo(1f, SPRING) }
|
|
372
|
-
is PressInteraction.Release, is PressInteraction.Cancel -> launch { lift.animateTo(0f, SPRING) }
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
return graphicsLayer {
|
|
377
|
-
scaleX = 1 + lift.value * (LENS_SCALE_X - 1) * 0.5f
|
|
378
|
-
scaleY = scaleX
|
|
379
|
-
}
|
|
380
|
-
}
|