Skip to content

Commit fcb2a98

Browse files
HowieHChenYuKongA
andauthored
library: New component [SuperSpinner] (#33)
* update: [SuperDropdown] Use items to optimize performance * add: New [SuperSpinner] component, supports for both dropdown modes and new dialog mode, also support for complex items --------- Co-authored-by: YuKongA <[email protected]>
1 parent d495f60 commit fcb2a98

File tree

4 files changed

+734
-38
lines changed

4 files changed

+734
-38
lines changed

composeApp/src/commonMain/kotlin/component/TextComponent.kt

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ import androidx.compose.runtime.remember
2222
import androidx.compose.runtime.setValue
2323
import androidx.compose.ui.Alignment
2424
import androidx.compose.ui.Modifier
25+
import androidx.compose.ui.geometry.CornerRadius
26+
import androidx.compose.ui.geometry.Size
27+
import androidx.compose.ui.graphics.Color
28+
import androidx.compose.ui.graphics.drawscope.DrawScope
29+
import androidx.compose.ui.graphics.painter.Painter
30+
import androidx.compose.ui.unit.Dp
2531
import androidx.compose.ui.unit.dp
2632
import top.yukonga.miuix.kmp.basic.BasicComponent
2733
import top.yukonga.miuix.kmp.basic.Box
@@ -35,10 +41,14 @@ import top.yukonga.miuix.kmp.basic.Text
3541
import top.yukonga.miuix.kmp.basic.TextButton
3642
import top.yukonga.miuix.kmp.basic.TextField
3743
import top.yukonga.miuix.kmp.extra.CheckboxLocation
44+
import top.yukonga.miuix.kmp.extra.DropDownMode
45+
import top.yukonga.miuix.kmp.extra.SpinnerEntry
46+
import top.yukonga.miuix.kmp.extra.SpinnerMode
3847
import top.yukonga.miuix.kmp.extra.SuperArrow
3948
import top.yukonga.miuix.kmp.extra.SuperCheckbox
4049
import top.yukonga.miuix.kmp.extra.SuperDialog
4150
import top.yukonga.miuix.kmp.extra.SuperDropdown
51+
import top.yukonga.miuix.kmp.extra.SuperSpinner
4252
import top.yukonga.miuix.kmp.extra.SuperSwitch
4353
import top.yukonga.miuix.kmp.theme.MiuixTheme
4454
import top.yukonga.miuix.kmp.utils.MiuixPopupUtil.Companion.dismissDialog
@@ -52,8 +62,17 @@ fun TextComponent() {
5262
var switch by remember { mutableStateOf(false) }
5363
var switchTrue by remember { mutableStateOf(true) }
5464
val dropdownOptions = listOf("Option 1", "Option 2", "Option 3", "Option 4")
55-
val dropdownSelectedOption = remember { mutableStateOf(0) }
56-
val dropdownSelectedOptionRight = remember { mutableStateOf(1) }
65+
val dropdownOptionSelected = remember { mutableStateOf(0) }
66+
val dropdownOptionSelectedRight = remember { mutableStateOf(1) }
67+
val spinnerOptions = listOf(
68+
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFFFF5B29)) }, "Option 1", "Red"),
69+
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFF36D167)) }, "Option 2", "Green"),
70+
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFF3482FF)) }, "Option 3", "Blue"),
71+
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFFFFB21D)) }, "Option 4", "Yellow"),
72+
)
73+
val spinnerOptionSelected = remember { mutableStateOf(0) }
74+
val spinnerOptionSelectedRight = remember { mutableStateOf(1) }
75+
val spinnerOptionSelectedDialog = remember { mutableStateOf(2) }
5776
var miuixSuperCheckbox by remember { mutableStateOf("State: false") }
5877
var miuixSuperCheckboxState by remember { mutableStateOf(false) }
5978
var miuixSuperRightCheckbox by remember { mutableStateOf("false") }
@@ -295,7 +314,6 @@ fun TextComponent() {
295314
}
296315

297316
SmallTitle(text = "Dropdown")
298-
299317
Card(
300318
modifier = Modifier
301319
.padding(horizontal = 12.dp)
@@ -305,17 +323,17 @@ fun TextComponent() {
305323
title = "Dropdown",
306324
summary = "Popup near click",
307325
items = dropdownOptions,
308-
selectedIndex = dropdownSelectedOption.value,
309-
onSelectedIndexChange = { newOption -> dropdownSelectedOption.value = newOption },
326+
selectedIndex = dropdownOptionSelected.value,
327+
onSelectedIndexChange = { newOption -> dropdownOptionSelected.value = newOption },
310328
)
311329

312330
SuperDropdown(
313331
title = "Dropdown",
314332
summary = "Popup always on right",
315-
alwaysRight = true,
316333
items = dropdownOptions,
317-
selectedIndex = dropdownSelectedOptionRight.value,
318-
onSelectedIndexChange = { newOption -> dropdownSelectedOptionRight.value = newOption },
334+
selectedIndex = dropdownOptionSelectedRight.value,
335+
onSelectedIndexChange = { newOption -> dropdownOptionSelectedRight.value = newOption },
336+
mode = DropDownMode.AlwaysOnRight
319337
)
320338

321339
SuperDropdown(
@@ -327,6 +345,47 @@ fun TextComponent() {
327345
)
328346
}
329347

348+
SmallTitle(text = "Spinner")
349+
Card(
350+
modifier = Modifier
351+
.padding(horizontal = 12.dp)
352+
.padding(bottom = 6.dp)
353+
) {
354+
SuperSpinner(
355+
title = "Spinner",
356+
summary = "Spinner near click",
357+
items = spinnerOptions,
358+
selectedIndex = spinnerOptionSelected.value,
359+
onSelectedIndexChange = { newOption -> spinnerOptionSelected.value = newOption },
360+
)
361+
362+
SuperSpinner(
363+
title = "Spinner",
364+
summary = "Spinner always on right",
365+
items = spinnerOptions,
366+
selectedIndex = spinnerOptionSelectedRight.value,
367+
onSelectedIndexChange = { newOption -> spinnerOptionSelectedRight.value = newOption },
368+
mode = SpinnerMode.AlwaysOnRight,
369+
)
370+
371+
SuperSpinner(
372+
title = "Spinner",
373+
summary = "Spinner as Dialog",
374+
dialogButtonString = "Cancel",
375+
items = spinnerOptions,
376+
selectedIndex = spinnerOptionSelectedDialog.value,
377+
onSelectedIndexChange = { newOption -> spinnerOptionSelectedDialog.value = newOption },
378+
)
379+
380+
SuperSpinner(
381+
title = "Disabled Spinner",
382+
items = listOf(SpinnerEntry(icon = null, title = "Option 4")),
383+
selectedIndex = 0,
384+
onSelectedIndexChange = {},
385+
enabled = false
386+
)
387+
}
388+
330389
dialog(showDialog)
331390
dialog2(showDialog2)
332391
}
@@ -424,3 +483,17 @@ fun dialog2(showDialog: MutableState<Boolean>) {
424483
}
425484
}
426485
}
486+
487+
class RoundedRectanglePainter(
488+
private val cornerRadius: Dp = 6.dp
489+
) : Painter() {
490+
override val intrinsicSize = Size.Unspecified
491+
492+
override fun DrawScope.onDraw() {
493+
drawRoundRect(
494+
color = Color.White,
495+
size = Size(size.width, size.height),
496+
cornerRadius = CornerRadius(cornerRadius.toPx(), cornerRadius.toPx())
497+
)
498+
}
499+
}

miuix/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,4 @@ android {
5757
defaultConfig {
5858
minSdk = libs.versions.android.minSdk.get().toInt()
5959
}
60-
}
60+
}

miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/extra/SuperDropdown.kt

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -86,34 +86,36 @@ import kotlin.math.roundToInt
8686
* @param title The title of the [SuperDropdown].
8787
* @param items The options of the [SuperDropdown].
8888
* @param selectedIndex The index of the selected option.
89-
* @param onSelectedIndexChange The callback when the index is selected.
9089
* @param modifier The modifier to be applied to the [SuperDropdown].
9190
* @param popupModifier The modifier to be applied to the popup of the [SuperDropdown].
9291
* @param titleColor The color of the title.
9392
* @param summary The summary of the [SuperDropdown].
9493
* @param summaryColor The color of the summary.
94+
* @param mode The dropdown show mode of the [SuperDropdown].
9595
* @param horizontalPadding The horizontal padding of the [SuperDropdown].
96-
* @param alwaysRight Whether the popup is always show on the right side.
9796
* @param insideMargin The margin inside the [SuperDropdown].
9897
* @param defaultWindowInsetsPadding Whether to apply default window insets padding to the [SuperDropdown].
9998
* @param enabled Whether the [SuperDropdown] is enabled.
99+
* @param showValue Whether to show the selected value of the [SuperDropdown].
100+
* @param onSelectedIndexChange The callback when the selected index of the [SuperDropdown] is changed.
100101
*/
101102
@Composable
102103
fun SuperDropdown(
103104
title: String,
104105
items: List<String>,
105106
selectedIndex: Int,
106-
onSelectedIndexChange: (Int) -> Unit,
107107
modifier: Modifier = Modifier,
108108
popupModifier: Modifier = Modifier,
109109
titleColor: BasicComponentColors = BasicComponentDefaults.titleColor(),
110110
summary: String? = null,
111111
summaryColor: BasicComponentColors = BasicComponentDefaults.summaryColor(),
112-
alwaysRight: Boolean = false,
112+
mode: DropDownMode = DropDownMode.Normal,
113113
horizontalPadding: Dp = 0.dp,
114114
insideMargin: PaddingValues = BasicComponentDefaults.InsideMargin,
115115
defaultWindowInsetsPadding: Boolean = true,
116-
enabled: Boolean = true
116+
enabled: Boolean = true,
117+
showValue: Boolean = true,
118+
onSelectedIndexChange: ((Int) -> Unit)?,
117119
) {
118120
val interactionSource = remember { MutableInteractionSource() }
119121
val isDropdownExpanded = remember { mutableStateOf(false) }
@@ -171,13 +173,15 @@ fun SuperDropdown(
171173
summary = summary,
172174
summaryColor = summaryColor,
173175
rightActions = {
174-
Text(
175-
modifier = Modifier.widthIn(max = 130.dp),
176-
text = items[selectedIndex],
177-
fontSize = MiuixTheme.textStyles.body2.fontSize,
178-
color = actionColor,
179-
textAlign = TextAlign.End,
180-
)
176+
if (showValue) {
177+
Text(
178+
modifier = Modifier.widthIn(max = 130.dp),
179+
text = items[selectedIndex],
180+
fontSize = MiuixTheme.textStyles.body2.fontSize,
181+
color = actionColor,
182+
textAlign = TextAlign.End,
183+
)
184+
}
181185
Image(
182186
modifier = Modifier
183187
.padding(start = 8.dp)
@@ -275,7 +279,7 @@ fun SuperDropdown(
275279
LazyColumn(
276280
modifier = Modifier
277281
.onGloballyPositioned { layoutCoordinates ->
278-
offsetXPx = if (alwaysRight || !alignLeft) {
282+
offsetXPx = if (mode == DropDownMode.AlwaysOnRight || !alignLeft) {
279283
dropdownOffsetXPx + componentWidthPx - insideRightPx - layoutCoordinates.size.width - paddingPx - if (defaultWindowInsetsPadding) displayCutoutLeftSize.value else 0
280284
} else {
281285
dropdownOffsetXPx + paddingPx + insideLeftPx - if (defaultWindowInsetsPadding) displayCutoutLeftSize.value else 0
@@ -303,21 +307,19 @@ fun SuperDropdown(
303307
.clip(SmoothRoundedCornerShape(16.dp))
304308
.background(MiuixTheme.colorScheme.surface)
305309
) {
306-
item {
307-
items.forEachIndexed { index, option ->
308-
DropdownImpl(
309-
text = option,
310-
optionSize = items.size,
311-
isSelected = items[selectedIndex] == option,
312-
onSelectedIndexChange = {
313-
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
314-
onSelectedIndexChange(it)
315-
dismissPopup(isDropdownExpanded)
316-
},
317-
textWidthDp = textWidthDp,
318-
index = index
319-
)
320-
}
310+
items(items.size) { index ->
311+
DropdownImpl(
312+
text = items[index],
313+
optionSize = items.size,
314+
isSelected = selectedIndex == index,
315+
onSelectedIndexChange = {
316+
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
317+
onSelectedIndexChange?.let { it1 -> it1(it) }
318+
dismissPopup(isDropdownExpanded)
319+
},
320+
textWidthDp = textWidthDp,
321+
index = index
322+
)
321323
}
322324
}
323325
}
@@ -377,7 +379,7 @@ fun DropdownImpl(
377379
Text(
378380
modifier = Modifier.width(textWidthDp ?: 50.dp),
379381
text = text,
380-
fontSize = 16.sp,
382+
fontSize = MiuixTheme.textStyles.body1.fontSize,
381383
fontWeight = FontWeight.Medium,
382384
color = textColor,
383385
)
@@ -439,3 +441,11 @@ fun calculateOffsetYPx(
439441
* Only one dropdown is allowed to be displayed at a time.
440442
*/
441443
val dropdownStates = mutableStateListOf<MutableState<Boolean>>()
444+
445+
/**
446+
* The dropdown show mode.
447+
*/
448+
enum class DropDownMode {
449+
Normal,
450+
AlwaysOnRight
451+
}

0 commit comments

Comments
 (0)