@momo-kits/native-kits 0.122.0-beta.1 → 0.122.0-beta.2
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/compose/build.gradle.kts +9 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +21 -2
- package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
- package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +4 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +43 -43
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +232 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +121 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +79 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +141 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +155 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +293 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +159 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +216 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/header/Header.kt +233 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/header/HeaderBackground.kt +53 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/header/HeaderRight.kt +169 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/header/HeaderTitle.kt +32 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +7 -3
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +8 -9
- package/gradle.properties +2 -2
- package/package.json +1 -1
package/compose/build.gradle.kts
CHANGED
|
@@ -48,6 +48,7 @@ kotlin {
|
|
|
48
48
|
dependencies {
|
|
49
49
|
implementation(libs.ktor.client.okhttp)
|
|
50
50
|
implementation(libs.jetbrains.coroutines.android)
|
|
51
|
+
api("androidx.activity:activity-compose:1.8.2")
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
val iosX64Main by getting
|
|
@@ -100,3 +101,11 @@ android {
|
|
|
100
101
|
jvmToolchain(17)
|
|
101
102
|
}
|
|
102
103
|
}
|
|
104
|
+
|
|
105
|
+
compose {
|
|
106
|
+
resources {
|
|
107
|
+
publicResClass = true
|
|
108
|
+
packageOfResClass = "vn.momo.uikits.resources"
|
|
109
|
+
generateResClass = always
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -3,16 +3,20 @@ package vn.momo.kits.platform
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.res.Resources
|
|
5
5
|
import android.graphics.BlurMaskFilter
|
|
6
|
+
import android.os.Build
|
|
7
|
+
import android.view.WindowInsets
|
|
6
8
|
import androidx.compose.runtime.Composable
|
|
7
9
|
import androidx.compose.ui.graphics.NativePaint
|
|
8
10
|
import androidx.compose.ui.platform.LocalConfiguration
|
|
11
|
+
import androidx.compose.ui.platform.LocalView
|
|
9
12
|
import androidx.compose.ui.unit.Dp
|
|
10
13
|
import androidx.compose.ui.unit.dp
|
|
14
|
+
import androidx.compose.ui.window.DialogWindowProvider
|
|
15
|
+
import androidx.core.view.WindowInsetsControllerCompat
|
|
16
|
+
import androidx.activity.compose.BackHandler as AndroidBackHandler
|
|
11
17
|
|
|
12
18
|
actual fun getPlatformName(): String = "Android"
|
|
13
19
|
|
|
14
|
-
actual fun disableOverscroll() {}
|
|
15
|
-
|
|
16
20
|
@Composable
|
|
17
21
|
actual fun getScreenDimensions(): ScreenDimension {
|
|
18
22
|
val config = LocalConfiguration.current
|
|
@@ -34,4 +38,19 @@ actual fun getStatusBarHeight(): Dp {
|
|
|
34
38
|
"android"
|
|
35
39
|
)
|
|
36
40
|
return if(resourceId > 0) Resources.getSystem().getDimensionPixelSize(resourceId).dp else 0.dp
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@Composable
|
|
44
|
+
actual fun BackHandler(enabled: Boolean, onBack: () -> Unit) {
|
|
45
|
+
AndroidBackHandler(enabled = enabled, onBack = onBack)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@SuppressLint("WrongConstant")
|
|
49
|
+
@Composable
|
|
50
|
+
actual fun ConfigureDialog() {
|
|
51
|
+
val dialogWindow = (LocalView.current.parent as? DialogWindowProvider)?.window
|
|
52
|
+
|
|
53
|
+
dialogWindow?.let {
|
|
54
|
+
dialogWindow.setDimAmount(0f)
|
|
55
|
+
}
|
|
37
56
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -15,7 +15,6 @@ import vn.momo.kits.const.AppTheme
|
|
|
15
15
|
import vn.momo.kits.const.Theme
|
|
16
16
|
import vn.momo.kits.const.ThemeAssets
|
|
17
17
|
import vn.momo.kits.const.defaultTheme
|
|
18
|
-
import vn.momo.kits.platform.disableOverscroll
|
|
19
18
|
import vn.momo.kits.platform.getStatusBarHeight
|
|
20
19
|
|
|
21
20
|
interface ComposeApi {
|
|
@@ -132,7 +131,6 @@ fun ApplicationContainer(
|
|
|
132
131
|
|
|
133
132
|
val appStatusBarHeight = statusBarHeight ?: getStatusBarHeight()
|
|
134
133
|
|
|
135
|
-
disableOverscroll()
|
|
136
134
|
CompositionLocalProvider(
|
|
137
135
|
AppTheme provides appTheme,
|
|
138
136
|
PlatformApi provides composeApi,
|
|
@@ -3,6 +3,7 @@ package vn.momo.kits.const
|
|
|
3
3
|
import androidx.compose.runtime.staticCompositionLocalOf
|
|
4
4
|
import androidx.compose.ui.graphics.Color
|
|
5
5
|
import androidx.compose.ui.unit.Dp
|
|
6
|
+
import androidx.compose.ui.unit.dp
|
|
6
7
|
|
|
7
8
|
data class Background(
|
|
8
9
|
val default: Color,
|
|
@@ -169,4 +170,6 @@ val darkTheme = Theme(
|
|
|
169
170
|
|
|
170
171
|
val AppTheme = staticCompositionLocalOf { defaultTheme }
|
|
171
172
|
|
|
172
|
-
val AppStatusBar = staticCompositionLocalOf
|
|
173
|
+
val AppStatusBar = staticCompositionLocalOf { 0.dp }
|
|
174
|
+
|
|
175
|
+
val AppNavigationBar = staticCompositionLocalOf { 0.dp }
|
|
@@ -11,11 +11,22 @@ import androidx.compose.ui.unit.sp
|
|
|
11
11
|
import org.jetbrains.compose.resources.Font
|
|
12
12
|
import org.jetbrains.compose.resources.FontResource
|
|
13
13
|
import org.jetbrains.compose.resources.InternalResourceApi
|
|
14
|
-
import org.jetbrains.compose.resources.ResourceItem
|
|
15
|
-
import vn.momo.kits.application.DesignSystemWhiteList
|
|
16
14
|
import vn.momo.kits.platform.getScreenDimensions
|
|
17
15
|
import kotlin.math.max
|
|
18
16
|
import kotlin.math.min
|
|
17
|
+
import vn.momo.uikits.resources.Res
|
|
18
|
+
import vn.momo.uikits.resources.momosignature
|
|
19
|
+
import vn.momo.uikits.resources.momotrustdisplay
|
|
20
|
+
import vn.momo.uikits.resources.sfprotext_black
|
|
21
|
+
import vn.momo.uikits.resources.sfprotext_bold
|
|
22
|
+
import vn.momo.uikits.resources.sfprotext_heavy
|
|
23
|
+
import vn.momo.uikits.resources.sfprotext_light
|
|
24
|
+
import vn.momo.uikits.resources.sfprotext_medium
|
|
25
|
+
import vn.momo.uikits.resources.sfprotext_regular
|
|
26
|
+
import vn.momo.uikits.resources.sfprotext_semibold
|
|
27
|
+
import vn.momo.uikits.resources.sfprotext_thin
|
|
28
|
+
import vn.momo.uikits.resources.sfprotext_ultralight
|
|
29
|
+
import vn.momo.kits.application.DesignSystemWhiteList
|
|
19
30
|
|
|
20
31
|
const val DEFAULT_SCREEN_SIZE = 375f
|
|
21
32
|
const val MAX_FONT_SCALE = 1.5f
|
|
@@ -50,56 +61,45 @@ fun scaleSize(size: Float): Float {
|
|
|
50
61
|
|
|
51
62
|
@OptIn(InternalResourceApi::class)
|
|
52
63
|
@Composable
|
|
53
|
-
fun getFont(font:
|
|
54
|
-
|
|
55
|
-
"font:${font.substringBeforeLast(".")}",
|
|
56
|
-
setOf(
|
|
57
|
-
ResourceItem(
|
|
58
|
-
setOf(),
|
|
59
|
-
"font/${font.substringBeforeLast(".")}.${font.substringAfterLast(".")}",
|
|
60
|
-
-1,
|
|
61
|
-
-1
|
|
62
|
-
)
|
|
63
|
-
)
|
|
64
|
-
)
|
|
65
|
-
return FontFamily(Font(fontResource))
|
|
64
|
+
fun getFont(font: FontResource): FontFamily {
|
|
65
|
+
return FontFamily(Font(font))
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
@Composable
|
|
69
69
|
fun getFontFamily(fontFamily: String, fontWeight: FontWeight? = FontWeight.Normal): FontFamily {
|
|
70
70
|
val key = "$fontFamily-${fontWeight?.weight}"
|
|
71
71
|
val fontMap = mapOf(
|
|
72
|
-
"SFProText-100" to
|
|
73
|
-
"SFProText-200" to
|
|
74
|
-
"SFProText-300" to
|
|
75
|
-
"SFProText-400" to
|
|
76
|
-
"SFProText-500" to
|
|
77
|
-
"SFProText-600" to
|
|
78
|
-
"SFProText-700" to
|
|
79
|
-
"SFProText-800" to
|
|
80
|
-
"SFProText-900" to
|
|
72
|
+
"SFProText-100" to Res.font.sfprotext_thin,
|
|
73
|
+
"SFProText-200" to Res.font.sfprotext_ultralight,
|
|
74
|
+
"SFProText-300" to Res.font.sfprotext_light,
|
|
75
|
+
"SFProText-400" to Res.font.sfprotext_regular,
|
|
76
|
+
"SFProText-500" to Res.font.sfprotext_medium,
|
|
77
|
+
"SFProText-600" to Res.font.sfprotext_semibold,
|
|
78
|
+
"SFProText-700" to Res.font.sfprotext_bold,
|
|
79
|
+
"SFProText-800" to Res.font.sfprotext_heavy,
|
|
80
|
+
"SFProText-900" to Res.font.sfprotext_black,
|
|
81
81
|
|
|
82
|
-
"MoMoSignature-100" to
|
|
83
|
-
"MoMoSignature-200" to
|
|
84
|
-
"MoMoSignature-300" to
|
|
85
|
-
"MoMoSignature-400" to
|
|
86
|
-
"MoMoSignature-500" to
|
|
87
|
-
"MoMoSignature-600" to
|
|
88
|
-
"MoMoSignature-700" to
|
|
89
|
-
"MoMoSignature-800" to
|
|
90
|
-
"MoMoSignature-900" to
|
|
82
|
+
"MoMoSignature-100" to Res.font.momosignature,
|
|
83
|
+
"MoMoSignature-200" to Res.font.momosignature,
|
|
84
|
+
"MoMoSignature-300" to Res.font.momosignature,
|
|
85
|
+
"MoMoSignature-400" to Res.font.momosignature,
|
|
86
|
+
"MoMoSignature-500" to Res.font.momosignature,
|
|
87
|
+
"MoMoSignature-600" to Res.font.momosignature,
|
|
88
|
+
"MoMoSignature-700" to Res.font.momosignature,
|
|
89
|
+
"MoMoSignature-800" to Res.font.momosignature,
|
|
90
|
+
"MoMoSignature-900" to Res.font.momosignature,
|
|
91
91
|
|
|
92
|
-
"MoMoTrustDisplay-100" to
|
|
93
|
-
"MoMoTrustDisplay-200" to
|
|
94
|
-
"MoMoTrustDisplay-300" to
|
|
95
|
-
"MoMoTrustDisplay-400" to
|
|
96
|
-
"MoMoTrustDisplay-500" to
|
|
97
|
-
"MoMoTrustDisplay-600" to
|
|
98
|
-
"MoMoTrustDisplay-700" to
|
|
99
|
-
"MoMoTrustDisplay-800" to
|
|
100
|
-
"MoMoTrustDisplay-900" to
|
|
92
|
+
"MoMoTrustDisplay-100" to Res.font.momotrustdisplay,
|
|
93
|
+
"MoMoTrustDisplay-200" to Res.font.momotrustdisplay,
|
|
94
|
+
"MoMoTrustDisplay-300" to Res.font.momotrustdisplay,
|
|
95
|
+
"MoMoTrustDisplay-400" to Res.font.momotrustdisplay,
|
|
96
|
+
"MoMoTrustDisplay-500" to Res.font.momotrustdisplay,
|
|
97
|
+
"MoMoTrustDisplay-600" to Res.font.momotrustdisplay,
|
|
98
|
+
"MoMoTrustDisplay-700" to Res.font.momotrustdisplay,
|
|
99
|
+
"MoMoTrustDisplay-800" to Res.font.momotrustdisplay,
|
|
100
|
+
"MoMoTrustDisplay-900" to Res.font.momotrustdisplay,
|
|
101
101
|
)
|
|
102
|
-
val font = fontMap[key] ?:
|
|
102
|
+
val font = fontMap[key] ?: Res.font.sfprotext_regular
|
|
103
103
|
return getFont(font)
|
|
104
104
|
}
|
|
105
105
|
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
package vn.momo.kits.navigation
|
|
2
|
+
|
|
3
|
+
import androidx.compose.animation.core.Animatable
|
|
4
|
+
import androidx.compose.animation.core.CubicBezierEasing
|
|
5
|
+
import androidx.compose.animation.core.tween
|
|
6
|
+
import androidx.compose.foundation.background
|
|
7
|
+
import androidx.compose.foundation.gestures.detectDragGestures
|
|
8
|
+
import androidx.compose.foundation.layout.Box
|
|
9
|
+
import androidx.compose.foundation.layout.Column
|
|
10
|
+
import androidx.compose.foundation.layout.Row
|
|
11
|
+
import androidx.compose.foundation.layout.Spacer
|
|
12
|
+
import androidx.compose.foundation.layout.fillMaxHeight
|
|
13
|
+
import androidx.compose.foundation.layout.fillMaxSize
|
|
14
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
15
|
+
import androidx.compose.foundation.layout.height
|
|
16
|
+
import androidx.compose.foundation.layout.offset
|
|
17
|
+
import androidx.compose.foundation.layout.padding
|
|
18
|
+
import androidx.compose.foundation.layout.size
|
|
19
|
+
import androidx.compose.foundation.layout.width
|
|
20
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
21
|
+
import androidx.compose.material.Text
|
|
22
|
+
import androidx.compose.runtime.Composable
|
|
23
|
+
import androidx.compose.runtime.DisposableEffect
|
|
24
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
25
|
+
import androidx.compose.runtime.remember
|
|
26
|
+
import androidx.compose.runtime.rememberCoroutineScope
|
|
27
|
+
import androidx.compose.ui.Alignment
|
|
28
|
+
import androidx.compose.ui.Modifier
|
|
29
|
+
import androidx.compose.ui.graphics.Color
|
|
30
|
+
import androidx.compose.ui.input.pointer.pointerInput
|
|
31
|
+
import androidx.compose.ui.platform.LocalDensity
|
|
32
|
+
import androidx.compose.ui.unit.IntOffset
|
|
33
|
+
import androidx.compose.ui.unit.dp
|
|
34
|
+
import kotlinx.coroutines.launch
|
|
35
|
+
import vn.momo.kits.components.Divider
|
|
36
|
+
import vn.momo.kits.components.Icon
|
|
37
|
+
import vn.momo.kits.const.AppNavigationBar
|
|
38
|
+
import vn.momo.kits.const.AppStatusBar
|
|
39
|
+
import vn.momo.kits.const.AppTheme
|
|
40
|
+
import vn.momo.kits.const.Colors
|
|
41
|
+
import vn.momo.kits.const.Radius
|
|
42
|
+
import vn.momo.kits.const.Spacing
|
|
43
|
+
import vn.momo.kits.const.Typography
|
|
44
|
+
import vn.momo.kits.modifier.conditional
|
|
45
|
+
import vn.momo.kits.modifier.noFeedbackClickable
|
|
46
|
+
import vn.momo.kits.platform.BackHandler
|
|
47
|
+
import vn.momo.kits.platform.getPlatformName
|
|
48
|
+
import vn.momo.kits.platform.getScreenDimensions
|
|
49
|
+
|
|
50
|
+
@Composable
|
|
51
|
+
fun BottomSheet(
|
|
52
|
+
content: @Composable () -> Unit,
|
|
53
|
+
header: BottomHeader,
|
|
54
|
+
barrierDismissible: Boolean = true,
|
|
55
|
+
onDismiss: (() -> Unit)?
|
|
56
|
+
) {
|
|
57
|
+
val navigator = LocalNavigator.current
|
|
58
|
+
|
|
59
|
+
val height = if(getPlatformName() == "iOS")
|
|
60
|
+
getScreenDimensions().height.dp
|
|
61
|
+
else getScreenDimensions().height.dp + AppStatusBar.current + AppNavigationBar.current
|
|
62
|
+
val screenHeight = with(LocalDensity.current) { height.toPx() }
|
|
63
|
+
val sheetOffset = remember { Animatable(screenHeight) }
|
|
64
|
+
|
|
65
|
+
val backgroundAlpha = remember { Animatable(0f) }
|
|
66
|
+
val dynamicAlpha = ((1f - (sheetOffset.value / screenHeight)).coerceIn(0f, 1f)) * 0.3f
|
|
67
|
+
val alpha = backgroundAlpha.value + dynamicAlpha
|
|
68
|
+
|
|
69
|
+
val coroutineScope = rememberCoroutineScope()
|
|
70
|
+
|
|
71
|
+
suspend fun openEvent(){
|
|
72
|
+
backgroundAlpha.animateTo(
|
|
73
|
+
targetValue = 0.3f,
|
|
74
|
+
animationSpec = tween(
|
|
75
|
+
durationMillis = 200,
|
|
76
|
+
easing = CubicBezierEasing(0.05f, 0.7f, 0.1f, 1f)
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
sheetOffset.animateTo(
|
|
80
|
+
targetValue = 0f,
|
|
81
|
+
animationSpec = tween(
|
|
82
|
+
durationMillis = 350,
|
|
83
|
+
easing = CubicBezierEasing(0.05f, 0.7f, 0.1f, 1f)
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fun closeEvent(){
|
|
89
|
+
coroutineScope.launch {
|
|
90
|
+
sheetOffset.animateTo(
|
|
91
|
+
targetValue = screenHeight,
|
|
92
|
+
animationSpec = tween(
|
|
93
|
+
durationMillis = 200,
|
|
94
|
+
easing = CubicBezierEasing(0.3f, 0.0f, 0.8f, 0.15f)
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
backgroundAlpha.animateTo(
|
|
98
|
+
targetValue = 0f,
|
|
99
|
+
animationSpec = tween(
|
|
100
|
+
durationMillis = 150,
|
|
101
|
+
easing = CubicBezierEasing(0.3f, 0.0f, 0.8f, 0.15f)
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
navigator.pop()
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
LaunchedEffect(Unit) {
|
|
109
|
+
openEvent()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
DisposableEffect(Unit) {
|
|
113
|
+
onDispose {
|
|
114
|
+
onDismiss?.invoke()
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
BackHandler(true){
|
|
119
|
+
closeEvent()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
Box(
|
|
123
|
+
modifier = Modifier
|
|
124
|
+
.fillMaxSize()
|
|
125
|
+
.background(Color.Black.copy(alpha = alpha))
|
|
126
|
+
.conditional(barrierDismissible){
|
|
127
|
+
noFeedbackClickable {
|
|
128
|
+
closeEvent()
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
) {
|
|
132
|
+
Box(
|
|
133
|
+
modifier = Modifier
|
|
134
|
+
.align(Alignment.BottomCenter)
|
|
135
|
+
.offset { IntOffset(x = 0, y = sheetOffset.value.toInt()) }
|
|
136
|
+
.fillMaxWidth()
|
|
137
|
+
.background(
|
|
138
|
+
color = AppTheme.current.colors.background.default,
|
|
139
|
+
shape = RoundedCornerShape(topStart = Radius.M, topEnd = Radius.M)
|
|
140
|
+
)
|
|
141
|
+
.noFeedbackClickable { },
|
|
142
|
+
contentAlignment = Alignment.BottomCenter
|
|
143
|
+
) {
|
|
144
|
+
Column {
|
|
145
|
+
Box(
|
|
146
|
+
modifier = Modifier
|
|
147
|
+
.height(72.dp)
|
|
148
|
+
.fillMaxWidth()
|
|
149
|
+
.pointerInput(Unit) {
|
|
150
|
+
detectDragGestures(
|
|
151
|
+
onDrag = { change, dragAmount ->
|
|
152
|
+
change.consume()
|
|
153
|
+
coroutineScope.launch {
|
|
154
|
+
val newOffset = (sheetOffset.value + dragAmount.y).coerceAtLeast(0f)
|
|
155
|
+
sheetOffset.snapTo(newOffset)
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
onDragEnd = {
|
|
159
|
+
coroutineScope.launch {
|
|
160
|
+
if (sheetOffset.value > 100f) {
|
|
161
|
+
closeEvent()
|
|
162
|
+
} else {
|
|
163
|
+
sheetOffset.animateTo(0f)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
) {
|
|
170
|
+
Column(
|
|
171
|
+
modifier = Modifier
|
|
172
|
+
.fillMaxSize()
|
|
173
|
+
.padding(horizontal = Spacing.M),
|
|
174
|
+
horizontalAlignment = Alignment.CenterHorizontally
|
|
175
|
+
) {
|
|
176
|
+
Spacer(Modifier.height(8.dp))
|
|
177
|
+
Box(
|
|
178
|
+
modifier = Modifier
|
|
179
|
+
.height(4.dp)
|
|
180
|
+
.width(40.dp)
|
|
181
|
+
.background(
|
|
182
|
+
color = Colors.black_06,
|
|
183
|
+
shape = RoundedCornerShape(Radius.S)
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
Spacer(Modifier.height(4.dp))
|
|
187
|
+
Box(Modifier.fillMaxWidth().height(56.dp), contentAlignment = Alignment.Center){
|
|
188
|
+
when (header) {
|
|
189
|
+
is BottomHeader.Title -> {
|
|
190
|
+
Row(modifier = Modifier.fillMaxSize(), verticalAlignment = Alignment.CenterVertically) {
|
|
191
|
+
Box(Modifier.size(24.dp))
|
|
192
|
+
Box(Modifier.fillMaxHeight().weight(1f), contentAlignment = Alignment.Center){
|
|
193
|
+
Text(
|
|
194
|
+
text = header.data,
|
|
195
|
+
color = Colors.black_17,
|
|
196
|
+
style = Typography.headerDefaultBold
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
Box(Modifier
|
|
200
|
+
.size(24.dp)
|
|
201
|
+
.noFeedbackClickable {
|
|
202
|
+
closeEvent()
|
|
203
|
+
},
|
|
204
|
+
contentAlignment = Alignment.Center
|
|
205
|
+
){
|
|
206
|
+
Icon(source = "navigation_close")
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
is BottomHeader.Custom -> {
|
|
211
|
+
header.content()
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
Divider()
|
|
218
|
+
content()
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
sealed class BottomHeader {
|
|
225
|
+
data class Title(
|
|
226
|
+
val data: String = "Bottom Sheet Title",
|
|
227
|
+
) : BottomHeader()
|
|
228
|
+
|
|
229
|
+
data class Custom(
|
|
230
|
+
val content: @Composable () -> Unit
|
|
231
|
+
) : BottomHeader()
|
|
232
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
package vn.momo.kits.navigation
|
|
2
|
+
|
|
3
|
+
import androidx.compose.animation.core.Animatable
|
|
4
|
+
import androidx.compose.animation.core.CubicBezierEasing
|
|
5
|
+
import androidx.compose.animation.core.tween
|
|
6
|
+
import androidx.compose.foundation.background
|
|
7
|
+
import androidx.compose.foundation.clickable
|
|
8
|
+
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
9
|
+
import androidx.compose.foundation.layout.Box
|
|
10
|
+
import androidx.compose.foundation.layout.fillMaxSize
|
|
11
|
+
import androidx.compose.foundation.layout.padding
|
|
12
|
+
import androidx.compose.foundation.layout.wrapContentSize
|
|
13
|
+
import androidx.compose.runtime.Composable
|
|
14
|
+
import androidx.compose.runtime.DisposableEffect
|
|
15
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
16
|
+
import androidx.compose.runtime.remember
|
|
17
|
+
import androidx.compose.runtime.rememberCoroutineScope
|
|
18
|
+
import androidx.compose.ui.Alignment
|
|
19
|
+
import androidx.compose.ui.Modifier
|
|
20
|
+
import androidx.compose.ui.draw.alpha
|
|
21
|
+
import androidx.compose.ui.draw.scale
|
|
22
|
+
import androidx.compose.ui.graphics.Color
|
|
23
|
+
import kotlinx.coroutines.launch
|
|
24
|
+
import vn.momo.kits.const.Spacing
|
|
25
|
+
import vn.momo.kits.modifier.conditional
|
|
26
|
+
import vn.momo.kits.modifier.noFeedbackClickable
|
|
27
|
+
import vn.momo.kits.platform.BackHandler
|
|
28
|
+
|
|
29
|
+
@Composable
|
|
30
|
+
fun ModalScreen(
|
|
31
|
+
content: @Composable () -> Unit,
|
|
32
|
+
barrierDismissible: Boolean = true,
|
|
33
|
+
onDismiss: (() -> Unit)? = null
|
|
34
|
+
) {
|
|
35
|
+
val navigator = LocalNavigator.current
|
|
36
|
+
|
|
37
|
+
val alpha = remember { Animatable(0f) }
|
|
38
|
+
val scale = remember { Animatable(0.8f) }
|
|
39
|
+
|
|
40
|
+
val coroutineScope = rememberCoroutineScope()
|
|
41
|
+
|
|
42
|
+
fun openEvent() {
|
|
43
|
+
coroutineScope.launch {
|
|
44
|
+
launch {
|
|
45
|
+
alpha.animateTo(
|
|
46
|
+
targetValue = 1f,
|
|
47
|
+
animationSpec = tween(durationMillis = 250)
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
launch {
|
|
51
|
+
scale.animateTo(
|
|
52
|
+
targetValue = 1f,
|
|
53
|
+
animationSpec = tween(
|
|
54
|
+
durationMillis = 250,
|
|
55
|
+
easing = CubicBezierEasing(0.2f, 0.0f, 0f, 1f)
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
fun closeEvent() {
|
|
63
|
+
coroutineScope.launch {
|
|
64
|
+
launch {
|
|
65
|
+
alpha.animateTo(
|
|
66
|
+
targetValue = 0f,
|
|
67
|
+
animationSpec = tween(durationMillis = 200)
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
launch {
|
|
71
|
+
scale.animateTo(
|
|
72
|
+
targetValue = 0.8f,
|
|
73
|
+
animationSpec = tween(durationMillis = 200)
|
|
74
|
+
)
|
|
75
|
+
}.invokeOnCompletion {
|
|
76
|
+
navigator.pop()
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
LaunchedEffect(Unit) {
|
|
82
|
+
openEvent()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
DisposableEffect(Unit) {
|
|
86
|
+
onDispose {
|
|
87
|
+
onDismiss?.invoke()
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
BackHandler(enabled = true) {
|
|
92
|
+
closeEvent()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
Box(
|
|
96
|
+
modifier = Modifier
|
|
97
|
+
.fillMaxSize()
|
|
98
|
+
.background(Color.Black.copy(alpha = 0.6f * alpha.value))
|
|
99
|
+
.conditional(barrierDismissible){
|
|
100
|
+
noFeedbackClickable {
|
|
101
|
+
closeEvent()
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
contentAlignment = Alignment.Center
|
|
105
|
+
) {
|
|
106
|
+
Box(
|
|
107
|
+
modifier = Modifier
|
|
108
|
+
.scale(scale.value)
|
|
109
|
+
.alpha(alpha.value)
|
|
110
|
+
.padding(Spacing.M)
|
|
111
|
+
.wrapContentSize()
|
|
112
|
+
.clickable(
|
|
113
|
+
indication = null,
|
|
114
|
+
interactionSource = remember { MutableInteractionSource() }
|
|
115
|
+
) {},
|
|
116
|
+
contentAlignment = Alignment.Center
|
|
117
|
+
) {
|
|
118
|
+
content()
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
package vn.momo.kits.navigation
|
|
2
|
+
|
|
3
|
+
import androidx.compose.foundation.ScrollState
|
|
4
|
+
import androidx.compose.runtime.Composable
|
|
5
|
+
import androidx.compose.runtime.State
|
|
6
|
+
import androidx.compose.runtime.mutableStateOf
|
|
7
|
+
import androidx.compose.runtime.staticCompositionLocalOf
|
|
8
|
+
import androidx.compose.ui.graphics.Color
|
|
9
|
+
import vn.momo.kits.navigation.bottomtab.setBottomTabOption
|
|
10
|
+
import vn.momo.kits.navigation.header.HeaderRight
|
|
11
|
+
import vn.momo.kits.navigation.header.HeaderTitle
|
|
12
|
+
import vn.momo.kits.navigation.header.HeaderType
|
|
13
|
+
|
|
14
|
+
class Navigation(
|
|
15
|
+
val id: Int = -1,
|
|
16
|
+
val bottomTabIndex: Int = -1,
|
|
17
|
+
val initOptions: NavigationOptions? = null
|
|
18
|
+
) {
|
|
19
|
+
private val _options = mutableStateOf(initOptions ?: NavigationOptions())
|
|
20
|
+
val currentOptions: State<NavigationOptions> get() = _options
|
|
21
|
+
val options: NavigationOptions get() = _options.value
|
|
22
|
+
|
|
23
|
+
fun setOptions(
|
|
24
|
+
onBackHandler: (() -> Unit)? = null,
|
|
25
|
+
hiddenBack: Boolean? = null,
|
|
26
|
+
headerTitle: HeaderTitle? = null,
|
|
27
|
+
headerRight: HeaderRight? = null,
|
|
28
|
+
headerType: HeaderType? = null,
|
|
29
|
+
scrollData: ScrollData? = null,
|
|
30
|
+
backgroundColor: Color? = null,
|
|
31
|
+
tintColor: Color? = null,
|
|
32
|
+
footerComponent: (@Composable () -> Unit)? = null,
|
|
33
|
+
) {
|
|
34
|
+
updateOptions(
|
|
35
|
+
options.copy(
|
|
36
|
+
onBackHandler = onBackHandler ?: options.onBackHandler,
|
|
37
|
+
hiddenBack = hiddenBack ?: options.hiddenBack,
|
|
38
|
+
headerTitle = headerTitle ?: options.headerTitle,
|
|
39
|
+
headerRight = headerRight ?: options.headerRight,
|
|
40
|
+
headerType = headerType ?: options.headerType,
|
|
41
|
+
scrollData = scrollData ?: options.scrollData,
|
|
42
|
+
backgroundColor = backgroundColor ?: options.backgroundColor,
|
|
43
|
+
tintColor = tintColor ?: options.tintColor,
|
|
44
|
+
footerComponent = footerComponent ?: options.footerComponent,
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
fun setOptions(newOptions: NavigationOptions) {
|
|
50
|
+
updateOptions(newOptions)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private fun updateOptions(updated: NavigationOptions) {
|
|
54
|
+
_options.value = updated
|
|
55
|
+
if (bottomTabIndex != -1) setBottomTabOption(bottomTabIndex, updated)
|
|
56
|
+
if (id != -1) DynamicScreenRegistry.setOptions(id, updated)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
val LocalNavigation = staticCompositionLocalOf<Navigation> {
|
|
61
|
+
error("No NavigationStack provided")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
data class NavigationOptions(
|
|
65
|
+
var onBackHandler: (() -> Unit)? = null,
|
|
66
|
+
val hiddenBack: Boolean = false,
|
|
67
|
+
val headerTitle: HeaderTitle = HeaderTitle.Default("Stack"),
|
|
68
|
+
val headerRight: HeaderRight = HeaderRight.Toolkit(),
|
|
69
|
+
val headerType: HeaderType = HeaderType.Default(),
|
|
70
|
+
val scrollData: ScrollData = ScrollData(),
|
|
71
|
+
val backgroundColor: Color? = null,
|
|
72
|
+
val tintColor: Color? = null,
|
|
73
|
+
val footerComponent: @Composable (() -> Unit)? = null,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
data class ScrollData(
|
|
77
|
+
val scrollable: Boolean = true,
|
|
78
|
+
val scrollState: ScrollState? = null,
|
|
79
|
+
)
|