11package top.yukonga.miuix.kmp.basic
22
3+ import androidx.compose.foundation.background
4+ import androidx.compose.foundation.clickable
35import androidx.compose.foundation.layout.Arrangement
46import androidx.compose.foundation.layout.Box
57import androidx.compose.foundation.layout.fillMaxHeight
68import androidx.compose.foundation.layout.fillMaxSize
79import androidx.compose.foundation.layout.fillMaxWidth
810import androidx.compose.foundation.layout.height
11+ import androidx.compose.foundation.layout.padding
912import androidx.compose.foundation.layout.width
1013import androidx.compose.foundation.lazy.LazyRow
1114import androidx.compose.foundation.lazy.itemsIndexed
1215import androidx.compose.foundation.lazy.rememberLazyListState
1316import androidx.compose.runtime.Composable
17+ import androidx.compose.runtime.Immutable
18+ import androidx.compose.runtime.Stable
1419import androidx.compose.runtime.derivedStateOf
1520import androidx.compose.runtime.remember
1621import androidx.compose.ui.Alignment
1722import androidx.compose.ui.Modifier
23+ import androidx.compose.ui.draw.clip
1824import androidx.compose.ui.graphics.Color
1925import androidx.compose.ui.platform.LocalDensity
2026import androidx.compose.ui.semantics.Role
2127import androidx.compose.ui.semantics.role
2228import androidx.compose.ui.semantics.semantics
2329import androidx.compose.ui.text.font.FontWeight
30+ import androidx.compose.ui.text.style.TextOverflow
2431import androidx.compose.ui.unit.Dp
2532import androidx.compose.ui.unit.dp
2633import androidx.compose.ui.unit.times
@@ -35,54 +42,44 @@ import top.yukonga.miuix.kmp.utils.overScrollHorizontal
3542 * @param tabs The text to be displayed in the [TabRow].
3643 * @param selectedTabIndex The selected tab index of the [TabRow]
3744 * @param modifier The modifier to be applied to the [TabRow].
38- * @param backgroundColor The background color of the tab in [TabRow].
39- * @param contentColor The text color of the tab in [TabRow].
40- * @param selectedBackgroundColor The background color of the selected tab in [TabRow].
41- * @param selectedColor The text color of the selected tab in [TabRow].
45+ * @param colors The colors of the [TabRow].
46+ * @param minWidth The minimum width of the tab in [TabRow].
47+ * @param height The height of the [TabRow].
4248 * @param cornerRadius The round corner radius of the tab in [TabRow].
43- * @param onSelect The callback when the tab of the [TabRow] is clicked .
49+ * @param onTabSelected The callback when a tab is selected .
4450 */
4551@Composable
4652fun TabRow (
4753 tabs : List <String >,
4854 selectedTabIndex : Int ,
4955 modifier : Modifier = Modifier ,
50- backgroundColor : Color = MiuixTheme .colorScheme.background,
51- contentColor : Color = MiuixTheme .colorScheme.onSurfaceVariantSummary,
52- selectedBackgroundColor : Color = MiuixTheme .colorScheme.surface,
53- selectedColor : Color = MiuixTheme .colorScheme.onSurface,
54- cornerRadius : Dp = 8.dp,
55- onSelect : ((Int ) -> Unit )? = null,
56+ colors : TabRowColors = TabRowDefaults .tabRowColors(),
57+ minWidth : Dp = TabRowDefaults .TabRowMinWidth ,
58+ height : Dp = TabRowDefaults .TabRowHeight ,
59+ cornerRadius : Dp = TabRowDefaults .TabRowCornerRadius ,
60+ onTabSelected : ((Int ) -> Unit )? = null,
5661) {
57- val listState = rememberLazyListState()
58- val windowSize = getWindowSize()
59- val density = LocalDensity .current
60- var tabWidth: Dp
61- with (density) {
62- tabWidth = ((windowSize.width.toDp() - (tabs.size - 1 ) * 9 .dp) / tabs.size).coerceAtLeast(62 .dp)
63- }
64-
65- val shape = remember { derivedStateOf { SmoothRoundedCornerShape (cornerRadius) } }
62+ val config = rememberTabRowConfig(tabs, minWidth, cornerRadius)
6663
6764 LazyRow (
68- state = listState,
65+ state = config. listState,
6966 modifier = modifier
70- .fillMaxWidth().height(42 .dp)
67+ .fillMaxWidth()
68+ .height(height)
69+ .clip(config.shape)
7170 .overScrollHorizontal(),
7271 verticalAlignment = Alignment .CenterVertically ,
7372 horizontalArrangement = Arrangement .spacedBy(9 .dp)
7473 ) {
7574 itemsIndexed(tabs) { index, tabText ->
7675 Surface (
77- shape = shape.value,
78- onClick = {
79- onSelect?.invoke(index)
80- },
81- enabled = onSelect != null ,
82- color = if (selectedTabIndex == index) selectedBackgroundColor else backgroundColor,
76+ shape = config.shape,
77+ onClick = { onTabSelected?.invoke(index) },
78+ enabled = onTabSelected != null ,
79+ color = colors.backgroundColor(selectedTabIndex == index),
8380 modifier = Modifier
8481 .fillMaxHeight()
85- .width(tabWidth)
82+ .width(config. tabWidth)
8683 .semantics { role = Role .Tab }
8784 ) {
8885 Box (
@@ -91,12 +88,164 @@ fun TabRow(
9188 ) {
9289 Text (
9390 text = tabText,
94- color = if (selectedTabIndex == index) selectedColor else contentColor ,
91+ color = colors.contentColor (selectedTabIndex == index),
9592 fontWeight = if (selectedTabIndex == index) FontWeight .Bold else FontWeight .Normal ,
96- maxLines = 1
93+ maxLines = 1 ,
94+ overflow = TextOverflow .Ellipsis
9795 )
9896 }
9997 }
10098 }
10199 }
100+ }
101+
102+ /* *
103+ * A [TabRowWithContour] with Miuix style.
104+ *
105+ * @param tabs The text to be displayed in the [TabRow].
106+ * @param selectedTabIndex The selected tab index of the [TabRow]
107+ * @param modifier The modifier to be applied to the [TabRow].
108+ * @param colors The colors of the [TabRow].
109+ * @param minWidth The minimum width of the tab in [TabRow].
110+ * @param height The height of the [TabRow].
111+ * @param cornerRadius The round corner radius of the tab in [TabRow].
112+ * @param onTabSelected The callback when a tab is selected.
113+ */
114+ @Composable
115+ fun TabRowWithContour (
116+ tabs : List <String >,
117+ selectedTabIndex : Int ,
118+ modifier : Modifier = Modifier ,
119+ colors : TabRowColors = TabRowDefaults .tabRowColors(),
120+ minWidth : Dp = TabRowDefaults .TabRowWithContourMinWidth ,
121+ height : Dp = TabRowDefaults .TabRowHeight ,
122+ cornerRadius : Dp = TabRowDefaults .TabRowWithContourCornerRadius ,
123+ onTabSelected : ((Int ) -> Unit )? = null,
124+ ) {
125+ val config = rememberTabRowConfig(tabs, minWidth, cornerRadius)
126+
127+ LazyRow (
128+ state = config.listState,
129+ modifier = modifier
130+ .fillMaxWidth()
131+ .height(height)
132+ .clip(SmoothRoundedCornerShape (cornerRadius + 5 .dp))
133+ .background(color = colors.backgroundColor(false ))
134+ .padding(5 .dp)
135+ .clip(SmoothRoundedCornerShape (cornerRadius))
136+ .overScrollHorizontal(),
137+ verticalAlignment = Alignment .CenterVertically
138+ ) {
139+ itemsIndexed(tabs) { index, tabText ->
140+ val isSelected = index == selectedTabIndex
141+
142+ Box (
143+ modifier = Modifier
144+ .fillMaxHeight()
145+ .width(config.tabWidth)
146+ .clip(config.shape)
147+ .background(colors.backgroundColor(selectedTabIndex == index))
148+ .clickable(enabled = onTabSelected != null ) {
149+ onTabSelected?.invoke(index)
150+ }
151+ .semantics { role = Role .Tab },
152+ contentAlignment = Alignment .Center
153+ ) {
154+ Text (
155+ text = tabText,
156+ color = colors.contentColor(selectedTabIndex == index),
157+ fontWeight = if (isSelected) FontWeight .Bold else FontWeight .Normal ,
158+ maxLines = 1 ,
159+ overflow = TextOverflow .Ellipsis
160+ )
161+ }
162+ }
163+ }
164+ }
165+
166+ /* *
167+ * Base configuration for TabRow implementations.
168+ */
169+ private data class TabRowConfig (
170+ val tabWidth : Dp ,
171+ val shape : SmoothRoundedCornerShape ,
172+ val listState : androidx.compose.foundation.lazy.LazyListState
173+ )
174+
175+ /* *
176+ * Prepare common TabRow configuration.
177+ */
178+ @Composable
179+ private fun rememberTabRowConfig (tabs : List <String >, minWidth : Dp , cornerRadius : Dp ): TabRowConfig {
180+ val listState = rememberLazyListState()
181+ val windowSize = getWindowSize()
182+ val density = LocalDensity .current
183+ val tabWidth = with (density) {
184+ ((windowSize.width.toDp() - (tabs.size - 1 ) * 9 .dp) / tabs.size).coerceAtLeast(minWidth)
185+ }
186+ val shape = remember { derivedStateOf { SmoothRoundedCornerShape (cornerRadius) } }
187+
188+ return TabRowConfig (tabWidth, shape.value, listState)
189+ }
190+
191+ object TabRowDefaults {
192+
193+ /* *
194+ * The default height of the [TabRow].
195+ */
196+ val TabRowHeight = 42 .dp
197+
198+ /* *
199+ * The default corner radius of the [TabRow].
200+ */
201+ val TabRowCornerRadius = 8 .dp
202+
203+ /* *
204+ * The default corner radius of the [TabRow] with contour style.
205+ */
206+ val TabRowWithContourCornerRadius = 10 .dp
207+
208+ /* *
209+ * The default minimum width of the [TabRow].
210+ */
211+ val TabRowMinWidth = 76 .dp
212+
213+ /* *
214+ * The default minimum width of the [TabRow] with contour style.
215+ */
216+ val TabRowWithContourMinWidth = 62 .dp
217+
218+ /* *
219+ * The default colors for the [TabRow].
220+ */
221+ @Composable
222+ fun tabRowColors (
223+ backgroundColor : Color = MiuixTheme .colorScheme.background,
224+ contentColor : Color = MiuixTheme .colorScheme.onSurfaceVariantSummary,
225+ selectedBackgroundColor : Color = MiuixTheme .colorScheme.surface,
226+ selectedContentColor : Color = MiuixTheme .colorScheme.onSurface
227+ ): TabRowColors {
228+ return TabRowColors (
229+ backgroundColor = backgroundColor,
230+ contentColor = contentColor,
231+ selectedBackgroundColor = selectedBackgroundColor,
232+ selectedContentColor = selectedContentColor
233+ )
234+ }
235+ }
236+
237+ @Immutable
238+ class TabRowColors (
239+ private val backgroundColor : Color ,
240+ private val contentColor : Color ,
241+ private val selectedBackgroundColor : Color ,
242+ private val selectedContentColor : Color
243+ ) {
244+ @Stable
245+ internal fun backgroundColor (selected : Boolean ): Color =
246+ if (selected) selectedBackgroundColor else backgroundColor
247+
248+ @Stable
249+ internal fun contentColor (selected : Boolean ): Color =
250+ if (selected) selectedContentColor else contentColor
102251}
0 commit comments