@momo-kits/native-kits 0.167.1-beta.3 → 0.167.1-beta.3-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/build.gradle.kts +11 -0
- package/compose/build.gradle.kts +190 -0
- package/compose/build.gradle.kts.backup +190 -0
- package/compose/compose.podspec +47 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +19 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +6 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +12 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +7 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +151 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +52 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +124 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +183 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +207 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +39 -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 +293 -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 +831 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +110 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +378 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +56 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +157 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +82 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +32 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +338 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +175 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +356 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +113 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +89 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +128 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +209 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +542 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +49 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +76 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +125 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +231 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +189 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +475 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +114 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +266 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +240 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +223 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +264 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +185 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +343 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +108 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +56 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +41 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +84 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +40 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +328 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +97 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +310 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +59 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +87 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +21 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +90 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +323 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +220 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +473 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +122 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +199 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +91 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +552 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +88 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +125 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +207 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +498 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +176 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +182 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +193 -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 +234 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +188 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +325 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +16 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +18 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +22 -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 +16 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +188 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +296 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +52 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +38 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +39 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +52 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +66 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +104 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +48 -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 +299 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +58 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +129 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +106 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +201 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +393 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +8 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +595 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +148 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +35 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +175 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +225 -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 +169 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +358 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +83 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +106 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +75 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +309 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +37 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +364 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +132 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +127 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +12 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +91 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +9 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +71 -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 +55 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +15 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +105 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +40 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +45 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +32 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +38 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +66 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +213 -0
- package/gradle/libs.versions.toml +69 -0
- package/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/gradle/wrapper/gradle-wrapper.properties +8 -0
- package/gradle.properties +26 -0
- package/gradlew +252 -0
- package/gradlew.bat +94 -0
- package/ios-demo/README.md +120 -0
- package/package.json +1 -1
- package/settings.gradle.kts +64 -0
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
package vn.momo.kits.components
|
|
2
|
+
|
|
3
|
+
import androidx.compose.animation.core.animateDpAsState
|
|
4
|
+
import androidx.compose.animation.core.tween
|
|
5
|
+
import androidx.compose.foundation.*
|
|
6
|
+
import androidx.compose.foundation.layout.*
|
|
7
|
+
import androidx.compose.foundation.pager.HorizontalPager
|
|
8
|
+
import androidx.compose.foundation.pager.rememberPagerState
|
|
9
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
10
|
+
import androidx.compose.runtime.*
|
|
11
|
+
import androidx.compose.ui.Alignment
|
|
12
|
+
import androidx.compose.ui.Modifier
|
|
13
|
+
import androidx.compose.ui.graphics.Color
|
|
14
|
+
import androidx.compose.ui.graphics.drawscope.withTransform
|
|
15
|
+
import androidx.compose.ui.graphics.drawscope.DrawScope
|
|
16
|
+
import androidx.compose.ui.graphics.Path
|
|
17
|
+
import androidx.compose.ui.layout.onGloballyPositioned
|
|
18
|
+
import androidx.compose.ui.layout.positionInParent
|
|
19
|
+
import androidx.compose.ui.platform.LocalDensity
|
|
20
|
+
import androidx.compose.ui.text.style.TextOverflow
|
|
21
|
+
import androidx.compose.ui.unit.Dp
|
|
22
|
+
import androidx.compose.ui.unit.dp
|
|
23
|
+
import kotlinx.coroutines.launch
|
|
24
|
+
import vn.momo.kits.application.IsShowBaseLineDebug
|
|
25
|
+
import vn.momo.kits.const.*
|
|
26
|
+
import vn.momo.kits.modifier.activeOpacityClickable
|
|
27
|
+
import vn.momo.kits.modifier.conditional
|
|
28
|
+
|
|
29
|
+
data class TabViewItem(
|
|
30
|
+
val title: String,
|
|
31
|
+
val icon: String? = null,
|
|
32
|
+
val renderIcon: (@Composable (active: Boolean) -> Unit)? = null,
|
|
33
|
+
val showDot: Boolean = false,
|
|
34
|
+
val dotSize: DotSize = DotSize.Small,
|
|
35
|
+
val badgeValue: String? = null,
|
|
36
|
+
val accessibilityLabel: String? = null,
|
|
37
|
+
val content: @Composable () -> Unit = {},
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
enum class TabViewType { DEFAULT, CARD }
|
|
41
|
+
|
|
42
|
+
enum class TabViewDirection { ROW, COLUMN }
|
|
43
|
+
|
|
44
|
+
@Composable
|
|
45
|
+
fun TabView(
|
|
46
|
+
modifier: Modifier = Modifier,
|
|
47
|
+
tabs: List<TabViewItem>,
|
|
48
|
+
initialPage: Int = 0,
|
|
49
|
+
type: TabViewType = TabViewType.DEFAULT,
|
|
50
|
+
scrollable: Boolean = false,
|
|
51
|
+
fitContent: Boolean = false,
|
|
52
|
+
selectedColor: Color? = null,
|
|
53
|
+
unselectedColor: Color? = null,
|
|
54
|
+
direction: TabViewDirection = TabViewDirection.ROW,
|
|
55
|
+
userScrollEnabled: Boolean = true,
|
|
56
|
+
onPressTabItem: (Int) -> Unit = {},
|
|
57
|
+
onPageSelected: (Int) -> Unit = {},
|
|
58
|
+
) {
|
|
59
|
+
if (tabs.isEmpty()) return
|
|
60
|
+
|
|
61
|
+
val theme = AppTheme.current
|
|
62
|
+
val resolvedSelectedColor = selectedColor ?: theme.colors.primary
|
|
63
|
+
val resolvedUnselectedColor = unselectedColor ?: theme.colors.text.default
|
|
64
|
+
|
|
65
|
+
val startPage = initialPage.coerceIn(0, tabs.lastIndex)
|
|
66
|
+
var selectedIndex by remember { mutableStateOf(startPage) }
|
|
67
|
+
|
|
68
|
+
// Track which pages have already been visited for lazy rendering
|
|
69
|
+
val lazyRendered = remember { mutableStateListOf(startPage) }
|
|
70
|
+
|
|
71
|
+
val pagerState = if (!fitContent) {
|
|
72
|
+
rememberPagerState(initialPage = startPage) { tabs.size }
|
|
73
|
+
} else null
|
|
74
|
+
|
|
75
|
+
val coroutineScope = rememberCoroutineScope()
|
|
76
|
+
|
|
77
|
+
// Keep selectedIndex in sync when user swipes the pager
|
|
78
|
+
if (pagerState != null) {
|
|
79
|
+
LaunchedEffect(pagerState) {
|
|
80
|
+
snapshotFlow { pagerState.currentPage }.collect { page ->
|
|
81
|
+
if (!lazyRendered.contains(page)) lazyRendered.add(page)
|
|
82
|
+
selectedIndex = page
|
|
83
|
+
onPageSelected(page)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
val onTabPressed: (Int) -> Unit = { index ->
|
|
89
|
+
onPressTabItem(index)
|
|
90
|
+
if (!lazyRendered.contains(index)) lazyRendered.add(index)
|
|
91
|
+
selectedIndex = index
|
|
92
|
+
if (pagerState != null) {
|
|
93
|
+
coroutineScope.launch { pagerState.animateScrollToPage(index) }
|
|
94
|
+
}
|
|
95
|
+
if (fitContent) {
|
|
96
|
+
onPageSelected(index)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
Column(
|
|
101
|
+
modifier = modifier
|
|
102
|
+
.fillMaxWidth()
|
|
103
|
+
.conditional(IsShowBaseLineDebug) { border(1.dp, Colors.blue_03) },
|
|
104
|
+
) {
|
|
105
|
+
// Tab bar
|
|
106
|
+
when {
|
|
107
|
+
type == TabViewType.CARD -> CardTabBar(
|
|
108
|
+
tabs = tabs,
|
|
109
|
+
selectedIndex = selectedIndex,
|
|
110
|
+
selectedColor = resolvedSelectedColor,
|
|
111
|
+
unselectedColor = resolvedUnselectedColor,
|
|
112
|
+
direction = direction,
|
|
113
|
+
onPressTabItem = onTabPressed,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
scrollable -> ScrollableTabBar(
|
|
117
|
+
tabs = tabs,
|
|
118
|
+
selectedIndex = selectedIndex,
|
|
119
|
+
selectedColor = resolvedSelectedColor,
|
|
120
|
+
unselectedColor = resolvedUnselectedColor,
|
|
121
|
+
direction = direction,
|
|
122
|
+
onPressTabItem = onTabPressed,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
else -> DefaultTabBar(
|
|
126
|
+
tabs = tabs,
|
|
127
|
+
selectedIndex = selectedIndex,
|
|
128
|
+
selectedColor = resolvedSelectedColor,
|
|
129
|
+
unselectedColor = resolvedUnselectedColor,
|
|
130
|
+
direction = direction,
|
|
131
|
+
onPressTabItem = onTabPressed,
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Content area
|
|
136
|
+
if (fitContent) {
|
|
137
|
+
Box(modifier = Modifier.fillMaxWidth()) {
|
|
138
|
+
tabs.getOrNull(selectedIndex)?.content?.invoke()
|
|
139
|
+
}
|
|
140
|
+
} else if (pagerState != null) {
|
|
141
|
+
HorizontalPager(
|
|
142
|
+
state = pagerState,
|
|
143
|
+
modifier = Modifier
|
|
144
|
+
.fillMaxWidth()
|
|
145
|
+
.weight(1f),
|
|
146
|
+
userScrollEnabled = userScrollEnabled,
|
|
147
|
+
) { page ->
|
|
148
|
+
Box(modifier = Modifier.fillMaxWidth()) {
|
|
149
|
+
if (lazyRendered.contains(page)) {
|
|
150
|
+
tabs[page].content()
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@Composable
|
|
159
|
+
private fun DefaultTabBar(
|
|
160
|
+
tabs: List<TabViewItem>,
|
|
161
|
+
selectedIndex: Int,
|
|
162
|
+
selectedColor: Color,
|
|
163
|
+
unselectedColor: Color,
|
|
164
|
+
direction: TabViewDirection,
|
|
165
|
+
onPressTabItem: (Int) -> Unit,
|
|
166
|
+
) {
|
|
167
|
+
val theme = AppTheme.current
|
|
168
|
+
val density = LocalDensity.current
|
|
169
|
+
var totalWidthPx by remember { mutableStateOf(0) }
|
|
170
|
+
|
|
171
|
+
val itemWidth: Dp = if (totalWidthPx > 0 && tabs.isNotEmpty()) {
|
|
172
|
+
with(density) { (totalWidthPx / tabs.size).toDp() }
|
|
173
|
+
} else 0.dp
|
|
174
|
+
|
|
175
|
+
val indicatorOffsetX: Dp by animateDpAsState(
|
|
176
|
+
targetValue = itemWidth * selectedIndex + Spacing.XS,
|
|
177
|
+
animationSpec = tween(durationMillis = 200),
|
|
178
|
+
label = "DefaultIndicatorX",
|
|
179
|
+
)
|
|
180
|
+
val indicatorWidth: Dp = (itemWidth - Spacing.XS * 2).coerceAtLeast(0.dp)
|
|
181
|
+
|
|
182
|
+
Box(
|
|
183
|
+
modifier = Modifier
|
|
184
|
+
.fillMaxWidth()
|
|
185
|
+
.onGloballyPositioned { totalWidthPx = it.size.width }
|
|
186
|
+
.background(theme.colors.background.surface)
|
|
187
|
+
.border(
|
|
188
|
+
width = 0.5.dp,
|
|
189
|
+
color = theme.colors.border.default,
|
|
190
|
+
shape = RoundedCornerShape(0.dp),
|
|
191
|
+
),
|
|
192
|
+
) {
|
|
193
|
+
Row(modifier = Modifier.fillMaxWidth()) {
|
|
194
|
+
tabs.forEachIndexed { index, tab ->
|
|
195
|
+
TabItemView(
|
|
196
|
+
modifier = Modifier.weight(1f),
|
|
197
|
+
tab = tab,
|
|
198
|
+
active = selectedIndex == index,
|
|
199
|
+
selectedColor = selectedColor,
|
|
200
|
+
unselectedColor = unselectedColor,
|
|
201
|
+
direction = direction,
|
|
202
|
+
onPress = { onPressTabItem(index) },
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Animated underline indicator
|
|
208
|
+
TabUnderlineIndicator(offsetX = indicatorOffsetX, width = indicatorWidth, color = selectedColor)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
@Composable
|
|
213
|
+
private fun ScrollableTabBar(
|
|
214
|
+
tabs: List<TabViewItem>,
|
|
215
|
+
selectedIndex: Int,
|
|
216
|
+
selectedColor: Color,
|
|
217
|
+
unselectedColor: Color,
|
|
218
|
+
direction: TabViewDirection,
|
|
219
|
+
onPressTabItem: (Int) -> Unit,
|
|
220
|
+
) {
|
|
221
|
+
val theme = AppTheme.current
|
|
222
|
+
val density = LocalDensity.current
|
|
223
|
+
val scrollState: ScrollState = rememberScrollState()
|
|
224
|
+
val coroutineScope = rememberCoroutineScope()
|
|
225
|
+
|
|
226
|
+
// Per-item (widthPx, xPx) measured relative to the scrollable row
|
|
227
|
+
val itemMeasures = remember { mutableStateListOf<Pair<Float, Float>>() }
|
|
228
|
+
|
|
229
|
+
val rawWidth = itemMeasures.getOrNull(selectedIndex)?.first ?: 0f
|
|
230
|
+
val rawX = itemMeasures.getOrNull(selectedIndex)?.second ?: 0f
|
|
231
|
+
|
|
232
|
+
val animatedWidth: Dp by animateDpAsState(
|
|
233
|
+
targetValue = with(density) { rawWidth.toDp() },
|
|
234
|
+
animationSpec = tween(250),
|
|
235
|
+
label = "ScrollableIndicatorWidth",
|
|
236
|
+
)
|
|
237
|
+
val animatedX: Dp by animateDpAsState(
|
|
238
|
+
targetValue = with(density) { rawX.toDp() },
|
|
239
|
+
animationSpec = tween(250),
|
|
240
|
+
label = "ScrollableIndicatorX",
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
// Auto-scroll bar to keep selected item visible
|
|
244
|
+
LaunchedEffect(selectedIndex, itemMeasures.size) {
|
|
245
|
+
val x = itemMeasures.getOrNull(selectedIndex)?.second ?: return@LaunchedEffect
|
|
246
|
+
val target = (x - with(density) { Spacing.S.toPx() }).coerceAtLeast(0f).toInt()
|
|
247
|
+
coroutineScope.launch { scrollState.animateScrollTo(target) }
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
val scrollOffsetDp = with(density) { scrollState.value.toDp() }
|
|
251
|
+
|
|
252
|
+
Box(
|
|
253
|
+
modifier = Modifier
|
|
254
|
+
.fillMaxWidth()
|
|
255
|
+
.background(theme.colors.background.surface)
|
|
256
|
+
.border(
|
|
257
|
+
width = 0.5.dp,
|
|
258
|
+
color = theme.colors.border.default,
|
|
259
|
+
shape = RoundedCornerShape(0.dp),
|
|
260
|
+
),
|
|
261
|
+
) {
|
|
262
|
+
Row(
|
|
263
|
+
modifier = Modifier
|
|
264
|
+
.fillMaxWidth()
|
|
265
|
+
.horizontalScroll(scrollState),
|
|
266
|
+
) {
|
|
267
|
+
tabs.forEachIndexed { index, tab ->
|
|
268
|
+
Box(
|
|
269
|
+
modifier = Modifier
|
|
270
|
+
.wrapContentWidth()
|
|
271
|
+
.onGloballyPositioned { coords ->
|
|
272
|
+
val w = coords.size.width.toFloat()
|
|
273
|
+
val x = coords.positionInParent().x
|
|
274
|
+
while (itemMeasures.size <= index) itemMeasures.add(0f to 0f)
|
|
275
|
+
itemMeasures[index] = w to x
|
|
276
|
+
},
|
|
277
|
+
) {
|
|
278
|
+
TabItemView(
|
|
279
|
+
tab = tab,
|
|
280
|
+
active = selectedIndex == index,
|
|
281
|
+
selectedColor = selectedColor,
|
|
282
|
+
unselectedColor = unselectedColor,
|
|
283
|
+
direction = direction,
|
|
284
|
+
scrollable = true,
|
|
285
|
+
onPress = { onPressTabItem(index) },
|
|
286
|
+
)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Animated underline indicator aligned to bottom of the bar
|
|
292
|
+
TabUnderlineIndicator(
|
|
293
|
+
offsetX = animatedX - scrollOffsetDp + Spacing.XS,
|
|
294
|
+
width = (animatedWidth - Spacing.XS * 2).coerceAtLeast(0.dp),
|
|
295
|
+
color = selectedColor,
|
|
296
|
+
)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
@Composable
|
|
301
|
+
private fun CardTabBar(
|
|
302
|
+
tabs: List<TabViewItem>,
|
|
303
|
+
selectedIndex: Int,
|
|
304
|
+
selectedColor: Color,
|
|
305
|
+
unselectedColor: Color,
|
|
306
|
+
direction: TabViewDirection,
|
|
307
|
+
onPressTabItem: (Int) -> Unit,
|
|
308
|
+
) {
|
|
309
|
+
val theme = AppTheme.current
|
|
310
|
+
val surface = theme.colors.background.surface
|
|
311
|
+
|
|
312
|
+
// react-kits `CardTabBar` draws the bar in TWO layers: a flat strip where every
|
|
313
|
+
// tab looks unselected, and one raised card laid OVER it for the active tab.
|
|
314
|
+
// The card is 4dp taller, sits `Spacing.XS` higher, and is joined to the strip
|
|
315
|
+
// on each side by a curved "ear" — the browser-tab silhouette that gives this
|
|
316
|
+
// variant its name. Rendering the active tab in place (just taller) loses both
|
|
317
|
+
// the overlap and the ears, and the strip used the theme's `tonal` pink where
|
|
318
|
+
// react-kits uses `Colors.black_02`, so the bar came out pink and flat.
|
|
319
|
+
val pathWidth = if (tabs.size > 2) 22.dp else 28.dp
|
|
320
|
+
val isFirst = selectedIndex == 0
|
|
321
|
+
val isLast = selectedIndex == tabs.lastIndex
|
|
322
|
+
|
|
323
|
+
// The bar is exactly as tall as the FLAT strip — react-kits' raised card is
|
|
324
|
+
// `position: absolute`, so it never adds height; it just overflows upward into
|
|
325
|
+
// the 4dp margin. Letting the 44dp card size this box instead left a 4dp band of
|
|
326
|
+
// background showing between the tabs and the panel they sit on.
|
|
327
|
+
BoxWithConstraints(
|
|
328
|
+
modifier = Modifier
|
|
329
|
+
.fillMaxWidth()
|
|
330
|
+
.padding(top = Spacing.XS)
|
|
331
|
+
.height(CARD_TAB_HEIGHT),
|
|
332
|
+
) {
|
|
333
|
+
val itemWidth = maxWidth / tabs.size
|
|
334
|
+
|
|
335
|
+
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.Bottom) {
|
|
336
|
+
tabs.forEachIndexed { index, tab ->
|
|
337
|
+
Box(
|
|
338
|
+
modifier = Modifier
|
|
339
|
+
.width(itemWidth)
|
|
340
|
+
.height(CARD_TAB_HEIGHT)
|
|
341
|
+
.background(
|
|
342
|
+
color = Colors.black_02,
|
|
343
|
+
shape = RoundedCornerShape(topStart = Radius.M, topEnd = Radius.M),
|
|
344
|
+
)
|
|
345
|
+
.activeOpacityClickable { onPressTabItem(index) }
|
|
346
|
+
.padding(horizontal = Spacing.S),
|
|
347
|
+
contentAlignment = Alignment.Center,
|
|
348
|
+
) {
|
|
349
|
+
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
350
|
+
TabLeadingIcon(tab = tab, active = false, color = unselectedColor)
|
|
351
|
+
Text(
|
|
352
|
+
text = tab.title,
|
|
353
|
+
style = Typography.bodyDefaultRegular,
|
|
354
|
+
color = unselectedColor,
|
|
355
|
+
maxLines = 1,
|
|
356
|
+
overflow = TextOverflow.Ellipsis,
|
|
357
|
+
modifier = Modifier.weight(1f, fill = false),
|
|
358
|
+
)
|
|
359
|
+
TabTrailingBadge(tab)
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
val active = tabs[selectedIndex]
|
|
366
|
+
Row(
|
|
367
|
+
modifier = Modifier
|
|
368
|
+
// Pinned to the strip's BOTTOM edge so the two always end on the same
|
|
369
|
+
// pixel; `requiredHeight` then grows it upward into the 4dp margin.
|
|
370
|
+
// Offsetting it up by hand instead left a sub-dp sliver of strip
|
|
371
|
+
// showing under the card once rounding got involved.
|
|
372
|
+
.align(Alignment.BottomStart)
|
|
373
|
+
.offset(x = itemWidth * selectedIndex - (if (isFirst) 0.dp else pathWidth))
|
|
374
|
+
// `requiredHeight`, not `height`: the bar box is only as tall as the
|
|
375
|
+
// flat strip, so a plain height would be coerced down to 40 and the
|
|
376
|
+
// raised card would stop 4dp short of the panel below, leaving a grey
|
|
377
|
+
// sliver. react-kits gets this free — its card is absolutely
|
|
378
|
+
// positioned and ignores the parent box entirely.
|
|
379
|
+
.requiredHeight(CARD_TAB_ACTIVE_HEIGHT),
|
|
380
|
+
verticalAlignment = Alignment.Bottom,
|
|
381
|
+
) {
|
|
382
|
+
// react-kits nudges each ear 8pt UNDER the card so the join has no seam.
|
|
383
|
+
if (!isFirst) CardTabEar(pathWidth, mirrored = true, color = surface, nudge = 8.dp)
|
|
384
|
+
Box(
|
|
385
|
+
modifier = Modifier
|
|
386
|
+
.width(itemWidth)
|
|
387
|
+
.fillMaxHeight()
|
|
388
|
+
.background(
|
|
389
|
+
color = surface,
|
|
390
|
+
shape = RoundedCornerShape(topStart = Radius.M, topEnd = Radius.M),
|
|
391
|
+
)
|
|
392
|
+
.activeOpacityClickable { onPressTabItem(selectedIndex) }
|
|
393
|
+
.padding(horizontal = if (tabs.size == 3) Spacing.S else Spacing.M),
|
|
394
|
+
contentAlignment = Alignment.Center,
|
|
395
|
+
) {
|
|
396
|
+
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
397
|
+
TabLeadingIcon(tab = active, active = true, color = selectedColor)
|
|
398
|
+
Text(
|
|
399
|
+
text = active.title,
|
|
400
|
+
style = Typography.headerSSemibold,
|
|
401
|
+
color = selectedColor,
|
|
402
|
+
maxLines = 1,
|
|
403
|
+
overflow = TextOverflow.Ellipsis,
|
|
404
|
+
modifier = Modifier.weight(1f, fill = false),
|
|
405
|
+
)
|
|
406
|
+
TabTrailingBadge(active)
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
if (!isLast) CardTabEar(pathWidth, mirrored = false, color = surface, nudge = -8.dp)
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
private val CARD_TAB_HEIGHT = 40.dp
|
|
415
|
+
private val CARD_TAB_ACTIVE_HEIGHT = 44.dp
|
|
416
|
+
@Composable
|
|
417
|
+
private fun CardTabEar(width: Dp, mirrored: Boolean, color: Color, nudge: Dp) {
|
|
418
|
+
Canvas(modifier = Modifier.offset(x = nudge).width(width).height(CARD_TAB_ACTIVE_HEIGHT)) {
|
|
419
|
+
val path = Path().apply {
|
|
420
|
+
moveTo(0f, 0f)
|
|
421
|
+
lineTo(0.60765f, 0f)
|
|
422
|
+
cubicTo(6.47582f, 0f, 11.4819f, 4.23831f, 12.4602f, 10.0244f)
|
|
423
|
+
cubicTo(13.9248f, 18.6872f, 15.9294f, 30.37f, 16.6925f, 34f)
|
|
424
|
+
cubicTo(17.9536f, 40f, 22.259f, 44f, 27.9996f, 44f)
|
|
425
|
+
lineTo(0f, 44f)
|
|
426
|
+
close()
|
|
427
|
+
}
|
|
428
|
+
val draw: DrawScope.() -> Unit = { drawPath(path = path, color = color) }
|
|
429
|
+
if (mirrored) withTransform({ scale(-1f, 1f) }) { draw() } else draw()
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
@Composable
|
|
434
|
+
private fun TabItemView(
|
|
435
|
+
modifier: Modifier = Modifier,
|
|
436
|
+
tab: TabViewItem,
|
|
437
|
+
active: Boolean,
|
|
438
|
+
selectedColor: Color,
|
|
439
|
+
unselectedColor: Color,
|
|
440
|
+
direction: TabViewDirection,
|
|
441
|
+
scrollable: Boolean = false,
|
|
442
|
+
onPress: () -> Unit,
|
|
443
|
+
) {
|
|
444
|
+
val textColor = if (active) selectedColor else unselectedColor
|
|
445
|
+
val textStyle = if (active) Typography.headerSSemibold else Typography.bodyDefaultRegular
|
|
446
|
+
|
|
447
|
+
if (direction == TabViewDirection.COLUMN) {
|
|
448
|
+
Column(
|
|
449
|
+
modifier = modifier
|
|
450
|
+
.height(68.dp)
|
|
451
|
+
.activeOpacityClickable { onPress() }
|
|
452
|
+
.padding(horizontal = Spacing.S),
|
|
453
|
+
horizontalAlignment = Alignment.CenterHorizontally,
|
|
454
|
+
verticalArrangement = Arrangement.Center,
|
|
455
|
+
) {
|
|
456
|
+
// Icon area with badge/dot overlay
|
|
457
|
+
Box(contentAlignment = Alignment.TopEnd) {
|
|
458
|
+
if (tab.renderIcon != null) {
|
|
459
|
+
tab.renderIcon.invoke(active)
|
|
460
|
+
} else if (tab.icon != null) {
|
|
461
|
+
Icon(
|
|
462
|
+
source = tab.icon,
|
|
463
|
+
size = 24.dp,
|
|
464
|
+
color = textColor,
|
|
465
|
+
)
|
|
466
|
+
}
|
|
467
|
+
if (tab.showDot) {
|
|
468
|
+
BadgeDot(
|
|
469
|
+
size = tab.dotSize,
|
|
470
|
+
modifier = Modifier
|
|
471
|
+
.align(Alignment.TopEnd)
|
|
472
|
+
.offset(x = 4.dp, y = (-4).dp),
|
|
473
|
+
)
|
|
474
|
+
} else if (!tab.badgeValue.isNullOrEmpty()) {
|
|
475
|
+
Badge(
|
|
476
|
+
label = tab.badgeValue,
|
|
477
|
+
modifier = Modifier
|
|
478
|
+
.align(Alignment.TopEnd)
|
|
479
|
+
.offset(x = 4.dp, y = (-4).dp),
|
|
480
|
+
)
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
if (tab.icon != null || tab.renderIcon != null) {
|
|
484
|
+
Spacer(modifier = Modifier.height(Spacing.XXS))
|
|
485
|
+
}
|
|
486
|
+
Text(
|
|
487
|
+
text = tab.title,
|
|
488
|
+
style = textStyle,
|
|
489
|
+
color = textColor,
|
|
490
|
+
maxLines = 1,
|
|
491
|
+
overflow = TextOverflow.Ellipsis,
|
|
492
|
+
)
|
|
493
|
+
}
|
|
494
|
+
} else {
|
|
495
|
+
Row(
|
|
496
|
+
modifier = modifier
|
|
497
|
+
.height(48.dp)
|
|
498
|
+
.activeOpacityClickable { onPress() }
|
|
499
|
+
.padding(horizontal = Spacing.M),
|
|
500
|
+
horizontalArrangement = Arrangement.Center,
|
|
501
|
+
verticalAlignment = Alignment.CenterVertically,
|
|
502
|
+
) {
|
|
503
|
+
TabLeadingIcon(tab = tab, active = active, color = textColor)
|
|
504
|
+
Text(
|
|
505
|
+
text = tab.title,
|
|
506
|
+
style = textStyle,
|
|
507
|
+
color = textColor,
|
|
508
|
+
maxLines = 1,
|
|
509
|
+
overflow = TextOverflow.Ellipsis,
|
|
510
|
+
modifier = if (scrollable) Modifier else Modifier.weight(1f, fill = false),
|
|
511
|
+
)
|
|
512
|
+
TabTrailingBadge(tab)
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
@Composable
|
|
518
|
+
private fun BoxScope.TabUnderlineIndicator(offsetX: Dp, width: Dp, color: Color) {
|
|
519
|
+
Box(
|
|
520
|
+
modifier = Modifier
|
|
521
|
+
.align(Alignment.BottomStart)
|
|
522
|
+
.offset(x = offsetX)
|
|
523
|
+
.width(width)
|
|
524
|
+
.height(2.dp)
|
|
525
|
+
.background(
|
|
526
|
+
color = color,
|
|
527
|
+
shape = RoundedCornerShape(topStart = Radius.XXS, topEnd = Radius.XXS),
|
|
528
|
+
),
|
|
529
|
+
)
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
@Composable
|
|
533
|
+
private fun TabLeadingIcon(tab: TabViewItem, active: Boolean, color: Color) {
|
|
534
|
+
if (tab.renderIcon != null) {
|
|
535
|
+
tab.renderIcon.invoke(active)
|
|
536
|
+
Spacer(modifier = Modifier.width(Spacing.XS))
|
|
537
|
+
} else if (tab.icon != null) {
|
|
538
|
+
Icon(source = tab.icon, size = 18.dp, color = color)
|
|
539
|
+
Spacer(modifier = Modifier.width(Spacing.XS))
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
@Composable
|
|
544
|
+
private fun TabTrailingBadge(tab: TabViewItem) {
|
|
545
|
+
if (tab.showDot) {
|
|
546
|
+
Spacer(modifier = Modifier.width(Spacing.XS))
|
|
547
|
+
BadgeDot(size = tab.dotSize)
|
|
548
|
+
} else if (!tab.badgeValue.isNullOrEmpty()) {
|
|
549
|
+
Spacer(modifier = Modifier.width(Spacing.XS))
|
|
550
|
+
Badge(label = tab.badgeValue, modifier = Modifier)
|
|
551
|
+
}
|
|
552
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
package vn.momo.kits.components
|
|
2
|
+
|
|
3
|
+
import androidx.compose.foundation.background
|
|
4
|
+
import androidx.compose.foundation.border
|
|
5
|
+
import androidx.compose.foundation.layout.Box
|
|
6
|
+
import androidx.compose.foundation.layout.Row
|
|
7
|
+
import androidx.compose.foundation.layout.height
|
|
8
|
+
import androidx.compose.foundation.layout.padding
|
|
9
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
10
|
+
import androidx.compose.runtime.Composable
|
|
11
|
+
import androidx.compose.ui.Alignment
|
|
12
|
+
import androidx.compose.ui.Modifier
|
|
13
|
+
import androidx.compose.ui.graphics.Color
|
|
14
|
+
import androidx.compose.ui.unit.dp
|
|
15
|
+
import vn.momo.kits.application.IsShowBaseLineDebug
|
|
16
|
+
import vn.momo.kits.const.*
|
|
17
|
+
import vn.momo.kits.modifier.conditional
|
|
18
|
+
|
|
19
|
+
enum class TagSize(val height: Float) {
|
|
20
|
+
Large(24.toFloat()),
|
|
21
|
+
Medium(18.toFloat())
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
enum class TagColor(val backgroundColor: Color, val textColor: Color) {
|
|
25
|
+
Default(Colors.black_04, Colors.black_17),
|
|
26
|
+
Orange(Colors.orange_08, Colors.orange_03),
|
|
27
|
+
Green(Colors.green_08, Colors.green_03),
|
|
28
|
+
Red(Colors.red_08, Colors.red_03),
|
|
29
|
+
Blue(Colors.blue_08, Colors.blue_03),
|
|
30
|
+
Grey(Colors.black_04, Colors.black_12),
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@Composable
|
|
34
|
+
fun Tag(
|
|
35
|
+
label: String = "Label",
|
|
36
|
+
icon: String? = null,
|
|
37
|
+
color: TagColor = TagColor.Default,
|
|
38
|
+
size: TagSize = TagSize.Large,
|
|
39
|
+
customColor: Color? = null,
|
|
40
|
+
) {
|
|
41
|
+
val primaryColors = listOf(
|
|
42
|
+
Color(0xFFF0F0F0),
|
|
43
|
+
Color(0xFFEB2F96),
|
|
44
|
+
Color(0xFF962AF0),
|
|
45
|
+
Color(0xFF4E4BFF),
|
|
46
|
+
Color(0xFF007AFF),
|
|
47
|
+
Color(0xFF13C2C2),
|
|
48
|
+
Color(0xFF34C759),
|
|
49
|
+
Color(0xFFA0D911),
|
|
50
|
+
Color(0xFFFFCC00),
|
|
51
|
+
Color(0xFFFA8C16),
|
|
52
|
+
Color(0xFFFA541C),
|
|
53
|
+
Color(0xFFF5222D)
|
|
54
|
+
)
|
|
55
|
+
var tagColor = color.backgroundColor
|
|
56
|
+
var labelColor = color.textColor
|
|
57
|
+
|
|
58
|
+
if (primaryColors.contains(customColor)) {
|
|
59
|
+
tagColor = color.backgroundColor
|
|
60
|
+
labelColor = color.textColor
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
Box(
|
|
64
|
+
modifier = Modifier
|
|
65
|
+
.height(scaleSize(size.height).dp)
|
|
66
|
+
.background(tagColor, shape = RoundedCornerShape(Radius.S))
|
|
67
|
+
.conditional(IsShowBaseLineDebug) {
|
|
68
|
+
border(1.dp, Colors.blue_03)
|
|
69
|
+
}
|
|
70
|
+
.padding(horizontal = Spacing.S),
|
|
71
|
+
contentAlignment = Alignment.Center
|
|
72
|
+
) {
|
|
73
|
+
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
74
|
+
if (icon != null) {
|
|
75
|
+
Icon(
|
|
76
|
+
source = icon,
|
|
77
|
+
size = 16.dp,
|
|
78
|
+
color = labelColor
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
Text(
|
|
82
|
+
text = label,
|
|
83
|
+
color = labelColor,
|
|
84
|
+
style = Typography.labelSMedium
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|