Instantly share code, notes, and snippets.
Created
November 7, 2022 06:47
-
Star
(1)
1
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save fvilarino/82b03461a158a9b0e17565eac0105048 to your computer and use it in GitHub Desktop.
App Bar Final
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
sealed interface ActionMenuItem { | |
val title: String | |
val onClick: () -> Unit | |
sealed interface IconMenuItem : ActionMenuItem { | |
val icon: ImageVector | |
val contentDescription: String? | |
data class AlwaysShown( | |
override val title: String, | |
override val contentDescription: String?, | |
override val onClick: () -> Unit, | |
override val icon: ImageVector, | |
) : IconMenuItem | |
data class ShownIfRoom( | |
override val title: String, | |
override val contentDescription: String?, | |
override val onClick: () -> Unit, | |
override val icon: ImageVector, | |
) : IconMenuItem | |
} | |
data class NeverShown( | |
override val title: String, | |
override val onClick: () -> Unit, | |
) : ActionMenuItem | |
} | |
@Composable | |
fun ActionsMenu( | |
items: List<ActionMenuItem>, | |
isOpen: Boolean, | |
onToggleOverflow: () -> Unit, | |
maxVisibleItems: Int, | |
) { | |
val menuItems = remember( | |
key1 = items, | |
key2 = maxVisibleItems, | |
) { | |
splitMenuItems(items, maxVisibleItems) | |
} | |
menuItems.alwaysShownItems.forEach { item -> | |
IconButton(onClick = item.onClick) { | |
Icon( | |
imageVector = item.icon, | |
contentDescription = item.contentDescription, | |
) | |
} | |
} | |
if (menuItems.overflowItems.isNotEmpty()) { | |
IconButton(onClick = onToggleOverflow) { | |
Icon( | |
imageVector = Icons.Filled.MoreVert, | |
contentDescription = "Overflow", | |
) | |
} | |
DropdownMenu( | |
expanded = isOpen, | |
onDismissRequest = onToggleOverflow, | |
) { | |
menuItems.overflowItems.forEach { item -> | |
DropdownMenuItem( | |
text = { | |
Text(item.title) | |
}, | |
onClick = item.onClick | |
) | |
} | |
} | |
} | |
} | |
private data class MenuItems( | |
val alwaysShownItems: List<ActionMenuItem.IconMenuItem>, | |
val overflowItems: List<ActionMenuItem>, | |
) | |
private fun splitMenuItems( | |
items: List<ActionMenuItem>, | |
maxVisibleItems: Int, | |
): MenuItems { | |
val alwaysShownItems: MutableList<ActionMenuItem.IconMenuItem> = | |
items.filterIsInstance<ActionMenuItem.IconMenuItem.AlwaysShown>().toMutableList() | |
val ifRoomItems: MutableList<ActionMenuItem.IconMenuItem> = | |
items.filterIsInstance<ActionMenuItem.IconMenuItem.ShownIfRoom>().toMutableList() | |
val overflowItems = items.filterIsInstance<ActionMenuItem.NeverShown>() | |
val hasOverflow = overflowItems.isNotEmpty() || | |
(alwaysShownItems.size + ifRoomItems.size - 1) > maxVisibleItems | |
val usedSlots = alwaysShownItems.size + (if (hasOverflow) 1 else 0) | |
val availableSlots = maxVisibleItems - usedSlots | |
if (availableSlots > 0 && ifRoomItems.isNotEmpty()) { | |
val visible = ifRoomItems.subList(0, availableSlots.coerceAtMost(ifRoomItems.size)) | |
alwaysShownItems.addAll(visible) | |
ifRoomItems.removeAll(visible) | |
} | |
return MenuItems( | |
alwaysShownItems = alwaysShownItems, | |
overflowItems = ifRoomItems + overflowItems, | |
) | |
} | |
@OptIn(ExperimentalMaterial3Api::class) | |
@Preview(widthDp = 440, showBackground = true) | |
@Preview(widthDp = 440, uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true) | |
@Composable | |
private fun ActionMenuPreview( | |
@PreviewParameter(ActionMenuParameterProvider::class) items: List<ActionMenuItem> | |
) { | |
PlaygroundTheme { | |
var menuOpen by remember { | |
mutableStateOf(false) | |
} | |
val numAlwaysShown = items.count { item -> item is ActionMenuItem.IconMenuItem.AlwaysShown } | |
val numIfRoom = items.count { item -> item is ActionMenuItem.IconMenuItem.ShownIfRoom } | |
val numOverflow = items.count { item -> item is ActionMenuItem.NeverShown } | |
val label = "Always: $numAlwaysShown Room: $numIfRoom Over: $numOverflow" | |
TopAppBar( | |
title = { Text(label) }, | |
modifier = Modifier.fillMaxWidth(), | |
actions = { | |
ActionsMenu( | |
items = items, | |
isOpen = menuOpen, | |
onToggleOverflow = { menuOpen = !menuOpen }, | |
maxVisibleItems = 3 | |
) | |
} | |
) | |
} | |
} | |
private class ActionMenuParameterProvider : PreviewParameterProvider<List<ActionMenuItem>> { | |
override val values: Sequence<List<ActionMenuItem>> | |
get() = sequenceOf( | |
listOf( | |
ActionMenuItem.IconMenuItem.AlwaysShown( | |
title = "Search", | |
onClick = {}, | |
icon = Icons.Filled.Search, | |
contentDescription = null, | |
), | |
ActionMenuItem.IconMenuItem.AlwaysShown( | |
title = "Favorite", | |
onClick = {}, | |
icon = Icons.Filled.FavoriteBorder, | |
contentDescription = null, | |
), | |
ActionMenuItem.IconMenuItem.AlwaysShown( | |
title = "Star", | |
onClick = {}, | |
icon = Icons.Filled.Star, | |
contentDescription = null, | |
), | |
ActionMenuItem.IconMenuItem.ShownIfRoom( | |
title = "Refresh", | |
onClick = {}, | |
icon = Icons.Filled.Refresh, | |
contentDescription = null, | |
), | |
ActionMenuItem.NeverShown( | |
title = "Settings", | |
onClick = {}, | |
), | |
ActionMenuItem.NeverShown( | |
title = "About", | |
onClick = {}, | |
), | |
), | |
listOf( | |
ActionMenuItem.IconMenuItem.AlwaysShown( | |
title = "Search", | |
onClick = {}, | |
icon = Icons.Filled.Search, | |
contentDescription = null, | |
), | |
ActionMenuItem.IconMenuItem.AlwaysShown( | |
title = "Favorite", | |
onClick = {}, | |
icon = Icons.Filled.FavoriteBorder, | |
contentDescription = null, | |
), | |
ActionMenuItem.IconMenuItem.ShownIfRoom( | |
title = "Star", | |
onClick = {}, | |
icon = Icons.Filled.Star, | |
contentDescription = null, | |
), | |
ActionMenuItem.IconMenuItem.ShownIfRoom( | |
title = "Refresh", | |
onClick = {}, | |
icon = Icons.Filled.Refresh, | |
contentDescription = null, | |
), | |
ActionMenuItem.NeverShown( | |
title = "Settings", | |
onClick = {}, | |
), | |
ActionMenuItem.NeverShown( | |
title = "About", | |
onClick = {}, | |
), | |
), | |
listOf( | |
ActionMenuItem.IconMenuItem.AlwaysShown( | |
title = "Search", | |
onClick = {}, | |
icon = Icons.Filled.Search, | |
contentDescription = null, | |
), | |
ActionMenuItem.IconMenuItem.ShownIfRoom( | |
title = "Favorite", | |
onClick = {}, | |
icon = Icons.Filled.FavoriteBorder, | |
contentDescription = null, | |
), | |
ActionMenuItem.IconMenuItem.ShownIfRoom( | |
title = "Star", | |
onClick = {}, | |
icon = Icons.Filled.Star, | |
contentDescription = null, | |
), | |
ActionMenuItem.IconMenuItem.ShownIfRoom( | |
title = "Refresh", | |
onClick = {}, | |
icon = Icons.Filled.Refresh, | |
contentDescription = null, | |
), | |
ActionMenuItem.NeverShown( | |
title = "Settings", | |
onClick = {}, | |
), | |
ActionMenuItem.NeverShown( | |
title = "About", | |
onClick = {}, | |
), | |
), | |
listOf( | |
ActionMenuItem.IconMenuItem.ShownIfRoom( | |
title = "Search", | |
onClick = {}, | |
icon = Icons.Filled.Search, | |
contentDescription = null, | |
), | |
ActionMenuItem.IconMenuItem.ShownIfRoom( | |
title = "Favorite", | |
onClick = {}, | |
icon = Icons.Filled.FavoriteBorder, | |
contentDescription = null, | |
), | |
ActionMenuItem.IconMenuItem.ShownIfRoom( | |
title = "Star", | |
onClick = {}, | |
icon = Icons.Filled.Star, | |
contentDescription = null, | |
), | |
ActionMenuItem.IconMenuItem.ShownIfRoom( | |
title = "Refresh", | |
onClick = {}, | |
icon = Icons.Filled.Refresh, | |
contentDescription = null, | |
), | |
ActionMenuItem.NeverShown( | |
title = "Settings", | |
onClick = {}, | |
), | |
ActionMenuItem.NeverShown( | |
title = "About", | |
onClick = {}, | |
), | |
), | |
listOf( | |
ActionMenuItem.IconMenuItem.ShownIfRoom( | |
title = "Search", | |
onClick = {}, | |
icon = Icons.Filled.Search, | |
contentDescription = null, | |
), | |
ActionMenuItem.IconMenuItem.ShownIfRoom( | |
title = "Favorite", | |
onClick = {}, | |
icon = Icons.Filled.FavoriteBorder, | |
contentDescription = null, | |
), | |
), | |
listOf( | |
ActionMenuItem.NeverShown( | |
title = "Search", | |
onClick = {}, | |
), | |
ActionMenuItem.NeverShown( | |
title = "Favorite", | |
onClick = {}, | |
), | |
), | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment