@momo-kits/native-kits 0.167.1-beta.4-debug → 0.167.1-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/build.gradle.kts +0 -11
- package/compose/build.gradle.kts +0 -190
- package/compose/build.gradle.kts.backup +0 -190
- package/compose/compose.podspec +0 -47
- package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +0 -19
- 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 -151
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -52
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -124
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -183
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -207
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -39
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -293
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -831
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +0 -269
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -110
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -378
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +0 -56
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
- 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 -82
- 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 -356
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -113
- 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 -76
- 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 -475
- 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 -266
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -240
- 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 -264
- 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 -328
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -97
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -310
- 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 -323
- 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 -199
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -91
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -552
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -88
- 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 -498
- 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 -234
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -188
- 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 -16
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +0 -18
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
- 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 -188
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -296
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -52
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -38
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -39
- 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 -299
- 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 -129
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -106
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -201
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -393
- 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 -595
- 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 -175
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -225
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
- 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 -358
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -83
- 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 -75
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -309
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -37
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -364
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -132
- 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 -71
- 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 -105
- package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +0 -40
- 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 -213
- 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/local.properties +0 -8
- package/settings.gradle.kts +0 -64
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
package vn.momo.kits.navigation
|
|
2
|
-
|
|
3
|
-
import androidx.compose.animation.AnimatedContentTransitionScope
|
|
4
|
-
import androidx.compose.animation.AnimatedVisibilityScope
|
|
5
|
-
import androidx.compose.animation.EnterExitState
|
|
6
|
-
import androidx.compose.animation.EnterTransition
|
|
7
|
-
import androidx.compose.animation.ExitTransition
|
|
8
|
-
import androidx.compose.animation.core.LinearEasing
|
|
9
|
-
import androidx.compose.animation.core.animateFloat
|
|
10
|
-
import androidx.compose.animation.core.tween
|
|
11
|
-
import androidx.compose.animation.slideInHorizontally
|
|
12
|
-
import androidx.compose.animation.slideOutHorizontally
|
|
13
|
-
import androidx.compose.foundation.layout.Box
|
|
14
|
-
import androidx.compose.foundation.layout.fillMaxSize
|
|
15
|
-
import androidx.compose.runtime.Composable
|
|
16
|
-
import androidx.compose.runtime.DisposableEffect
|
|
17
|
-
import androidx.compose.runtime.Stable
|
|
18
|
-
import androidx.compose.runtime.derivedStateOf
|
|
19
|
-
import androidx.compose.runtime.getValue
|
|
20
|
-
import androidx.compose.runtime.mutableStateMapOf
|
|
21
|
-
import androidx.compose.runtime.remember
|
|
22
|
-
import androidx.compose.runtime.staticCompositionLocalOf
|
|
23
|
-
import androidx.compose.ui.Modifier
|
|
24
|
-
import androidx.compose.ui.draw.drawBehind
|
|
25
|
-
import androidx.compose.ui.geometry.Offset
|
|
26
|
-
import androidx.compose.ui.geometry.Size
|
|
27
|
-
import androidx.compose.ui.graphics.Brush
|
|
28
|
-
import androidx.compose.ui.graphics.Color
|
|
29
|
-
import androidx.compose.ui.platform.LocalDensity
|
|
30
|
-
import androidx.compose.ui.unit.IntOffset
|
|
31
|
-
import androidx.compose.ui.unit.dp
|
|
32
|
-
import androidx.navigation.NavBackStackEntry
|
|
33
|
-
import androidx.navigation.NavDestination.Companion.hasRoute
|
|
34
|
-
import vn.momo.kits.const.Colors
|
|
35
|
-
import kotlin.math.roundToInt
|
|
36
|
-
|
|
37
|
-
internal const val STACK_TRANSITION_DURATION = 300
|
|
38
|
-
|
|
39
|
-
// Ported 1:1 from the RN kit's `forStackTransitionWithOverlay` so both kits look identical.
|
|
40
|
-
private const val PARALLAX_RATIO = 0.3f
|
|
41
|
-
private const val SCRIM_MAX_ALPHA = 0.6f
|
|
42
|
-
private const val EDGE_SHADOW_MAX_ALPHA = 0.3f
|
|
43
|
-
private val EDGE_SHADOW_WIDTH = 8.dp
|
|
44
|
-
|
|
45
|
-
/** Kill switch: `false` restores pre-DS-760 back handling without a kit release. Transitions
|
|
46
|
-
* stay on the new curve either way. */
|
|
47
|
-
object SwipeBackConfig {
|
|
48
|
-
var enabled: Boolean = true
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// LinearEasing is mandatory: seekTo() maps drag fraction onto the timeline, so a curve would
|
|
52
|
-
// decouple the card from the finger.
|
|
53
|
-
private val stackSpec = tween<IntOffset>(STACK_TRANSITION_DURATION, easing = LinearEasing)
|
|
54
|
-
|
|
55
|
-
internal fun AnimatedContentTransitionScope<NavBackStackEntry>.stackEnter(): EnterTransition =
|
|
56
|
-
slideInHorizontally(stackSpec) { it }
|
|
57
|
-
|
|
58
|
-
internal fun AnimatedContentTransitionScope<NavBackStackEntry>.stackExit(): ExitTransition =
|
|
59
|
-
if (targetState.destination.hasRoute<DynamicDialogRoute>()) ExitTransition.None
|
|
60
|
-
else slideOutHorizontally(stackSpec) { -(it * PARALLAX_RATIO).roundToInt() }
|
|
61
|
-
|
|
62
|
-
internal fun AnimatedContentTransitionScope<NavBackStackEntry>.stackPopEnter(): EnterTransition =
|
|
63
|
-
if (initialState.destination.hasRoute<DynamicDialogRoute>()) EnterTransition.None
|
|
64
|
-
else slideInHorizontally(stackSpec) { -(it * PARALLAX_RATIO).roundToInt() }
|
|
65
|
-
|
|
66
|
-
internal fun AnimatedContentTransitionScope<NavBackStackEntry>.stackPopExit(): ExitTransition =
|
|
67
|
-
slideOutHorizontally(stackSpec) { it }
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Which composed screen is the top card. Ids are monotonic and NavHost keeps both entries composed
|
|
71
|
-
* for the whole segment, so "highest id" is stable both ways. `currentBackStackEntry` is not: it
|
|
72
|
-
* flips at frame 0 of a pop and the scrim would vanish instead of fading.
|
|
73
|
-
*/
|
|
74
|
-
@Stable
|
|
75
|
-
internal class StackTransitionCoordinator {
|
|
76
|
-
private val composedRoutes = mutableStateMapOf<Int, Boolean>()
|
|
77
|
-
|
|
78
|
-
val topStackId: Int by derivedStateOf {
|
|
79
|
-
composedRoutes.entries.filter { it.value }.maxOfOrNull { it.key } ?: -1
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
fun attach(id: Int, isStackRoute: Boolean) {
|
|
83
|
-
composedRoutes[id] = isStackRoute
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
fun detach(id: Int) {
|
|
87
|
-
composedRoutes.remove(id)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
internal val LocalStackTransition = staticCompositionLocalOf { StackTransitionCoordinator() }
|
|
92
|
-
|
|
93
|
-
/** Draws the covered-screen scrim and the top card's left-edge shadow around a stack destination. */
|
|
94
|
-
@Composable
|
|
95
|
-
internal fun AnimatedVisibilityScope.StackTransitionDecor(
|
|
96
|
-
id: Int,
|
|
97
|
-
isStackRoute: Boolean,
|
|
98
|
-
content: @Composable () -> Unit,
|
|
99
|
-
) {
|
|
100
|
-
val coordinator = LocalStackTransition.current
|
|
101
|
-
coordinator.attach(id, isStackRoute)
|
|
102
|
-
DisposableEffect(coordinator, id) {
|
|
103
|
-
onDispose { coordinator.detach(id) }
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
val isTopCard = isStackRoute && id == coordinator.topStackId
|
|
107
|
-
val isCovered = isStackRoute && !isTopCard
|
|
108
|
-
|
|
109
|
-
val scrimAlpha = transition.animateFloat(
|
|
110
|
-
transitionSpec = { tween(STACK_TRANSITION_DURATION, easing = LinearEasing) },
|
|
111
|
-
label = "stackScrimAlpha",
|
|
112
|
-
) { state -> if (isCovered && state != EnterExitState.Visible) SCRIM_MAX_ALPHA else 0f }
|
|
113
|
-
|
|
114
|
-
val shadowAlpha = transition.animateFloat(
|
|
115
|
-
transitionSpec = { tween(STACK_TRANSITION_DURATION, easing = LinearEasing) },
|
|
116
|
-
label = "stackEdgeShadowAlpha",
|
|
117
|
-
) { state -> if (isTopCard && state == EnterExitState.Visible) EDGE_SHADOW_MAX_ALPHA else 0f }
|
|
118
|
-
|
|
119
|
-
val shadowWidthPx = with(LocalDensity.current) { EDGE_SHADOW_WIDTH.toPx() }
|
|
120
|
-
val shadowBrush = remember(shadowWidthPx) {
|
|
121
|
-
Brush.horizontalGradient(
|
|
122
|
-
colors = listOf(Color.Transparent, Colors.black_20),
|
|
123
|
-
startX = -shadowWidthPx,
|
|
124
|
-
endX = 0f,
|
|
125
|
-
)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
Box(Modifier.fillMaxSize()) {
|
|
129
|
-
content()
|
|
130
|
-
|
|
131
|
-
if (isStackRoute) {
|
|
132
|
-
Box(
|
|
133
|
-
Modifier
|
|
134
|
-
.matchParentSize()
|
|
135
|
-
// read inside drawBehind: in the body this would recompose every gesture frame
|
|
136
|
-
.drawBehind {
|
|
137
|
-
drawRect(
|
|
138
|
-
brush = shadowBrush,
|
|
139
|
-
topLeft = Offset(-shadowWidthPx, 0f),
|
|
140
|
-
size = Size(shadowWidthPx, size.height),
|
|
141
|
-
alpha = shadowAlpha.value,
|
|
142
|
-
)
|
|
143
|
-
drawRect(Colors.pink_09, alpha = scrimAlpha.value)
|
|
144
|
-
}
|
|
145
|
-
)
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
package vn.momo.kits.navigation
|
|
2
|
-
|
|
3
|
-
import androidx.compose.runtime.Composable
|
|
4
|
-
import androidx.compose.runtime.LaunchedEffect
|
|
5
|
-
import androidx.compose.runtime.snapshotFlow
|
|
6
|
-
import vn.momo.kits.platform.getScreenDimensions
|
|
7
|
-
import vn.momo.kits.platform.rememberBackGestureProgress
|
|
8
|
-
import vn.momo.maxapi.IMaxApi
|
|
9
|
-
|
|
10
|
-
// Unscaled, so the tick lands at the same physical distance on every device.
|
|
11
|
-
private const val SWIPE_BACK_HAPTIC_THRESHOLD_DP = 50f
|
|
12
|
-
|
|
13
|
-
/** One light tick per crossing, re-armed below the threshold. iOS only. */
|
|
14
|
-
@Composable
|
|
15
|
-
internal fun SwipeBackHaptic(maxApi: IMaxApi?) {
|
|
16
|
-
if (maxApi == null) return
|
|
17
|
-
|
|
18
|
-
val progress = rememberBackGestureProgress()
|
|
19
|
-
val screenWidth = getScreenDimensions().width.toFloat()
|
|
20
|
-
|
|
21
|
-
LaunchedEffect(maxApi, screenWidth) {
|
|
22
|
-
if (screenWidth <= 0f) return@LaunchedEffect
|
|
23
|
-
val threshold = SWIPE_BACK_HAPTIC_THRESHOLD_DP / screenWidth
|
|
24
|
-
var isSendHaptic = false
|
|
25
|
-
snapshotFlow { progress.value }.collect { fraction ->
|
|
26
|
-
val travelled = fraction ?: 0f
|
|
27
|
-
if (travelled >= threshold && !isSendHaptic) {
|
|
28
|
-
runCatching { maxApi.triggerEventVibration("light", callback = null) }
|
|
29
|
-
isSendHaptic = true
|
|
30
|
-
} else if (travelled < threshold && isSendHaptic) {
|
|
31
|
-
isSendHaptic = false
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
package vn.momo.kits.navigation.bottomtab
|
|
2
|
-
|
|
3
|
-
import androidx.compose.animation.core.tween
|
|
4
|
-
import androidx.compose.animation.fadeIn
|
|
5
|
-
import androidx.compose.animation.fadeOut
|
|
6
|
-
import androidx.compose.animation.scaleIn
|
|
7
|
-
import androidx.compose.animation.scaleOut
|
|
8
|
-
import androidx.compose.foundation.background
|
|
9
|
-
import androidx.compose.foundation.layout.*
|
|
10
|
-
import androidx.compose.runtime.Composable
|
|
11
|
-
import androidx.compose.runtime.DisposableEffect
|
|
12
|
-
import androidx.compose.runtime.LaunchedEffect
|
|
13
|
-
import androidx.compose.ui.Alignment
|
|
14
|
-
import androidx.compose.ui.Modifier
|
|
15
|
-
import androidx.compose.ui.unit.dp
|
|
16
|
-
import androidx.navigation.compose.NavHost
|
|
17
|
-
import androidx.navigation.compose.composable
|
|
18
|
-
import androidx.navigation.compose.currentBackStackEntryAsState
|
|
19
|
-
import androidx.navigation.compose.rememberNavController
|
|
20
|
-
import vn.momo.kits.const.AppNavigationBar
|
|
21
|
-
import vn.momo.kits.const.AppTheme
|
|
22
|
-
import vn.momo.kits.const.Spacing
|
|
23
|
-
import vn.momo.kits.navigation.KeyboardOptions
|
|
24
|
-
import vn.momo.kits.navigation.LocalNavigation
|
|
25
|
-
import vn.momo.kits.navigation.LocalNavigator
|
|
26
|
-
import vn.momo.kits.navigation.NavigationOptions
|
|
27
|
-
import vn.momo.kits.navigation.StackScreen
|
|
28
|
-
import vn.momo.kits.navigation.component.HeaderType
|
|
29
|
-
import vn.momo.kits.platform.reportFloatingArrowBack
|
|
30
|
-
|
|
31
|
-
private var bottomTabOptionItems : MutableList<NavigationOptions?> = mutableListOf()
|
|
32
|
-
internal fun setBottomTabOption(index: Int, options: NavigationOptions){
|
|
33
|
-
if (index in bottomTabOptionItems.indices) {
|
|
34
|
-
bottomTabOptionItems[index] = options
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
internal fun getBottomTabOption(index: Int): NavigationOptions? {
|
|
38
|
-
return if (index in bottomTabOptionItems.indices) {
|
|
39
|
-
bottomTabOptionItems[index]
|
|
40
|
-
} else null
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
@Composable
|
|
44
|
-
fun BottomTab(
|
|
45
|
-
items: List<BottomTabItem>,
|
|
46
|
-
floatingButton: BottomTabFloatingButton? = null
|
|
47
|
-
) {
|
|
48
|
-
val navigation = LocalNavigation.current
|
|
49
|
-
val navigator = LocalNavigator.current
|
|
50
|
-
val navController = rememberNavController()
|
|
51
|
-
|
|
52
|
-
bottomTabOptionItems = items.mapIndexed { index, item ->
|
|
53
|
-
item.options ?: NavigationOptions()
|
|
54
|
-
}.toMutableList()
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
LaunchedEffect(Unit){
|
|
58
|
-
navigation.markAsBottomTabRoot()
|
|
59
|
-
navigation.setOptions(
|
|
60
|
-
headerType = HeaderType.None,
|
|
61
|
-
keyboardOptions = KeyboardOptions(useAvoidKeyboard = false)
|
|
62
|
-
)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Tab 0 keeps the seeked transition; other tabs go to the host's floating arrow. Matches RN.
|
|
66
|
-
val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
|
|
67
|
-
val isFirstTab = currentRoute == null || currentRoute == "option0"
|
|
68
|
-
|
|
69
|
-
DisposableEffect(isFirstTab) {
|
|
70
|
-
reportFloatingArrowBack(!isFirstTab)
|
|
71
|
-
navigator.tabBackHandler = if (isFirstTab) null else ({
|
|
72
|
-
navController.popBackStack(navController.graph.startDestinationRoute!!, false)
|
|
73
|
-
})
|
|
74
|
-
// both live outside this composition
|
|
75
|
-
onDispose {
|
|
76
|
-
reportFloatingArrowBack(false)
|
|
77
|
-
navigator.tabBackHandler = null
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
|
|
82
|
-
Box(modifier = Modifier
|
|
83
|
-
.fillMaxSize()
|
|
84
|
-
) {
|
|
85
|
-
NavHost(
|
|
86
|
-
navController = navController,
|
|
87
|
-
startDestination = "option0"
|
|
88
|
-
) {
|
|
89
|
-
items.forEachIndexed { index, item ->
|
|
90
|
-
composable(
|
|
91
|
-
route = "option$index",
|
|
92
|
-
enterTransition = {
|
|
93
|
-
fadeIn(animationSpec = tween(200)) +
|
|
94
|
-
scaleIn(
|
|
95
|
-
initialScale = 0.97f,
|
|
96
|
-
animationSpec = tween(200)
|
|
97
|
-
)
|
|
98
|
-
},
|
|
99
|
-
exitTransition = {
|
|
100
|
-
fadeOut(animationSpec = tween(200)) +
|
|
101
|
-
scaleOut(
|
|
102
|
-
targetScale = 0.97f,
|
|
103
|
-
animationSpec = tween(200)
|
|
104
|
-
)
|
|
105
|
-
},
|
|
106
|
-
popEnterTransition = {
|
|
107
|
-
fadeIn(animationSpec = tween(200)) +
|
|
108
|
-
scaleIn(
|
|
109
|
-
initialScale = 0.97f,
|
|
110
|
-
animationSpec = tween(200)
|
|
111
|
-
)
|
|
112
|
-
},
|
|
113
|
-
popExitTransition = {
|
|
114
|
-
fadeOut(animationSpec = tween(200)) +
|
|
115
|
-
scaleOut(
|
|
116
|
-
targetScale = 0.97f,
|
|
117
|
-
animationSpec = tween(200)
|
|
118
|
-
)
|
|
119
|
-
}
|
|
120
|
-
) {
|
|
121
|
-
val option = getBottomTabOption(index)
|
|
122
|
-
|
|
123
|
-
StackScreen(
|
|
124
|
-
content = item.screen,
|
|
125
|
-
navigationOptions = option,
|
|
126
|
-
bottomTabIndex = index,
|
|
127
|
-
onBackHandler = {
|
|
128
|
-
if (index != 0) {
|
|
129
|
-
navController.popBackStack(
|
|
130
|
-
navController.graph.startDestinationRoute!!,
|
|
131
|
-
inclusive = false
|
|
132
|
-
)
|
|
133
|
-
} else {
|
|
134
|
-
navigator.pop()
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
)
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
Column {
|
|
143
|
-
BottomTabBar(
|
|
144
|
-
items = items,
|
|
145
|
-
floatingButton = floatingButton,
|
|
146
|
-
navController = navController,
|
|
147
|
-
onTabSelected = {
|
|
148
|
-
val currentRoute = navController.currentBackStackEntry?.destination?.route
|
|
149
|
-
val targetRoute = "option$it"
|
|
150
|
-
if (currentRoute != targetRoute) {
|
|
151
|
-
navController.navigate(targetRoute)
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
)
|
|
155
|
-
Spacer(modifier = Modifier.fillMaxWidth().height(AppNavigationBar.current + Spacing.S).background(AppTheme.current.colors.background.surface))
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
data class BottomTabItem(
|
|
161
|
-
val name: String,
|
|
162
|
-
val label: String,
|
|
163
|
-
val icon: String,
|
|
164
|
-
val showDot: Boolean = false,
|
|
165
|
-
val badgeLabel: String? = null,
|
|
166
|
-
val screen: @Composable () -> Unit,
|
|
167
|
-
val options: NavigationOptions? = null,
|
|
168
|
-
val initialParams: Any? = null
|
|
169
|
-
)
|
|
170
|
-
|
|
171
|
-
data class BottomTabFloatingButton(
|
|
172
|
-
val icon: String,
|
|
173
|
-
val label: String,
|
|
174
|
-
val onPress: () -> Unit,
|
|
175
|
-
)
|
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
package vn.momo.kits.navigation.bottomtab
|
|
2
|
-
|
|
3
|
-
import androidx.compose.animation.core.FastOutSlowInEasing
|
|
4
|
-
import androidx.compose.animation.core.animateDpAsState
|
|
5
|
-
import androidx.compose.animation.core.tween
|
|
6
|
-
import androidx.compose.foundation.background
|
|
7
|
-
import androidx.compose.foundation.border
|
|
8
|
-
import androidx.compose.foundation.layout.*
|
|
9
|
-
import androidx.compose.foundation.shape.CircleShape
|
|
10
|
-
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
11
|
-
import androidx.compose.runtime.Composable
|
|
12
|
-
import androidx.compose.runtime.getValue
|
|
13
|
-
import androidx.compose.ui.Alignment
|
|
14
|
-
import androidx.compose.ui.Modifier
|
|
15
|
-
import androidx.compose.ui.draw.clip
|
|
16
|
-
import androidx.compose.ui.graphics.Brush
|
|
17
|
-
import androidx.compose.ui.graphics.Color
|
|
18
|
-
import androidx.compose.ui.text.style.TextAlign
|
|
19
|
-
import androidx.compose.ui.text.style.TextOverflow
|
|
20
|
-
import androidx.compose.ui.unit.dp
|
|
21
|
-
import androidx.navigation.NavController
|
|
22
|
-
import androidx.navigation.compose.currentBackStackEntryAsState
|
|
23
|
-
import vn.momo.kits.application.IsShowBaseLineDebug
|
|
24
|
-
import vn.momo.kits.components.*
|
|
25
|
-
import vn.momo.kits.const.*
|
|
26
|
-
import vn.momo.kits.modifier.InternalApi
|
|
27
|
-
import vn.momo.kits.modifier.conditional
|
|
28
|
-
import vn.momo.kits.modifier.noFeedbackClickable
|
|
29
|
-
import vn.momo.kits.platform.getScreenDimensions
|
|
30
|
-
|
|
31
|
-
internal val floatingButtonWidth = 75.dp
|
|
32
|
-
@InternalApi
|
|
33
|
-
const val BOTTOM_TAB_BAR_HEIGHT = 64
|
|
34
|
-
|
|
35
|
-
@Composable
|
|
36
|
-
internal fun BottomTabBar(
|
|
37
|
-
items: List<BottomTabItem>,
|
|
38
|
-
floatingButton: BottomTabFloatingButton? = null,
|
|
39
|
-
navController: NavController,
|
|
40
|
-
onTabSelected: (Int) -> Unit,
|
|
41
|
-
) {
|
|
42
|
-
val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
|
|
43
|
-
val selectedIndex = items.indexOfFirst { "option${items.indexOf(it)}" == currentRoute }
|
|
44
|
-
|
|
45
|
-
val screenWidth = getScreenDimensions().width.dp
|
|
46
|
-
val mid = items.size / 2
|
|
47
|
-
|
|
48
|
-
val tabWidth = if (floatingButton != null) {
|
|
49
|
-
(screenWidth - floatingButtonWidth) / items.size
|
|
50
|
-
} else {
|
|
51
|
-
screenWidth / items.size
|
|
52
|
-
}
|
|
53
|
-
val adjustSize = if(floatingButton != null && selectedIndex >= mid) floatingButtonWidth else 0.dp
|
|
54
|
-
val targetOffset = tabWidth * selectedIndex + tabWidth / 2 - screenWidth / 2 + adjustSize
|
|
55
|
-
|
|
56
|
-
val indicatorOffsetX by animateDpAsState(
|
|
57
|
-
targetValue = targetOffset,
|
|
58
|
-
animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing),
|
|
59
|
-
label = "IndicatorX"
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
Box(
|
|
63
|
-
contentAlignment = Alignment.BottomCenter
|
|
64
|
-
) {
|
|
65
|
-
Row(
|
|
66
|
-
modifier = Modifier
|
|
67
|
-
.fillMaxWidth()
|
|
68
|
-
.height(BOTTOM_TAB_BAR_HEIGHT.dp)
|
|
69
|
-
.background(AppTheme.current.colors.background.surface)
|
|
70
|
-
.conditional(IsShowBaseLineDebug) {
|
|
71
|
-
border(1.dp, Colors.blue_03)
|
|
72
|
-
},
|
|
73
|
-
horizontalArrangement = Arrangement.SpaceAround,
|
|
74
|
-
) {
|
|
75
|
-
if (floatingButton == null) {
|
|
76
|
-
RenderTabBarItem(items, 0, items.size, selectedIndex, onTabSelected)
|
|
77
|
-
} else {
|
|
78
|
-
RenderTabBarItem(items, 0, mid, selectedIndex, onTabSelected)
|
|
79
|
-
Spacer(modifier = Modifier.width(floatingButtonWidth).padding(horizontal = Spacing.XXS))
|
|
80
|
-
RenderTabBarItem(items, mid, items.size, selectedIndex, onTabSelected)
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
Box(modifier = Modifier
|
|
85
|
-
.offset(x = indicatorOffsetX, y = (-BOTTOM_TAB_BAR_HEIGHT + 2).dp)
|
|
86
|
-
.height(2.dp)
|
|
87
|
-
.width(44.dp)
|
|
88
|
-
.background(
|
|
89
|
-
color = AppTheme.current.colors.primary ,
|
|
90
|
-
shape = RoundedCornerShape(bottomStart = 2.dp, bottomEnd = 2.dp)
|
|
91
|
-
)
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
Box(modifier = Modifier
|
|
95
|
-
.fillMaxWidth()
|
|
96
|
-
.height(6.dp)
|
|
97
|
-
.offset(x = 0.dp, y = (-BOTTOM_TAB_BAR_HEIGHT).dp)
|
|
98
|
-
.background(
|
|
99
|
-
brush = Brush.verticalGradient(
|
|
100
|
-
colors = listOf(Color.Transparent, Color.Black.copy(alpha = 0.05f))
|
|
101
|
-
)
|
|
102
|
-
)
|
|
103
|
-
)
|
|
104
|
-
floatingButton?.let { FloatingButton(it) }
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
@Composable
|
|
109
|
-
internal fun RowScope.RenderTabBarItem(
|
|
110
|
-
items: List<BottomTabItem>,
|
|
111
|
-
fromIndex: Int,
|
|
112
|
-
toIndex: Int,
|
|
113
|
-
selectedIndex: Int,
|
|
114
|
-
onTabSelected: (Int) -> Unit
|
|
115
|
-
){
|
|
116
|
-
for (index in fromIndex until toIndex) {
|
|
117
|
-
Box(modifier = Modifier.weight(1f)) {
|
|
118
|
-
TabBarItem(
|
|
119
|
-
item = items[index],
|
|
120
|
-
selected = index == selectedIndex,
|
|
121
|
-
onClick = { onTabSelected(index) }
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
@Composable
|
|
128
|
-
internal fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> Unit) {
|
|
129
|
-
fun isNumber(label: String): Boolean {
|
|
130
|
-
val numberRegex = "^\\d+$".toRegex()
|
|
131
|
-
return numberRegex.matches(label)
|
|
132
|
-
}
|
|
133
|
-
fun formatLabel(label: String): String? {
|
|
134
|
-
if (isNumber(label) && label.toInt() == 0) {
|
|
135
|
-
return null
|
|
136
|
-
}
|
|
137
|
-
return label
|
|
138
|
-
}
|
|
139
|
-
val badgeLabel = item.badgeLabel?.let { formatLabel(it) }
|
|
140
|
-
|
|
141
|
-
Box(modifier = Modifier
|
|
142
|
-
.fillMaxSize()
|
|
143
|
-
.padding(horizontal = Spacing.XXS)
|
|
144
|
-
.conditional(IsShowBaseLineDebug) {
|
|
145
|
-
border(1.dp, Colors.blue_03)
|
|
146
|
-
}
|
|
147
|
-
.noFeedbackClickable {
|
|
148
|
-
onClick()
|
|
149
|
-
},
|
|
150
|
-
contentAlignment = Alignment.BottomStart
|
|
151
|
-
){
|
|
152
|
-
Column(
|
|
153
|
-
modifier = Modifier
|
|
154
|
-
.fillMaxSize()
|
|
155
|
-
.padding(horizontal = Spacing.S, vertical = Spacing.S)
|
|
156
|
-
.noFeedbackClickable {
|
|
157
|
-
onClick()
|
|
158
|
-
},
|
|
159
|
-
horizontalAlignment = Alignment.CenterHorizontally,
|
|
160
|
-
verticalArrangement = Arrangement.Center
|
|
161
|
-
) {
|
|
162
|
-
Icon(
|
|
163
|
-
source = item.icon,
|
|
164
|
-
size = 28.dp,
|
|
165
|
-
modifier = Modifier.padding(Spacing.XS),
|
|
166
|
-
color = if (selected) AppTheme.current.colors.primary else AppTheme.current.colors.text.hint)
|
|
167
|
-
Text(
|
|
168
|
-
text = item.label,
|
|
169
|
-
color = if (selected) AppTheme.current.colors.primary else AppTheme.current.colors.text.hint,
|
|
170
|
-
style = if (selected) Typography.labelXsMedium else Typography.descriptionXsRegular,
|
|
171
|
-
maxLines = 1,
|
|
172
|
-
overflow = TextOverflow.Ellipsis
|
|
173
|
-
)
|
|
174
|
-
}
|
|
175
|
-
if (badgeLabel != null) {
|
|
176
|
-
if (badgeLabel.isEmpty())
|
|
177
|
-
BadgeDot(
|
|
178
|
-
size = DotSize.Small,
|
|
179
|
-
modifier = Modifier.offset(x = 50.dp, y = (-48).dp)
|
|
180
|
-
)
|
|
181
|
-
else
|
|
182
|
-
Badge(badgeLabel, modifier = Modifier.offset(x = 44.dp, y = (-42).dp))
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
@Composable
|
|
188
|
-
internal fun FloatingButton(data: BottomTabFloatingButton) {
|
|
189
|
-
Column(
|
|
190
|
-
modifier = Modifier
|
|
191
|
-
.width(floatingButtonWidth)
|
|
192
|
-
.padding(horizontal = Spacing.XXS, vertical = Spacing.S)
|
|
193
|
-
.conditional(IsShowBaseLineDebug) {
|
|
194
|
-
border(1.dp, Colors.blue_03)
|
|
195
|
-
}
|
|
196
|
-
.noFeedbackClickable {
|
|
197
|
-
data.onPress()
|
|
198
|
-
},
|
|
199
|
-
verticalArrangement = Arrangement.Bottom,
|
|
200
|
-
horizontalAlignment = Alignment.CenterHorizontally
|
|
201
|
-
|
|
202
|
-
){
|
|
203
|
-
CurvedContainer()
|
|
204
|
-
Box(
|
|
205
|
-
modifier = Modifier
|
|
206
|
-
.size(48.dp)
|
|
207
|
-
.clip(CircleShape)
|
|
208
|
-
.background(AppTheme.current.colors.primary),
|
|
209
|
-
contentAlignment = Alignment.Center
|
|
210
|
-
) {
|
|
211
|
-
Icon(source = data.icon, color = Color.White, size = 28.dp)
|
|
212
|
-
}
|
|
213
|
-
Spacer(modifier = Modifier.height(4.dp))
|
|
214
|
-
Text(
|
|
215
|
-
text = data.label,
|
|
216
|
-
style = Typography.labelXsMedium,
|
|
217
|
-
color = Colors.black_01,
|
|
218
|
-
textAlign = TextAlign.Center,
|
|
219
|
-
modifier = Modifier
|
|
220
|
-
.height(14.dp)
|
|
221
|
-
.fillMaxWidth()
|
|
222
|
-
.background(AppTheme.current.colors.primary, shape = RoundedCornerShape(size = Radius.XL))
|
|
223
|
-
)
|
|
224
|
-
}
|
|
225
|
-
}
|
|
@@ -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
|
-
}
|