@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.
Files changed (36) hide show
  1. package/compose/build.gradle.kts +9 -0
  2. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +21 -2
  3. package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
  4. package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
  5. package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
  6. package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
  7. package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
  8. package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
  9. package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
  10. package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
  11. package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
  12. package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
  13. package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
  14. package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
  15. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
  16. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -2
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +4 -1
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +43 -43
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +232 -0
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +121 -0
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +79 -0
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +141 -0
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +155 -0
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +293 -0
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +159 -0
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +216 -0
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/header/Header.kt +233 -0
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/header/HeaderBackground.kt +53 -0
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/header/HeaderRight.kt +169 -0
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/header/HeaderTitle.kt +32 -0
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +7 -3
  34. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +8 -9
  35. package/gradle.properties +2 -2
  36. package/package.json +1 -1
@@ -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
  }
@@ -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<Dp?> { null }
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: String): FontFamily {
54
- val fontResource = FontResource(
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 "sfprotext_thin.ttf",
73
- "SFProText-200" to "sfprotext_ultralight.ttf",
74
- "SFProText-300" to "sfprotext_light.ttf",
75
- "SFProText-400" to "sfprotext_regular.ttf",
76
- "SFProText-500" to "sfprotext_medium.ttf",
77
- "SFProText-600" to "sfprotext_semibold.ttf",
78
- "SFProText-700" to "sfprotext_bold.ttf",
79
- "SFProText-800" to "sfprotext_heavy.ttf",
80
- "SFProText-900" to "sfprotext_black.ttf",
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 "momosignature.otf",
83
- "MoMoSignature-200" to "momosignature.otf",
84
- "MoMoSignature-300" to "momosignature.otf",
85
- "MoMoSignature-400" to "momosignature.otf",
86
- "MoMoSignature-500" to "momosignature.otf",
87
- "MoMoSignature-600" to "momosignature.otf",
88
- "MoMoSignature-700" to "momosignature.otf",
89
- "MoMoSignature-800" to "momosignature.otf",
90
- "MoMoSignature-900" to "momosignature.otf",
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 "momotrustdisplay.otf",
93
- "MoMoTrustDisplay-200" to "momotrustdisplay.otf",
94
- "MoMoTrustDisplay-300" to "momotrustdisplay.otf",
95
- "MoMoTrustDisplay-400" to "momotrustdisplay.otf",
96
- "MoMoTrustDisplay-500" to "momotrustdisplay.otf",
97
- "MoMoTrustDisplay-600" to "momotrustdisplay.otf",
98
- "MoMoTrustDisplay-700" to "momotrustdisplay.otf",
99
- "MoMoTrustDisplay-800" to "momotrustdisplay.otf",
100
- "MoMoTrustDisplay-900" to "momotrustdisplay.otf",
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] ?: "sfprotext_regular.ttf"
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
+ )