@momo-kits/native-kits 0.162.39-debug → 0.162.40-debug
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compose/build.gradle.kts +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +13 -11
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +22 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +11 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +26 -17
- package/gradle.properties +1 -1
- package/package.json +1 -1
package/compose/build.gradle.kts
CHANGED
|
@@ -4,6 +4,7 @@ import androidx.compose.foundation.background
|
|
|
4
4
|
import androidx.compose.foundation.layout.Arrangement
|
|
5
5
|
import androidx.compose.foundation.layout.Box
|
|
6
6
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
|
7
|
+
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
|
7
8
|
import androidx.compose.foundation.layout.FlowRow
|
|
8
9
|
import androidx.compose.foundation.layout.PaddingValues
|
|
9
10
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
@@ -14,6 +15,7 @@ import androidx.compose.runtime.CompositionLocalProvider
|
|
|
14
15
|
import androidx.compose.ui.Modifier
|
|
15
16
|
import androidx.compose.ui.draw.clip
|
|
16
17
|
import androidx.compose.ui.draw.shadow
|
|
18
|
+
import androidx.compose.ui.graphics.Color
|
|
17
19
|
import androidx.compose.ui.unit.dp
|
|
18
20
|
import vn.momo.kits.components.Image
|
|
19
21
|
import vn.momo.kits.const.AppTheme
|
|
@@ -21,30 +23,30 @@ import vn.momo.kits.const.Radius
|
|
|
21
23
|
import vn.momo.kits.const.Spacing
|
|
22
24
|
|
|
23
25
|
@Composable
|
|
24
|
-
|
|
26
|
+
@OptIn(ExperimentalLayoutApi::class)
|
|
27
|
+
fun Card(
|
|
25
28
|
useShadow: Boolean = false,
|
|
26
29
|
backgroundImage: String? = null,
|
|
30
|
+
backgroundColor: Color? = null,
|
|
27
31
|
contentPadding: PaddingValues = PaddingValues(Spacing.M),
|
|
28
32
|
modifier: Modifier = Modifier,
|
|
29
33
|
content: @Composable () -> Unit,
|
|
30
34
|
) {
|
|
31
|
-
val columns =
|
|
32
|
-
val
|
|
35
|
+
val columns = gridColumns()
|
|
36
|
+
val gap = Spacing.S
|
|
33
37
|
val shape = RoundedCornerShape(Radius.M)
|
|
34
|
-
var surface = modifier.fillMaxWidth().padding(horizontal =
|
|
35
|
-
if (useShadow) surface = surface.shadow(
|
|
36
|
-
surface = surface.clip(shape)
|
|
37
|
-
if (backgroundImage == null) surface = surface.background(AppTheme.current.colors.background.surface)
|
|
38
|
+
var surface = modifier.fillMaxWidth().padding(horizontal = Spacing.M)
|
|
39
|
+
if (useShadow) surface = surface.shadow(6.dp, shape)
|
|
40
|
+
surface = surface.clip(shape).background(backgroundColor ?: AppTheme.current.colors.background.surface)
|
|
38
41
|
Box(modifier = surface) {
|
|
39
42
|
if (backgroundImage != null) {
|
|
40
43
|
Image(source = backgroundImage, modifier = Modifier.matchParentSize())
|
|
41
44
|
}
|
|
42
45
|
BoxWithConstraints(modifier = Modifier.fillMaxWidth().padding(contentPadding)) {
|
|
43
|
-
|
|
44
|
-
CompositionLocalProvider(LocalGridContext provides GridContext(columns, gutter, sizePerSpan)) {
|
|
46
|
+
CompositionLocalProvider(LocalGridContext provides gridContext(constraints.maxWidth, columns, gap)) {
|
|
45
47
|
FlowRow(
|
|
46
|
-
horizontalArrangement = Arrangement.spacedBy(
|
|
47
|
-
verticalArrangement = Arrangement.spacedBy(
|
|
48
|
+
horizontalArrangement = Arrangement.spacedBy(gap),
|
|
49
|
+
verticalArrangement = Arrangement.spacedBy(gap),
|
|
48
50
|
) { content() }
|
|
49
51
|
}
|
|
50
52
|
}
|
|
@@ -6,33 +6,46 @@ import androidx.compose.foundation.layout.width
|
|
|
6
6
|
import androidx.compose.runtime.Composable
|
|
7
7
|
import androidx.compose.runtime.compositionLocalOf
|
|
8
8
|
import androidx.compose.ui.Modifier
|
|
9
|
+
import androidx.compose.ui.platform.LocalDensity
|
|
9
10
|
import androidx.compose.ui.unit.Dp
|
|
10
11
|
import androidx.compose.ui.unit.dp
|
|
12
|
+
import vn.momo.kits.platform.getScreenDimensions
|
|
13
|
+
import kotlin.math.floor
|
|
11
14
|
|
|
12
15
|
internal data class GridContext(
|
|
13
16
|
val numberOfColumns: Int = 12,
|
|
14
|
-
val
|
|
15
|
-
val
|
|
17
|
+
val gutterPx: Int = 0,
|
|
18
|
+
val columnPx: Float = 0f,
|
|
19
|
+
val density: Float = 1f,
|
|
16
20
|
) {
|
|
17
|
-
fun sizeForSpan(span: Int): Dp =
|
|
21
|
+
fun sizeForSpan(span: Int): Dp = (floor(columnPx * span + gutterPx * (span - 1)) / density).dp
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
internal val LocalGridContext = compositionLocalOf<GridContext?> { null }
|
|
21
25
|
|
|
22
26
|
@Composable
|
|
23
|
-
internal fun
|
|
24
|
-
|
|
27
|
+
internal fun gridColumns(): Int = if (getScreenDimensions().width > 600) 24 else 12
|
|
28
|
+
|
|
29
|
+
@Composable
|
|
30
|
+
internal fun gridContext(maxWidthPx: Int, columns: Int, gap: Dp): GridContext {
|
|
31
|
+
val density = LocalDensity.current
|
|
32
|
+
val gutterPx = with(density) { gap.roundToPx() }
|
|
33
|
+
return GridContext(columns, gutterPx, (maxWidthPx - gutterPx * (columns - 1)).toFloat() / columns, density.density)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Composable
|
|
37
|
+
fun Item(
|
|
38
|
+
widthSpan: Int? = null,
|
|
25
39
|
heightSpan: Int = 0,
|
|
26
40
|
modifier: Modifier = Modifier,
|
|
41
|
+
contentModifier: Modifier = Modifier,
|
|
27
42
|
content: @Composable () -> Unit,
|
|
28
43
|
) {
|
|
29
44
|
val grid = LocalGridContext.current
|
|
30
45
|
var m = modifier
|
|
31
46
|
if (grid != null) {
|
|
32
|
-
m = m.width(grid.sizeForSpan(widthSpan))
|
|
47
|
+
m = m.width(grid.sizeForSpan(widthSpan ?: grid.numberOfColumns))
|
|
33
48
|
if (heightSpan > 0) m = m.height(grid.sizeForSpan(heightSpan))
|
|
34
49
|
}
|
|
35
|
-
|
|
36
|
-
// on top of each other — matches the React Item's flex-column content.
|
|
37
|
-
Column(modifier = m) { content() }
|
|
50
|
+
Column(modifier = m.then(contentModifier)) { content() }
|
|
38
51
|
}
|
|
@@ -3,6 +3,7 @@ package vn.momo.kits.layout
|
|
|
3
3
|
import androidx.compose.foundation.layout.Arrangement
|
|
4
4
|
import androidx.compose.foundation.layout.Box
|
|
5
5
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
|
6
|
+
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
|
6
7
|
import androidx.compose.foundation.layout.FlowRow
|
|
7
8
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
8
9
|
import androidx.compose.foundation.layout.padding
|
|
@@ -11,27 +12,28 @@ import androidx.compose.runtime.CompositionLocalProvider
|
|
|
11
12
|
import androidx.compose.ui.Modifier
|
|
12
13
|
import androidx.compose.ui.unit.dp
|
|
13
14
|
import vn.momo.kits.components.Image
|
|
15
|
+
import vn.momo.kits.const.Spacing
|
|
14
16
|
|
|
15
17
|
@Composable
|
|
16
|
-
|
|
18
|
+
@OptIn(ExperimentalLayoutApi::class)
|
|
19
|
+
fun Section(
|
|
17
20
|
useMargin: Boolean = true,
|
|
18
21
|
backgroundImage: String? = null,
|
|
19
22
|
modifier: Modifier = Modifier,
|
|
20
23
|
content: @Composable () -> Unit,
|
|
21
24
|
) {
|
|
22
|
-
val columns =
|
|
23
|
-
val
|
|
24
|
-
val margin = if (useMargin)
|
|
25
|
-
Box(modifier =
|
|
25
|
+
val columns = gridColumns()
|
|
26
|
+
val gap = Spacing.M
|
|
27
|
+
val margin = if (useMargin) Spacing.M else 0.dp
|
|
28
|
+
Box(modifier = Modifier.fillMaxWidth().padding(horizontal = margin).then(modifier)) {
|
|
26
29
|
if (backgroundImage != null) {
|
|
27
30
|
Image(source = backgroundImage, modifier = Modifier.matchParentSize())
|
|
28
31
|
}
|
|
29
32
|
BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
|
|
30
|
-
|
|
31
|
-
CompositionLocalProvider(LocalGridContext provides GridContext(columns, gutter, sizePerSpan)) {
|
|
33
|
+
CompositionLocalProvider(LocalGridContext provides gridContext(constraints.maxWidth, columns, gap)) {
|
|
32
34
|
FlowRow(
|
|
33
|
-
horizontalArrangement = Arrangement.spacedBy(
|
|
34
|
-
verticalArrangement = Arrangement.spacedBy(
|
|
35
|
+
horizontalArrangement = Arrangement.spacedBy(gap),
|
|
36
|
+
verticalArrangement = Arrangement.spacedBy(gap),
|
|
35
37
|
) { content() }
|
|
36
38
|
}
|
|
37
39
|
}
|
|
@@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.*
|
|
|
10
10
|
import androidx.compose.runtime.Composable
|
|
11
11
|
import androidx.compose.runtime.DisposableEffect
|
|
12
12
|
import androidx.compose.runtime.LaunchedEffect
|
|
13
|
+
import androidx.compose.runtime.remember
|
|
13
14
|
import androidx.compose.ui.Alignment
|
|
14
15
|
import androidx.compose.ui.Modifier
|
|
15
16
|
import androidx.compose.ui.unit.dp
|
|
@@ -26,6 +27,7 @@ import vn.momo.kits.navigation.LocalNavigator
|
|
|
26
27
|
import vn.momo.kits.navigation.NavigationOptions
|
|
27
28
|
import vn.momo.kits.navigation.StackScreen
|
|
28
29
|
import vn.momo.kits.navigation.component.HeaderType
|
|
30
|
+
import vn.momo.kits.platform.BackHandler
|
|
29
31
|
import vn.momo.kits.platform.reportFloatingArrowBack
|
|
30
32
|
|
|
31
33
|
private var bottomTabOptionItems : MutableList<NavigationOptions?> = mutableListOf()
|
|
@@ -43,11 +45,17 @@ internal fun getBottomTabOption(index: Int): NavigationOptions? {
|
|
|
43
45
|
@Composable
|
|
44
46
|
fun BottomTab(
|
|
45
47
|
items: List<BottomTabItem>,
|
|
46
|
-
floatingButton: BottomTabFloatingButton? = null
|
|
48
|
+
floatingButton: BottomTabFloatingButton? = null,
|
|
49
|
+
initialRouteName: String? = null,
|
|
50
|
+
onTabPress: ((index: Int, switchTab: () -> Unit) -> Unit)? = null,
|
|
51
|
+
backToInitialTab: Boolean = true,
|
|
47
52
|
) {
|
|
48
53
|
val navigation = LocalNavigation.current
|
|
49
54
|
val navigator = LocalNavigator.current
|
|
50
55
|
val navController = rememberNavController()
|
|
56
|
+
val startRoute = remember {
|
|
57
|
+
"option${items.indexOfFirst { it.name == initialRouteName }.coerceAtLeast(0)}"
|
|
58
|
+
}
|
|
51
59
|
|
|
52
60
|
bottomTabOptionItems = items.mapIndexed { index, item ->
|
|
53
61
|
item.options ?: NavigationOptions()
|
|
@@ -62,15 +70,14 @@ fun BottomTab(
|
|
|
62
70
|
)
|
|
63
71
|
}
|
|
64
72
|
|
|
65
|
-
// Tab 0 keeps the seeked transition; other tabs go to the host's floating arrow. Matches RN.
|
|
66
73
|
val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
|
|
67
|
-
val
|
|
74
|
+
val onStartTab = currentRoute == null || currentRoute == startRoute
|
|
75
|
+
val isFirstTab = onStartTab || !backToInitialTab
|
|
76
|
+
val backToStartTab = { navController.popBackStack(startRoute, inclusive = false) }
|
|
68
77
|
|
|
69
78
|
DisposableEffect(isFirstTab) {
|
|
70
79
|
reportFloatingArrowBack(!isFirstTab)
|
|
71
|
-
navigator.tabBackHandler = if (isFirstTab) null else ({
|
|
72
|
-
navController.popBackStack(navController.graph.startDestinationRoute!!, false)
|
|
73
|
-
})
|
|
80
|
+
navigator.tabBackHandler = if (isFirstTab) null else ({ backToStartTab() })
|
|
74
81
|
// both live outside this composition
|
|
75
82
|
onDispose {
|
|
76
83
|
reportFloatingArrowBack(false)
|
|
@@ -84,7 +91,7 @@ fun BottomTab(
|
|
|
84
91
|
) {
|
|
85
92
|
NavHost(
|
|
86
93
|
navController = navController,
|
|
87
|
-
startDestination =
|
|
94
|
+
startDestination = startRoute
|
|
88
95
|
) {
|
|
89
96
|
items.forEachIndexed { index, item ->
|
|
90
97
|
composable(
|
|
@@ -125,11 +132,8 @@ fun BottomTab(
|
|
|
125
132
|
navigationOptions = option,
|
|
126
133
|
bottomTabIndex = index,
|
|
127
134
|
onBackHandler = {
|
|
128
|
-
if (index !=
|
|
129
|
-
|
|
130
|
-
navController.graph.startDestinationRoute!!,
|
|
131
|
-
inclusive = false
|
|
132
|
-
)
|
|
135
|
+
if (backToInitialTab && "option$index" != startRoute) {
|
|
136
|
+
backToStartTab()
|
|
133
137
|
} else {
|
|
134
138
|
navigator.pop()
|
|
135
139
|
}
|
|
@@ -139,17 +143,22 @@ fun BottomTab(
|
|
|
139
143
|
}
|
|
140
144
|
}
|
|
141
145
|
}
|
|
146
|
+
BackHandler(enabled = !onStartTab) {
|
|
147
|
+
if (backToInitialTab) backToStartTab() else navigator.pop()
|
|
148
|
+
}
|
|
142
149
|
Column {
|
|
143
150
|
BottomTabBar(
|
|
144
151
|
items = items,
|
|
145
152
|
floatingButton = floatingButton,
|
|
146
153
|
navController = navController,
|
|
147
|
-
onTabSelected = {
|
|
148
|
-
val
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
154
|
+
onTabSelected = { index ->
|
|
155
|
+
val switchTab = {
|
|
156
|
+
val targetRoute = "option$index"
|
|
157
|
+
if (navController.currentBackStackEntry?.destination?.route != targetRoute) {
|
|
158
|
+
navController.navigate(targetRoute)
|
|
159
|
+
}
|
|
152
160
|
}
|
|
161
|
+
onTabPress?.invoke(index, switchTab) ?: switchTab()
|
|
153
162
|
}
|
|
154
163
|
)
|
|
155
164
|
Spacer(modifier = Modifier.fillMaxWidth().height(AppNavigationBar.current + Spacing.S).background(AppTheme.current.colors.background.surface))
|
package/gradle.properties
CHANGED