Skip to content

Commit

Permalink
library: Restricts Dropdown item size (#44)
Browse files Browse the repository at this point in the history
* fix: fix width anomaly when Dropdown item is too wide;
* fix: fix crash when Dropdown list is too long;
  • Loading branch information
HowieHChen authored Dec 4, 2024
1 parent 4987585 commit dbb1962
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 33 deletions.
23 changes: 10 additions & 13 deletions composeApp/src/commonMain/kotlin/UITest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.Composable
Expand All @@ -38,6 +37,7 @@ import top.yukonga.miuix.kmp.basic.HorizontalPager
import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.IconButton
import top.yukonga.miuix.kmp.basic.ListPopup
import top.yukonga.miuix.kmp.basic.ListPopupColumn
import top.yukonga.miuix.kmp.basic.ListPopupDefaults
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
import top.yukonga.miuix.kmp.basic.NavigationBar
Expand Down Expand Up @@ -131,10 +131,10 @@ fun UITest(
isTopPopupExpanded.value = false
}
) {
LazyColumn {
items(items.take(3).size) { index ->
ListPopupColumn {
items.take(3).forEachIndexed { index, navigationItem ->
DropdownImpl(
text = items[index].label,
text = navigationItem.label,
optionSize = items.take(3).size,
isSelected = items[index] == items[targetPage],
onSelectedIndexChange = {
Expand All @@ -145,7 +145,6 @@ fun UITest(
dismissPopup(showTopPopup)
isTopPopupExpanded.value = false
},
textWidthDp = 100.dp,
index = index
)
}
Expand Down Expand Up @@ -180,10 +179,10 @@ fun UITest(
isTopPopupExpanded.value = false
}
) {
LazyColumn {
items(items.take(3).size) { index ->
ListPopupColumn {
items.take(3).forEachIndexed { index, navigationItem ->
DropdownImpl(
text = items[index].label,
text = navigationItem.label,
optionSize = items.take(3).size,
isSelected = items[index] == items[targetPage],
onSelectedIndexChange = {
Expand All @@ -194,7 +193,6 @@ fun UITest(
dismissPopup(showTopPopup)
isTopPopupExpanded.value = false
},
textWidthDp = 100.dp,
index = index
)
}
Expand Down Expand Up @@ -234,10 +232,10 @@ fun UITest(
isBottomPopupExpanded.value = false
}
) {
LazyColumn {
items(items.take(3).size) { index ->
ListPopupColumn {
items.take(3).forEachIndexed { index, navigationItem ->
DropdownImpl(
text = items[index].label,
text = navigationItem.label,
optionSize = items.take(3).size,
isSelected = items[index] == items[targetPage],
onSelectedIndexChange = {
Expand All @@ -248,7 +246,6 @@ fun UITest(
dismissPopup(showBottomPopup)
isBottomPopupExpanded.value = false
},
textWidthDp = 100.dp,
index = index
)
}
Expand Down
15 changes: 13 additions & 2 deletions composeApp/src/commonMain/kotlin/component/TextComponent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,23 @@ fun TextComponent(
miuixSuperSwitchAnimState: MutableState<Boolean>,
) {
val dropdownOptions = listOf("Option 1", "Option 2", "Option 3", "Option 4")
val dropdownOptions2 = listOf(
"Option 1", "Option 2", "Option 3", "Option 4",
"Option 5", "Option 6", "Option 7", "Option 8",
"Option 9", "Option 10", "Option 11", "Option 12 (this is a long long long option)"
)
val spinnerOptions = listOf(
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFFFF5B29)) }, "Option 1", "Red"),
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFF36D167)) }, "Option 2", "Green"),
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFF3482FF)) }, "Option 3", "Blue"),
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFFFFB21D)) }, "Option 4", "Yellow"),
)
val spinnerOptions2 = listOf(
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFFFF5B29)) }, "Option 1", "Red"),
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFF36D167)) }, "Option 2", "Green"),
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFF3482FF)) }, "Option 3", "Blue"),
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFFFFB21D)) }, "Option 4 (this is a long option)", "Yellow"),
)
SmallTitle(text = "Basic")
Card(
modifier = Modifier
Expand Down Expand Up @@ -329,7 +340,7 @@ fun TextComponent(
SuperDropdown(
title = "Dropdown",
summary = "Popup always on right",
items = dropdownOptions,
items = dropdownOptions2,
selectedIndex = dropdownOptionSelectedRight.value,
onSelectedIndexChange = { newOption -> dropdownOptionSelectedRight.value = newOption },
mode = DropDownMode.AlwaysOnRight
Expand Down Expand Up @@ -361,7 +372,7 @@ fun TextComponent(
SuperSpinner(
title = "Spinner",
summary = "Spinner always on right",
items = spinnerOptions,
items = spinnerOptions2,
selectedIndex = spinnerOptionSelectedRight.value,
onSelectedIndexChange = { newOption -> spinnerOptionSelectedRight.value = newOption },
mode = SpinnerMode.AlwaysOnRight,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import androidx.compose.foundation.layout.captionBar
import androidx.compose.foundation.layout.displayCutout
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
Expand All @@ -27,6 +29,7 @@ import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.SubcomposeLayout
import androidx.compose.ui.layout.layout
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInWindow
Expand All @@ -43,6 +46,7 @@ import top.yukonga.miuix.kmp.utils.MiuixPopupUtil.Companion.dismissPopup
import top.yukonga.miuix.kmp.utils.MiuixPopupUtil.Companion.showPopup
import top.yukonga.miuix.kmp.utils.SmoothRoundedCornerShape
import top.yukonga.miuix.kmp.utils.getWindowSize
import kotlin.math.min

/**
* A popup with a list of items.
Expand All @@ -52,7 +56,7 @@ import top.yukonga.miuix.kmp.utils.getWindowSize
* @param popupPositionProvider The [PopupPositionProvider] of the [ListPopup].
* @param alignment The alignment of the [ListPopup].
* @param onDismissRequest The callback when the [ListPopup] is dismissed.
* @param content The [Composable] content of the [ListPopup].
* @param content The [Composable] content of the [ListPopup]. You should use the [ListPopupColumn] in general.
*/
@Composable
fun ListPopup(
Expand Down Expand Up @@ -151,7 +155,7 @@ fun ListPopup(
constraints.copy(
minWidth = 200.dp.roundToPx(),
minHeight = 50.dp.roundToPx(),
maxHeight = windowBounds.height
maxHeight = windowBounds.height - popupMargin.top - popupMargin.bottom
)
)
popupContentSize = IntSize(placeable.width, placeable.height)
Expand Down Expand Up @@ -222,6 +226,42 @@ fun ListPopup(
}
}

/**
* A column that automatically aligns the width to the widest item
* @param content The items
*/
@Composable
fun ListPopupColumn(
content: @Composable () -> Unit
) {
SubcomposeLayout(
modifier = Modifier.verticalScroll(rememberScrollState())
) { constraints ->
var listHeight = 0
val tempConstraints = constraints.copy(
minWidth = 200.dp.roundToPx(), maxWidth = 288.dp.roundToPx()
)
val listWidth = subcompose("miuixPopupListFake", content).map {
it.measure(tempConstraints)
}.maxOf { it.width }.coerceIn(200.dp.roundToPx(), 288.dp.roundToPx())
val childConstraints = constraints.copy(
minWidth = listWidth, maxWidth = listWidth
)
val placeables = subcompose("miuixPopupListReal", content).map {
val placeable = it.measure(childConstraints)
listHeight += placeable.height
placeable
}
layout(listWidth, min(constraints.maxHeight, listHeight)) {
var height = 0
placeables.forEach {
it.place(0, height)
height += it.height
}
}
}
}

interface PopupPositionProvider {
/**
* Calculate the position (offset) of Popup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
Expand All @@ -33,13 +31,13 @@ import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
import top.yukonga.miuix.kmp.basic.BasicComponent
import top.yukonga.miuix.kmp.basic.BasicComponentColors
import top.yukonga.miuix.kmp.basic.BasicComponentDefaults
import top.yukonga.miuix.kmp.basic.ListPopup
import top.yukonga.miuix.kmp.basic.ListPopupColumn
import top.yukonga.miuix.kmp.basic.PopupPositionProvider
import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.icon.MiuixIcons
Expand Down Expand Up @@ -139,10 +137,10 @@ fun SuperDropdown(
isDropdownExpanded.value = false
}
) {
LazyColumn {
items(items.size) { index ->
ListPopupColumn {
items.forEachIndexed { index, string ->
DropdownImpl(
text = items[index],
text = string,
optionSize = items.size,
isSelected = selectedIndex == index,
onSelectedIndexChange = {
Expand All @@ -151,7 +149,6 @@ fun SuperDropdown(
dismissPopup(showPopup)
isDropdownExpanded.value = false
},
textWidthDp = 128.dp,
index = index
)
}
Expand Down Expand Up @@ -205,16 +202,14 @@ fun SuperDropdown(
* @param isSelected Whether the option is selected.
* @param index The index of the current option in the options.
* @param onSelectedIndexChange The callback when the index is selected.
* @param textWidthDp The maximum width of text in options.
*/
@Composable
fun DropdownImpl(
text: String,
optionSize: Int,
isSelected: Boolean,
index: Int,
onSelectedIndexChange: (Int) -> Unit,
textWidthDp: Dp?
onSelectedIndexChange: (Int) -> Unit
) {
val additionalTopPadding = if (index == 0) 20f.dp else 12f.dp
val additionalBottomPadding = if (index == optionSize - 1) 20f.dp else 12f.dp
Expand All @@ -241,12 +236,11 @@ fun DropdownImpl(
onSelectedIndexChange(index)
}
.background(backgroundColor)
.widthIn(200.dp, 288.dp)
.padding(horizontal = 20.dp)
.padding(top = additionalTopPadding, bottom = additionalBottomPadding)
) {
Text(
modifier = Modifier.width(textWidthDp ?: 128.dp),
modifier = Modifier.widthIn(max = 216.dp),
text = text,
fontSize = MiuixTheme.textStyles.body1.fontSize,
fontWeight = FontWeight.Medium,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import top.yukonga.miuix.kmp.basic.BasicComponent
import top.yukonga.miuix.kmp.basic.BasicComponentColors
import top.yukonga.miuix.kmp.basic.BasicComponentDefaults
import top.yukonga.miuix.kmp.basic.ListPopup
import top.yukonga.miuix.kmp.basic.ListPopupColumn
import top.yukonga.miuix.kmp.basic.PopupPositionProvider
import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.basic.TextButton
Expand Down Expand Up @@ -150,10 +151,10 @@ fun SuperSpinner(
isDropdownExpanded.value = false
}
) {
LazyColumn {
items(items.size) { index ->
ListPopupColumn {
items.forEachIndexed { index, spinnerEntry ->
SpinnerItemImpl(
entry = items[index],
entry = spinnerEntry,
entryCount = items.size,
isSelected = selectedIndex == index,
index = index,
Expand Down Expand Up @@ -434,11 +435,12 @@ fun SpinnerItemImpl(
.background(backgroundColor)
.then(
if (dialogMode) Modifier.heightIn(min = 56.dp).widthIn(min = 200.dp).fillMaxWidth().padding(horizontal = 28.dp)
else Modifier.widthIn(200.dp, 288.dp).padding(horizontal = 20.dp)
else Modifier.padding(horizontal = 20.dp)
)
.padding(top = additionalTopPadding, bottom = additionalBottomPadding)
) {
Row(
modifier = if (dialogMode) Modifier else Modifier.widthIn(max = 216.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
Expand Down

0 comments on commit dbb1962

Please sign in to comment.