@momo-kits/native-kits 0.152.4-beta.2 → 0.152.4-beta.3
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/CODE_OF_CONDUCT.md +133 -0
- package/CONTRIBUTING.md +114 -0
- package/LICENSE +20 -0
- package/README.md +7 -0
- package/build.gradle.kts +32 -0
- package/compose/MoMoComposeKits.podspec +54 -0
- package/compose/build.gradle.kts +149 -0
- package/compose/src/androidMain/AndroidManifest.xml +2 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +105 -0
- package/compose/src/commonMain/composeResources/files/lottie_circle_loader.json +1 -0
- 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/AnimationSearchInput.kt +57 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +201 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +222 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +48 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +76 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +76 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +306 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +715 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +214 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +236 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +77 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +27 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +334 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +345 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +90 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +131 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +543 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +23 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +69 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +143 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +179 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +111 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +384 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +160 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +234 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +223 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +232 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +236 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +228 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +364 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +50 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +34 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +85 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +33 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +338 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +95 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +64 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +89 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +91 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +84 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +208 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +172 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +199 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +237 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +191 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +306 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +13 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +191 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +258 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +2 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +35 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +2 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +59 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +68 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +49 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +232 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +111 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +94 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +159 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +232 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScaleSizeScope.kt +17 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +459 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +169 -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/component/FloatingButton.kt +180 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +251 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +80 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +306 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +31 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +385 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +38 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +62 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +88 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +144 -0
- package/gradle.properties +19 -0
- package/gradlew +240 -0
- package/gradlew.bat +91 -0
- package/ios/Application/ApplicationEnvironment.swift +50 -0
- package/ios/Application/Components.swift +263 -0
- package/ios/Application/ComposeApi.swift +22 -0
- package/ios/Application/FloatingButton.swift +172 -0
- package/ios/Application/HeaderRight.swift +271 -0
- package/ios/Application/Screen.swift +249 -0
- package/ios/Badge/BadgeDot.swift +31 -0
- package/ios/Button/Button.swift +211 -0
- package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +126 -0
- package/ios/Checkbox/Checkbox.swift +81 -0
- package/ios/Chip/Chip.swift +96 -0
- package/ios/Colors+Radius+Spacing/Colors.swift +172 -0
- package/ios/Colors+Radius+Spacing/Radius.swift +22 -0
- package/ios/Colors+Radius+Spacing/Spacing.swift +12 -0
- package/ios/Extensions/Color++.swift +25 -0
- package/ios/Icon/Icon.swift +51 -0
- package/ios/Image/Image.swift +70 -0
- package/ios/Input/Input.swift +207 -0
- package/ios/Input/InputPhoneNumber.swift +176 -0
- package/ios/Input/InputSearch.swift +238 -0
- package/ios/Input/InputTextArea.swift +242 -0
- package/ios/Lottie/LottieView.swift +86 -0
- package/ios/OTPKeyboard/KeyboardButton.swift +41 -0
- package/ios/OTPKeyboard/OTPKeyboard.swift +145 -0
- package/ios/Popup/PopupDisplay.swift +284 -0
- package/ios/Popup/PopupInput.swift +96 -0
- package/ios/Popup/PopupPromotion.swift +73 -0
- package/ios/PopupView/FullscreenPopup.swift +251 -0
- package/ios/PopupView/Modifiers.swift +158 -0
- package/ios/PopupView/PopupView.swift +289 -0
- package/ios/PopupView/Utils++.swift +281 -0
- package/ios/ScrollIndicator/ScrollIndicator.swift +110 -0
- package/ios/Swipeable/SwipeCell.swift +278 -0
- package/ios/Swipeable/SwipeCellModel.swift +86 -0
- package/ios/Switch/Switch.swift +44 -0
- package/ios/Template/Logo/Logo.swift +75 -0
- package/ios/Template/TrustBanner/TrustBanner.swift +120 -0
- package/ios/Theme.md +18 -0
- package/ios/Typography/Text.swift +140 -0
- package/ios/Typography/Typography.swift +95 -0
- package/ios/native-kits.podspec +18 -0
- package/package.json +6 -7
- package/settings.gradle.kts +25 -0
- package/shared/build.gradle.kts +0 -74
|
@@ -0,0 +1,169 @@
|
|
|
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.Box
|
|
10
|
+
import androidx.compose.foundation.layout.Column
|
|
11
|
+
import androidx.compose.foundation.layout.Spacer
|
|
12
|
+
import androidx.compose.foundation.layout.fillMaxSize
|
|
13
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
14
|
+
import androidx.compose.foundation.layout.height
|
|
15
|
+
import androidx.compose.foundation.layout.padding
|
|
16
|
+
import androidx.compose.runtime.Composable
|
|
17
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
18
|
+
import androidx.compose.ui.Alignment
|
|
19
|
+
import androidx.compose.ui.Modifier
|
|
20
|
+
import androidx.compose.ui.unit.dp
|
|
21
|
+
import androidx.compose.ui.unit.min
|
|
22
|
+
import androidx.navigation.compose.NavHost
|
|
23
|
+
import androidx.navigation.compose.composable
|
|
24
|
+
import androidx.navigation.compose.rememberNavController
|
|
25
|
+
import vn.momo.kits.const.AppNavigationBar
|
|
26
|
+
import vn.momo.kits.const.AppTheme
|
|
27
|
+
import vn.momo.kits.const.Spacing
|
|
28
|
+
import vn.momo.kits.navigation.LocalNavigation
|
|
29
|
+
import vn.momo.kits.navigation.LocalNavigator
|
|
30
|
+
import vn.momo.kits.navigation.NavigationOptions
|
|
31
|
+
import vn.momo.kits.navigation.StackScreen
|
|
32
|
+
import vn.momo.kits.navigation.component.HeaderType
|
|
33
|
+
import vn.momo.kits.platform.getScreenHeight
|
|
34
|
+
|
|
35
|
+
private var bottomTabOptionItems : MutableList<NavigationOptions?> = mutableListOf()
|
|
36
|
+
fun setBottomTabOption(index: Int, options: NavigationOptions){
|
|
37
|
+
if (index in bottomTabOptionItems.indices) {
|
|
38
|
+
bottomTabOptionItems[index] = options
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
fun getBottomTabOption(index: Int): NavigationOptions? {
|
|
42
|
+
return if (index in bottomTabOptionItems.indices) {
|
|
43
|
+
bottomTabOptionItems[index]
|
|
44
|
+
} else null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@Composable
|
|
48
|
+
fun BottomTab(
|
|
49
|
+
items: List<BottomTabItem>,
|
|
50
|
+
floatingButton: BottomTabFloatingButton? = null
|
|
51
|
+
) {
|
|
52
|
+
val navigation = LocalNavigation.current
|
|
53
|
+
val navigator = LocalNavigator.current
|
|
54
|
+
val navController = rememberNavController()
|
|
55
|
+
|
|
56
|
+
bottomTabOptionItems = items.mapIndexed { index, item ->
|
|
57
|
+
val options = item.options ?: NavigationOptions()
|
|
58
|
+
options.copy(
|
|
59
|
+
onBackHandler = {
|
|
60
|
+
if (index != 0) {
|
|
61
|
+
navController.popBackStack()
|
|
62
|
+
} else {
|
|
63
|
+
navigator.pop()
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
}.toMutableList()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
LaunchedEffect(Unit){
|
|
71
|
+
navigation.setOptions(
|
|
72
|
+
headerType = HeaderType.None
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
Box(modifier = Modifier.fillMaxWidth().height(getScreenHeight()), contentAlignment = Alignment.BottomCenter) {
|
|
77
|
+
Box(modifier = Modifier
|
|
78
|
+
.fillMaxSize()
|
|
79
|
+
.padding(bottom = BOTTOM_TAB_BAR_HEIGHT.dp + AppNavigationBar.current)
|
|
80
|
+
) {
|
|
81
|
+
NavHost(
|
|
82
|
+
navController = navController,
|
|
83
|
+
startDestination = "option0"
|
|
84
|
+
) {
|
|
85
|
+
items.forEachIndexed { index, item ->
|
|
86
|
+
composable(
|
|
87
|
+
route = "option$index",
|
|
88
|
+
enterTransition = {
|
|
89
|
+
fadeIn(animationSpec = tween(200)) +
|
|
90
|
+
scaleIn(
|
|
91
|
+
initialScale = 0.97f,
|
|
92
|
+
animationSpec = tween(200)
|
|
93
|
+
)
|
|
94
|
+
},
|
|
95
|
+
exitTransition = {
|
|
96
|
+
fadeOut(animationSpec = tween(200)) +
|
|
97
|
+
scaleOut(
|
|
98
|
+
targetScale = 0.97f,
|
|
99
|
+
animationSpec = tween(200)
|
|
100
|
+
)
|
|
101
|
+
},
|
|
102
|
+
popEnterTransition = {
|
|
103
|
+
fadeIn(animationSpec = tween(200)) +
|
|
104
|
+
scaleIn(
|
|
105
|
+
initialScale = 0.97f,
|
|
106
|
+
animationSpec = tween(200)
|
|
107
|
+
)
|
|
108
|
+
},
|
|
109
|
+
popExitTransition = {
|
|
110
|
+
fadeOut(animationSpec = tween(200)) +
|
|
111
|
+
scaleOut(
|
|
112
|
+
targetScale = 0.97f,
|
|
113
|
+
animationSpec = tween(200)
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
) {
|
|
117
|
+
val option = getBottomTabOption(index)?.copy(
|
|
118
|
+
onBackHandler = {
|
|
119
|
+
if (index != 0) {
|
|
120
|
+
navController.popBackStack()
|
|
121
|
+
} else {
|
|
122
|
+
navigator.pop()
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
StackScreen(
|
|
128
|
+
content = item.screen,
|
|
129
|
+
navigationOptions = option,
|
|
130
|
+
bottomTabIndex = index
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
Column {
|
|
137
|
+
BottomTabBar(
|
|
138
|
+
items = items,
|
|
139
|
+
floatingButton = floatingButton,
|
|
140
|
+
navController = navController,
|
|
141
|
+
onTabSelected = {
|
|
142
|
+
val currentRoute = navController.currentBackStackEntry?.destination?.route
|
|
143
|
+
val targetRoute = "option$it"
|
|
144
|
+
if (currentRoute != targetRoute){
|
|
145
|
+
navController.navigate(targetRoute)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
)
|
|
149
|
+
Spacer(modifier = Modifier.fillMaxWidth().height(min(AppNavigationBar.current, 21.dp) + Spacing.S).background(AppTheme.current.colors.background.surface))
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
data class BottomTabItem(
|
|
155
|
+
val name: String,
|
|
156
|
+
val label: String,
|
|
157
|
+
val icon: String,
|
|
158
|
+
val showDot: Boolean = false,
|
|
159
|
+
val badgeLabel: String? = null,
|
|
160
|
+
val screen: @Composable () -> Unit,
|
|
161
|
+
val options: NavigationOptions? = null,
|
|
162
|
+
val initialParams: Any? = null
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
data class BottomTabFloatingButton(
|
|
166
|
+
val icon: String,
|
|
167
|
+
val label: String,
|
|
168
|
+
val onPress: () -> Unit,
|
|
169
|
+
)
|
|
@@ -0,0 +1,216 @@
|
|
|
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.layout.Arrangement
|
|
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.RowScope
|
|
12
|
+
import androidx.compose.foundation.layout.Spacer
|
|
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.CircleShape
|
|
21
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
22
|
+
import androidx.compose.runtime.Composable
|
|
23
|
+
import androidx.compose.runtime.getValue
|
|
24
|
+
import androidx.compose.ui.Alignment
|
|
25
|
+
import androidx.compose.ui.Modifier
|
|
26
|
+
import androidx.compose.ui.draw.clip
|
|
27
|
+
import androidx.compose.ui.graphics.Brush
|
|
28
|
+
import androidx.compose.ui.graphics.Color
|
|
29
|
+
import androidx.compose.ui.text.style.TextAlign
|
|
30
|
+
import androidx.compose.ui.text.style.TextOverflow
|
|
31
|
+
import androidx.compose.ui.unit.dp
|
|
32
|
+
import androidx.navigation.NavController
|
|
33
|
+
import androidx.navigation.compose.currentBackStackEntryAsState
|
|
34
|
+
import vn.momo.kits.components.Badge
|
|
35
|
+
import vn.momo.kits.components.Icon
|
|
36
|
+
import vn.momo.kits.components.Text
|
|
37
|
+
import vn.momo.kits.const.AppTheme
|
|
38
|
+
import vn.momo.kits.const.Colors
|
|
39
|
+
import vn.momo.kits.const.Radius
|
|
40
|
+
import vn.momo.kits.const.Spacing
|
|
41
|
+
import vn.momo.kits.const.Typography
|
|
42
|
+
import vn.momo.kits.modifier.noFeedbackClickable
|
|
43
|
+
import vn.momo.kits.platform.getScreenDimensions
|
|
44
|
+
|
|
45
|
+
val floatingButtonWidth = 75.dp
|
|
46
|
+
const val BOTTOM_TAB_BAR_HEIGHT = 56
|
|
47
|
+
|
|
48
|
+
@Composable
|
|
49
|
+
fun BottomTabBar(
|
|
50
|
+
items: List<BottomTabItem>,
|
|
51
|
+
floatingButton: BottomTabFloatingButton? = null,
|
|
52
|
+
navController: NavController,
|
|
53
|
+
onTabSelected: (Int) -> Unit,
|
|
54
|
+
) {
|
|
55
|
+
val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
|
|
56
|
+
val selectedIndex = items.indexOfFirst { "option${items.indexOf(it)}" == currentRoute }
|
|
57
|
+
|
|
58
|
+
val screenWidth = getScreenDimensions().width.dp
|
|
59
|
+
val mid = items.size / 2
|
|
60
|
+
|
|
61
|
+
val tabWidth = if (floatingButton != null) {
|
|
62
|
+
(screenWidth - floatingButtonWidth) / items.size
|
|
63
|
+
} else {
|
|
64
|
+
screenWidth / items.size
|
|
65
|
+
}
|
|
66
|
+
val adjustSize = if(floatingButton != null && selectedIndex >= mid) floatingButtonWidth else 0.dp
|
|
67
|
+
val targetOffset = tabWidth * selectedIndex + tabWidth / 2 - screenWidth / 2 + adjustSize
|
|
68
|
+
|
|
69
|
+
val indicatorOffsetX by animateDpAsState(
|
|
70
|
+
targetValue = targetOffset,
|
|
71
|
+
animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing),
|
|
72
|
+
label = "IndicatorX"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
Box(
|
|
76
|
+
contentAlignment = Alignment.BottomCenter
|
|
77
|
+
) {
|
|
78
|
+
Row(
|
|
79
|
+
modifier = Modifier
|
|
80
|
+
.fillMaxWidth()
|
|
81
|
+
.height(BOTTOM_TAB_BAR_HEIGHT.dp)
|
|
82
|
+
.background(AppTheme.current.colors.background.surface),
|
|
83
|
+
horizontalArrangement = Arrangement.SpaceAround,
|
|
84
|
+
) {
|
|
85
|
+
if (floatingButton == null) {
|
|
86
|
+
renderTabBarItem(items, 0, items.size, selectedIndex, onTabSelected)
|
|
87
|
+
} else {
|
|
88
|
+
renderTabBarItem(items, 0, mid, selectedIndex, onTabSelected)
|
|
89
|
+
Spacer(modifier = Modifier.width(floatingButtonWidth).padding(horizontal = Spacing.XXS))
|
|
90
|
+
renderTabBarItem(items, mid, items.size, selectedIndex, onTabSelected)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
Box(modifier = Modifier
|
|
95
|
+
.offset(x = indicatorOffsetX, y = (-BOTTOM_TAB_BAR_HEIGHT + 2).dp)
|
|
96
|
+
.height(2.dp)
|
|
97
|
+
.width(44.dp)
|
|
98
|
+
.background(
|
|
99
|
+
color = AppTheme.current.colors.primary ,
|
|
100
|
+
shape = RoundedCornerShape(bottomStart = 2.dp, bottomEnd = 2.dp)
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
Box(modifier = Modifier
|
|
105
|
+
.fillMaxWidth()
|
|
106
|
+
.height(6.dp)
|
|
107
|
+
.offset(x = 0.dp, y = (-BOTTOM_TAB_BAR_HEIGHT).dp)
|
|
108
|
+
.background(
|
|
109
|
+
brush = Brush.verticalGradient(
|
|
110
|
+
colors = listOf(Color.Transparent, Color.Black.copy(alpha = 0.05f))
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
floatingButton?.let { FloatingButton(it) }
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@Composable
|
|
119
|
+
fun RowScope.renderTabBarItem(
|
|
120
|
+
items: List<BottomTabItem>,
|
|
121
|
+
fromIndex: Int,
|
|
122
|
+
toIndex: Int,
|
|
123
|
+
selectedIndex: Int,
|
|
124
|
+
onTabSelected: (Int) -> Unit
|
|
125
|
+
){
|
|
126
|
+
for (index in fromIndex until toIndex) {
|
|
127
|
+
Box(modifier = Modifier.weight(1f)) {
|
|
128
|
+
TabBarItem(
|
|
129
|
+
item = items[index],
|
|
130
|
+
selected = index == selectedIndex,
|
|
131
|
+
onClick = { onTabSelected(index) }
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@Composable
|
|
138
|
+
fun TabBarItem(item: BottomTabItem, selected: Boolean, onClick: () -> Unit) {
|
|
139
|
+
Box(modifier = Modifier
|
|
140
|
+
.fillMaxSize()
|
|
141
|
+
.padding(horizontal = Spacing.XXS)
|
|
142
|
+
.noFeedbackClickable {
|
|
143
|
+
onClick()
|
|
144
|
+
},
|
|
145
|
+
contentAlignment = Alignment.BottomStart
|
|
146
|
+
){
|
|
147
|
+
Column(
|
|
148
|
+
modifier = Modifier
|
|
149
|
+
.fillMaxSize()
|
|
150
|
+
.padding(horizontal = Spacing.XXS)
|
|
151
|
+
.noFeedbackClickable {
|
|
152
|
+
onClick()
|
|
153
|
+
},
|
|
154
|
+
horizontalAlignment = Alignment.CenterHorizontally,
|
|
155
|
+
verticalArrangement = Arrangement.Bottom
|
|
156
|
+
) {
|
|
157
|
+
Icon(
|
|
158
|
+
source = item.icon,
|
|
159
|
+
color = if (selected) AppTheme.current.colors.primary else AppTheme.current.colors.text.hint)
|
|
160
|
+
Spacer(modifier = Modifier.height(6.dp))
|
|
161
|
+
Box(modifier = Modifier.height(14.dp), contentAlignment = Alignment.Center){
|
|
162
|
+
Text(
|
|
163
|
+
text = item.label,
|
|
164
|
+
color = if (selected) AppTheme.current.colors.primary else AppTheme.current.colors.text.hint,
|
|
165
|
+
style = Typography.labelXsMedium,
|
|
166
|
+
maxLines = 1,
|
|
167
|
+
overflow = TextOverflow.Ellipsis
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if(item.badgeLabel != null){
|
|
172
|
+
Box(modifier = Modifier
|
|
173
|
+
.offset(x = 44.dp, y = (-32).dp)
|
|
174
|
+
){
|
|
175
|
+
Badge(item.badgeLabel)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@Composable
|
|
182
|
+
fun FloatingButton(data: BottomTabFloatingButton) {
|
|
183
|
+
Column(
|
|
184
|
+
modifier = Modifier
|
|
185
|
+
.width(floatingButtonWidth)
|
|
186
|
+
.padding(horizontal = Spacing.XXS)
|
|
187
|
+
.noFeedbackClickable {
|
|
188
|
+
data.onPress()
|
|
189
|
+
},
|
|
190
|
+
verticalArrangement = Arrangement.Bottom,
|
|
191
|
+
horizontalAlignment = Alignment.CenterHorizontally
|
|
192
|
+
|
|
193
|
+
){
|
|
194
|
+
CurvedContainer()
|
|
195
|
+
Box(
|
|
196
|
+
modifier = Modifier
|
|
197
|
+
.size(48.dp)
|
|
198
|
+
.clip(CircleShape)
|
|
199
|
+
.background(AppTheme.current.colors.primary),
|
|
200
|
+
contentAlignment = Alignment.Center
|
|
201
|
+
) {
|
|
202
|
+
Icon(source = data.icon, color = Color.White, size = 28.dp)
|
|
203
|
+
}
|
|
204
|
+
Spacer(modifier = Modifier.height(4.dp))
|
|
205
|
+
Text(
|
|
206
|
+
text = data.label,
|
|
207
|
+
style = Typography.labelXsMedium,
|
|
208
|
+
color = Colors.black_01,
|
|
209
|
+
textAlign = TextAlign.Center,
|
|
210
|
+
modifier = Modifier
|
|
211
|
+
.height(14.dp)
|
|
212
|
+
.fillMaxWidth()
|
|
213
|
+
.background(AppTheme.current.colors.primary, shape = RoundedCornerShape(size = Radius.XL))
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
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
|
+
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
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
package vn.momo.kits.navigation.component
|
|
2
|
+
|
|
3
|
+
import androidx.compose.animation.AnimatedVisibility
|
|
4
|
+
import androidx.compose.animation.core.CubicBezierEasing
|
|
5
|
+
import androidx.compose.animation.core.LinearEasing
|
|
6
|
+
import androidx.compose.animation.core.animateDpAsState
|
|
7
|
+
import androidx.compose.animation.core.tween
|
|
8
|
+
import androidx.compose.animation.expandHorizontally
|
|
9
|
+
import androidx.compose.animation.fadeIn
|
|
10
|
+
import androidx.compose.animation.fadeOut
|
|
11
|
+
import androidx.compose.animation.shrinkHorizontally
|
|
12
|
+
import androidx.compose.foundation.ScrollState
|
|
13
|
+
import androidx.compose.foundation.background
|
|
14
|
+
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
15
|
+
import androidx.compose.foundation.layout.Arrangement
|
|
16
|
+
import androidx.compose.foundation.layout.Box
|
|
17
|
+
import androidx.compose.foundation.layout.Row
|
|
18
|
+
import androidx.compose.foundation.layout.Spacer
|
|
19
|
+
import androidx.compose.foundation.layout.fillMaxSize
|
|
20
|
+
import androidx.compose.foundation.layout.height
|
|
21
|
+
import androidx.compose.foundation.layout.offset
|
|
22
|
+
import androidx.compose.foundation.layout.padding
|
|
23
|
+
import androidx.compose.foundation.layout.sizeIn
|
|
24
|
+
import androidx.compose.foundation.layout.width
|
|
25
|
+
import androidx.compose.material3.AlertDialogDefaults
|
|
26
|
+
import androidx.compose.material3.FloatingActionButton
|
|
27
|
+
import androidx.compose.material3.FloatingActionButtonDefaults
|
|
28
|
+
import androidx.compose.material3.FloatingActionButtonElevation
|
|
29
|
+
import androidx.compose.runtime.Composable
|
|
30
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
31
|
+
import androidx.compose.runtime.getValue
|
|
32
|
+
import androidx.compose.runtime.mutableStateOf
|
|
33
|
+
import androidx.compose.runtime.remember
|
|
34
|
+
import androidx.compose.runtime.rememberCoroutineScope
|
|
35
|
+
import androidx.compose.runtime.saveable.rememberSaveable
|
|
36
|
+
import androidx.compose.runtime.setValue
|
|
37
|
+
import androidx.compose.ui.Alignment
|
|
38
|
+
import androidx.compose.ui.Modifier
|
|
39
|
+
import androidx.compose.ui.graphics.Color
|
|
40
|
+
import androidx.compose.ui.unit.Dp
|
|
41
|
+
import androidx.compose.ui.unit.dp
|
|
42
|
+
import kotlinx.coroutines.delay
|
|
43
|
+
import kotlinx.coroutines.flow.MutableStateFlow
|
|
44
|
+
import kotlinx.coroutines.flow.collectLatest
|
|
45
|
+
import kotlinx.coroutines.launch
|
|
46
|
+
import vn.momo.kits.components.Icon
|
|
47
|
+
import vn.momo.kits.components.Text
|
|
48
|
+
import vn.momo.kits.const.Typography
|
|
49
|
+
|
|
50
|
+
enum class FABSize { SMALL, DEFAULT }
|
|
51
|
+
enum class FABPosition { END, CENTER }
|
|
52
|
+
|
|
53
|
+
data class FloatingButtonProps(
|
|
54
|
+
val icon: String,
|
|
55
|
+
val iconColor: Color? = null,
|
|
56
|
+
val label: String? = null,
|
|
57
|
+
val onClick: () -> Unit,
|
|
58
|
+
val size: FABSize = FABSize.DEFAULT,
|
|
59
|
+
val bottom: Dp? = null,
|
|
60
|
+
val scrollState: ScrollState? = null,
|
|
61
|
+
val position: FABPosition? = FABPosition.END,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
private val FabSmallSize = 36.dp
|
|
65
|
+
private val FabDefaultSize = 48.dp
|
|
66
|
+
private val FabExpandedWidth = 80.dp
|
|
67
|
+
private val FabIconSizeSmall = 12.dp
|
|
68
|
+
private val FabIconSizeDefault = 24.dp
|
|
69
|
+
private val FabPaddingHorizontal = 12.dp
|
|
70
|
+
|
|
71
|
+
@Composable
|
|
72
|
+
fun FloatingButton(
|
|
73
|
+
scrollPosition: Int,
|
|
74
|
+
bottom: Dp,
|
|
75
|
+
onClick: () -> Unit,
|
|
76
|
+
containerColor: Color,
|
|
77
|
+
contentColor: Color = containerColor,
|
|
78
|
+
icon: String,
|
|
79
|
+
iconColor: Color? = null,
|
|
80
|
+
text: String? = null,
|
|
81
|
+
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(4.dp),
|
|
82
|
+
size: FABSize = FABSize.DEFAULT,
|
|
83
|
+
position: FABPosition = FABPosition.END,
|
|
84
|
+
keyboardSize: Dp = 0.dp,
|
|
85
|
+
) {
|
|
86
|
+
val scrollStateFlow = remember { MutableStateFlow(scrollPosition) }
|
|
87
|
+
var lastScrollPosition by rememberSaveable { mutableStateOf(0) }
|
|
88
|
+
var isExpanded by rememberSaveable { mutableStateOf(false) }
|
|
89
|
+
|
|
90
|
+
val coroutineScope = rememberCoroutineScope()
|
|
91
|
+
|
|
92
|
+
// Animations
|
|
93
|
+
val baseSize = if (size == FABSize.SMALL) FabSmallSize else FabDefaultSize
|
|
94
|
+
val width by animateDpAsState(
|
|
95
|
+
targetValue = if (isExpanded && text != null) FabExpandedWidth else baseSize,
|
|
96
|
+
animationSpec = tween(100, easing = CubicBezierEasing(0.2f, 0.2f, 0.2f, 0.2f))
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
// Scroll listener
|
|
100
|
+
LaunchedEffect(text) {
|
|
101
|
+
if (text != null) {
|
|
102
|
+
coroutineScope.launch {
|
|
103
|
+
scrollStateFlow.collectLatest { newScroll ->
|
|
104
|
+
isExpanded = newScroll <= lastScrollPosition
|
|
105
|
+
delay(100)
|
|
106
|
+
lastScrollPosition = newScroll
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
LaunchedEffect(scrollPosition) {
|
|
113
|
+
scrollStateFlow.value = scrollPosition
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
Box(
|
|
117
|
+
modifier = Modifier
|
|
118
|
+
.fillMaxSize()
|
|
119
|
+
.padding(end = 12.dp, bottom = bottom)
|
|
120
|
+
.offset(y = -keyboardSize)
|
|
121
|
+
.background(Color.Transparent),
|
|
122
|
+
contentAlignment = if (position == FABPosition.END) Alignment.BottomEnd else Alignment.BottomCenter,
|
|
123
|
+
) {
|
|
124
|
+
FloatingActionButton(
|
|
125
|
+
onClick = onClick,
|
|
126
|
+
shape = AlertDialogDefaults.shape,
|
|
127
|
+
containerColor = containerColor,
|
|
128
|
+
contentColor = contentColor,
|
|
129
|
+
elevation = elevation,
|
|
130
|
+
interactionSource = remember { MutableInteractionSource() },
|
|
131
|
+
modifier = Modifier
|
|
132
|
+
.sizeIn(minWidth = width)
|
|
133
|
+
.height(baseSize)
|
|
134
|
+
) {
|
|
135
|
+
Row(
|
|
136
|
+
modifier = Modifier.padding(
|
|
137
|
+
start = if (isExpanded) FabPaddingHorizontal else 0.dp,
|
|
138
|
+
end = if (isExpanded) FabPaddingHorizontal else 0.dp,
|
|
139
|
+
),
|
|
140
|
+
verticalAlignment = Alignment.CenterVertically,
|
|
141
|
+
horizontalArrangement = if (isExpanded) Arrangement.Start else Arrangement.Center
|
|
142
|
+
) {
|
|
143
|
+
Icon(
|
|
144
|
+
source = icon,
|
|
145
|
+
size = if (size == FABSize.SMALL) FabIconSizeSmall else FabIconSizeDefault,
|
|
146
|
+
color = iconColor ?: Color.White
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
AnimatedVisibility(
|
|
150
|
+
visible = isExpanded,
|
|
151
|
+
enter = fadeInExpandAnimation,
|
|
152
|
+
exit = fadeOutShrinkAnimation,
|
|
153
|
+
) {
|
|
154
|
+
Row {
|
|
155
|
+
Spacer(Modifier.width(12.dp))
|
|
156
|
+
Text(
|
|
157
|
+
text = text ?: "",
|
|
158
|
+
color = Color.White,
|
|
159
|
+
style = Typography.actionDefaultBold
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private val fadeOutShrinkAnimation = fadeOut(
|
|
169
|
+
animationSpec = tween(100, easing = LinearEasing)
|
|
170
|
+
) + shrinkHorizontally(
|
|
171
|
+
animationSpec = tween(100, easing = LinearEasing),
|
|
172
|
+
shrinkTowards = Alignment.Start
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
private val fadeInExpandAnimation = fadeIn(
|
|
176
|
+
animationSpec = tween(100, easing = LinearEasing)
|
|
177
|
+
) + expandHorizontally(
|
|
178
|
+
animationSpec = tween(100, easing = LinearEasing),
|
|
179
|
+
expandFrom = Alignment.Start
|
|
180
|
+
)
|