From 411d997f924a13e1b168092c5238d9017c7b5545 Mon Sep 17 00:00:00 2001 From: Florent Maitre Date: Tue, 18 Jul 2023 11:12:19 +0200 Subject: [PATCH 1/7] [#572] Add OdsIcon component for internal usage --- .../component/appbar/top/OdsTopAppBar.kt | 2 +- .../compose/component/button/OdsIconButton.kt | 108 ++++++++++++++++-- .../ods/compose/component/icon/OdsIcon.kt | 55 +++++++++ .../textfield/search/OdsSearchTextField.kt | 2 +- 4 files changed, 158 insertions(+), 9 deletions(-) create mode 100644 lib/src/main/java/com/orange/ods/compose/component/icon/OdsIcon.kt diff --git a/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBar.kt b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBar.kt index b4f1273b8..f7e71d167 100644 --- a/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBar.kt +++ b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBar.kt @@ -110,7 +110,7 @@ fun OdsTopAppBarActionButton( ) { OdsIconButton( onClick = onClick, - painter = painter, + graphicsObject = painter, contentDescription = contentDescription, modifier = modifier, enabled = enabled, diff --git a/lib/src/main/java/com/orange/ods/compose/component/button/OdsIconButton.kt b/lib/src/main/java/com/orange/ods/compose/component/button/OdsIconButton.kt index 0bcb27b8e..9d81ddf72 100644 --- a/lib/src/main/java/com/orange/ods/compose/component/button/OdsIconButton.kt +++ b/lib/src/main/java/com/orange/ods/compose/component/button/OdsIconButton.kt @@ -18,14 +18,16 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource import com.orange.ods.compose.component.OdsComposable +import com.orange.ods.compose.component.icon.OdsIcon import com.orange.ods.compose.component.utilities.Preview import com.orange.ods.compose.component.utilities.UiModePreviews import com.orange.ods.compose.theme.OdsDisplaySurface import com.orange.ods.compose.theme.OdsTheme -import com.orange.ods.utilities.extension.enable /** * OdsIconButton is a clickable icon, used to represent actions. An OdsIconButton has an overall minimum @@ -36,9 +38,9 @@ import com.orange.ods.utilities.extension.enable * This component is typically used inside an App Bar for the navigation icon / actions. * * @param onClick the lambda to be invoked when this icon is pressed - * @param painter the painter to be drawn inside the IconButton + * @param painter the painter to be drawn inside the OdsIconButton * @param contentDescription the content description associated to this OdsIconButton. - * @param modifier optional [Modifier] for this IconButton + * @param modifier optional [Modifier] for this OdsIconButton * @param enabled whether or not this OdsIconButton will handle input events and appear enabled for * semantics purposes, true by default * @param displaySurface optional allow to force the button display on a dark or light @@ -53,13 +55,106 @@ fun OdsIconButton( modifier: Modifier = Modifier, enabled: Boolean = true, displaySurface: OdsDisplaySurface = OdsDisplaySurface.Default +) { + OdsIconButton( + onClick = onClick, + graphicsObject = painter as Any, + contentDescription = contentDescription, + modifier = modifier, + enabled = enabled, + displaySurface = displaySurface + ) +} + +/** + * OdsIconButton is a clickable icon, used to represent actions. An OdsIconButton has an overall minimum + * touch target size of 48 x 48dp, to meet accessibility guidelines. It contains an [Icon] centered + * inside the OdsIconButton. + * If using a custom icon, note that the typical size for the internal icon is 24 x 24 dp. + * + * This component is typically used inside an App Bar for the navigation icon / actions. + * + * @param onClick the lambda to be invoked when this icon is pressed + * @param imageVector [ImageVector] to draw inside this OdsIconButton + * @param contentDescription the content description associated to this OdsIconButton. + * @param modifier optional [Modifier] for this OdsIconButton + * @param enabled whether or not this OdsIconButton will handle input events and appear enabled for + * semantics purposes, true by default + * @param displaySurface optional allow to force the button display on a dark or light + * surface. By default the appearance applied is based on the system night mode value. + */ +@Composable +@OdsComposable +fun OdsIconButton( + onClick: () -> Unit, + imageVector: ImageVector, + contentDescription: String, + modifier: Modifier = Modifier, + enabled: Boolean = true, + displaySurface: OdsDisplaySurface = OdsDisplaySurface.Default +) { + OdsIconButton( + onClick = onClick, + graphicsObject = imageVector as Any, + contentDescription = contentDescription, + modifier = modifier, + enabled = enabled, + displaySurface = displaySurface + ) +} + +/** + * OdsIconButton is a clickable icon, used to represent actions. An OdsIconButton has an overall minimum + * touch target size of 48 x 48dp, to meet accessibility guidelines. It contains an [Icon] centered + * inside the OdsIconButton. + * If using a custom icon, note that the typical size for the internal icon is 24 x 24 dp. + * + * This component is typically used inside an App Bar for the navigation icon / actions. + * + * @param onClick the lambda to be invoked when this icon is pressed + * @param bitmap [ImageBitmap] to draw inside this OdsIconButton + * @param contentDescription the content description associated to this OdsIconButton. + * @param modifier optional [Modifier] for this OdsIconButton + * @param enabled whether or not this OdsIconButton will handle input events and appear enabled for + * semantics purposes, true by default + * @param displaySurface optional allow to force the button display on a dark or light + * surface. By default the appearance applied is based on the system night mode value. + */ +@Composable +@OdsComposable +fun OdsIconButton( + onClick: () -> Unit, + bitmap: ImageBitmap, + contentDescription: String, + modifier: Modifier = Modifier, + enabled: Boolean = true, + displaySurface: OdsDisplaySurface = OdsDisplaySurface.Default +) { + OdsIconButton( + onClick = onClick, + graphicsObject = bitmap as Any, + contentDescription = contentDescription, + modifier = modifier, + enabled = enabled, + displaySurface = displaySurface + ) +} + +@Composable +internal fun OdsIconButton( + onClick: () -> Unit, + graphicsObject: Any, + contentDescription: String, + modifier: Modifier = Modifier, + enabled: Boolean = true, + displaySurface: OdsDisplaySurface = OdsDisplaySurface.Default ) { CompositionLocalProvider( LocalRippleTheme provides displaySurface.rippleTheme ) { OdsIconButton( onClick = onClick, - painter = painter, + graphicsObject = graphicsObject, contentDescription = contentDescription, modifier = modifier.background(color = iconButtonBackgroundColor(displaySurface = displaySurface)), enabled = enabled, @@ -71,18 +166,17 @@ fun OdsIconButton( @Composable internal fun OdsIconButton( onClick: () -> Unit, - painter: Painter, + graphicsObject: Any, contentDescription: String, tint: Color, modifier: Modifier = Modifier, enabled: Boolean = true, ) { IconButton(onClick = onClick, modifier = modifier, enabled = enabled) { - Icon(painter = painter, contentDescription = contentDescription, tint = tint.enable(enabled = enabled)) + OdsIcon(graphicsObject = graphicsObject, contentDescription = contentDescription, tint = tint, enabled = enabled) } } - @Composable private fun iconButtonBackgroundColor(displaySurface: OdsDisplaySurface) = when (displaySurface) { diff --git a/lib/src/main/java/com/orange/ods/compose/component/icon/OdsIcon.kt b/lib/src/main/java/com/orange/ods/compose/component/icon/OdsIcon.kt new file mode 100644 index 000000000..bd99f8b96 --- /dev/null +++ b/lib/src/main/java/com/orange/ods/compose/component/icon/OdsIcon.kt @@ -0,0 +1,55 @@ +/* + * + * 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.icon + +import androidx.compose.material.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.vector.ImageVector +import com.orange.ods.compose.theme.OdsDisplaySurface +import com.orange.ods.utilities.extension.enable + +@Composable +internal fun OdsIcon( + graphicsObject: Any, + contentDescription: String, + tint: Color, + modifier: Modifier = Modifier, + enabled: Boolean = true, +) { + val iconTint = tint.enable(enabled = enabled) + when (graphicsObject) { + is Painter -> Icon(painter = graphicsObject, contentDescription = contentDescription, modifier = modifier, tint = iconTint) + is ImageVector -> Icon(imageVector = graphicsObject, contentDescription = contentDescription, modifier = modifier, tint = iconTint) + is ImageBitmap -> Icon(bitmap = graphicsObject, contentDescription = contentDescription, modifier = modifier, tint = iconTint) + else -> {} + } +} + +@Composable +internal fun OdsIcon( + graphicsObject: Any, + contentDescription: String, + modifier: Modifier = Modifier, + displaySurface: OdsDisplaySurface = OdsDisplaySurface.Default, + enabled: Boolean = true +) { + OdsIcon( + graphicsObject = graphicsObject, + contentDescription = contentDescription, + tint = displaySurface.themeColors.onSurface, + modifier = modifier, + enabled = enabled + ) +} diff --git a/lib/src/main/java/com/orange/ods/compose/component/textfield/search/OdsSearchTextField.kt b/lib/src/main/java/com/orange/ods/compose/component/textfield/search/OdsSearchTextField.kt index feea28f67..f45a36e68 100644 --- a/lib/src/main/java/com/orange/ods/compose/component/textfield/search/OdsSearchTextField.kt +++ b/lib/src/main/java/com/orange/ods/compose/component/textfield/search/OdsSearchTextField.kt @@ -70,7 +70,7 @@ fun OdsSearchTextField( trailingIcon = { OdsIconButton( onClick = { onValueChange(TextFieldValue("")) }, - painter = rememberVectorPainter(image = Icons.Default.Close), + graphicsObject = rememberVectorPainter(image = Icons.Default.Close), contentDescription = stringResource(id = R.string.search_clear), tint = OdsTheme.colors.component.topAppBar.barContent ) From 4c4e199686bfd2dd54853a6cf4cab260cb8e7385 Mon Sep 17 00:00:00 2001 From: Florent Maitre Date: Tue, 18 Jul 2023 15:57:29 +0200 Subject: [PATCH 2/7] [#572] Replace composable parameter of OdsDropdownMenu with a list of OdsDropdownMenuItem --- .../app/ui/components/menus/MenuDropdown.kt | 41 ++--- changelog.md | 4 + .../component/content/OdsComponentContent.kt | 31 ++++ .../component/content/OdsComponentIcon.kt | 73 ++++++++ .../compose/component/menu/OdsDropdownMenu.kt | 159 ++++++++++++------ .../component/menu/OdsExposedDropdownMenu.kt | 14 +- 6 files changed, 243 insertions(+), 79 deletions(-) create mode 100644 lib/src/main/java/com/orange/ods/compose/component/content/OdsComponentContent.kt create mode 100644 lib/src/main/java/com/orange/ods/compose/component/content/OdsComponentIcon.kt diff --git a/app/src/main/java/com/orange/ods/app/ui/components/menus/MenuDropdown.kt b/app/src/main/java/com/orange/ods/app/ui/components/menus/MenuDropdown.kt index 936b83b5e..e813243c4 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/menus/MenuDropdown.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/menus/MenuDropdown.kt @@ -37,7 +37,6 @@ import com.orange.ods.app.ui.components.utilities.clickOnElement import com.orange.ods.app.ui.utilities.composable.CodeImplementationColumn import com.orange.ods.app.ui.utilities.composable.FunctionCallCode import com.orange.ods.compose.OdsComposable -import com.orange.ods.compose.component.divider.OdsDivider import com.orange.ods.compose.component.list.OdsIconTrailing import com.orange.ods.compose.component.list.OdsListItem import com.orange.ods.compose.component.list.OdsSwitchTrailing @@ -79,6 +78,8 @@ fun MenuDropdown() { text = stringResource(id = R.string.component_menu_dropdown_description) ) + val dividerIndex = 1 + Box(modifier = Modifier.padding(top = dimensionResource(id = R.dimen.spacing_s))) { OdsListItem( modifier = Modifier.padding(top = dimensionResource(id = R.dimen.spacing_s)), @@ -90,18 +91,22 @@ fun MenuDropdown() { contentDescription = stringResource(id = R.string.component_menu_show_ingredients), ) ) - OdsDropdownMenu(expanded = menuExpanded, onDismissRequest = { menuExpanded = false }, offset = DpOffset(x = (-100).dp, y = (-10).dp)) { - recipes.take(MenuDropdownCustomizationState.MenuItemCount).forEachIndexed { index, recipe -> + + val items = recipes.take(MenuDropdownCustomizationState.MenuItemCount) + .mapIndexed { index, recipe -> OdsDropdownMenuItem( text = recipe.title, icon = if (hasIcons && recipe.iconResId != null) painterResource(id = recipe.iconResId) else null, + divider = hasDividerExample && index == dividerIndex, onClick = { clickOnElement(context, recipe.title) } ) - if (hasDividerExample && index == 2) { - OdsDivider() - } } - } + OdsDropdownMenu( + items = items, + expanded = menuExpanded, + onDismissRequest = { menuExpanded = false }, + offset = DpOffset(x = (-100).dp, y = (-10).dp) + ) } CodeImplementationColumn( @@ -113,22 +118,18 @@ fun MenuDropdown() { parameters = { stringRepresentation("expanded", menuExpanded) lambda("onDismissRequest") - } - ) { - recipes.take(2).forEachIndexed { index, recipe -> - FunctionCallCode( - name = OdsComposable.OdsDropdownMenuItem.name, - parameters = { - string("text", recipe.title) - onClick() - if (hasIcons && recipe.iconResId != null) icon() + list("items") { + recipes.take(2).forEachIndexed { index, recipe -> + classInstance(OdsDropdownMenuItem::class.java) { + string("text", recipe.title) + if (hasIcons && recipe.iconResId != null) icon() + if (hasDividerExample && index == dividerIndex) stringRepresentation("divider", true) + onClick() + } } - ) - if (hasDividerExample && index == 0) { - FunctionCallCode(name = OdsComposable.OdsDivider.name) } } - } + ) } } } diff --git a/changelog.md b/changelog.md index d237c32bb..406d4640c 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/Orange-OpenSource/ods-android/compare/0.14.0...develop) +### Changed + +- \[Lib\] Replace composable parameter of `OdsDropdownMenu` with a list of `OdsDropdownMenuItem` ([#572](https://github.com/Orange-OpenSource/ods-android/issues/572)) + ### Fixed - [\App\] Screen displayed on filter chip variant click was not the good one ([#580](https://github.com/Orange-OpenSource/ods-android/issues/580)) diff --git a/lib/src/main/java/com/orange/ods/compose/component/content/OdsComponentContent.kt b/lib/src/main/java/com/orange/ods/compose/component/content/OdsComponentContent.kt new file mode 100644 index 000000000..601be5c55 --- /dev/null +++ b/lib/src/main/java/com/orange/ods/compose/component/content/OdsComponentContent.kt @@ -0,0 +1,31 @@ +/* + * + * 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.content + +import androidx.compose.runtime.Composable + +/** + * The content of a component. + * + * Subclasses of [OdsComponentContent] should be used instead of composable methods when passing parameters to components. + * This prevents using generic composable methods that can encapsulate any kind of views and thus helps developers to follow UI guidelines more easily. + * This also allows to group parameters that are related to the same content inside a component. + * For instance it is possible to create an `Icon` subclass to replace both `icon: @Composable () -> Unit` and `onIconClick: () -> Unit` parameters with a single `icon: Icon` parameter. + */ +abstract class OdsComponentContent { + + /** + * The Jetpack Compose UI for this component content. + * Subclasses must implement this method to provide content. + */ + @Composable + internal abstract fun Content() +} diff --git a/lib/src/main/java/com/orange/ods/compose/component/content/OdsComponentIcon.kt b/lib/src/main/java/com/orange/ods/compose/component/content/OdsComponentIcon.kt new file mode 100644 index 000000000..2622ddd76 --- /dev/null +++ b/lib/src/main/java/com/orange/ods/compose/component/content/OdsComponentIcon.kt @@ -0,0 +1,73 @@ +/* + * + * 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.content + +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.vector.ImageVector +import com.orange.ods.compose.component.button.OdsIconButton +import com.orange.ods.compose.component.icon.OdsIcon +import com.orange.ods.utilities.extension.orElse + +/** + * An icon in a component. + */ +abstract class OdsComponentIcon protected constructor( + protected val graphicsObject: Any, + protected val contentDescription: String, + protected val enabled: Boolean = true, + protected open var onClick: (() -> Unit)? = null +) : OdsComponentContent() { + + protected open val tint: Color? + @Composable + get() = null + + protected constructor( + painter: Painter, + contentDescription: String, + enabled: Boolean = true, + onClick: (() -> Unit)? = null + ) : this(painter as Any, contentDescription, enabled, onClick) + + protected constructor( + imageVector: ImageVector, + contentDescription: String, + enabled: Boolean = true, + onClick: (() -> Unit)? = null + ) : this(imageVector as Any, contentDescription, enabled, onClick) + + protected constructor( + bitmap: ImageBitmap, + contentDescription: String, + enabled: Boolean = true, + onClick: (() -> Unit)? = null + ) : this(bitmap as Any, contentDescription, enabled, onClick) + + @Composable + override fun Content() { + onClick?.let { onClick -> + tint?.let { tint -> + OdsIconButton(onClick = onClick, graphicsObject = graphicsObject, contentDescription = contentDescription, tint = tint, enabled = enabled) + }.orElse { + OdsIconButton(onClick = onClick, graphicsObject = graphicsObject, contentDescription = contentDescription, enabled = enabled) + } + }.orElse { + tint?.let { tint -> + OdsIcon(graphicsObject = graphicsObject, contentDescription = contentDescription, tint = tint) + }.orElse { + OdsIcon(graphicsObject = graphicsObject, contentDescription = contentDescription) + } + } + } +} diff --git a/lib/src/main/java/com/orange/ods/compose/component/menu/OdsDropdownMenu.kt b/lib/src/main/java/com/orange/ods/compose/component/menu/OdsDropdownMenu.kt index 059495a2d..b08eed69e 100644 --- a/lib/src/main/java/com/orange/ods/compose/component/menu/OdsDropdownMenu.kt +++ b/lib/src/main/java/com/orange/ods/compose/component/menu/OdsDropdownMenu.kt @@ -10,17 +10,16 @@ package com.orange.ods.compose.component.menu -import androidx.annotation.DrawableRes +import android.graphics.Bitmap import androidx.compose.foundation.background -import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.padding import androidx.compose.material.DropdownMenu import androidx.compose.material.DropdownMenuItem -import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.DpOffset @@ -28,6 +27,9 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.PopupProperties import com.orange.ods.R import com.orange.ods.compose.component.OdsComposable +import com.orange.ods.compose.component.content.OdsComponentContent +import com.orange.ods.compose.component.divider.OdsDivider +import com.orange.ods.compose.component.icon.OdsIcon import com.orange.ods.compose.component.utilities.Preview import com.orange.ods.compose.component.utilities.UiModePreviews import com.orange.ods.compose.theme.OdsTheme @@ -37,23 +39,23 @@ import com.orange.ods.compose.theme.OdsTheme * * @see androidx.compose.material.DropdownMenu * + * @param items The items of the dropdown menu * @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 modifier The modifier to be applied to the menu * @param offset [DpOffset] to be added to the position of the menu * @param properties [PopupProperties] for further customization of the popup's behavior - * @param content The content of the dropdown menu */ @OdsComposable @Composable fun OdsDropdownMenu( + items: List, expanded: Boolean, onDismissRequest: () -> Unit, modifier: Modifier = Modifier, offset: DpOffset = DpOffset(0.dp, 0.dp), - properties: PopupProperties = PopupProperties(focusable = true), - content: @Composable ColumnScope.() -> Unit + properties: PopupProperties = PopupProperties(focusable = true) ) { DropdownMenu( expanded = expanded, @@ -61,40 +63,109 @@ fun OdsDropdownMenu( modifier = modifier.background(OdsTheme.colors.surface), offset = offset, properties = properties, - content = content + content = { items.forEach { it.Content() } } ) } /** * @see androidx.compose.material.DropdownMenuItem * - * @param text The text of the menu item - * @param onClick Called when the menu item was clicked - * @param icon Optional icon to display in 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 + * An item displayed in a [OdsDropdownMenu]. + * + * @property text The text of the menu item + * @property icon Optional icon to display in the menu item + * @property enabled Controls the enabled state of the menu item - when `false`, the menu item + * @property divider Whether or not a divider is displayed at the bottom of the menu item + * @property onClick Called when the menu item was clicked */ -@Composable -@OdsComposable -fun ColumnScope.OdsDropdownMenuItem( - text: String, - onClick: () -> Unit, - icon: Painter? = null, - enabled: Boolean = true -) { - DropdownMenuItem( - onClick = onClick, - enabled = enabled - ) { - icon?.let { - Icon( - modifier = Modifier.padding(end = dimensionResource(id = R.dimen.spacing_m)), - painter = icon, - contentDescription = null, - tint = OdsTheme.colors.onSurface - ) +class OdsDropdownMenuItem private constructor( + val text: String, + val icon: Any?, + val enabled: Boolean, + val divider: Boolean, + val onClick: () -> Unit +) : OdsComponentContent() { + + /** + * Creates an instance of [OdsDropdownMenuItem]. + * + * @param text The text of the menu item + * @param enabled Controls the enabled state of the menu item - when `false`, the menu item + * @param divider Whether or not a divider is displayed at the bottom of the menu item + * @param onClick Called when the menu item was clicked + */ + constructor( + text: String, + enabled: Boolean = true, + divider: Boolean = false, + onClick: () -> Unit + ) : this(text, null as Any?, enabled, divider, onClick) + + /** + * Creates an instance of [OdsDropdownMenuItem]. + * + * @param text The text of the menu item + * @param icon Optional icon to display in the menu item + * @param enabled Controls the enabled state of the menu item - when `false`, the menu item + * @param divider Whether or not a divider is displayed at the bottom of the menu item + * @param onClick Called when the menu item was clicked + */ + constructor( + text: String, + icon: Painter? = null, + enabled: Boolean = true, + divider: Boolean = false, + onClick: () -> Unit + ) : this(text, icon as Any?, enabled, divider, onClick) + + /** + * Creates an instance of [OdsDropdownMenuItem]. + * + * @param text The text of the menu item + * @param icon Optional icon to display in the menu item + * @param enabled Controls the enabled state of the menu item - when `false`, the menu item + * @param divider Whether or not a divider is displayed at the bottom of the menu item + * @param onClick Called when the menu item was clicked + */ + constructor( + text: String, + icon: ImageVector? = null, + enabled: Boolean = true, + divider: Boolean = false, + onClick: () -> Unit + ) : this(text, icon as Any?, enabled, divider, onClick) + + /** + * Creates an instance of [OdsDropdownMenuItem]. + * + * @param text The text of the menu item + * @param icon Optional icon to display in the menu item + * @param enabled Controls the enabled state of the menu item - when `false`, the menu item + * @param divider Whether or not a divider is displayed at the bottom of the menu item + * @param onClick Called when the menu item was clicked + */ + constructor( + text: String, + icon: Bitmap? = null, + enabled: Boolean = true, + divider: Boolean = false, + onClick: () -> Unit + ) : this(text, icon as Any?, enabled, divider, onClick) + + @Composable + override fun Content() { + DropdownMenuItem(onClick = onClick, enabled = enabled) { + icon?.let { + OdsIcon( + graphicsObject = icon, + contentDescription = "", + tint = OdsTheme.colors.onSurface, + modifier = Modifier.padding(end = dimensionResource(id = R.dimen.spacing_m)), + ) + } + Text(text = text, style = OdsTheme.typography.body1, color = OdsTheme.colors.onSurface) } - Text(text = text, style = OdsTheme.typography.body1, color = OdsTheme.colors.onSurface) + if (divider) OdsDivider() } } @@ -104,25 +175,11 @@ fun ColumnScope.OdsDropdownMenuItem( @UiModePreviews.Default @Composable private fun PreviewOdsDropdownMenu() = Preview { - data class Item(@DrawableRes val iconResId: Int, val text: String) - val items = listOf( - Item(android.R.drawable.ic_dialog_email, "First menu item"), - Item(android.R.drawable.ic_dialog_map, "Second menu item"), - Item(android.R.drawable.ic_dialog_dialer, "Third menu item"), - Item(android.R.drawable.ic_dialog_info, "Fourth menu item") + OdsDropdownMenuItem("First menu item", painterResource(id = android.R.drawable.ic_dialog_email)) {}, + OdsDropdownMenuItem("Second menu item", painterResource(id = android.R.drawable.ic_dialog_map), divider = true) {}, + OdsDropdownMenuItem("Third menu item", painterResource(id = android.R.drawable.ic_dialog_dialer)) {}, + OdsDropdownMenuItem("Fourth menu item", painterResource(id = android.R.drawable.ic_dialog_info)) {} ) - - OdsDropdownMenu( - expanded = true, - onDismissRequest = { }, - ) { - items.forEach { item -> - OdsDropdownMenuItem( - text = item.text, - icon = painterResource(id = item.iconResId), - onClick = { } - ) - } - } + OdsDropdownMenu(items = items, expanded = true, onDismissRequest = {}) } diff --git a/lib/src/main/java/com/orange/ods/compose/component/menu/OdsExposedDropdownMenu.kt b/lib/src/main/java/com/orange/ods/compose/component/menu/OdsExposedDropdownMenu.kt index 2fd1cb6a2..dd2072686 100644 --- a/lib/src/main/java/com/orange/ods/compose/component/menu/OdsExposedDropdownMenu.kt +++ b/lib/src/main/java/com/orange/ods/compose/component/menu/OdsExposedDropdownMenu.kt @@ -69,16 +69,14 @@ fun OdsExposedDropdownMenu( enabled = enabled ) OdsDropdownMenu( - modifier = Modifier.exposedDropdownSize(), expanded = if (enabled) expanded else false, onDismissRequest = { expanded = false }, - content = { - items.forEach { item -> - OdsDropdownMenuItem(text = item.label, icon = item.iconResId?.let { painterResource(id = it) }, onClick = { - selectedItem.value = item - expanded = false - onItemSelectionChange(item) - }) + modifier = Modifier.exposedDropdownSize(), + items = items.map { item -> + OdsDropdownMenuItem(text = item.label, icon = item.iconResId?.let { painterResource(id = it) }) { + selectedItem.value = item + expanded = false + onItemSelectionChange(item) } } ) From c1fe7ab3bb1f0ac40c603d28428309017e55619a Mon Sep 17 00:00:00 2001 From: Florent Maitre Date: Wed, 12 Jul 2023 16:12:35 +0200 Subject: [PATCH 3/7] [#572] Update OdsTopAppBar API --- .../com/orange/ods/app/ui/MainTopAppBar.kt | 127 +++++++-------- .../appbars/top/ComponentTopAppBar.kt | 50 +++--- .../composable/CodeImplementation.kt | 16 +- changelog.md | 1 + .../component/appbar/top/OdsLargeTopAppBar.kt | 78 +++++----- .../component/appbar/top/OdsTopAppBar.kt | 145 +++++------------- .../appbar/top/OdsTopAppBarsCommon.kt | 139 +++++++++++++++++ .../component/content/OdsComponentContent.kt | 3 +- lib/src/main/res/values/strings.xml | 4 +- 9 files changed, 319 insertions(+), 244 deletions(-) create mode 100644 lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBarsCommon.kt diff --git a/app/src/main/java/com/orange/ods/app/ui/MainTopAppBar.kt b/app/src/main/java/com/orange/ods/app/ui/MainTopAppBar.kt index 72af9668b..5dc4bd3d7 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainTopAppBar.kt +++ b/app/src/main/java/com/orange/ods/app/ui/MainTopAppBar.kt @@ -12,10 +12,8 @@ package com.orange.ods.app.ui import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.material.Icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material3.ExperimentalMaterial3Api @@ -37,13 +35,14 @@ import com.orange.ods.app.R import com.orange.ods.app.domain.recipes.LocalRecipes import com.orange.ods.app.ui.components.utilities.clickOnElement import com.orange.ods.app.ui.utilities.extension.isDarkModeEnabled -import com.orange.ods.compose.component.appbar.top.OdsLargeTopAppBar -import com.orange.ods.compose.component.appbar.top.OdsTopAppBar +import com.orange.ods.compose.component.appbar.top.OdsLargeTopAppBarInternal import com.orange.ods.compose.component.appbar.top.OdsTopAppBarActionButton -import com.orange.ods.compose.component.appbar.top.OdsTopAppBarOverflowMenuBox +import com.orange.ods.compose.component.appbar.top.OdsTopAppBarInternal +import com.orange.ods.compose.component.appbar.top.OdsTopAppBarNavigationIcon +import com.orange.ods.compose.component.appbar.top.OdsTopAppBarOverflowMenuActionItem +import com.orange.ods.compose.component.content.OdsComponentContent import com.orange.ods.compose.component.list.OdsListItem import com.orange.ods.compose.component.list.OdsRadioButtonTrailing -import com.orange.ods.compose.component.menu.OdsDropdownMenuItem import com.orange.ods.compose.component.textfield.search.OdsSearchTextField import com.orange.ods.compose.text.OdsTextH6 import com.orange.ods.compose.theme.OdsTheme @@ -63,37 +62,34 @@ fun MainTopAppBar( val showSearchAction = topAppBarState.titleRes.value == R.string.navigation_item_search val title = stringResource(id = topAppBarState.titleRes.value) - val navigationIcon: (@Composable () -> Unit)? = if (shouldShowUpNavigationIcon && topAppBarState.isNavigationIconEnabled) { - { - Icon( - imageVector = Icons.Filled.ArrowBack, - contentDescription = stringResource(id = R.string.top_app_bar_back_icon_desc) - ) - } - } else null + val navigationIcon = if (shouldShowUpNavigationIcon && topAppBarState.isNavigationIconEnabled) { + OdsTopAppBarNavigationIcon(Icons.Filled.ArrowBack, stringResource(id = R.string.top_app_bar_back_icon_desc), upPress) + } else { + null + } - val actions: @Composable RowScope.() -> Unit = { - if (showSearchAction) { - TopAppBarSearchAction(searchedText = topAppBarState.searchedText) - } else { - TopAppBarActions(topAppBarState, onSearchActionClick) { changeThemeDialogVisible = true } - } + val actions = if (showSearchAction) { + listOf(getTopAppBarSearchTextFieldAction(searchedText = topAppBarState.searchedText)) + } else { + getTopAppBarActions(topAppBarState, onSearchActionClick) { changeThemeDialogVisible = true } } + val overflowMenuActions = getTopAppBarOverflowMenuActions(topAppBarState) + if (topAppBarState.isLarge) { - OdsLargeTopAppBar( + OdsLargeTopAppBarInternal( title = title, navigationIcon = navigationIcon, - onNavigationIconClick = upPress, actions = actions, + overflowMenuActions = overflowMenuActions, scrollBehavior = if (topAppBarState.hasScrollBehavior) scrollBehavior else null ) } else { - OdsTopAppBar( + OdsTopAppBarInternal( title = title, navigationIcon = navigationIcon, - onNavigationIconClick = upPress, actions = actions, + overflowMenuActions = overflowMenuActions, elevated = false // elevation is managed in [MainScreen] cause of tabs ) } @@ -111,41 +107,50 @@ fun MainTopAppBar( } } - @Composable -private fun TopAppBarSearchAction(searchedText: MutableState) { - val focusRequester = remember { FocusRequester() } - OdsSearchTextField( - value = searchedText.value, - onValueChange = { value -> - searchedText.value = value - }, - placeholder = stringResource(id = R.string.search_text_field_hint), - modifier = Modifier - .fillMaxWidth() - .focusRequester(focusRequester) - ) - LaunchedEffect(Unit) { - focusRequester.requestFocus() +private fun getTopAppBarSearchTextFieldAction(searchedText: MutableState): OdsComponentContent { + return object : OdsComponentContent() { + + @Composable + override fun Content() { + val focusRequester = remember { FocusRequester() } + OdsSearchTextField( + value = searchedText.value, + onValueChange = { value -> + searchedText.value = value + }, + placeholder = stringResource(id = R.string.search_text_field_hint), + modifier = Modifier + .fillMaxWidth() + .focusRequester(focusRequester) + ) + LaunchedEffect(Unit) { + focusRequester.requestFocus() + } + } } } @Composable -private fun TopAppBarActions(state: MainTopAppBarState, onSearchActionClick: () -> Unit, onChangeThemeActionClick: () -> Unit) { - state.actions.value.forEach { action -> +private fun getTopAppBarActions( + state: MainTopAppBarState, + onSearchActionClick: () -> Unit, + onChangeThemeActionClick: () -> Unit +): List { + return state.actions.value.mapNotNull { action -> when (action) { - TopAppBarConfiguration.Action.Search -> TopAppBarSearchActionButton(onClick = onSearchActionClick) - TopAppBarConfiguration.Action.Theme -> TopAppBarChangeThemeActionButton(onClick = onChangeThemeActionClick) - TopAppBarConfiguration.Action.Mode -> TopAppBarChangeModeActionButton() - TopAppBarConfiguration.Action.OverflowMenu -> TopAppBarOverflowMenuBox() - is TopAppBarConfiguration.Action.Custom -> TopAppBarCustomActionButton(action = action) + TopAppBarConfiguration.Action.Search -> getTopAppBarSearchAction(onClick = onSearchActionClick) + TopAppBarConfiguration.Action.Theme -> getTopAppBarChangeThemeAction(onClick = onChangeThemeActionClick) + TopAppBarConfiguration.Action.Mode -> getTopAppBarChangeModeAction() + TopAppBarConfiguration.Action.OverflowMenu -> null + is TopAppBarConfiguration.Action.Custom -> getTopAppBarCustomAction(action = action) } } } @Composable -private fun TopAppBarChangeThemeActionButton(onClick: () -> Unit) { - OdsTopAppBarActionButton( +private fun getTopAppBarChangeThemeAction(onClick: () -> Unit): OdsTopAppBarActionButton { + return OdsTopAppBarActionButton( onClick = onClick, painter = painterResource(id = R.drawable.ic_palette), contentDescription = stringResource(id = R.string.top_app_bar_action_change_mode_to_dark_desc) @@ -153,7 +158,7 @@ private fun TopAppBarChangeThemeActionButton(onClick: () -> Unit) { } @Composable -private fun TopAppBarChangeModeActionButton() { +private fun getTopAppBarChangeModeAction(): OdsTopAppBarActionButton { val configuration = LocalConfiguration.current val themeManager = LocalThemeManager.current @@ -161,7 +166,7 @@ private fun TopAppBarChangeModeActionButton() { val iconDesc = if (configuration.isDarkModeEnabled) R.string.top_app_bar_action_change_mode_to_light_desc else R.string.top_app_bar_action_change_mode_to_dark_desc - OdsTopAppBarActionButton( + return OdsTopAppBarActionButton( onClick = { themeManager.darkModeEnabled = !configuration.isDarkModeEnabled }, painter = painterResource(id = painterRes), contentDescription = stringResource(id = iconDesc) @@ -169,8 +174,8 @@ private fun TopAppBarChangeModeActionButton() { } @Composable -private fun TopAppBarSearchActionButton(onClick: () -> Unit) { - OdsTopAppBarActionButton( +private fun getTopAppBarSearchAction(onClick: () -> Unit): OdsTopAppBarActionButton { + return OdsTopAppBarActionButton( onClick = onClick, painter = painterResource(id = R.drawable.ic_search), contentDescription = stringResource(id = R.string.search_content_description) @@ -178,24 +183,24 @@ private fun TopAppBarSearchActionButton(onClick: () -> Unit) { } @Composable -private fun TopAppBarOverflowMenuBox() { - val context = LocalContext.current - OdsTopAppBarOverflowMenuBox( - overflowIconContentDescription = stringResource(id = R.string.component_app_bars_top_element_overflow_menu) - ) { - LocalRecipes.current.forEach { recipe -> - OdsDropdownMenuItem( +private fun getTopAppBarOverflowMenuActions(state: MainTopAppBarState): List { + return if (state.actions.value.contains(TopAppBarConfiguration.Action.OverflowMenu)) { + val context = LocalContext.current + LocalRecipes.current.map { recipe -> + OdsTopAppBarOverflowMenuActionItem( text = recipe.title, onClick = { clickOnElement(context, recipe.title) } ) } + } else { + emptyList() } } @Composable -private fun TopAppBarCustomActionButton(action: TopAppBarConfiguration.Action.Custom) { +private fun getTopAppBarCustomAction(action: TopAppBarConfiguration.Action.Custom): OdsTopAppBarActionButton { val context = LocalContext.current - OdsTopAppBarActionButton( + return OdsTopAppBarActionButton( onClick = { clickOnElement(context, action.name) }, painter = painterResource(id = action.iconResId), contentDescription = action.name diff --git a/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/ComponentTopAppBar.kt b/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/ComponentTopAppBar.kt index 636f1a3e9..dbf282dd2 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/ComponentTopAppBar.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/ComponentTopAppBar.kt @@ -41,6 +41,8 @@ import com.orange.ods.app.ui.components.utilities.ComponentCustomizationBottomSh import com.orange.ods.app.ui.utilities.NavigationItem import com.orange.ods.app.ui.utilities.composable.* import com.orange.ods.compose.OdsComposable +import com.orange.ods.compose.component.appbar.top.OdsTopAppBarActionButton +import com.orange.ods.compose.component.appbar.top.OdsTopAppBarNavigationIcon import com.orange.ods.compose.component.chip.OdsChoiceChip import com.orange.ods.compose.component.chip.OdsChoiceChipsFlowRow import com.orange.ods.compose.component.list.OdsListItem @@ -107,41 +109,29 @@ fun ComponentTopAppBar(variant: Variant) { title(context.getString(R.string.component_app_bars_top_regular)) if (isNavigationIconEnabled) { - composable(name = "navigationIcon") { - FunctionCallCode( - name = "Icon", - parameters = { - simple("imageVector", "") - contentDescription(context.getString(R.string.top_app_bar_back_icon_desc)) - } - ) + classInstance("navigationIcon", OdsTopAppBarNavigationIcon::class.java) { + simple("imageVector", "") + contentDescription(context.getString(R.string.top_app_bar_back_icon_desc)) } } - composable(name = "actions") { + list("actions") { repeat(actionCount.value) { - FunctionCallCode( - name = OdsComposable.OdsTopAppBarActionButton.name, - parameters = { - onClick() - painter() - contentDescription("icon description") - } - ) + classInstance(OdsTopAppBarActionButton::class.java) { + onClick() + painter() + contentDescription("icon description") + } } - if (isOverflowMenuEnabled) { - FunctionCallCode( - name = OdsComposable.OdsTopAppBarOverflowMenuBox.name, - parameters = { string("overflowIconContentDescription", "Open overflow menu") } - ) { - for (i in 1..2) { - FunctionCallCode( - name = OdsComposable.OdsDropdownMenuItem.name, - parameters = { - text("Menu $i") - onClick() - } - ) + } + + if (isOverflowMenuEnabled) { + list("overflowMenuActions") { + for (i in 1..2) { + // The classInstance method displays the original type of type aliases, that's why function is used instead + function("OdsTopAppBarOverflowMenuActionItem") { + text("Menu $i") + onClick() } } } diff --git a/app/src/main/java/com/orange/ods/app/ui/utilities/composable/CodeImplementation.kt b/app/src/main/java/com/orange/ods/app/ui/utilities/composable/CodeImplementation.kt index 120fd4c1c..27f314dbc 100644 --- a/app/src/main/java/com/orange/ods/app/ui/utilities/composable/CodeImplementation.kt +++ b/app/src/main/java/com/orange/ods/app/ui/utilities/composable/CodeImplementation.kt @@ -74,21 +74,21 @@ private open class FunctionParameter(name: String, val value: Function) : CodePa private class ClassInstanceParameter(name: String, value: ClassInstance) : FunctionParameter(name, with(value) { Function(clazz.simpleName, parameters) }) -private class ListParameter(name: String, val value: List) : CodeParameter(name) { +private class ListParameter(name: String, val value: List) : CodeParameter(name) { override val code get() = @Composable { TechnicalText(text = "$name = listOf(") IndentCodeColumn { value.forEach { item -> - FunctionCallCode(name = item.clazz.simpleName, parameters = item.parameters, trailingComma = true, exhaustiveParameters = true) + FunctionCallCode(name = item.name, parameters = item.parameters, trailingComma = true, exhaustiveParameters = true) } } TechnicalText(text = "),") } } -data class ClassInstance(val clazz: Class<*>, val parameters: ParametersBuilder.() -> Unit = {}) -data class Function(val name: String, val parameters: ParametersBuilder.() -> Unit = {}) +class ClassInstance(val clazz: Class<*>, parameters: ParametersBuilder.() -> Unit = {}) : Function(clazz.simpleName, parameters) +open class Function(val name: String, val parameters: ParametersBuilder.() -> Unit = {}) private sealed class PredefinedParameter { object Icon : SimpleParameter("icon", IconPainterValue) @@ -260,11 +260,13 @@ annotation class CodeImplementationDslMarker @CodeImplementationDslMarker class ListParameterValueBuilder { - private val classInstances = mutableListOf() + private val functions = mutableListOf() - fun classInstance(clazz: Class<*>, parameters: ParametersBuilder.() -> Unit = {}) = apply { classInstances.add(ClassInstance(clazz, parameters)) } + fun classInstance(clazz: Class<*>, parameters: ParametersBuilder.() -> Unit = {}) = apply { functions.add(ClassInstance(clazz, parameters)) } - fun build() = classInstances.toList() + fun function(functionName: String, parameters: ParametersBuilder.() -> Unit = {}) = apply { functions.add(Function(functionName, parameters)) } + + fun build() = functions.toList() } @CodeImplementationDslMarker diff --git a/changelog.md b/changelog.md index 406d4640c..c4235f848 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - \[Lib\] Replace composable parameter of `OdsDropdownMenu` with a list of `OdsDropdownMenuItem` ([#572](https://github.com/Orange-OpenSource/ods-android/issues/572)) +- \[Lib\] Update `OdsTopAppBar` and `OdsLargeTopAppBar` APIs ([#572](https://github.com/Orange-OpenSource/ods-android/issues/572)) ### Fixed diff --git a/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsLargeTopAppBar.kt b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsLargeTopAppBar.kt index 35db77889..ff55e0348 100644 --- a/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsLargeTopAppBar.kt +++ b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsLargeTopAppBar.kt @@ -10,13 +10,10 @@ package com.orange.ods.compose.component.appbar.top -import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.padding -import androidx.compose.material.Icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.IconButton import androidx.compose.material3.LargeTopAppBar import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults @@ -34,7 +31,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import com.orange.ods.R import com.orange.ods.compose.component.OdsComposable -import com.orange.ods.compose.component.menu.OdsDropdownMenuItem +import com.orange.ods.compose.component.content.OdsComponentContent import com.orange.ods.compose.component.utilities.BasicPreviewParameterProvider import com.orange.ods.compose.component.utilities.Preview import com.orange.ods.compose.component.utilities.UiModePreviews @@ -46,9 +43,31 @@ import com.orange.ods.compose.theme.OdsTheme fun OdsLargeTopAppBar( title: String, modifier: Modifier = Modifier, - navigationIcon: @Composable (() -> Unit)? = null, - onNavigationIconClick: (() -> Unit)? = null, - actions: @Composable RowScope.() -> Unit = {}, + navigationIcon: OdsTopAppBarNavigationIcon? = null, + actions: List = emptyList(), + overflowMenuActions: List = emptyList(), + scrollBehavior: TopAppBarScrollBehavior? = null +) { + OdsLargeTopAppBarInternal( + title = title, + modifier = modifier, + navigationIcon = navigationIcon, + actions = actions, + overflowMenuActions = overflowMenuActions, + scrollBehavior = scrollBehavior + ) +} + +// TODO: Remove this method once OdsSearchTopAppBar is developed +@OptIn(ExperimentalMaterial3Api::class) +@Composable +@OdsComposable +fun OdsLargeTopAppBarInternal( + title: String, + modifier: Modifier = Modifier, + navigationIcon: OdsTopAppBarNavigationIcon? = null, + actions: List = emptyList(), + overflowMenuActions: List = emptyList(), scrollBehavior: TopAppBarScrollBehavior? = null ) { val contentColor = OdsTheme.colors.component.topAppBar.barContent @@ -100,18 +119,11 @@ fun OdsLargeTopAppBar( ) }, modifier = modifier, - navigationIcon = { - if (navigationIcon != null) { - if (onNavigationIconClick != null) { - IconButton(onClick = onNavigationIconClick) { - navigationIcon() - } - } else { - navigationIcon() - } - } + navigationIcon = { navigationIcon?.Content() }, + actions = { + actions.forEach { it.Content() } + if (overflowMenuActions.isNotEmpty()) OdsTopAppBarOverflowMenu(items = overflowMenuActions) }, - actions = actions, colors = TopAppBarDefaults.largeTopAppBarColors( containerColor = OdsTheme.colors.component.topAppBar.barBackground, navigationIconContentColor = contentColor, @@ -127,28 +139,22 @@ fun OdsLargeTopAppBar( @Composable private fun PreviewOdsLargeTopAppBar(@PreviewParameter(OdsLargeTopAppBarPreviewParameterProvider::class) parameter: OdsLargeTopAppBarPreviewParameter) = Preview { + val actions = listOf(OdsTopAppBarActionButton(painterResource(id = android.R.drawable.ic_dialog_info), "Info") {}) + val overflowMenuActions = listOf( + OdsTopAppBarOverflowMenuActionItem("Settings") { }, + OdsTopAppBarOverflowMenuActionItem("Account") { } + ) OdsLargeTopAppBar( title = parameter.title, navigationIcon = parameter.navigationIcon, - actions = { - OdsTopAppBarActionButton( - onClick = {}, - painter = painterResource(id = android.R.drawable.ic_dialog_info), - contentDescription = "Info" - ) - OdsTopAppBarOverflowMenuBox( - overflowIconContentDescription = "More options" - ) { - OdsDropdownMenuItem(text = "Settings", onClick = { }) - OdsDropdownMenuItem(text = "Account", onClick = { }) - } - } + actions = actions, + overflowMenuActions = overflowMenuActions ) } internal data class OdsLargeTopAppBarPreviewParameter( val title: String, - val navigationIcon: @Composable () -> Unit + val navigationIcon: OdsTopAppBarNavigationIcon? ) private class OdsLargeTopAppBarPreviewParameterProvider : @@ -156,15 +162,11 @@ private class OdsLargeTopAppBarPreviewParameterProvider : private val previewParameterValues: List get() { - val navigationIcon = @Composable { - IconButton(onClick = {}) { - Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = null) - } - } + val navigationIcon = OdsTopAppBarNavigationIcon(Icons.Filled.ArrowBack, "") {} return listOf( OdsLargeTopAppBarPreviewParameter("One line title", navigationIcon), OdsLargeTopAppBarPreviewParameter("Two lines title is allowed in large top app bar", navigationIcon), OdsLargeTopAppBarPreviewParameter("The title will be truncated if it is too long to fit in the large top app bar like this one", navigationIcon), - OdsLargeTopAppBarPreviewParameter("One line title", { }) + OdsLargeTopAppBarPreviewParameter("One line title", null) ) } \ No newline at end of file diff --git a/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBar.kt b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBar.kt index f7e71d167..794bc0eb6 100644 --- a/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBar.kt +++ b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBar.kt @@ -10,32 +10,18 @@ package com.orange.ods.compose.component.appbar.top -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.RowScope import androidx.compose.material.AppBarDefaults -import androidx.compose.material.Icon -import androidx.compose.material.IconButton 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.OdsComposable -import com.orange.ods.compose.component.button.OdsIconButton -import com.orange.ods.compose.component.menu.OdsDropdownMenu -import com.orange.ods.compose.component.menu.OdsDropdownMenuItem +import com.orange.ods.compose.component.content.OdsComponentContent import com.orange.ods.compose.component.utilities.Preview import com.orange.ods.compose.component.utilities.UiModePreviews import com.orange.ods.compose.theme.OdsTheme @@ -50,124 +36,71 @@ import com.orange.ods.compose.theme.OdsTheme * centering the title, use the other TopAppBar overload for a generic TopAppBar with no * restriction on content. * - * @param modifier The [Modifier] to be applied to this TopAppBar * @param title The title to be displayed in the center of the TopAppBar - * @param navigationIcon Optional navigation icon displayed at the start of the TopAppBar. This should be an [Icon]. - * @param onNavigationIconClick Optional action executed on the navigation icon click. - * @param actions The actions displayed at the end of the TopAppBar. This should be [OdsTopAppBarActionButton]s. + * @param modifier The [Modifier] to be applied to this TopAppBar + * @param navigationIcon Optional navigation icon displayed at the start of the TopAppBar. + * @param actions The actions displayed at the end of the TopAppBar. * The default layout here is a [Row], so icons inside will be placed horizontally. + * @param overflowMenuActions The actions displayed in the overflow menu. * @param elevated True to set an elevation to the top app bar (shadow displayed), false otherwise. */ @Composable @OdsComposable fun OdsTopAppBar( + title: String, modifier: Modifier = Modifier, - title: String? = null, - navigationIcon: @Composable (() -> Unit)? = null, - onNavigationIconClick: (() -> Unit)? = null, - actions: @Composable RowScope.() -> Unit = {}, + navigationIcon: OdsTopAppBarNavigationIcon? = null, + actions: List = emptyList(), + overflowMenuActions: List = emptyList(), elevated: Boolean = true ) { - TopAppBar( - title = { title?.let { Text(text = title, style = OdsTheme.typography.h6) } }, + OdsTopAppBarInternal( + title = title, modifier = modifier, - navigationIcon = navigationIcon?.let { navIcon -> - { - if (onNavigationIconClick != null) { - IconButton(onClick = onNavigationIconClick) { - navIcon() - } - } else { - navIcon() - } - } - }, + navigationIcon = navigationIcon, actions = actions, - backgroundColor = OdsTheme.colors.component.topAppBar.barBackground, - contentColor = OdsTheme.colors.component.topAppBar.barContent, - elevation = if (elevated) AppBarDefaults.TopAppBarElevation else 0.dp + overflowMenuActions = overflowMenuActions, + elevated = elevated ) } -/** - * 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. - */ +// TODO: Remove this method once OdsSearchTopAppBar is developed @Composable @OdsComposable -fun OdsTopAppBarActionButton( - onClick: () -> Unit, - painter: Painter, - contentDescription: String, +fun OdsTopAppBarInternal( + title: String, modifier: Modifier = Modifier, - enabled: Boolean = true + navigationIcon: OdsTopAppBarNavigationIcon? = null, + actions: List = emptyList(), + overflowMenuActions: List = emptyList(), + elevated: Boolean = true ) { - OdsIconButton( - onClick = onClick, - graphicsObject = painter, - contentDescription = contentDescription, + TopAppBar( + title = { Text(text = title, style = OdsTheme.typography.h6) }, modifier = modifier, - enabled = enabled, - tint = OdsTheme.colors.component.topAppBar.barContent + navigationIcon = navigationIcon?.let { { it.Content() } }, + actions = { + actions.forEach { it.Content() } + if (overflowMenuActions.isNotEmpty()) OdsTopAppBarOverflowMenu(items = overflowMenuActions) + }, + backgroundColor = OdsTheme.colors.component.topAppBar.barBackground, + contentColor = OdsTheme.colors.component.topAppBar.barContent, + elevation = if (elevated) AppBarDefaults.TopAppBarElevation else 0.dp ) } -/** - * 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 content The content of the overflow dropdown menu - */ -@Composable -@OdsComposable -fun OdsTopAppBarOverflowMenuBox( - overflowIconContentDescription: String, - content: @Composable ColumnScope.() -> Unit -) { - 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 }, - content = content - ) - } -} - @UiModePreviews.Default @Composable private fun PreviewOdsTopAppBar() = Preview { + val actions = listOf(OdsTopAppBarActionButton(painterResource(id = android.R.drawable.ic_dialog_info), "Info") {}) + val overflowMenuItems = listOf( + OdsTopAppBarOverflowMenuActionItem("Settings") {}, + OdsTopAppBarOverflowMenuActionItem("Account") {} + ) OdsTopAppBar( title = "Title", - navigationIcon = { - IconButton(onClick = {}) { - Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = null) - } - }, - actions = { - OdsTopAppBarActionButton( - onClick = {}, - painter = painterResource(id = android.R.drawable.ic_dialog_info), - contentDescription = "Info" - ) - OdsTopAppBarOverflowMenuBox( - overflowIconContentDescription = "More options" - ) { - OdsDropdownMenuItem(text = "Settings", onClick = { }) - OdsDropdownMenuItem(text = "Account", onClick = { }) - } - } + navigationIcon = OdsTopAppBarNavigationIcon(Icons.Filled.ArrowBack, "") {}, + actions = actions, + overflowMenuActions = overflowMenuItems ) } diff --git a/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBarsCommon.kt b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBarsCommon.kt new file mode 100644 index 000000000..bfd608a3b --- /dev/null +++ b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBarsCommon.kt @@ -0,0 +1,139 @@ +/* + * + * 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.appbar.top + +import androidx.compose.foundation.layout.Box +import androidx.compose.material.icons.Icons +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.Color +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import com.orange.ods.R +import com.orange.ods.compose.component.OdsComposable +import com.orange.ods.compose.component.content.OdsComponentIcon +import com.orange.ods.compose.component.menu.OdsDropdownMenu +import com.orange.ods.compose.component.menu.OdsDropdownMenuItem +import com.orange.ods.compose.theme.OdsTheme + +@Composable +internal fun OdsTopAppBarOverflowMenu(items: List) { + Box { + var showMenu by remember { mutableStateOf(false) } + val contentDescription = stringResource(id = R.string.top_app_bar_overflow_menu_content_description) + val dropdownMenuAction = OdsTopAppBarActionButton(Icons.Filled.MoreVert, contentDescription, true) { showMenu = !showMenu } + dropdownMenuAction.Content() + OdsDropdownMenu( + items = items, + expanded = showMenu, + onDismissRequest = { showMenu = false } + ) + } +} + +/** + * A navigation icon in an [OdsTopAppBar]. + */ +class OdsTopAppBarNavigationIcon : OdsComponentIcon { + + /** + * Creates an instance of [OdsTopAppBarNavigationIcon]. + * + * @param painter Painter of the icon. + * @param contentDescription The content description associated to this [OdsTopAppBarNavigationIcon]. + * @param onClick Will be called when the user clicks on the action icon button. + */ + constructor(painter: Painter, contentDescription: String, onClick: () -> Unit) : super(painter as Any, contentDescription, onClick = onClick) + + /** + * Creates an instance of [OdsTopAppBarNavigationIcon]. + * + * @param imageVector Image vector of the icon. + * @param contentDescription The content description associated to this [OdsTopAppBarNavigationIcon]. + * @param onClick Will be called when the user clicks on the action icon button. + */ + constructor(imageVector: ImageVector, contentDescription: String, onClick: () -> Unit) : super(imageVector as Any, contentDescription, onClick = onClick) + + /** + * Creates an instance of [OdsTopAppBarNavigationIcon]. + * + * @param bitmap Image bitmap of the icon. + * @param contentDescription The content description associated to this [OdsTopAppBarNavigationIcon]. + * @param onClick Will be called when the user clicks on the action icon button. + */ + constructor(bitmap: ImageBitmap, contentDescription: String, onClick: () -> Unit) : super(bitmap as Any, contentDescription, onClick = onClick) +} + +/** + * An action button displayed in an [OdsTopAppBar]. + */ +open class OdsTopAppBarActionButton : OdsComponentIcon { + + /** + * Creates an instance of [OdsTopAppBarActionButton]. + * + * @param painter Painter of the icon. + * @param contentDescription The content description associated to this [OdsTopAppBarActionButton]. + * @param enabled whether or not this [OdsTopAppBarActionButton] will handle input events and appear enabled for + * semantics purposes, true by default. + * @param onClick Will be called when the user clicks on the action icon button. + */ + constructor( + painter: Painter, + contentDescription: String, + enabled: Boolean = true, + onClick: () -> Unit + ) : super(painter as Any, contentDescription, enabled, onClick) + + /** + * Creates an instance of [OdsTopAppBarActionButton]. + * + * @param imageVector Image vector of the icon. + * @param contentDescription The content description associated to this [OdsTopAppBarActionButton]. + * @param enabled whether or not this [OdsTopAppBarActionButton] will handle input events and appear enabled for + * semantics purposes, true by default. + * @param onClick Will be called when the user clicks on the action icon button. + */ + constructor( + imageVector: ImageVector, + contentDescription: String, + enabled: Boolean = true, + onClick: () -> Unit + ) : super(imageVector as Any, contentDescription, enabled, onClick) + + /** + * Creates an instance of [OdsTopAppBarActionButton]. + * + * @param bitmap Image bitmap of the icon. + * @param contentDescription The content description associated to this [OdsTopAppBarActionButton]. + * @param enabled whether or not this [OdsTopAppBarActionButton] will handle input events and appear enabled for + * semantics purposes, true by default. + * @param onClick Will be called when the user clicks on the action icon button. + */ + constructor( + bitmap: ImageBitmap, + contentDescription: String, + enabled: Boolean = true, + onClick: () -> Unit + ) : super(bitmap as Any, contentDescription, enabled, onClick) + + override val tint: Color? + @Composable + get() = OdsTheme.colors.component.topAppBar.barContent +} + +typealias OdsTopAppBarOverflowMenuActionItem = OdsDropdownMenuItem diff --git a/lib/src/main/java/com/orange/ods/compose/component/content/OdsComponentContent.kt b/lib/src/main/java/com/orange/ods/compose/component/content/OdsComponentContent.kt index 601be5c55..3c1643f1d 100644 --- a/lib/src/main/java/com/orange/ods/compose/component/content/OdsComponentContent.kt +++ b/lib/src/main/java/com/orange/ods/compose/component/content/OdsComponentContent.kt @@ -26,6 +26,7 @@ abstract class OdsComponentContent { * The Jetpack Compose UI for this component content. * Subclasses must implement this method to provide content. */ + // TODO: Set this method internal once OdsSearchTopAppBar is developed @Composable - internal abstract fun Content() + abstract fun Content() } diff --git a/lib/src/main/res/values/strings.xml b/lib/src/main/res/values/strings.xml index 5207eda10..05bf689a1 100644 --- a/lib/src/main/res/values/strings.xml +++ b/lib/src/main/res/values/strings.xml @@ -15,7 +15,9 @@ Show password Hide password + More options + %s%% - + Clear search \ No newline at end of file From c2510595a011afadaa91972330d8ac4bcae72a64 Mon Sep 17 00:00:00 2001 From: Florent Maitre Date: Wed, 19 Jul 2023 18:31:57 +0200 Subject: [PATCH 4/7] [#572] Remove empty file --- .../password/OdsPasswordTextFieldsCommon.kt | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 lib/src/main/java/com/orange/ods/compose/component/textfield/password/OdsPasswordTextFieldsCommon.kt diff --git a/lib/src/main/java/com/orange/ods/compose/component/textfield/password/OdsPasswordTextFieldsCommon.kt b/lib/src/main/java/com/orange/ods/compose/component/textfield/password/OdsPasswordTextFieldsCommon.kt deleted file mode 100644 index 7c96f199c..000000000 --- a/lib/src/main/java/com/orange/ods/compose/component/textfield/password/OdsPasswordTextFieldsCommon.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * - * 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.textfield.password - From f28ea378a09ba576d3ee1b43c7a67697331a9789 Mon Sep 17 00:00:00 2001 From: Florent Maitre Date: Fri, 21 Jul 2023 11:05:58 +0200 Subject: [PATCH 5/7] [#572] Review: Several OdsComponentIcon properties are now private --- .../ods/compose/component/content/OdsComponentIcon.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/main/java/com/orange/ods/compose/component/content/OdsComponentIcon.kt b/lib/src/main/java/com/orange/ods/compose/component/content/OdsComponentIcon.kt index 2622ddd76..faad1684e 100644 --- a/lib/src/main/java/com/orange/ods/compose/component/content/OdsComponentIcon.kt +++ b/lib/src/main/java/com/orange/ods/compose/component/content/OdsComponentIcon.kt @@ -23,10 +23,10 @@ import com.orange.ods.utilities.extension.orElse * An icon in a component. */ abstract class OdsComponentIcon protected constructor( - protected val graphicsObject: Any, - protected val contentDescription: String, - protected val enabled: Boolean = true, - protected open var onClick: (() -> Unit)? = null + private val graphicsObject: Any, + private val contentDescription: String, + private val enabled: Boolean = true, + private val onClick: (() -> Unit)? = null ) : OdsComponentContent() { protected open val tint: Color? From 643079eb092c45af9f709a9de619e0c809cdbb07 Mon Sep 17 00:00:00 2001 From: Florent Maitre Date: Fri, 21 Jul 2023 11:38:33 +0200 Subject: [PATCH 6/7] [#572] Review: Total number of actions in top app bars is now limited to 3 --- .../component/appbar/top/OdsLargeTopAppBar.kt | 5 +--- .../component/appbar/top/OdsTopAppBar.kt | 5 +--- .../appbar/top/OdsTopAppBarsCommon.kt | 29 +++++++++++-------- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsLargeTopAppBar.kt b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsLargeTopAppBar.kt index ff55e0348..4b4026db2 100644 --- a/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsLargeTopAppBar.kt +++ b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsLargeTopAppBar.kt @@ -120,10 +120,7 @@ fun OdsLargeTopAppBarInternal( }, modifier = modifier, navigationIcon = { navigationIcon?.Content() }, - actions = { - actions.forEach { it.Content() } - if (overflowMenuActions.isNotEmpty()) OdsTopAppBarOverflowMenu(items = overflowMenuActions) - }, + actions = { OdsTopAppBarActions(actions = actions, overflowMenuActions = overflowMenuActions) }, colors = TopAppBarDefaults.largeTopAppBarColors( containerColor = OdsTheme.colors.component.topAppBar.barBackground, navigationIconContentColor = contentColor, diff --git a/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBar.kt b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBar.kt index 794bc0eb6..a94340d28 100644 --- a/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBar.kt +++ b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBar.kt @@ -79,10 +79,7 @@ fun OdsTopAppBarInternal( title = { Text(text = title, style = OdsTheme.typography.h6) }, modifier = modifier, navigationIcon = navigationIcon?.let { { it.Content() } }, - actions = { - actions.forEach { it.Content() } - if (overflowMenuActions.isNotEmpty()) OdsTopAppBarOverflowMenu(items = overflowMenuActions) - }, + actions = { OdsTopAppBarActions(actions = actions, overflowMenuActions = overflowMenuActions) }, backgroundColor = OdsTheme.colors.component.topAppBar.barBackground, contentColor = OdsTheme.colors.component.topAppBar.barContent, elevation = if (elevated) AppBarDefaults.TopAppBarElevation else 0.dp diff --git a/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBarsCommon.kt b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBarsCommon.kt index bfd608a3b..5049cce64 100644 --- a/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBarsCommon.kt +++ b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsTopAppBarsCommon.kt @@ -24,24 +24,29 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import com.orange.ods.R -import com.orange.ods.compose.component.OdsComposable +import com.orange.ods.compose.component.content.OdsComponentContent import com.orange.ods.compose.component.content.OdsComponentIcon import com.orange.ods.compose.component.menu.OdsDropdownMenu import com.orange.ods.compose.component.menu.OdsDropdownMenuItem import com.orange.ods.compose.theme.OdsTheme @Composable -internal fun OdsTopAppBarOverflowMenu(items: List) { - Box { - var showMenu by remember { mutableStateOf(false) } - val contentDescription = stringResource(id = R.string.top_app_bar_overflow_menu_content_description) - val dropdownMenuAction = OdsTopAppBarActionButton(Icons.Filled.MoreVert, contentDescription, true) { showMenu = !showMenu } - dropdownMenuAction.Content() - OdsDropdownMenu( - items = items, - expanded = showMenu, - onDismissRequest = { showMenu = false } - ) +internal fun OdsTopAppBarActions(actions: List, overflowMenuActions: List) { + val maxTotalActionCount = 3 + val maxActionCount = if (overflowMenuActions.isNotEmpty()) maxTotalActionCount - 1 else maxTotalActionCount + actions.take(maxActionCount).forEach { it.Content() } + if (overflowMenuActions.isNotEmpty()) { + Box { + var showMenu by remember { mutableStateOf(false) } + val contentDescription = stringResource(id = R.string.top_app_bar_overflow_menu_content_description) + val dropdownMenuAction = OdsTopAppBarActionButton(Icons.Filled.MoreVert, contentDescription, true) { showMenu = !showMenu } + dropdownMenuAction.Content() + OdsDropdownMenu( + items = overflowMenuActions, + expanded = showMenu, + onDismissRequest = { showMenu = false } + ) + } } } From 3f28e7bfade3688832736fc7d06ffdeafda84bf4 Mon Sep 17 00:00:00 2001 From: Florent Maitre Date: Fri, 21 Jul 2023 12:02:26 +0200 Subject: [PATCH 7/7] [#572] Review: Update documentation --- docs/components/AppBarsTop.md | 74 ++++++++++++++++++----------------- docs/components/Menus.md | 35 +++++++++-------- 2 files changed, 57 insertions(+), 52 deletions(-) diff --git a/docs/components/AppBarsTop.md b/docs/components/AppBarsTop.md index a1918cc4e..b51fb0d83 100644 --- a/docs/components/AppBarsTop.md +++ b/docs/components/AppBarsTop.md @@ -71,25 +71,27 @@ Add `OdsTopAppBar` composable to your Scaffold topBar: ```kotlin OdsTopAppBar( - title = { - Text(text = "Title") - }, - navigationIcon = { - Icon( - painter = painterResource(id = R.drawable.ic_back), - contentDescription = "content description" - ) - }, - onNavigationIconClick = { - // Do something - }, - actions = { + title = "Title", + navigationIcon = OdsTopAppBarNavigationIcon( + painter = painterResource(id = R.drawable.ic_back), + contentDescription = "content description", + onClick = { /* Do something */ } + ), + actions = listOf( OdsTopAppBarActionButton( - onClick = { }, painter = painterResource(id = R.drawable.ic_share), - contentDescription = "content description" - ) // Each action should be an `OdsTopAppBarActionButton`. They are displayed in a `Row`, so icons inside will be placed horizontally. - } + contentDescription = "content description", + onClick = { } + ), + // ... + ), + overflowMenuActions = listOf( + OdsTopAppBarOverflowMenuActionItem( + text = "Text", + onClick = { } + ), + // ... + ) ) ``` @@ -159,7 +161,7 @@ In menu/navigation icons: ```xml - + ``` In code: @@ -240,25 +242,27 @@ Then you can add `OdsLargeTopAppBar` composable to your Scaffold topBar: ```kotlin OdsLargeTopAppBar( - title = { - Text(text = "Title") - }, - navigationIcon = { - Icon( - painter = painterResource(id = R.drawable.ic_back), - contentDescription = "content description" - ) - }, - onNavigationIconClick = { - // Do something - }, - actions = { + title = "Title", + navigationIcon = OdsTopAppBarNavigationIcon( + painter = painterResource(id = R.drawable.ic_back), + contentDescription = "content description", + onClick = { /* Do something */ } + ), + actions = listOf( OdsTopAppBarActionButton( - onClick = { }, painter = painterResource(id = R.drawable.ic_share), - contentDescription = "content description" - ) // Each action should be an `OdsTopAppBarActionButton`. They are displayed in a `Row`, so icons inside will be placed horizontally. - }, + contentDescription = "content description", + onClick = { } + ), + // ... + ), + overflowMenuActions = listOf( + OdsTopAppBarOverflowMenuActionItem( + text = "Text", + onClick = { } + ), + // ... + ) scrollBehavior = null // See below to attach a scroll behavior and make the top app bar collapsible ) ``` diff --git a/docs/components/Menus.md b/docs/components/Menus.md index ec5c65dcd..40b633717 100644 --- a/docs/components/Menus.md +++ b/docs/components/Menus.md @@ -45,24 +45,25 @@ var menuExpanded by remember { mutableStateOf(false) } OdsDropdownMenu( expanded = menuExpanded, onDismissRequest = { menuExpanded = false }, - offset = DpOffset(x = (-100).dp, y = (-10).dp) -) { - OdsDropdownMenuItem( - text = "Summer salad", - icon = painterResource(id = R.drawable.ic_salad), - onClick = { - // Do something - } + offset = DpOffset(x = (-100).dp, y = (-10).dp), + items = listOf( + OdsDropdownMenuItem( + text = "Summer salad", + icon = painterResource(id = R.drawable.ic_salad), + divider = true, // Allow to add a divider between the 2 items + onClick = { + // Do something + } + ), + OdsDropdownMenuItem( + text = "Brocoli soup", + icon = painterResource(id = R.drawable.ic_soup), + onClick = { + // Do something + } + ) ) - OdsDivider() // Allow to add a divider between the 2 items - OdsDropdownMenuItem( - text = "Brocoli soup", - icon = painterResource(id = R.drawable.ic_soup), - onClick = { - // Do something - } - ) -} +) ``` > **XML implementation**