package com.suno.android.common_ui.components.menus import androidx.compose.foundation.layout.Box 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.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.DropdownMenu import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import com.suno.android.common_ui.R import com.suno.android.common_ui.theme.ExtendedTheme import com.suno.android.common_ui.theme.SunoTheme import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList enum class SelectorDropdownMenuIconSide { Start, End, } @Composable fun SelectorDropdownMenu( modifier: Modifier = Modifier, selectedItem: T, options: ImmutableList, textStyle: TextStyle = ExtendedTheme.typography.introCopy.copy( textAlign = TextAlign.Center, ), iconSize: DpSize = DpSize(16.dp, 16.dp), iconSide: SelectorDropdownMenuIconSide = SelectorDropdownMenuIconSide.Start, shape: Shape = CircleShape, color: Color = ExtendedTheme.colors.backgroundSecondary, title: @Composable (T.() -> String), itemContent: @Composable (T, dismiss: () -> Unit) -> Unit, ) { var expanded by remember { mutableStateOf(false) } val density = LocalDensity.current val textMeasurer = rememberTextMeasurer() val maxWidth = options.map { it.title() }.maxOfOrNull { text -> val textLayoutResult = textMeasurer.measure( text = AnnotatedString(text), style = textStyle, ) with(density) { textLayoutResult.size.width.toDp() } } ?: 80.dp Box( contentAlignment = Alignment.Center, ) { Surface( shape = shape, color = color, onClick = { expanded = !expanded }, ) { Row( modifier = modifier.padding(horizontal = 12.dp, vertical = 4.dp), verticalAlignment = Alignment.CenterVertically, ) { @Composable fun Icon() { Icon( modifier = Modifier.size(iconSize), painter = painterResource(R.drawable.chevron_down), contentDescription = null, ) } if (iconSide == SelectorDropdownMenuIconSide.Start) { Icon() } Text( text = selectedItem.title(), style = textStyle, modifier = Modifier.width(maxWidth), ) if (iconSide == SelectorDropdownMenuIconSide.End) { Icon() } } } DropdownMenu( shape = RoundedCornerShape(16.dp), containerColor = color, expanded = expanded, onDismissRequest = { expanded = false }, ) { options.forEachIndexed { index, optionItem -> itemContent(optionItem, { expanded = false }) if (index != options.lastIndex) { HorizontalDivider(color = ExtendedTheme.colors.textTertiary) } } } } } @PreviewLightDark @Composable private fun BasicMenu_Preview() { val items = listOf( ModelSelectorDropdownState( title = "v4.5", showProBadge = true, songsLeft = null, description = "This is sparta!!!", isSelected = true, ), ModelSelectorDropdownState( title = "v4.5.3.28", showProBadge = true, songsLeft = null, description = "This is sparta!!!", isSelected = true, ), ).toImmutableList() var selected by remember { mutableStateOf(items.first()) } SunoTheme { SelectorDropdownMenu( selectedItem = selected, options = items, title = { title }, itemContent = { item, dismiss -> ModelSelectorDropdownItem( modelSelectorDropdownState = item, onClick = { selected = item dismiss() }, ) }, ) } }