Skip to content

Commit

Permalink
[#349] Add overflow menu composable in the library
Browse files Browse the repository at this point in the history
  • Loading branch information
paulinea committed Dec 2, 2022
1 parent 92ee8db commit 36ac9d6
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 42 deletions.
57 changes: 16 additions & 41 deletions demo/src/main/java/com/orange/ods/demo/ui/MainTopAppBar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,18 @@ package com.orange.ods.demo.ui

import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Box
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.Icon
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.MoreVert
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.graphics.vector.rememberVectorPainter
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import com.orange.ods.compose.component.appbar.top.OdsTopAppBar
import com.orange.ods.compose.component.appbar.top.OdsTopAppBarActionButton
import com.orange.ods.compose.text.OdsTextBody1
import com.orange.ods.compose.component.appbar.top.OdsTopAppBarOverFlowMenuBox
import com.orange.ods.compose.component.menu.OdsMenuItem
import com.orange.ods.demo.R
import com.orange.ods.demo.ui.components.utilities.clickOnElement
import com.orange.ods.demo.ui.utilities.extension.isDarkModeEnabled
Expand Down Expand Up @@ -79,46 +71,29 @@ fun MainTopAppBar(
}
}
if (state.isOverflowMenuEnabled) {
OverflowMenu()
OdsTopAppBarOverFlowMenuBox(
overflowIconContentDescription = stringResource(id = R.string.component_app_bars_top_element_overflow_menu),
overflowMenuItems = listOf(
OdsMenuItem(
text = stringResource(id = R.string.component_app_bars_top_action_account),
onClick = { clickOnElement(context, context.getString(R.string.component_app_bars_top_action_account)) }
),
OdsMenuItem(
text = stringResource(id = R.string.component_app_bars_top_action_settings),
onClick = { clickOnElement(context, context.getString(R.string.component_app_bars_top_action_settings)) }
)
)
)
}
},
elevated = false // elevation is managed in [MainScreen] cause of tabs
)
}

@Composable
private fun OverflowMenu() {
var showMenu by remember { mutableStateOf(false) }
val context = LocalContext.current

Box {
OdsTopAppBarActionButton(
onClick = { showMenu = !showMenu },
painter = rememberVectorPainter(image = Icons.Filled.MoreVert),
contentDescription = stringResource(id = R.string.component_app_bars_top_element_overflow_menu)
)
DropdownMenu(
expanded = showMenu,
onDismissRequest = { showMenu = false }
) {
topAppBarDemoOverflowActions.forEach {
DropdownMenuItem(onClick = { clickOnElement(context, context.getString(it.titleRes)) }) {
OdsTextBody1(text = stringResource(id = it.titleRes))
}
}
}
}

}

private val topAppBarDemoActions = listOf(
TopAppBarAction(R.drawable.ic_heart, R.string.component_app_bars_top_action_favourites),
TopAppBarAction(R.drawable.ic_alert, R.string.component_app_bars_top_action_alerts),
)

private val topAppBarDemoOverflowActions = listOf(
TopAppBarAction(R.drawable.ic_account, R.string.component_app_bars_top_action_account),
TopAppBarAction(R.drawable.ic_settings, R.string.component_app_bars_top_action_settings)
)
private data class TopAppBarAction(@DrawableRes val iconRes: Int, @StringRes val titleRes: Int)

private data class TopAppBarAction(@DrawableRes val iconRes: Int, @StringRes val titleRes: Int)
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@ fun ComponentTopAppBar() {
)

}) {
// Nothing to display in screen
// Nothing to display in screen (see MainTopAppBar.kt)
}
}
31 changes: 31 additions & 0 deletions docs/components/AppBarsTop.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ description: Top app bars display information and actions relating to the curren
* [Specifications references](#specifications-references)
* [Accessibility](#accessibility)
* [Implementation](#implementation)
* [Extras](#extras)
* [Overflow menu](#overflow-menu)
* [Component specific tokens](#component-specific-tokens)

---
Expand Down Expand Up @@ -236,6 +238,35 @@ If you need to have a top app bar with some elevation you can set the `@style/Wi
</androidx.coordinatorlayout.widget.CoordinatorLayout>
```

## Extras

### Overflow menu

![Overflow menu light](images/app_bar_top_overflow_menu_light.png)
![Overflow menu dark](images/app_bar_top_overflow_menu_dark.png)

You can easily add an overflow menu to your top app bar by using the `OdsTopAppBarOverFlowMenuBox` composable as follow:

```kotlin
OdsTopAppBarOverFlowMenuBox(
overflowIconContentDescription = "more actions",
overflowMenuItems = listOf(
OdsMenuItem(
text = "Account",
onClick = {
// do something
}
),
OdsMenuItem(
text = "Settings",
onClick = {
// do something
}
)
)
)
```

## Component specific tokens

_Soon available_
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

package com.orange.ods.compose.component.appbar.top

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.material.AppBarDefaults
Expand All @@ -19,13 +20,21 @@ import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.MoreVert
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.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.orange.ods.compose.component.OdsComponentApi
import com.orange.ods.compose.component.button.OdsIconButton
import com.orange.ods.compose.component.menu.OdsDropdownMenu
import com.orange.ods.compose.component.menu.OdsMenuItem
import com.orange.ods.compose.component.utilities.Preview
import com.orange.ods.compose.component.utilities.UiModePreviews
import com.orange.ods.compose.theme.OdsTheme
Expand Down Expand Up @@ -79,6 +88,16 @@ fun OdsTopAppBar(
)
}

/**
* Action icon button displayed in an [OdsTopAppBar].
*
* @param onClick Will be called when the user clicks on the action icon button.
* @param painter Painter of the icon.
* @param contentDescription The content description associated to this OdsTopAppBarActionButton.
* @param modifier The [Modifier] to be applied to this OdsTopAppBarActionButton.
* @param enabled whether or not this OdsTopAppBarActionButton will handle input events and appear enabled for
* semantics purposes, true by default.
*/
@Composable
@OdsComponentApi
fun OdsTopAppBarActionButton(
Expand All @@ -98,6 +117,33 @@ fun OdsTopAppBarActionButton(
)
}

/**
* Overflow menu displayed in an [OdsTopAppBar]. It displays the overflow icon (3 vertical dots) and the menu appearing on click.
*
* @param overflowIconContentDescription The content description of the overflow icon.
* @param overflowMenuItems The list of the [OdsMenuItem] to display in the menu.
*/
@Composable
fun OdsTopAppBarOverFlowMenuBox(
overflowIconContentDescription: String,
overflowMenuItems: List<OdsMenuItem> = emptyList()
) {
var showMenu by remember { mutableStateOf(false) }

Box {
OdsTopAppBarActionButton(
onClick = { showMenu = !showMenu },
painter = rememberVectorPainter(image = Icons.Filled.MoreVert),
contentDescription = overflowIconContentDescription
)
OdsDropdownMenu(
expanded = showMenu,
onDismissRequest = { showMenu = false },
menuItems = overflowMenuItems
)
}
}

@UiModePreviews.Default
@Composable
private fun PreviewOdsTopAppBar() = Preview {
Expand All @@ -114,6 +160,13 @@ private fun PreviewOdsTopAppBar() = Preview {
painter = painterResource(id = android.R.drawable.ic_dialog_info),
contentDescription = "Info"
)
OdsTopAppBarOverFlowMenuBox(
overflowIconContentDescription = "more options",
overflowMenuItems = listOf(
OdsMenuItem(text = "settings", {}),
OdsMenuItem(text = "account", {})
)
)
}
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
*
* Copyright 2021 Orange
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
* /
*/

package com.orange.ods.compose.component.menu

import androidx.compose.foundation.background
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.PopupProperties
import com.orange.ods.compose.theme.OdsTheme

/**
* <a href="https://system.design.orange.com/0c1af118d/p/07a69b-menus/b/862cbb" class="external" target="_blank">ODS menus</a>.
*
* @see androidx.compose.material.DropdownMenu
*
* @param expanded Whether the menu is currently open and visible to the user
* @param onDismissRequest Called when the user requests to dismiss the menu, such as by
* tapping outside the menu's bounds
* @param offset [DpOffset] to be added to the position of the menu
*/
@Composable
fun OdsDropdownMenu(
expanded: Boolean,
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
offset: DpOffset = DpOffset(0.dp, 0.dp),
properties: PopupProperties = PopupProperties(focusable = true),
menuItems: List<OdsMenuItem> = emptyList()
) {
DropdownMenu(
expanded = expanded,
onDismissRequest = onDismissRequest,
modifier = modifier.background(OdsTheme.colors.surface),
offset = offset,
properties = properties
) {
menuItems.forEach { item ->
OdsDropdownMenuItem(text = item.text, onClick = item.onClick, enabled = item.enabled)
}
}
}

/**
* @see androidx.compose.material.DropdownMenuItem
*
* @param text The text of the menu item
* @param onClick Called when the menu item was clicked
* @param modifier The modifier to be applied to the menu item
* @param enabled Controls the enabled state of the menu item - when `false`, the menu item
* will not be clickable and [onClick] will not be invoked
*/
@Composable
private fun OdsDropdownMenuItem(
text: String,
onClick: () -> Unit,
enabled: Boolean = true
) {
DropdownMenuItem(
onClick = onClick,
enabled = enabled
) {
Text(text = text, style = OdsTheme.typography.body1, color = OdsTheme.colors.onSurface)
}
}

/**
* An item displayed in a menu
*
* @property text The text of the menu item
* @property onClick Action executed when the menu item was clicked
* @property enabled Determines the enabled state of the menu item
*/
data class OdsMenuItem(val text: String, val onClick: () -> Unit, val enabled: Boolean = true)

0 comments on commit 36ac9d6

Please sign in to comment.