From d9a3f8a964088eecec177f738a796c3f4ee45e41 Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Mon, 6 Mar 2023 17:19:08 +0100 Subject: [PATCH 01/47] Add top app bar extended variant --- .../orange/ods/app/ui/components/Component.kt | 3 ++- .../ui/components/ComponentVariantScreen.kt | 3 ++- .../appbars/top/ComponentTopAppBarExtended.kt | 18 ++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 4 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/orange/ods/app/ui/components/appbars/top/ComponentTopAppBarExtended.kt diff --git a/app/src/main/java/com/orange/ods/app/ui/components/Component.kt b/app/src/main/java/com/orange/ods/app/ui/components/Component.kt index 22268b5f2..f7062ed7b 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/Component.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/Component.kt @@ -37,7 +37,7 @@ sealed class Component( R.drawable.il_app_bars_top, R.drawable.il_app_bars_top_small, R.string.component_app_bars_top_description, - variants = listOf(Variant.AppBarsTopRegular), + variants = listOf(Variant.AppBarsTopRegular, Variant.AppBarsTopExtended), imageAlignment = Alignment.TopCenter ) @@ -233,6 +233,7 @@ sealed class Variant( val id: Long = Variant::class.sealedSubclasses.indexOf(this::class).toLong() object AppBarsTopRegular : Variant(R.string.component_app_bars_top_regular, OdsComposable.OdsTopAppBar.name) + object AppBarsTopExtended : Variant(R.string.component_app_bars_top_extended, OdsComposable.OdsTopAppBar.name) object ButtonsPrimary : Variant(R.string.component_buttons_highest_emphasis, "${OdsComposable.OdsButton.name} with ${OdsButtonStyle.Primary.name}") object ButtonsDefault : Variant(R.string.component_buttons_high_emphasis, "${OdsComposable.OdsButton.name} with ${OdsButtonStyle.Default.name}") diff --git a/app/src/main/java/com/orange/ods/app/ui/components/ComponentVariantScreen.kt b/app/src/main/java/com/orange/ods/app/ui/components/ComponentVariantScreen.kt index c8c3099d6..337951e84 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/ComponentVariantScreen.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/ComponentVariantScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import com.orange.ods.app.ui.LocalMainTopAppBarManager import com.orange.ods.app.ui.components.appbars.top.ComponentTopAppBar +import com.orange.ods.app.ui.components.appbars.top.ComponentTopAppBarExtended import com.orange.ods.app.ui.components.buttons.ComponentButtons import com.orange.ods.app.ui.components.buttons.icons.ComponentButtonsIcons import com.orange.ods.app.ui.components.cards.ComponentCard @@ -32,7 +33,7 @@ fun ComponentVariantScreen(variantId: Long) { variant?.let { LocalMainTopAppBarManager.current.updateTopAppBarTitle(variant.titleRes) when (component) { - Component.AppBarsTop -> ComponentTopAppBar() + Component.AppBarsTop -> if (variant == Variant.AppBarsTopExtended) ComponentTopAppBarExtended() else ComponentTopAppBar() Component.Buttons -> ComponentButtons(variant = variant) Component.ButtonsIcons -> ComponentButtonsIcons(variant = variant) Component.Cards -> ComponentCard(variant = variant) diff --git a/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/ComponentTopAppBarExtended.kt b/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/ComponentTopAppBarExtended.kt new file mode 100644 index 000000000..05a1031a9 --- /dev/null +++ b/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/ComponentTopAppBarExtended.kt @@ -0,0 +1,18 @@ +/* + * + * 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.app.ui.components.appbars.top + +import androidx.compose.runtime.Composable + +@Composable +fun ComponentTopAppBarExtended() { + +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f93d4394e..b3f4fcad1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -99,6 +99,7 @@ App bars: top The app bar is a key component and organizes titling, navigation and action buttons. Regular + Extended Navigation icon Overflow menu Actions count From cce2f6a5563fbe7da9efb8c71d562948fe90bb87 Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Thu, 8 Jun 2023 10:29:16 +0200 Subject: [PATCH 02/47] Add compose material 3 dependency --- app/build.gradle.kts | 1 + buildSrc/src/main/kotlin/com/orange/ods/gradle/Dependencies.kt | 1 + buildSrc/src/main/kotlin/com/orange/ods/gradle/Versions.kt | 1 + 3 files changed, 3 insertions(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e4e578dfb..06a437848 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -119,6 +119,7 @@ dependencies { implementation(Dependencies.composeUi) implementation(Dependencies.lifecycleViewModelKtx) implementation(Dependencies.composeMaterial) + implementation(Dependencies.composeMaterial3) implementation(Dependencies.composeUiToolingPreview) implementation(Dependencies.lifecycleRuntimeKtx) implementation(Dependencies.activityCompose) diff --git a/buildSrc/src/main/kotlin/com/orange/ods/gradle/Dependencies.kt b/buildSrc/src/main/kotlin/com/orange/ods/gradle/Dependencies.kt index 6f4858073..25c2f2110 100644 --- a/buildSrc/src/main/kotlin/com/orange/ods/gradle/Dependencies.kt +++ b/buildSrc/src/main/kotlin/com/orange/ods/gradle/Dependencies.kt @@ -24,6 +24,7 @@ object Dependencies { const val coil = "io.coil-kt:coil:${Versions.coil}" const val coilCompose = "io.coil-kt:coil-compose:${Versions.coil}" const val composeMaterial = "androidx.compose.material:material:${Versions.compose}" + const val composeMaterial3 = "androidx.compose.material3:material3:${Versions.composeM3}" const val composeUi = "androidx.compose.ui:ui:${Versions.compose}" const val composeUiTooling = "androidx.compose.ui:ui-tooling:${Versions.compose}" const val composeUiToolingPreview = "androidx.compose.ui:ui-tooling-preview:${Versions.compose}" diff --git a/buildSrc/src/main/kotlin/com/orange/ods/gradle/Versions.kt b/buildSrc/src/main/kotlin/com/orange/ods/gradle/Versions.kt index 58fd1db65..52e4612da 100644 --- a/buildSrc/src/main/kotlin/com/orange/ods/gradle/Versions.kt +++ b/buildSrc/src/main/kotlin/com/orange/ods/gradle/Versions.kt @@ -22,6 +22,7 @@ object Versions { const val appCompat = "1.5.1" const val browser = "1.4.0" const val compose = "1.3.1" //TODO: When upgrading, see TODO in OdsOutlinedTextField.kt + const val composeM3 = "1.0.1" const val coil = "2.2.2" const val constraintLayoutCompose = "1.0.1" const val core = "1.9.0" From f10b9413e935bf005e92ff512396eb6b66070578 Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Fri, 9 Jun 2023 14:28:32 +0200 Subject: [PATCH 03/47] Set no elevation to top app bar when in dark mode --- .../main/java/com/orange/ods/app/ui/MainScreen.kt | 12 +++--------- .../ods/compose/component/appbar/top/OdsTopAppBar.kt | 3 ++- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt b/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt index 2ff4713f5..177384aeb 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt +++ b/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt @@ -21,20 +21,14 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.AppBarDefaults import androidx.compose.material.Scaffold import androidx.compose.material.Surface -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.SideEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavBackStackEntry @@ -109,7 +103,7 @@ fun MainScreen(themeConfigurations: Set, mainView Scaffold( backgroundColor = OdsTheme.colors.background, topBar = { - Surface(elevation = AppBarDefaults.TopAppBarElevation) { + Surface(elevation = if (isSystemInDarkTheme()) 0.dp else AppBarDefaults.TopAppBarElevation) { Column { SystemBarsColorSideEffect() MainTopAppBar( 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 d1d6fbafd..804f1175c 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,6 +10,7 @@ package com.orange.ods.compose.component.appbar.top +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row @@ -85,7 +86,7 @@ fun OdsTopAppBar( actions = actions, backgroundColor = OdsTheme.colors.component.topAppBar.barBackground, contentColor = OdsTheme.colors.component.topAppBar.barContent, - elevation = if (elevated) AppBarDefaults.TopAppBarElevation else 0.dp + elevation = if (elevated && !isSystemInDarkTheme()) AppBarDefaults.TopAppBarElevation else 0.dp ) } From 962038130e8788ec4f1a9fdf7fda1c9d3a52f186 Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Fri, 9 Jun 2023 14:38:16 +0200 Subject: [PATCH 04/47] Remove titleRes parameter from MainTopAppBar composable cause we can get it by using provided state --- app/src/main/java/com/orange/ods/app/ui/MainScreen.kt | 1 - app/src/main/java/com/orange/ods/app/ui/MainTopAppBar.kt | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt b/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt index 177384aeb..c7d843f1c 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt +++ b/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt @@ -107,7 +107,6 @@ fun MainScreen(themeConfigurations: Set, mainView Column { SystemBarsColorSideEffect() MainTopAppBar( - titleRes = mainState.topAppBarState.titleRes.value, shouldShowUpNavigationIcon = !mainState.shouldShowBottomBar, state = mainState.topAppBarState, upPress = mainState::upPress, 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 c33fbca6c..1b2bc1c3a 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 @@ -36,7 +36,6 @@ import com.orange.ods.compose.component.textfield.search.OdsSearchTextField @Composable fun MainTopAppBar( - titleRes: Int, shouldShowUpNavigationIcon: Boolean, state: MainTopAppBarState, upPress: () -> Unit, @@ -44,7 +43,7 @@ fun MainTopAppBar( onSearchActionClick: () -> Unit ) { OdsTopAppBar( - title = stringResource(id = titleRes), + title = stringResource(id = state.titleRes.value), navigationIcon = if (shouldShowUpNavigationIcon && state.isNavigationIconEnabled) { { Icon( From e71dd5b733d37ac05404ea7f01d2f5eb2f1e39b0 Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Fri, 9 Jun 2023 15:04:20 +0200 Subject: [PATCH 05/47] Move theme switch management in the MainTopAppBar --- .../java/com/orange/ods/app/ui/MainScreen.kt | 60 +------------- .../java/com/orange/ods/app/ui/MainState.kt | 4 +- .../com/orange/ods/app/ui/MainTopAppBar.kt | 83 ++++++++++++++++--- .../ui/{MainThemeState.kt => ThemeState.kt} | 12 +-- .../ods/app/ui/components/chips/Chip.kt | 4 +- .../ods/app/ui/components/chips/ChipFilter.kt | 4 +- .../spacing/GuidelineSpacingScreen.kt | 4 +- .../ods/app/ui/utilities/DrawableManager.kt | 4 +- 8 files changed, 89 insertions(+), 86 deletions(-) rename app/src/main/java/com/orange/ods/app/ui/{MainThemeState.kt => ThemeState.kt} (83%) diff --git a/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt b/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt index c7d843f1c..3d57916a5 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt +++ b/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt @@ -13,7 +13,6 @@ package com.orange.ods.app.ui import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut -import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -25,11 +24,8 @@ import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration -import androidx.compose.ui.res.dimensionResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.Dialog import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder @@ -49,9 +45,6 @@ import com.orange.ods.app.ui.guidelines.addGuidelinesGraph import com.orange.ods.app.ui.search.SearchScreen import com.orange.ods.app.ui.utilities.extension.isDarkModeEnabled import com.orange.ods.app.ui.utilities.extension.isOrange -import com.orange.ods.compose.component.list.OdsListItem -import com.orange.ods.compose.component.list.OdsRadioButtonTrailing -import com.orange.ods.compose.text.OdsTextH6 import com.orange.ods.compose.theme.OdsTheme import com.orange.ods.theme.OdsThemeConfigurationContract import com.orange.ods.utilities.extension.orElse @@ -62,7 +55,7 @@ import com.orange.ods.xml.utilities.extension.xml fun MainScreen(themeConfigurations: Set, mainViewModel: MainViewModel = viewModel()) { val isSystemInDarkTheme = isSystemInDarkTheme() val mainState = rememberMainState( - themeState = rememberMainThemeState( + themeState = rememberThemeState( currentThemeConfiguration = rememberSaveable { mutableStateOf( getCurrentThemeConfiguration( @@ -88,14 +81,12 @@ fun MainScreen(themeConfigurations: Set, mainView CompositionLocalProvider( LocalConfiguration provides configuration, LocalMainTopAppBarManager provides mainState.topAppBarState, - LocalMainThemeManager provides mainState.themeState, + LocalThemeManager provides mainState.themeState, LocalOdsGuideline provides mainState.themeState.currentThemeConfiguration.guideline, LocalRecipes provides mainViewModel.recipes, LocalCategories provides mainViewModel.categories, LocalUiFramework provides mainState.uiFramework ) { - var changeThemeDialogVisible by remember { mutableStateOf(false) } - OdsTheme( themeConfiguration = mainState.themeState.currentThemeConfiguration, darkThemeEnabled = configuration.isDarkModeEnabled @@ -108,9 +99,8 @@ fun MainScreen(themeConfigurations: Set, mainView SystemBarsColorSideEffect() MainTopAppBar( shouldShowUpNavigationIcon = !mainState.shouldShowBottomBar, - state = mainState.topAppBarState, + topAppBarState = mainState.topAppBarState, upPress = mainState::upPress, - onChangeThemeActionClick = { changeThemeDialogVisible = true }, onSearchActionClick = { mainState.navController.navigate(MainDestinations.SearchRoute) } @@ -139,18 +129,6 @@ fun MainScreen(themeConfigurations: Set, mainView mainNavGraph(navigateToElement = mainState::navigateToElement, searchedText = mainState.topAppBarState.searchedText) } } - - if (changeThemeDialogVisible) { - ChangeThemeDialog( - themeState = mainState.themeState, - dismissDialog = { - changeThemeDialogVisible = false - }, - onThemeSelected = { - mainViewModel.storeUserThemeName(mainState.themeState.currentThemeConfiguration.name) - } - ) - } } } } @@ -163,38 +141,6 @@ private fun getCurrentThemeConfiguration(storedUserThemeName: String?, themeConf .orElse { themeConfigurations.first() } } -@Composable -private fun ChangeThemeDialog(themeState: MainThemeState, dismissDialog: () -> Unit, onThemeSelected: () -> Unit) { - val selectedRadio = rememberSaveable { mutableStateOf(themeState.currentThemeConfiguration.name) } - - Dialog(onDismissRequest = dismissDialog) { - Column(modifier = Modifier.background(OdsTheme.colors.surface)) { - OdsTextH6( - text = stringResource(R.string.top_app_bar_action_change_theme_desc), - modifier = Modifier - .padding(top = dimensionResource(R.dimen.spacing_m), bottom = dimensionResource(id = R.dimen.spacing_s)) - .padding(horizontal = dimensionResource(R.dimen.screen_horizontal_margin)) - ) - themeState.themeConfigurations.forEach { themeConfiguration -> - OdsListItem( - text = themeConfiguration.name, - trailing = OdsRadioButtonTrailing( - selectedRadio = selectedRadio, - currentRadio = themeConfiguration.name, - onClick = { - if (themeConfiguration != themeState.currentThemeConfiguration) { - themeState.currentThemeConfiguration = themeConfiguration - onThemeSelected() - } - dismissDialog() - } - ) - ) - } - } - } -} - @Composable private fun SystemBarsColorSideEffect() { val systemUiController = rememberSystemUiController() diff --git a/app/src/main/java/com/orange/ods/app/ui/MainState.kt b/app/src/main/java/com/orange/ods/app/ui/MainState.kt index f164c6b02..f15bf6456 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainState.kt +++ b/app/src/main/java/com/orange/ods/app/ui/MainState.kt @@ -47,7 +47,7 @@ object MainDestinations { @Composable fun rememberMainState( - themeState: MainThemeState, + themeState: ThemeState, navController: NavHostController = rememberNavController(), topAppBarState: MainTopAppBarState = rememberMainTopAppBarState(), uiFramework: MutableState = rememberSaveable { mutableStateOf(UiFramework.Compose) } @@ -57,7 +57,7 @@ fun rememberMainState( } class MainState( - val themeState: MainThemeState, + val themeState: ThemeState, val navController: NavHostController, val topAppBarState: MainTopAppBarState, val uiFramework: MutableState 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 1b2bc1c3a..b6bc31a7c 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 @@ -10,20 +10,25 @@ package com.orange.ods.app.ui +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column 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.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.remember +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.window.Dialog +import androidx.lifecycle.viewmodel.compose.viewModel import com.orange.ods.app.R import com.orange.ods.app.domain.recipes.LocalRecipes import com.orange.ods.app.ui.components.utilities.clickOnElement @@ -31,20 +36,27 @@ import com.orange.ods.app.ui.utilities.extension.isDarkModeEnabled import com.orange.ods.compose.component.appbar.top.OdsTopAppBar import com.orange.ods.compose.component.appbar.top.OdsTopAppBarActionButton import com.orange.ods.compose.component.appbar.top.OdsTopAppBarOverflowMenuBox +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 @Composable fun MainTopAppBar( shouldShowUpNavigationIcon: Boolean, - state: MainTopAppBarState, + topAppBarState: MainTopAppBarState, upPress: () -> Unit, - onChangeThemeActionClick: () -> Unit, - onSearchActionClick: () -> Unit + onSearchActionClick: () -> Unit, + mainViewModel: MainViewModel = viewModel() ) { + val themeManager = LocalThemeManager.current + var changeThemeDialogVisible by remember { mutableStateOf(false) } + OdsTopAppBar( - title = stringResource(id = state.titleRes.value), - navigationIcon = if (shouldShowUpNavigationIcon && state.isNavigationIconEnabled) { + title = stringResource(id = topAppBarState.titleRes.value), + navigationIcon = if (shouldShowUpNavigationIcon && topAppBarState.isNavigationIconEnabled) { { Icon( imageVector = Icons.Filled.ArrowBack, @@ -54,12 +66,12 @@ fun MainTopAppBar( } else null, onNavigationIconClick = upPress, actions = { - if (state.titleRes.value == R.string.navigation_item_search) { + if (topAppBarState.titleRes.value == R.string.navigation_item_search) { val focusRequester = remember { FocusRequester() } OdsSearchTextField( - value = state.searchedText.value, + value = topAppBarState.searchedText.value, onValueChange = { value -> - state.searchedText.value = value + topAppBarState.searchedText.value = value }, placeholder = stringResource(id = R.string.search_text_field_hint), modifier = Modifier @@ -70,11 +82,24 @@ fun MainTopAppBar( focusRequester.requestFocus() } } else { - TopAppBarActions(state, onSearchActionClick, onChangeThemeActionClick) + TopAppBarActions(topAppBarState, onSearchActionClick) { changeThemeDialogVisible = true } } }, elevated = false // elevation is managed in [MainScreen] cause of tabs ) + + if (changeThemeDialogVisible) { + ChangeThemeDialog( + themeManager = themeManager, + dismissDialog = { + changeThemeDialogVisible = false + }, + onThemeSelected = { + mainViewModel.storeUserThemeName(themeManager.currentThemeConfiguration.name) + } + ) + } + } @Composable @@ -102,7 +127,7 @@ private fun TopAppBarChangeThemeActionButton(onClick: () -> Unit) { @Composable private fun TopAppBarChangeModeActionButton() { val configuration = LocalConfiguration.current - val mainThemeManager = LocalMainThemeManager.current + val mainThemeManager = LocalThemeManager.current val painterRes = if (configuration.isDarkModeEnabled) R.drawable.ic_ui_light_mode else R.drawable.ic_ui_dark_mode val iconDesc = @@ -148,3 +173,35 @@ private fun TopAppBarCustomActionButton(action: TopAppBarConfiguration.Action.Cu contentDescription = action.name ) } + +@Composable +private fun ChangeThemeDialog(themeManager: ThemeManager, dismissDialog: () -> Unit, onThemeSelected: () -> Unit) { + val selectedRadio = rememberSaveable { mutableStateOf(themeManager.currentThemeConfiguration.name) } + + Dialog(onDismissRequest = dismissDialog) { + Column(modifier = Modifier.background(OdsTheme.colors.surface)) { + OdsTextH6( + text = stringResource(R.string.top_app_bar_action_change_theme_desc), + modifier = Modifier + .padding(top = dimensionResource(R.dimen.spacing_m), bottom = dimensionResource(id = R.dimen.spacing_s)) + .padding(horizontal = dimensionResource(R.dimen.screen_horizontal_margin)) + ) + themeManager.themeConfigurations.forEach { themeConfiguration -> + OdsListItem( + text = themeConfiguration.name, + trailing = OdsRadioButtonTrailing( + selectedRadio = selectedRadio, + currentRadio = themeConfiguration.name, + onClick = { + if (themeConfiguration != themeManager.currentThemeConfiguration) { + themeManager.currentThemeConfiguration = themeConfiguration + onThemeSelected() + } + dismissDialog() + } + ) + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/orange/ods/app/ui/MainThemeState.kt b/app/src/main/java/com/orange/ods/app/ui/ThemeState.kt similarity index 83% rename from app/src/main/java/com/orange/ods/app/ui/MainThemeState.kt rename to app/src/main/java/com/orange/ods/app/ui/ThemeState.kt index 9245cd3e5..7d9727e6e 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainThemeState.kt +++ b/app/src/main/java/com/orange/ods/app/ui/ThemeState.kt @@ -19,10 +19,10 @@ import androidx.compose.runtime.staticCompositionLocalOf import com.orange.ods.theme.OdsThemeConfigurationContract import com.orange.ods.theme.guideline.OdsGuideline -val LocalMainThemeManager = staticCompositionLocalOf { error("CompositionLocal LocalMainThemeManager not present") } +val LocalThemeManager = staticCompositionLocalOf { error("CompositionLocal LocalMainThemeManager not present") } val LocalOdsGuideline = staticCompositionLocalOf { error("CompositionLocal LocalOdsGuideline not present") } -interface MainThemeManager { +interface ThemeManager { val themeConfigurations: List @@ -33,20 +33,20 @@ interface MainThemeManager { } @Composable -fun rememberMainThemeState( +fun rememberThemeState( themeConfigurations: List, currentThemeConfiguration: MutableState, darkModeEnabled: MutableState, ) = remember(themeConfigurations, currentThemeConfiguration, darkModeEnabled) { - MainThemeState(themeConfigurations, currentThemeConfiguration, darkModeEnabled) + ThemeState(themeConfigurations, currentThemeConfiguration, darkModeEnabled) } -class MainThemeState( +class ThemeState( override val themeConfigurations: List, currentThemeConfiguration: MutableState, darkModeEnabled: MutableState -) : MainThemeManager { +) : ThemeManager { // ---------------------------------------------------------- // Theme state source of truth diff --git a/app/src/main/java/com/orange/ods/app/ui/components/chips/Chip.kt b/app/src/main/java/com/orange/ods/app/ui/components/chips/Chip.kt index 812a22952..66f5725c4 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/chips/Chip.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/chips/Chip.kt @@ -28,7 +28,7 @@ import androidx.compose.ui.res.stringResource import coil.compose.rememberAsyncImagePainter import com.orange.ods.app.R import com.orange.ods.app.domain.recipes.LocalRecipes -import com.orange.ods.app.ui.LocalMainThemeManager +import com.orange.ods.app.ui.LocalThemeManager import com.orange.ods.app.ui.components.Variant import com.orange.ods.app.ui.components.chips.ChipCustomizationState.ChipType import com.orange.ods.app.ui.components.chips.ChipCustomizationState.LeadingElement @@ -104,7 +104,7 @@ fun ChipTypeDemo(chipType: ChipType, content: @Composable () -> Unit) { @Composable private fun Chip(chipCustomizationState: ChipCustomizationState) { val context = LocalContext.current - val outlinedChips = LocalMainThemeManager.current.currentThemeConfiguration.componentsConfiguration.chipStyle == ComponentStyle.Outlined + val outlinedChips = LocalThemeManager.current.currentThemeConfiguration.componentsConfiguration.chipStyle == ComponentStyle.Outlined val cancelCrossLabel = stringResource(id = R.string.component_element_cancel_cross) val recipes = LocalRecipes.current.take(4) diff --git a/app/src/main/java/com/orange/ods/app/ui/components/chips/ChipFilter.kt b/app/src/main/java/com/orange/ods/app/ui/components/chips/ChipFilter.kt index 02a390057..9ddba3fc4 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/chips/ChipFilter.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/chips/ChipFilter.kt @@ -28,7 +28,7 @@ import coil.compose.rememberAsyncImagePainter import com.google.accompanist.flowlayout.FlowRow import com.orange.ods.app.R import com.orange.ods.app.domain.recipes.LocalRecipes -import com.orange.ods.app.ui.LocalMainThemeManager +import com.orange.ods.app.ui.LocalThemeManager import com.orange.ods.app.ui.components.utilities.ComponentCustomizationBottomSheetScaffold import com.orange.ods.app.ui.utilities.DrawableManager import com.orange.ods.app.ui.utilities.composable.CodeImplementationColumn @@ -49,7 +49,7 @@ fun ChipFilter() { val chipCustomizationState = rememberChipCustomizationState(chipType = rememberSaveable { mutableStateOf(ChipCustomizationState.ChipType.Filter) }) val recipes = LocalRecipes.current val recipe = rememberSaveable { recipes.filter { it.ingredients.count() >= 3 }.random() } - val outlinedChips = LocalMainThemeManager.current.currentThemeConfiguration.componentsConfiguration.chipStyle == ComponentStyle.Outlined + val outlinedChips = LocalThemeManager.current.currentThemeConfiguration.componentsConfiguration.chipStyle == ComponentStyle.Outlined with(chipCustomizationState) { ComponentCustomizationBottomSheetScaffold( diff --git a/app/src/main/java/com/orange/ods/app/ui/guidelines/spacing/GuidelineSpacingScreen.kt b/app/src/main/java/com/orange/ods/app/ui/guidelines/spacing/GuidelineSpacingScreen.kt index 0df9f01b5..466088373 100644 --- a/app/src/main/java/com/orange/ods/app/ui/guidelines/spacing/GuidelineSpacingScreen.kt +++ b/app/src/main/java/com/orange/ods/app/ui/guidelines/spacing/GuidelineSpacingScreen.kt @@ -30,7 +30,7 @@ import com.orange.ods.compose.component.list.OdsListItem import com.orange.ods.compose.component.list.divider import com.orange.ods.compose.text.OdsTextSubtitle1 import com.orange.ods.app.R -import com.orange.ods.app.ui.LocalMainThemeManager +import com.orange.ods.app.ui.LocalThemeManager import com.orange.ods.app.ui.LocalMainTopAppBarManager import com.orange.ods.app.ui.guidelines.Guideline import com.orange.ods.app.ui.utilities.DrawableManager @@ -89,7 +89,7 @@ fun GuidelineSpacingImage(spacing: Spacing) { val spacingWidth = dimensionResource(id = spacing.dimenRes).coerceAtLeast(1.dp) val imageWidth = dimensionResource(id = R.dimen.guideline_spacing_image_width) val imageHeight = dimensionResource(id = R.dimen.guideline_spacing_image_height) - val isOrangeTheme = LocalMainThemeManager.current.currentThemeConfiguration.isOrange + val isOrangeTheme = LocalThemeManager.current.currentThemeConfiguration.isOrange Canvas( modifier = Modifier diff --git a/app/src/main/java/com/orange/ods/app/ui/utilities/DrawableManager.kt b/app/src/main/java/com/orange/ods/app/ui/utilities/DrawableManager.kt index 32b18eda7..f01350458 100644 --- a/app/src/main/java/com/orange/ods/app/ui/utilities/DrawableManager.kt +++ b/app/src/main/java/com/orange/ods/app/ui/utilities/DrawableManager.kt @@ -14,7 +14,7 @@ import androidx.annotation.DrawableRes import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.Composable import com.orange.ods.app.R -import com.orange.ods.app.ui.LocalMainThemeManager +import com.orange.ods.app.ui.LocalThemeManager import com.orange.ods.app.ui.utilities.extension.isOrange import com.orange.ods.utilities.extension.orElse @@ -62,7 +62,7 @@ object DrawableManager { @Composable fun getDrawableResIdForCurrentTheme(@DrawableRes resId: Int): Int { - val isOrangeTheme = LocalMainThemeManager.current.currentThemeConfiguration.isOrange + val isOrangeTheme = LocalThemeManager.current.currentThemeConfiguration.isOrange val currentThemeResId = if (isOrangeTheme) orangeResIdByGenericResId[resId] else genericResIdByOrangeResId[resId] return currentThemeResId.orElse { resId } From c84fd063234330970de3044cf8a5833c6ec2b8e6 Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Wed, 14 Jun 2023 09:58:30 +0200 Subject: [PATCH 06/47] Simplify management of components and variants screen display. Add extended attribute to top app bar state. --- .../java/com/orange/ods/app/ui/MainScreen.kt | 9 +- .../orange/ods/app/ui/MainTopAppBarState.kt | 30 ++-- .../orange/ods/app/ui/components/Component.kt | 130 +++++++++++++----- .../app/ui/components/ComponentDemoScreen.kt | 53 ------- .../ui/components/ComponentVariantScreen.kt | 48 ------- .../app/ui/components/ComponentsNavGraph.kt | 26 +++- 6 files changed, 142 insertions(+), 154 deletions(-) delete mode 100644 app/src/main/java/com/orange/ods/app/ui/components/ComponentDemoScreen.kt delete mode 100644 app/src/main/java/com/orange/ods/app/ui/components/ComponentVariantScreen.kt diff --git a/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt b/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt index 3d57916a5..390064502 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt +++ b/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt @@ -14,7 +14,6 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.material.AppBarDefaults @@ -124,10 +123,10 @@ fun MainScreen(themeConfigurations: Set, mainView } } ) { innerPadding -> - Box(modifier = Modifier.padding(innerPadding)) { - NavHost(mainState.navController, startDestination = MainDestinations.HomeRoute) { - mainNavGraph(navigateToElement = mainState::navigateToElement, searchedText = mainState.topAppBarState.searchedText) - } + NavHost( + navController = mainState.navController, startDestination = MainDestinations.HomeRoute, modifier = Modifier.padding(innerPadding) + ) { + mainNavGraph(navigateToElement = mainState::navigateToElement, searchedText = mainState.topAppBarState.searchedText) } } } diff --git a/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt b/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt index ce5b9fad6..d235fa4e1 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt +++ b/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt @@ -12,12 +12,8 @@ package com.orange.ods.app.ui import android.os.Parcelable import androidx.annotation.DrawableRes -import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.text.input.TextFieldValue import com.orange.ods.app.R import kotlinx.parcelize.Parcelize @@ -27,6 +23,8 @@ val LocalMainTopAppBarManager = staticCompositionLocalOf { interface MainTopAppBarManager { fun updateTopAppBar(topAppBarConfiguration: TopAppBarConfiguration) fun updateTopAppBarTitle(titleRes: Int) + fun setExtended(extended: Boolean) + fun updateTopAppBarTabs(tabsConfiguration: TabsConfiguration) fun clearTopAppBarTabs() @@ -40,10 +38,11 @@ fun rememberMainTopAppBarState( actions: MutableState> = rememberSaveable { mutableStateOf(MainTopAppBarState.DefaultConfiguration.actions) }, navigationIconEnabled: MutableState = rememberSaveable { mutableStateOf(MainTopAppBarState.DefaultConfiguration.isNavigationIconEnabled) }, searchedText: MutableState = remember { mutableStateOf(TextFieldValue("")) }, + extended: MutableState = rememberSaveable { mutableStateOf(false) }, tabsState: MainTabsState = rememberMainTabsState(), ) = - remember(titleRes, actions, searchedText, navigationIconEnabled, tabsState) { - MainTopAppBarState(titleRes, actions, searchedText, navigationIconEnabled, tabsState) + remember(titleRes, actions, searchedText, navigationIconEnabled, extended, tabsState) { + MainTopAppBarState(titleRes, actions, searchedText, navigationIconEnabled, extended, tabsState) } @@ -52,11 +51,13 @@ class MainTopAppBarState( val actions: MutableState>, var searchedText: MutableState, private val navigationIconEnabled: MutableState, + private var extended: MutableState, val tabsState: MainTabsState, ) : MainTopAppBarManager { companion object { val DefaultConfiguration = TopAppBarConfiguration( + isExtended = false, isNavigationIconEnabled = true, actions = listOf(TopAppBarConfiguration.Action.Theme, TopAppBarConfiguration.Action.Mode) ) @@ -66,10 +67,18 @@ class MainTopAppBarState( // TopAppBar state source of truth // ---------------------------------------------------------- + val isExtended: Boolean + get() = extended.value + val isNavigationIconEnabled: Boolean get() = navigationIconEnabled.value + override fun setExtended(extended: Boolean) { + this.extended.value = extended + } + override fun updateTopAppBar(topAppBarConfiguration: TopAppBarConfiguration) { + extended.value = topAppBarConfiguration.isExtended navigationIconEnabled.value = topAppBarConfiguration.isNavigationIconEnabled actions.value = topAppBarConfiguration.actions } @@ -93,6 +102,7 @@ class MainTopAppBarState( } data class TopAppBarConfiguration constructor( + val isExtended: Boolean, val isNavigationIconEnabled: Boolean, val actions: List ) { @@ -117,10 +127,12 @@ data class TopAppBarConfiguration constructor( class Builder { + private var isExtended = false private var isNavigationIconEnabled = true - private var actions = mutableListOf() + fun extended(value: Boolean) = apply { isExtended = value } + fun navigationIconEnabled(enabled: Boolean) = apply { isNavigationIconEnabled = enabled } fun actions(builderAction: MutableList.() -> Unit) = apply { actions.builderAction() } @@ -135,7 +147,7 @@ data class TopAppBarConfiguration constructor( actions { add(action) } } - fun build() = TopAppBarConfiguration(isNavigationIconEnabled, actions) + fun build() = TopAppBarConfiguration(isExtended, isNavigationIconEnabled, actions) } fun newBuilder() = Builder().apply { diff --git a/app/src/main/java/com/orange/ods/app/ui/components/Component.kt b/app/src/main/java/com/orange/ods/app/ui/components/Component.kt index f7062ed7b..3a3128e94 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/Component.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/Component.kt @@ -12,8 +12,29 @@ package com.orange.ods.app.ui.components import androidx.annotation.DrawableRes import androidx.annotation.StringRes +import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import com.orange.ods.app.R +import com.orange.ods.app.ui.components.appbars.top.ComponentTopAppBar +import com.orange.ods.app.ui.components.appbars.top.ComponentTopAppBarExtended +import com.orange.ods.app.ui.components.banners.ComponentBanners +import com.orange.ods.app.ui.components.bottomnavigation.ComponentBottomNavigation +import com.orange.ods.app.ui.components.buttons.ComponentButtons +import com.orange.ods.app.ui.components.cards.ComponentCard +import com.orange.ods.app.ui.components.checkboxes.ComponentCheckboxes +import com.orange.ods.app.ui.components.chips.Chip +import com.orange.ods.app.ui.components.dialogs.ComponentDialog +import com.orange.ods.app.ui.components.floatingactionbuttons.ComponentFloatingActionButton +import com.orange.ods.app.ui.components.lists.ComponentLists +import com.orange.ods.app.ui.components.menus.ComponentMenu +import com.orange.ods.app.ui.components.progress.ComponentProgress +import com.orange.ods.app.ui.components.radiobuttons.ComponentRadioButtons +import com.orange.ods.app.ui.components.sheets.ComponentSheetsBottom +import com.orange.ods.app.ui.components.sliders.ComponentSliders +import com.orange.ods.app.ui.components.snackbars.ComponentSnackbars +import com.orange.ods.app.ui.components.switches.ComponentSwitches +import com.orange.ods.app.ui.components.tabs.ComponentTabs +import com.orange.ods.app.ui.components.textfields.ComponentTextField import com.orange.ods.compose.OdsComposable import com.orange.ods.compose.component.button.OdsButtonStyle @@ -24,6 +45,7 @@ sealed class Component( @StringRes val descriptionRes: Int, val variants: List = emptyList(), val composableName: String? = null, + val screenContent: @Composable (() -> Unit)? = null, val imageAlignment: Alignment = Alignment.Center, ) { companion object { @@ -47,6 +69,7 @@ sealed class Component( null, R.string.component_banners_description, composableName = OdsComposable.OdsBanner.name, + screenContent = { ComponentBanners() } ) object BottomNavigation : @@ -56,6 +79,7 @@ sealed class Component( null, R.string.component_bottom_navigation_description, composableName = OdsComposable.OdsBottomNavigation.name, + screenContent = { ComponentBottomNavigation() }, imageAlignment = Alignment.TopCenter ) @@ -98,7 +122,8 @@ sealed class Component( R.drawable.il_checkboxes, null, R.string.component_checkboxes_description, - composableName = OdsComposable.OdsCheckbox.name + composableName = OdsComposable.OdsCheckbox.name, + screenContent = { ComponentCheckboxes() } ) object Chips : Component( @@ -114,7 +139,8 @@ sealed class Component( R.drawable.il_dialogs, null, R.string.component_dialogs_description, - composableName = OdsComposable.OdsAlertDialog.name + composableName = OdsComposable.OdsAlertDialog.name, + screenContent = { ComponentDialog() } ) object FloatingActionButtons : Component( @@ -122,7 +148,8 @@ sealed class Component( R.drawable.il_fab, null, R.string.component_floating_action_buttons_description, - composableName = OdsComposable.OdsFloatingActionButton.name + composableName = OdsComposable.OdsFloatingActionButton.name, + screenContent = { ComponentFloatingActionButton() } ) object ImageItem : Component( @@ -139,6 +166,7 @@ sealed class Component( null, R.string.component_lists_description, composableName = OdsComposable.OdsListItem.name, + screenContent = { ComponentLists() }, imageAlignment = Alignment.BottomCenter ) @@ -170,7 +198,8 @@ sealed class Component( R.drawable.il_radio_buttons, null, R.string.component_radio_buttons_description, - composableName = OdsComposable.OdsRadioButton.name + composableName = OdsComposable.OdsRadioButton.name, + screenContent = { ComponentRadioButtons() } ) object SheetsBottom : Component( @@ -178,7 +207,8 @@ sealed class Component( R.drawable.il_bottom_sheet, null, R.string.component_sheet_bottom_description, - composableName = OdsComposable.OdsBottomSheetScaffold.name + composableName = OdsComposable.OdsBottomSheetScaffold.name, + screenContent = { ComponentSheetsBottom() } ) object Sliders : Component( @@ -187,6 +217,7 @@ sealed class Component( null, R.string.component_sliders_description, composableName = OdsComposable.OdsSlider.name, + screenContent = { ComponentSliders() }, imageAlignment = Alignment.CenterEnd ) @@ -195,7 +226,8 @@ sealed class Component( R.drawable.il_snackbars, R.drawable.il_snackbars_small, R.string.component_snackbars_description, - composableName = OdsComposable.OdsSnackbar.name + composableName = OdsComposable.OdsSnackbar.name, + screenContent = { ComponentSnackbars() } ) object Switches : Component( @@ -203,7 +235,8 @@ sealed class Component( R.drawable.il_switches, R.drawable.il_switches_small, R.string.component_switches_description, - composableName = OdsComposable.OdsSwitch.name + composableName = OdsComposable.OdsSwitch.name, + screenContent = { ComponentSwitches() } ) object TextFields : Component( @@ -229,43 +262,70 @@ val components = Component::class.sealedSubclasses.mapNotNull { it.objectInstanc sealed class Variant( @StringRes val titleRes: Int, val composableName: String, + val screenContent: @Composable () -> Unit, + val extendedTopAppBar: Boolean = false ) { val id: Long = Variant::class.sealedSubclasses.indexOf(this::class).toLong() - object AppBarsTopRegular : Variant(R.string.component_app_bars_top_regular, OdsComposable.OdsTopAppBar.name) - object AppBarsTopExtended : Variant(R.string.component_app_bars_top_extended, OdsComposable.OdsTopAppBar.name) + object AppBarsTopRegular : Variant(R.string.component_app_bars_top_regular, OdsComposable.OdsTopAppBar.name, { ComponentTopAppBar() }) + object AppBarsTopExtended : Variant(R.string.component_app_bars_top_extended, OdsComposable.OdsTopAppBar.name, { ComponentTopAppBarExtended() }, true) - object ButtonsPrimary : Variant(R.string.component_buttons_highest_emphasis, "${OdsComposable.OdsButton.name} with ${OdsButtonStyle.Primary.name}") - object ButtonsDefault : Variant(R.string.component_buttons_high_emphasis, "${OdsComposable.OdsButton.name} with ${OdsButtonStyle.Default.name}") - object ButtonsOutlined : Variant(R.string.component_buttons_medium_emphasis, OdsComposable.OdsOutlinedButton.name) - object ButtonsText : Variant(R.string.component_buttons_low_emphasis, OdsComposable.OdsTextButton.name) - object ButtonsFunctional : Variant(R.string.component_buttons_functional, "${OdsComposable.OdsButton.name} with a functional style") + object ButtonsPrimary : Variant( + R.string.component_buttons_highest_emphasis, + "${OdsComposable.OdsButton.name} with ${OdsButtonStyle.Primary.name}", + { ComponentButtons(variant = ButtonsPrimary) }) - object ButtonsIcon : Variant(R.string.component_buttons_icon, OdsComposable.OdsIconButton.name) - object ButtonsIconToggle : Variant(R.string.component_buttons_icon_toggle, OdsComposable.OdsIconToggleButton.name) - object ButtonsIconToggleGroup : Variant(R.string.component_buttons_icon_toggle_group, OdsComposable.OdsIconToggleButtonsRow.name) + object ButtonsDefault : Variant( + R.string.component_buttons_high_emphasis, + "${OdsComposable.OdsButton.name} with ${OdsButtonStyle.Default.name}", + { ComponentButtons(variant = ButtonsDefault) }) - object CardVerticalImageFirst : Variant(R.string.component_card_vertical_image_first, OdsComposable.OdsVerticalImageFirstCard.name) - object CardVerticalHeaderFirst : Variant(R.string.component_card_vertical_header_first, OdsComposable.OdsVerticalHeaderFirstCard.name) - object CardSmall : Variant(R.string.component_card_small, OdsComposable.OdsSmallCard.name) - object CardHorizontal : Variant(R.string.component_card_horizontal, OdsComposable.OdsHorizontalCard.name) + object ButtonsOutlined : + Variant(R.string.component_buttons_medium_emphasis, OdsComposable.OdsOutlinedButton.name, { ComponentButtons(variant = ButtonsOutlined) }) - object ChipAction : Variant(R.string.component_chip_action, OdsComposable.OdsChip.name) - object ChipChoice : Variant(R.string.component_chip_choice, OdsComposable.OdsChip.name) - object ChipInput : Variant(R.string.component_chip_input, OdsComposable.OdsChip.name) - object ChipFilter : Variant(R.string.component_chip_filter, OdsComposable.OdsFilterChip.name) + object ButtonsText : Variant(R.string.component_buttons_low_emphasis, OdsComposable.OdsTextButton.name, { ComponentButtons(variant = ButtonsText) }) + object ButtonsFunctional : Variant( + R.string.component_buttons_functional, + "${OdsComposable.OdsButton.name} with a functional style", + { ComponentButtons(variant = ButtonsFunctional) }) - object DropdownMenu : Variant(R.string.component_menu_dropdown, OdsComposable.OdsDropdownMenu.name) - object ExposedDropdownMenu : Variant(R.string.component_menu_exposed_dropdown, OdsComposable.OdsExposedDropdownMenu.name) + object ButtonsIcon : Variant(R.string.component_buttons_icon, OdsComposable.OdsIconButton.name, { ComponentButtons(variant = ButtonsIcon) }) + object ButtonsIconToggle : + Variant(R.string.component_buttons_icon_toggle, OdsComposable.OdsIconToggleButton.name, { ComponentButtons(variant = ButtonsIconToggle) }) - object ProgressLinear : Variant(R.string.component_progress_linear, OdsComposable.OdsLinearProgressIndicator.name) - object ProgressCircular : Variant(R.string.component_progress_circular, OdsComposable.OdsCircularProgressIndicator.name) + object ButtonsIconToggleGroup : + Variant(R.string.component_buttons_icon_toggle_group, OdsComposable.OdsIconToggleButtonsRow.name, { ComponentButtons(variant = ButtonsIconToggle) }) - object TextField : Variant(R.string.component_text_field_text, OdsComposable.OdsTextField.name) - object TextFieldPassword : Variant(R.string.component_text_field_password, OdsComposable.OdsPasswordTextField.name) + object CardVerticalImageFirst : + Variant(R.string.component_card_vertical_image_first, OdsComposable.OdsVerticalImageFirstCard.name, { ComponentCard(variant = CardVerticalImageFirst) }) - object TabsFixed : Variant(R.string.component_tabs_fixed, OdsComposable.OdsTabRow.name) - object TabsScrollable : Variant(R.string.component_tabs_scrollable, OdsComposable.OdsScrollableTabRow.name) -} + object CardVerticalHeaderFirst : Variant( + R.string.component_card_vertical_header_first, + OdsComposable.OdsVerticalHeaderFirstCard.name, + { ComponentCard(variant = CardVerticalHeaderFirst) }) + + object CardSmall : Variant(R.string.component_card_small, OdsComposable.OdsSmallCard.name, { ComponentCard(variant = CardSmall) }) + object CardHorizontal : Variant(R.string.component_card_horizontal, OdsComposable.OdsHorizontalCard.name, { ComponentCard(variant = CardHorizontal) }) + + object ChipAction : Variant(R.string.component_chip_action, OdsComposable.OdsChip.name, { Chip(variant = ChipAction) }) + object ChipChoice : Variant(R.string.component_chip_choice, OdsComposable.OdsChip.name, { Chip(variant = ChipChoice) }) + object ChipInput : Variant(R.string.component_chip_input, OdsComposable.OdsChip.name, { Chip(variant = ChipInput) }) + object ChipFilter : Variant(R.string.component_chip_filter, OdsComposable.OdsFilterChip.name, { Chip(variant = ChipFilter) }) + + object DropdownMenu : Variant(R.string.component_menu_dropdown, OdsComposable.OdsDropdownMenu.name, { ComponentMenu(variant = DropdownMenu) }) + object ExposedDropdownMenu : + Variant(R.string.component_menu_exposed_dropdown, OdsComposable.OdsExposedDropdownMenu.name, { ComponentMenu(variant = ExposedDropdownMenu) }) + + object ProgressLinear : + Variant(R.string.component_progress_linear, OdsComposable.OdsLinearProgressIndicator.name, { ComponentProgress(variant = ProgressLinear) }) + + object ProgressCircular : + Variant(R.string.component_progress_circular, OdsComposable.OdsCircularProgressIndicator.name, { ComponentProgress(variant = ProgressCircular) }) + + object TextField : Variant(R.string.component_text_field_text, OdsComposable.OdsTextField.name, { ComponentTextField(variant = TextField) }) + object TextFieldPassword : + Variant(R.string.component_text_field_password, OdsComposable.OdsPasswordTextField.name, { ComponentTextField(variant = TextFieldPassword) }) -val variants = Variant::class.sealedSubclasses.mapNotNull { it.objectInstance } + object TabsFixed : Variant(R.string.component_tabs_fixed, OdsComposable.OdsTabRow.name, { ComponentTabs(variant = TabsFixed) }) + object TabsScrollable : Variant(R.string.component_tabs_scrollable, OdsComposable.OdsScrollableTabRow.name, { ComponentTabs(variant = TabsScrollable) }) +} \ No newline at end of file diff --git a/app/src/main/java/com/orange/ods/app/ui/components/ComponentDemoScreen.kt b/app/src/main/java/com/orange/ods/app/ui/components/ComponentDemoScreen.kt deleted file mode 100644 index 0c496527a..000000000 --- a/app/src/main/java/com/orange/ods/app/ui/components/ComponentDemoScreen.kt +++ /dev/null @@ -1,53 +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.app.ui.components - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import com.orange.ods.app.ui.LocalMainTopAppBarManager -import com.orange.ods.app.ui.components.banners.ComponentBanners -import com.orange.ods.app.ui.components.bottomnavigation.ComponentBottomNavigation -import com.orange.ods.app.ui.components.checkboxes.ComponentCheckboxes -import com.orange.ods.app.ui.components.dialogs.ComponentDialog -import com.orange.ods.app.ui.components.floatingactionbuttons.ComponentFloatingActionButton -import com.orange.ods.app.ui.components.imageitem.ComponentImageItem -import com.orange.ods.app.ui.components.lists.ComponentLists -import com.orange.ods.app.ui.components.navigationdrawers.ComponentModalDrawers -import com.orange.ods.app.ui.components.radiobuttons.ComponentRadioButtons -import com.orange.ods.app.ui.components.sheets.ComponentSheetsBottom -import com.orange.ods.app.ui.components.sliders.ComponentSliders -import com.orange.ods.app.ui.components.snackbars.ComponentSnackbars -import com.orange.ods.app.ui.components.switches.ComponentSwitches - -@Composable -fun ComponentDemoScreen(componentId: Long) { - val component = remember { components.firstOrNull { it.id == componentId } } - - component?.let { - LocalMainTopAppBarManager.current.updateTopAppBarTitle(component.titleRes) - when (component) { - Component.Banners -> ComponentBanners() - Component.BottomNavigation -> ComponentBottomNavigation() - Component.Checkboxes -> ComponentCheckboxes() - Component.Dialogs -> ComponentDialog() - Component.FloatingActionButtons -> ComponentFloatingActionButton() - Component.ImageItem -> ComponentImageItem() - Component.Lists -> ComponentLists() - Component.ModalDrawers -> ComponentModalDrawers() - Component.RadioButtons -> ComponentRadioButtons() - Component.SheetsBottom -> ComponentSheetsBottom() - Component.Sliders -> ComponentSliders() - Component.Snackbars -> ComponentSnackbars() - Component.Switches -> ComponentSwitches() - else -> {} - } - } -} diff --git a/app/src/main/java/com/orange/ods/app/ui/components/ComponentVariantScreen.kt b/app/src/main/java/com/orange/ods/app/ui/components/ComponentVariantScreen.kt deleted file mode 100644 index 337951e84..000000000 --- a/app/src/main/java/com/orange/ods/app/ui/components/ComponentVariantScreen.kt +++ /dev/null @@ -1,48 +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.app.ui.components - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import com.orange.ods.app.ui.LocalMainTopAppBarManager -import com.orange.ods.app.ui.components.appbars.top.ComponentTopAppBar -import com.orange.ods.app.ui.components.appbars.top.ComponentTopAppBarExtended -import com.orange.ods.app.ui.components.buttons.ComponentButtons -import com.orange.ods.app.ui.components.buttons.icons.ComponentButtonsIcons -import com.orange.ods.app.ui.components.cards.ComponentCard -import com.orange.ods.app.ui.components.chips.Chip -import com.orange.ods.app.ui.components.chips.ChipFilter -import com.orange.ods.app.ui.components.menus.ComponentMenu -import com.orange.ods.app.ui.components.progress.ComponentProgress -import com.orange.ods.app.ui.components.tabs.ComponentTabs -import com.orange.ods.app.ui.components.textfields.ComponentTextField - -@Composable -fun ComponentVariantScreen(variantId: Long) { - val component = remember { components.firstOrNull { component -> component.variants.any { variant -> variant.id == variantId } } } - val variant = remember { components.flatMap { it.variants }.firstOrNull { it.id == variantId } } - - variant?.let { - LocalMainTopAppBarManager.current.updateTopAppBarTitle(variant.titleRes) - when (component) { - Component.AppBarsTop -> if (variant == Variant.AppBarsTopExtended) ComponentTopAppBarExtended() else ComponentTopAppBar() - Component.Buttons -> ComponentButtons(variant = variant) - Component.ButtonsIcons -> ComponentButtonsIcons(variant = variant) - Component.Cards -> ComponentCard(variant = variant) - Component.Chips -> if (variant == Variant.ChipFilter) ChipFilter() else Chip(variant = variant) - Component.Menus -> ComponentMenu(variant = variant) - Component.Progress -> ComponentProgress(variant = variant) - Component.TextFields -> ComponentTextField(variant = variant) - Component.Tabs -> ComponentTabs(variant) - else -> {} - } - } -} diff --git a/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt b/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt index fe5acde32..5d98e1320 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt @@ -10,6 +10,7 @@ package com.orange.ods.app.ui.components +import androidx.compose.runtime.remember import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder import androidx.navigation.NavType @@ -39,19 +40,36 @@ fun NavGraphBuilder.addComponentsGraph(navigateToElement: (String, Long?, NavBac "${MainDestinations.ComponentVariantRoute}/{${MainDestinations.ComponentVariantIdKey}}", arguments = listOf(navArgument(MainDestinations.ComponentVariantIdKey) { type = NavType.LongType }) ) { from -> + LocalMainTopAppBarManager.current.reset() + val arguments = requireNotNull(from.arguments) val variantId = arguments.getLong(MainDestinations.ComponentVariantIdKey) - LocalMainTopAppBarManager.current.updateTopAppBar(MainTopAppBarState.DefaultConfiguration) - ComponentVariantScreen(variantId = variantId) + val variant = remember { components.flatMap { it.variants }.firstOrNull { it.id == variantId } } + + variant?.let { + with(LocalMainTopAppBarManager.current) { + updateTopAppBarTitle(variant.titleRes) + setExtended(variant.extendedTopAppBar) + } + + variant.screenContent() + } } composable( "${MainDestinations.ComponentDemoRoute}/{${MainDestinations.ComponentIdKey}}", arguments = listOf(navArgument(MainDestinations.ComponentIdKey) { type = NavType.LongType }) ) { from -> + LocalMainTopAppBarManager.current.updateTopAppBar(MainTopAppBarState.DefaultConfiguration) + val arguments = requireNotNull(from.arguments) val componentId = arguments.getLong(MainDestinations.ComponentIdKey) - LocalMainTopAppBarManager.current.updateTopAppBar(MainTopAppBarState.DefaultConfiguration) - ComponentDemoScreen(componentId = componentId) + val component = remember { components.firstOrNull { it.id == componentId } } + + component?.let { + LocalMainTopAppBarManager.current.updateTopAppBarTitle(component.titleRes) + component.screenContent?.let { it() } + } + } } \ No newline at end of file From d21123326200de753365bc7326b21330ab8f3155 Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Wed, 14 Jun 2023 09:59:13 +0200 Subject: [PATCH 07/47] Add a large top app bar in the lib and display it in the demo --- app/build.gradle.kts | 2 - .../com/orange/ods/app/ui/MainTopAppBar.kt | 90 ++++++++++++------- lib/build.gradle.kts | 5 +- .../appbar/top/OdsExtendedTopAppBar.kt | 49 ++++++++++ 4 files changed, 109 insertions(+), 37 deletions(-) create mode 100644 lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsExtendedTopAppBar.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 06a437848..9b72cca65 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -118,8 +118,6 @@ dependencies { implementation(Dependencies.material) implementation(Dependencies.composeUi) implementation(Dependencies.lifecycleViewModelKtx) - implementation(Dependencies.composeMaterial) - implementation(Dependencies.composeMaterial3) implementation(Dependencies.composeUiToolingPreview) implementation(Dependencies.lifecycleRuntimeKtx) implementation(Dependencies.activityCompose) 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 b6bc31a7c..5d7faf79f 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,11 +12,13 @@ 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 import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier @@ -27,12 +29,14 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.window.Dialog import androidx.lifecycle.viewmodel.compose.viewModel 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.OdsExtendedTopAppBar import com.orange.ods.compose.component.appbar.top.OdsTopAppBar import com.orange.ods.compose.component.appbar.top.OdsTopAppBarActionButton import com.orange.ods.compose.component.appbar.top.OdsTopAppBarOverflowMenuBox @@ -43,6 +47,7 @@ import com.orange.ods.compose.component.textfield.search.OdsSearchTextField import com.orange.ods.compose.text.OdsTextH6 import com.orange.ods.compose.theme.OdsTheme +@OptIn(ExperimentalMaterial3Api::class) @Composable fun MainTopAppBar( shouldShowUpNavigationIcon: Boolean, @@ -54,39 +59,40 @@ fun MainTopAppBar( val themeManager = LocalThemeManager.current var changeThemeDialogVisible by remember { mutableStateOf(false) } - OdsTopAppBar( - title = stringResource(id = topAppBarState.titleRes.value), - navigationIcon = if (shouldShowUpNavigationIcon && topAppBarState.isNavigationIconEnabled) { - { - Icon( - imageVector = Icons.Filled.ArrowBack, - contentDescription = stringResource(id = R.string.top_app_bar_back_icon_desc) - ) - } - } else null, - onNavigationIconClick = upPress, - actions = { - if (topAppBarState.titleRes.value == R.string.navigation_item_search) { - val focusRequester = remember { FocusRequester() } - OdsSearchTextField( - value = topAppBarState.searchedText.value, - onValueChange = { value -> - topAppBarState.searchedText.value = value - }, - placeholder = stringResource(id = R.string.search_text_field_hint), - modifier = Modifier - .fillMaxWidth() - .focusRequester(focusRequester) - ) - LaunchedEffect(Unit) { - focusRequester.requestFocus() - } - } else { - TopAppBarActions(topAppBarState, onSearchActionClick) { changeThemeDialogVisible = true } - } - }, - elevated = false // elevation is managed in [MainScreen] cause of tabs - ) + 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 actions: @Composable RowScope.() -> Unit = { + if (topAppBarState.titleRes.value == R.string.navigation_item_search) { + TopAppBarSearchAction(searchedText = topAppBarState.searchedText) + } else { + TopAppBarActions(topAppBarState, onSearchActionClick) { changeThemeDialogVisible = true } + } + } + + if (topAppBarState.isExtended) { + OdsExtendedTopAppBar( + title = title, + navigationIcon = navigationIcon ?: { }, + onNavigationIconClick = upPress, + actions = actions + ) + } else { + OdsTopAppBar( + title = title, + navigationIcon = navigationIcon, + onNavigationIconClick = upPress, + actions = actions, + elevated = false // elevation is managed in [MainScreen] cause of tabs + ) + } if (changeThemeDialogVisible) { ChangeThemeDialog( @@ -99,7 +105,25 @@ 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() + } } @Composable diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 7df646b8d..6293e0f1e 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -73,7 +73,9 @@ android { dependencies { api(project(":theme-contract")) api(project(":theme-orange")) - + api(Dependencies.composeMaterial) + api(Dependencies.composeMaterial3) + implementation(Dependencies.kotlinStdlibJdk8) implementation(Dependencies.kotlinReflect) compileOnly(project(":composable-processor")) @@ -81,7 +83,6 @@ dependencies { implementation(Dependencies.accompanistFlowLayout) implementation(Dependencies.appCompat) implementation(Dependencies.composeUi) - implementation(Dependencies.composeMaterial) implementation(Dependencies.composeUiTooling) implementation(Dependencies.composeUiToolingPreview) implementation(Dependencies.coreKtx) diff --git a/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsExtendedTopAppBar.kt b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsExtendedTopAppBar.kt new file mode 100644 index 000000000..4f21d2b68 --- /dev/null +++ b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsExtendedTopAppBar.kt @@ -0,0 +1,49 @@ +/* + * + * 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.RowScope +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.orange.ods.compose.component.OdsComposable +import com.orange.ods.compose.theme.OdsTheme + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +@OdsComposable +fun OdsExtendedTopAppBar( + title: String, + modifier: Modifier = Modifier, + navigationIcon: @Composable () -> Unit = {}, + onNavigationIconClick: () -> Unit = {}, + actions: @Composable RowScope.() -> Unit = {}, + scrollBehavior: TopAppBarScrollBehavior? = null +) { + val contentColor = OdsTheme.colors.component.topAppBar.barContent + LargeTopAppBar( + title = { Text(text = title) }, + modifier = modifier, + navigationIcon = { + IconButton(onClick = onNavigationIconClick) { + navigationIcon() + } + }, + actions = actions, + colors = TopAppBarDefaults.largeTopAppBarColors( + containerColor = OdsTheme.colors.component.topAppBar.barBackground, + navigationIconContentColor = contentColor, + titleContentColor = contentColor, + actionIconContentColor = contentColor + ), + scrollBehavior = scrollBehavior + ) +} \ No newline at end of file From 3bd9abc94d508acf33abd998e88cd0d1d0f3f61c Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Wed, 14 Jun 2023 10:59:31 +0200 Subject: [PATCH 08/47] Fix modal drawer and image item component demo --- .../main/java/com/orange/ods/app/ui/components/Component.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/orange/ods/app/ui/components/Component.kt b/app/src/main/java/com/orange/ods/app/ui/components/Component.kt index 3a3128e94..996029268 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/Component.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/Component.kt @@ -25,8 +25,10 @@ import com.orange.ods.app.ui.components.checkboxes.ComponentCheckboxes import com.orange.ods.app.ui.components.chips.Chip import com.orange.ods.app.ui.components.dialogs.ComponentDialog import com.orange.ods.app.ui.components.floatingactionbuttons.ComponentFloatingActionButton +import com.orange.ods.app.ui.components.imageitem.ComponentImageItem import com.orange.ods.app.ui.components.lists.ComponentLists import com.orange.ods.app.ui.components.menus.ComponentMenu +import com.orange.ods.app.ui.components.navigationdrawers.ComponentModalDrawers import com.orange.ods.app.ui.components.progress.ComponentProgress import com.orange.ods.app.ui.components.radiobuttons.ComponentRadioButtons import com.orange.ods.app.ui.components.sheets.ComponentSheetsBottom @@ -157,7 +159,8 @@ sealed class Component( R.drawable.il_image_item, null, R.string.component_image_item_description, - composableName = OdsComposable.OdsImageItem.name + composableName = OdsComposable.OdsImageItem.name, + screenContent = { ComponentImageItem() } ) object Lists : Component( @@ -183,6 +186,7 @@ sealed class Component( R.drawable.il_navigation_drawers, null, R.string.component_modal_drawers_description, + screenContent = { ComponentModalDrawers() } ) object Progress : Component( From f1e93b63b6e25558401046e6af7b68a008be4650 Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Mon, 26 Jun 2023 12:18:58 +0200 Subject: [PATCH 09/47] Rename extended top app bar into large top app bar and add its customization through bottom sheet --- .../com/orange/ods/app/ui/MainTopAppBar.kt | 6 ++-- .../orange/ods/app/ui/MainTopAppBarState.kt | 22 ++++++------ .../orange/ods/app/ui/components/Component.kt | 12 ++++--- .../app/ui/components/ComponentsNavGraph.kt | 2 +- .../appbars/top/ComponentTopAppBar.kt | 34 ++++++++++++++++--- .../appbars/top/ComponentTopAppBarExtended.kt | 18 ---------- .../top/TopAppBarCustomizationState.kt | 15 ++++++-- app/src/main/res/values/strings.xml | 10 ++++-- ...endedTopAppBar.kt => OdsLargeTopAppBar.kt} | 17 ++++++++-- 9 files changed, 88 insertions(+), 48 deletions(-) delete mode 100644 app/src/main/java/com/orange/ods/app/ui/components/appbars/top/ComponentTopAppBarExtended.kt rename lib/src/main/java/com/orange/ods/compose/component/appbar/top/{OdsExtendedTopAppBar.kt => OdsLargeTopAppBar.kt} (72%) 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 5d7faf79f..95dc9b65a 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 @@ -36,7 +36,7 @@ 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.OdsExtendedTopAppBar +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.OdsTopAppBarActionButton import com.orange.ods.compose.component.appbar.top.OdsTopAppBarOverflowMenuBox @@ -77,8 +77,8 @@ fun MainTopAppBar( } } - if (topAppBarState.isExtended) { - OdsExtendedTopAppBar( + if (topAppBarState.isLarge) { + OdsLargeTopAppBar( title = title, navigationIcon = navigationIcon ?: { }, onNavigationIconClick = upPress, diff --git a/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt b/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt index d235fa4e1..eb6803d26 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt +++ b/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt @@ -23,7 +23,9 @@ val LocalMainTopAppBarManager = staticCompositionLocalOf { interface MainTopAppBarManager { fun updateTopAppBar(topAppBarConfiguration: TopAppBarConfiguration) fun updateTopAppBarTitle(titleRes: Int) - fun setExtended(extended: Boolean) + + /** If set to true, a large top app bar will be displayed **/ + fun setLargeTopAppBar(value: Boolean) fun updateTopAppBarTabs(tabsConfiguration: TabsConfiguration) fun clearTopAppBarTabs() @@ -51,7 +53,7 @@ class MainTopAppBarState( val actions: MutableState>, var searchedText: MutableState, private val navigationIconEnabled: MutableState, - private var extended: MutableState, + private var large: MutableState, val tabsState: MainTabsState, ) : MainTopAppBarManager { @@ -67,18 +69,18 @@ class MainTopAppBarState( // TopAppBar state source of truth // ---------------------------------------------------------- - val isExtended: Boolean - get() = extended.value + val isLarge: Boolean + get() = large.value val isNavigationIconEnabled: Boolean get() = navigationIconEnabled.value - override fun setExtended(extended: Boolean) { - this.extended.value = extended + override fun setLargeTopAppBar(value: Boolean) { + this.large.value = value } override fun updateTopAppBar(topAppBarConfiguration: TopAppBarConfiguration) { - extended.value = topAppBarConfiguration.isExtended + large.value = topAppBarConfiguration.isExtended navigationIconEnabled.value = topAppBarConfiguration.isNavigationIconEnabled actions.value = topAppBarConfiguration.actions } @@ -127,11 +129,11 @@ data class TopAppBarConfiguration constructor( class Builder { - private var isExtended = false + private var isLarge = false private var isNavigationIconEnabled = true private var actions = mutableListOf() - fun extended(value: Boolean) = apply { isExtended = value } + fun large(value: Boolean) = apply { isLarge = value } fun navigationIconEnabled(enabled: Boolean) = apply { isNavigationIconEnabled = enabled } @@ -147,7 +149,7 @@ data class TopAppBarConfiguration constructor( actions { add(action) } } - fun build() = TopAppBarConfiguration(isExtended, isNavigationIconEnabled, actions) + fun build() = TopAppBarConfiguration(isLarge, isNavigationIconEnabled, actions) } fun newBuilder() = Builder().apply { diff --git a/app/src/main/java/com/orange/ods/app/ui/components/Component.kt b/app/src/main/java/com/orange/ods/app/ui/components/Component.kt index 996029268..fdc354220 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/Component.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/Component.kt @@ -16,7 +16,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import com.orange.ods.app.R import com.orange.ods.app.ui.components.appbars.top.ComponentTopAppBar -import com.orange.ods.app.ui.components.appbars.top.ComponentTopAppBarExtended import com.orange.ods.app.ui.components.banners.ComponentBanners import com.orange.ods.app.ui.components.bottomnavigation.ComponentBottomNavigation import com.orange.ods.app.ui.components.buttons.ComponentButtons @@ -61,7 +60,7 @@ sealed class Component( R.drawable.il_app_bars_top, R.drawable.il_app_bars_top_small, R.string.component_app_bars_top_description, - variants = listOf(Variant.AppBarsTopRegular, Variant.AppBarsTopExtended), + variants = listOf(Variant.AppBarsTopRegular, Variant.AppBarsTopLarge), imageAlignment = Alignment.TopCenter ) @@ -267,12 +266,15 @@ sealed class Variant( @StringRes val titleRes: Int, val composableName: String, val screenContent: @Composable () -> Unit, - val extendedTopAppBar: Boolean = false + val largeTopAppBar: Boolean = false ) { val id: Long = Variant::class.sealedSubclasses.indexOf(this::class).toLong() - object AppBarsTopRegular : Variant(R.string.component_app_bars_top_regular, OdsComposable.OdsTopAppBar.name, { ComponentTopAppBar() }) - object AppBarsTopExtended : Variant(R.string.component_app_bars_top_extended, OdsComposable.OdsTopAppBar.name, { ComponentTopAppBarExtended() }, true) + object AppBarsTopRegular : + Variant(R.string.component_app_bars_top_regular, OdsComposable.OdsTopAppBar.name, { ComponentTopAppBar(variant = AppBarsTopRegular) }) + + object AppBarsTopLarge : + Variant(R.string.component_app_bars_top_large, OdsComposable.OdsLargeTopAppBar.name, { ComponentTopAppBar(variant = AppBarsTopLarge) }, true) object ButtonsPrimary : Variant( R.string.component_buttons_highest_emphasis, diff --git a/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt b/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt index 5d98e1320..f73c8b014 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt @@ -49,7 +49,7 @@ fun NavGraphBuilder.addComponentsGraph(navigateToElement: (String, Long?, NavBac variant?.let { with(LocalMainTopAppBarManager.current) { updateTopAppBarTitle(variant.titleRes) - setExtended(variant.extendedTopAppBar) + setLargeTopAppBar(variant.largeTopAppBar) } variant.screenContent() 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 df5fc46fc..a39e6c088 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 @@ -25,20 +25,25 @@ import com.orange.ods.app.R import com.orange.ods.app.ui.LocalMainTopAppBarManager import com.orange.ods.app.ui.MainTopAppBarState import com.orange.ods.app.ui.TopAppBarConfiguration +import com.orange.ods.app.ui.components.Variant import com.orange.ods.app.ui.components.utilities.ComponentCountRow import com.orange.ods.app.ui.components.utilities.ComponentCustomizationBottomSheetScaffold import com.orange.ods.app.ui.utilities.NavigationItem import com.orange.ods.app.ui.utilities.composable.CodeImplementationColumn import com.orange.ods.app.ui.utilities.composable.FunctionCallCode +import com.orange.ods.app.ui.utilities.composable.Subtitle import com.orange.ods.compose.OdsComposable +import com.orange.ods.compose.component.chip.OdsChoiceChip +import com.orange.ods.compose.component.chip.OdsChoiceChipsFlowRow import com.orange.ods.compose.component.list.OdsListItem import com.orange.ods.compose.component.list.OdsSwitchTrailing import kotlin.math.max @OptIn(ExperimentalMaterialApi::class) @Composable -fun ComponentTopAppBar() { +fun ComponentTopAppBar(variant: Variant) { val customizationState = rememberTopAppBarCustomizationState() + val isLargeVariant = variant == Variant.AppBarsTopLarge with(customizationState) { val customActionCount = max(0, actionCount.value - MainTopAppBarState.DefaultConfiguration.actions.size) @@ -46,6 +51,7 @@ fun ComponentTopAppBar() { .take(customActionCount) .map { TopAppBarConfiguration.Action.Custom(stringResource(id = it.textResId), it.iconResId) } val topAppBarConfiguration = TopAppBarConfiguration.Builder() + .large(isLargeVariant) .navigationIconEnabled(isNavigationIconEnabled) .actions { addAll(MainTopAppBarState.DefaultConfiguration.actions.take(actionCount.value)) @@ -53,7 +59,11 @@ fun ComponentTopAppBar() { if (isOverflowMenuEnabled) add(TopAppBarConfiguration.Action.OverflowMenu) } .build() - LocalMainTopAppBarManager.current.updateTopAppBar(topAppBarConfiguration) + + with(LocalMainTopAppBarManager.current) { + updateTopAppBar(topAppBarConfiguration) + updateTopAppBarTitle(titleLength.value.titleResId) + } ComponentCustomizationBottomSheetScaffold( bottomSheetScaffoldState = rememberBottomSheetScaffoldState(), @@ -80,14 +90,30 @@ fun ComponentTopAppBar() { enabled = isOverflowMenuSwitchEnabled ) ) - + if (isLargeVariant) { + Subtitle(textRes = R.string.component_element_title, horizontalPadding = true) + OdsChoiceChipsFlowRow( + selectedChip = titleLength, + modifier = Modifier + .padding(horizontal = dimensionResource(id = R.dimen.spacing_m)) + .padding(bottom = dimensionResource(id = R.dimen.spacing_s)), + outlinedChips = true + ) { + OdsChoiceChip(textRes = R.string.component_app_bars_top_large_title_one_line, value = TopAppBarCustomizationState.TitleLength.OneLine) + OdsChoiceChip(textRes = R.string.component_app_bars_top_large_title_two_lines, value = TopAppBarCustomizationState.TitleLength.TwoLines) + OdsChoiceChip( + textRes = R.string.component_app_bars_top_large_title_truncated, + value = TopAppBarCustomizationState.TitleLength.Truncated + ) + } + } }) { val context = LocalContext.current Column(modifier = Modifier.verticalScroll(rememberScrollState())) { CodeImplementationColumn(modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.screen_horizontal_margin))) { FunctionCallCode( - name = OdsComposable.OdsTopAppBar.name, + name = if (isLargeVariant) OdsComposable.OdsLargeTopAppBar.name else OdsComposable.OdsTopAppBar.name, exhaustiveParameters = false, parameters = { title(context.getString(R.string.component_app_bars_top_regular)) diff --git a/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/ComponentTopAppBarExtended.kt b/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/ComponentTopAppBarExtended.kt deleted file mode 100644 index 05a1031a9..000000000 --- a/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/ComponentTopAppBarExtended.kt +++ /dev/null @@ -1,18 +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.app.ui.components.appbars.top - -import androidx.compose.runtime.Composable - -@Composable -fun ComponentTopAppBarExtended() { - -} \ No newline at end of file diff --git a/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/TopAppBarCustomizationState.kt b/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/TopAppBarCustomizationState.kt index bc3ef5a54..cd65498e5 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/TopAppBarCustomizationState.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/TopAppBarCustomizationState.kt @@ -15,6 +15,7 @@ import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable +import com.orange.ods.app.R import com.orange.ods.app.ui.MainTopAppBarState import com.orange.ods.app.ui.TopAppBarConfiguration @@ -29,16 +30,24 @@ fun rememberTopAppBarCustomizationState( ) ) }, + titleLength: MutableState = rememberSaveable { mutableStateOf(TopAppBarCustomizationState.TitleLength.OneLine) }, ) = - remember(navigationIconEnabled, actionCount, overflowMenuEnabled) { - TopAppBarCustomizationState(navigationIconEnabled, actionCount, overflowMenuEnabled) + remember(navigationIconEnabled, actionCount, overflowMenuEnabled, titleLength) { + TopAppBarCustomizationState(navigationIconEnabled, actionCount, overflowMenuEnabled, titleLength) } class TopAppBarCustomizationState( val navigationIconEnabled: MutableState, val actionCount: MutableState, - val overflowMenuEnabled: MutableState + val overflowMenuEnabled: MutableState, + val titleLength: MutableState ) { + enum class TitleLength(val titleResId: Int) { + OneLine(R.string.component_app_bars_top_large_title_one_line_value), + TwoLines(R.string.component_app_bars_top_large_title_two_lines_value), + Truncated(R.string.component_app_bars_top_large_title_truncated_value) + } + private val maxActionCount = 3 val minActionCount = 0 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b3f4fcad1..4e004c109 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -99,13 +99,19 @@ App bars: top The app bar is a key component and organizes titling, navigation and action buttons. Regular - Extended + Large Navigation icon Overflow menu Actions count Remove action Add action - Ice Cream + One line + Two lines + Truncated + Large: One line title + Large: Two lines title allowed in top app bar + Large: Title will be truncated if it is too long to fit in the top app bar + Bottom navigation diff --git a/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsExtendedTopAppBar.kt b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsLargeTopAppBar.kt similarity index 72% rename from lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsExtendedTopAppBar.kt rename to lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsLargeTopAppBar.kt index 4f21d2b68..5a00365ab 100644 --- a/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsExtendedTopAppBar.kt +++ b/lib/src/main/java/com/orange/ods/compose/component/appbar/top/OdsLargeTopAppBar.kt @@ -11,16 +11,21 @@ package com.orange.ods.compose.component.appbar.top import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.padding import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import com.orange.ods.R import com.orange.ods.compose.component.OdsComposable import com.orange.ods.compose.theme.OdsTheme @OptIn(ExperimentalMaterial3Api::class) @Composable @OdsComposable -fun OdsExtendedTopAppBar( +fun OdsLargeTopAppBar( title: String, modifier: Modifier = Modifier, navigationIcon: @Composable () -> Unit = {}, @@ -30,7 +35,15 @@ fun OdsExtendedTopAppBar( ) { val contentColor = OdsTheme.colors.component.topAppBar.barContent LargeTopAppBar( - title = { Text(text = title) }, + title = { + Text( + modifier = Modifier.padding(start = 56.dp, end = dimensionResource(id = R.dimen.spacing_m)), + text = title, + style = OdsTheme.typography.h6, + overflow = TextOverflow.Ellipsis, + maxLines = 2 + ) + }, modifier = modifier, navigationIcon = { IconButton(onClick = onNavigationIconClick) { From d32dfcbe9cfe1c7b0c6e88271e6d1297a3ccf0eb Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Mon, 3 Jul 2023 17:21:41 +0200 Subject: [PATCH 10/47] Add scroll behavior to large top app bar --- .idea/codeStyles/Project.xml | 3 +- .../java/com/orange/ods/app/ui/MainScreen.kt | 68 ++++++++++++------- .../com/orange/ods/app/ui/MainTopAppBar.kt | 22 +++--- .../orange/ods/app/ui/MainTopAppBarState.kt | 6 +- .../ui/components/ComponentDetailScreen.kt | 57 +++++++--------- .../app/ui/components/ComponentsNavGraph.kt | 47 +++++++++---- .../appbars/top/ComponentTopAppBar.kt | 10 ++- .../component/appbar/top/OdsLargeTopAppBar.kt | 44 +++++++++++- 8 files changed, 175 insertions(+), 82 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index db9926f35..a92b7a9a0 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,7 +1,8 @@ - diff --git a/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt b/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt index 390064502..5b06154da 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt +++ b/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt @@ -19,9 +19,14 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.AppBarDefaults import androidx.compose.material.Scaffold import androidx.compose.material.Surface +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior +import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp @@ -50,6 +55,7 @@ import com.orange.ods.utilities.extension.orElse import com.orange.ods.xml.theme.OdsXml import com.orange.ods.xml.utilities.extension.xml +@OptIn(ExperimentalMaterial3Api::class) @Composable fun MainScreen(themeConfigurations: Set, mainViewModel: MainViewModel = viewModel()) { val isSystemInDarkTheme = isSystemInDarkTheme() @@ -90,7 +96,23 @@ fun MainScreen(themeConfigurations: Set, mainView themeConfiguration = mainState.themeState.currentThemeConfiguration, darkThemeEnabled = configuration.isDarkModeEnabled ) { + val topBarScrollBehavior: TopAppBarScrollBehavior? + val modifier: Modifier + if (mainState.topAppBarState.isLarge) { + topBarScrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) + val nestedScrollConnection = remember { topBarScrollBehavior.nestedScrollConnection } + modifier = Modifier.nestedScroll(nestedScrollConnection) + } else { + topBarScrollBehavior = null + modifier = Modifier + } + + val showTabs by remember { + derivedStateOf { mainState.topAppBarState.tabsState.hasTabs } + } + Scaffold( + modifier = modifier, backgroundColor = OdsTheme.colors.background, topBar = { Surface(elevation = if (isSystemInDarkTheme()) 0.dp else AppBarDefaults.TopAppBarElevation) { @@ -98,14 +120,16 @@ fun MainScreen(themeConfigurations: Set, mainView SystemBarsColorSideEffect() MainTopAppBar( shouldShowUpNavigationIcon = !mainState.shouldShowBottomBar, - topAppBarState = mainState.topAppBarState, + topAppBarStateProvider = { mainState.topAppBarState }, upPress = mainState::upPress, onSearchActionClick = { mainState.navController.navigate(MainDestinations.SearchRoute) - } + }, + scrollBehavior = topBarScrollBehavior ) - // Display tabs in the top bar if needed - MainTabs(mainTabsState = mainState.topAppBarState.tabsState) + if (showTabs) { + MainTabs(mainTabsState = mainState.topAppBarState.tabsState) + } } } }, @@ -156,25 +180,23 @@ private fun SystemBarsColorSideEffect() { private fun MainTabs(mainTabsState: MainTabsState) { with(mainTabsState) { pagerState?.let { pagerState -> - if (hasTabs) { - // Do not use tabs directly because this is a SnapshotStateList - // Thus its value can be modified and can lead to crashes if it becomes empty - val tabs = tabs.toList() - if (scrollableTabs.value) { - ScrollableTabRow( - tabs = tabs, - pagerState = pagerState, - tabIconType = tabIconType.value, - tabTextEnabled = tabTextEnabled.value - ) - } else { - FixedTabRow( - tabs = tabs, - pagerState = pagerState, - tabIconType = tabIconType.value, - tabTextEnabled = tabTextEnabled.value - ) - } + // Do not use tabs directly because this is a SnapshotStateList + // Thus its value can be modified and can lead to crashes if it becomes empty + val tabs = tabs.toList() + if (scrollableTabs.value) { + ScrollableTabRow( + tabs = tabs, + pagerState = pagerState, + tabIconType = tabIconType.value, + tabTextEnabled = tabTextEnabled.value + ) + } else { + FixedTabRow( + tabs = tabs, + pagerState = pagerState, + tabIconType = tabIconType.value, + tabTextEnabled = tabTextEnabled.value + ) } } } 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 95dc9b65a..a4e435daa 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 @@ -19,6 +19,7 @@ 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.TopAppBarScrollBehavior import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier @@ -51,16 +52,18 @@ import com.orange.ods.compose.theme.OdsTheme @Composable fun MainTopAppBar( shouldShowUpNavigationIcon: Boolean, - topAppBarState: MainTopAppBarState, + topAppBarStateProvider: () -> MainTopAppBarState, upPress: () -> Unit, onSearchActionClick: () -> Unit, - mainViewModel: MainViewModel = viewModel() + mainViewModel: MainViewModel = viewModel(), + scrollBehavior: TopAppBarScrollBehavior? ) { val themeManager = LocalThemeManager.current var changeThemeDialogVisible by remember { mutableStateOf(false) } + val showSearchAction = topAppBarStateProvider().titleRes.value == R.string.navigation_item_search - val title = stringResource(id = topAppBarState.titleRes.value) - val navigationIcon: (@Composable () -> Unit)? = if (shouldShowUpNavigationIcon && topAppBarState.isNavigationIconEnabled) { + val title = stringResource(id = topAppBarStateProvider().titleRes.value) + val navigationIcon: (@Composable () -> Unit)? = if (shouldShowUpNavigationIcon && topAppBarStateProvider().isNavigationIconEnabled) { { Icon( imageVector = Icons.Filled.ArrowBack, @@ -70,19 +73,20 @@ fun MainTopAppBar( } else null val actions: @Composable RowScope.() -> Unit = { - if (topAppBarState.titleRes.value == R.string.navigation_item_search) { - TopAppBarSearchAction(searchedText = topAppBarState.searchedText) + if (showSearchAction) { + TopAppBarSearchAction(searchedText = topAppBarStateProvider().searchedText) } else { - TopAppBarActions(topAppBarState, onSearchActionClick) { changeThemeDialogVisible = true } + TopAppBarActions(topAppBarStateProvider(), onSearchActionClick) { changeThemeDialogVisible = true } } } - if (topAppBarState.isLarge) { + if (topAppBarStateProvider().isLarge) { OdsLargeTopAppBar( title = title, navigationIcon = navigationIcon ?: { }, onNavigationIconClick = upPress, - actions = actions + actions = actions, + scrollBehavior = scrollBehavior ) } else { OdsTopAppBar( diff --git a/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt b/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt index eb6803d26..21bf5a6a6 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt +++ b/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt @@ -12,8 +12,12 @@ package com.orange.ods.app.ui import android.os.Parcelable import androidx.annotation.DrawableRes -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.text.input.TextFieldValue import com.orange.ods.app.R import kotlinx.parcelize.Parcelize diff --git a/app/src/main/java/com/orange/ods/app/ui/components/ComponentDetailScreen.kt b/app/src/main/java/com/orange/ods/app/ui/components/ComponentDetailScreen.kt index 6dd4e71ac..e3d597023 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/ComponentDetailScreen.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/ComponentDetailScreen.kt @@ -16,60 +16,53 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import com.orange.ods.app.R +import com.orange.ods.app.ui.utilities.DrawableManager +import com.orange.ods.app.ui.utilities.composable.DetailScreenHeader import com.orange.ods.compose.component.list.OdsListItem import com.orange.ods.compose.component.list.OdsListItemIcon import com.orange.ods.compose.component.list.OdsListItemIconType import com.orange.ods.compose.component.list.iconType -import com.orange.ods.app.R -import com.orange.ods.app.ui.LocalMainTopAppBarManager -import com.orange.ods.app.ui.utilities.DrawableManager -import com.orange.ods.app.ui.utilities.composable.DetailScreenHeader @Composable fun ComponentDetailScreen( - componentId: Long, + component: Component, onVariantClick: (Long) -> Unit, onDemoClick: () -> Unit ) { val context = LocalContext.current - val component = remember { components.firstOrNull { component -> component.id == componentId } } - - component?.let { - LocalMainTopAppBarManager.current.updateTopAppBarTitle(component.titleRes) + Column( + modifier = Modifier + .verticalScroll(rememberScrollState()) + .padding(bottom = dimensionResource(id = R.dimen.screen_vertical_margin)) + ) { + DetailScreenHeader( + imageRes = DrawableManager.getDrawableResIdForCurrentTheme(resId = component.imageRes), + imageAlignment = component.imageAlignment, + descriptionRes = component.descriptionRes + ) Column( - modifier = Modifier - .verticalScroll(rememberScrollState()) - .padding(bottom = dimensionResource(id = R.dimen.screen_vertical_margin)) + modifier = Modifier.padding(top = dimensionResource(id = R.dimen.spacing_m)) ) { - DetailScreenHeader( - imageRes = DrawableManager.getDrawableResIdForCurrentTheme(resId = component.imageRes), - imageAlignment = component.imageAlignment, - descriptionRes = component.descriptionRes - ) - Column( - modifier = Modifier.padding(top = dimensionResource(id = R.dimen.spacing_m)) - ) { - if (component.variants.isEmpty()) { - ComponentDetailLinkItem( - label = context.getString(R.string.component_demo, context.getString(component.titleRes)), - composableName = component.composableName - ) { onDemoClick() } - } else { - component.variants.forEach { variant -> - ComponentDetailLinkItem(label = stringResource(id = variant.titleRes), composableName = variant.composableName) { - onVariantClick(variant.id) - } + if (component.variants.isEmpty()) { + ComponentDetailLinkItem( + label = context.getString(R.string.component_demo, context.getString(component.titleRes)), + composableName = component.composableName + ) { onDemoClick() } + } else { + component.variants.forEach { variant -> + ComponentDetailLinkItem(label = stringResource(id = variant.titleRes), composableName = variant.composableName) { + onVariantClick(variant.id) } } - } + } } } diff --git a/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt b/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt index f73c8b014..a49c4b535 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt @@ -10,7 +10,11 @@ package com.orange.ods.app.ui.components +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder import androidx.navigation.NavType @@ -21,37 +25,56 @@ import com.orange.ods.app.ui.MainDestinations import com.orange.ods.app.ui.MainTopAppBarState fun NavGraphBuilder.addComponentsGraph(navigateToElement: (String, Long?, NavBackStackEntry) -> Unit) { + composable( "${MainDestinations.ComponentDetailRoute}/{${MainDestinations.ComponentIdKey}}", arguments = listOf(navArgument(MainDestinations.ComponentIdKey) { type = NavType.LongType }) ) { from -> + // Lot of recompositions with this line LocalMainTopAppBarManager.current.reset() val arguments = requireNotNull(from.arguments) - val componentId = arguments.getLong(MainDestinations.ComponentIdKey) - ComponentDetailScreen( - componentId = componentId, - onVariantClick = { variantId -> navigateToElement(MainDestinations.ComponentVariantRoute, variantId, from) }, - onDemoClick = { navigateToElement(MainDestinations.ComponentDemoRoute, componentId, from) } - ) + var currentComponentId: Long by remember { mutableStateOf(-1) } + val routeComponentId = arguments.getLong(MainDestinations.ComponentIdKey) + val shouldUpdateTitle by remember { + derivedStateOf { currentComponentId != routeComponentId } + } + + val component = remember(routeComponentId) { components.firstOrNull { component -> component.id == routeComponentId } } + component?.let { + if (shouldUpdateTitle) { + LocalMainTopAppBarManager.current.updateTopAppBarTitle(component.titleRes) + currentComponentId = routeComponentId + } + ComponentDetailScreen( + component = component, + onVariantClick = { variantId -> navigateToElement(MainDestinations.ComponentVariantRoute, variantId, from) }, + onDemoClick = { navigateToElement(MainDestinations.ComponentDemoRoute, routeComponentId, from) } + ) + } } composable( "${MainDestinations.ComponentVariantRoute}/{${MainDestinations.ComponentVariantIdKey}}", arguments = listOf(navArgument(MainDestinations.ComponentVariantIdKey) { type = NavType.LongType }) ) { from -> + // Lot of recompositions with this line LocalMainTopAppBarManager.current.reset() val arguments = requireNotNull(from.arguments) - val variantId = arguments.getLong(MainDestinations.ComponentVariantIdKey) - val variant = remember { components.flatMap { it.variants }.firstOrNull { it.id == variantId } } + var currentVariantId: Long by remember { mutableStateOf(-1) } + val routeVariantId = arguments.getLong(MainDestinations.ComponentVariantIdKey) + val variant = remember(routeVariantId) { components.flatMap { it.variants }.firstOrNull { it.id == routeVariantId } } + val shouldUpdateTitle by remember { + derivedStateOf { currentVariantId != routeVariantId } + } variant?.let { - with(LocalMainTopAppBarManager.current) { - updateTopAppBarTitle(variant.titleRes) - setLargeTopAppBar(variant.largeTopAppBar) + if (shouldUpdateTitle) { + LocalMainTopAppBarManager.current.updateTopAppBarTitle(variant.titleRes) + currentVariantId = routeVariantId } - + if (variant.largeTopAppBar) LocalMainTopAppBarManager.current.setLargeTopAppBar(true) variant.screenContent() } } 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 a39e6c088..6526d8be0 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 @@ -37,6 +37,10 @@ import com.orange.ods.compose.component.chip.OdsChoiceChip import com.orange.ods.compose.component.chip.OdsChoiceChipsFlowRow import com.orange.ods.compose.component.list.OdsListItem import com.orange.ods.compose.component.list.OdsSwitchTrailing +import com.orange.ods.compose.text.OdsTextH1 +import com.orange.ods.compose.text.OdsTextH2 +import com.orange.ods.compose.text.OdsTextH3 +import com.orange.ods.compose.text.OdsTextH4 import kotlin.math.max @OptIn(ExperimentalMaterialApi::class) @@ -62,7 +66,7 @@ fun ComponentTopAppBar(variant: Variant) { with(LocalMainTopAppBarManager.current) { updateTopAppBar(topAppBarConfiguration) - updateTopAppBarTitle(titleLength.value.titleResId) + if (isLargeVariant) updateTopAppBarTitle(titleLength.value.titleResId) } ComponentCustomizationBottomSheetScaffold( @@ -111,6 +115,10 @@ fun ComponentTopAppBar(variant: Variant) { val context = LocalContext.current Column(modifier = Modifier.verticalScroll(rememberScrollState())) { + OdsTextH1(text = "test pour scroll") + OdsTextH2(text = "test pour scroll") + OdsTextH3(text = "test pour scroll") + OdsTextH4(text = "test pour scroll") CodeImplementationColumn(modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.screen_horizontal_margin))) { FunctionCallCode( name = if (isLargeVariant) OdsComposable.OdsLargeTopAppBar.name else OdsComposable.OdsTopAppBar.name, 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 5a00365ab..20a9c7c25 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 @@ -12,9 +12,18 @@ package com.orange.ods.compose.component.appbar.top import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.padding -import androidx.compose.material3.* +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 +import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -34,14 +43,43 @@ fun OdsLargeTopAppBar( scrollBehavior: TopAppBarScrollBehavior? = null ) { val contentColor = OdsTheme.colors.component.topAppBar.barContent + val stateChangeFraction = 0.7 + + val titleStartPadding by remember { + derivedStateOf { + if (scrollBehavior != null && scrollBehavior.state.collapsedFraction >= 0.85) 8.dp else 48.dp + } + } + + val titleAlpha by remember { + derivedStateOf { + if (scrollBehavior != null) { + when (scrollBehavior.state.collapsedFraction) { + in 0.0..stateChangeFraction -> 1 - (scrollBehavior.state.collapsedFraction * 1.5) + in (stateChangeFraction + 0.15)..1.0 -> 0 + scrollBehavior.state.collapsedFraction + else -> 0 + }.toFloat() + } else { + 1.0f + } + } + } + val titleMaxLines by remember { + derivedStateOf { + if (scrollBehavior != null && scrollBehavior.state.collapsedFraction >= stateChangeFraction) 1 else 2 + } + } + LargeTopAppBar( title = { Text( - modifier = Modifier.padding(start = 56.dp, end = dimensionResource(id = R.dimen.spacing_m)), + modifier = Modifier + .padding(start = titleStartPadding, end = dimensionResource(id = R.dimen.spacing_m)) + .alpha(titleAlpha), text = title, style = OdsTheme.typography.h6, overflow = TextOverflow.Ellipsis, - maxLines = 2 + maxLines = titleMaxLines, ) }, modifier = modifier, From 2d042320db6fd8ba4f2526d25a827e4ca53806e2 Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Mon, 3 Jul 2023 18:13:17 +0200 Subject: [PATCH 11/47] Optimize recompositions --- .../main/java/com/orange/ods/app/ui/MainScreen.kt | 14 +++++++++----- .../com/orange/ods/app/ui/MainTopAppBarState.kt | 13 +++++++------ .../ods/app/ui/components/ComponentsNavGraph.kt | 4 +--- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt b/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt index 5b06154da..73425f87e 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt +++ b/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt @@ -98,7 +98,15 @@ fun MainScreen(themeConfigurations: Set, mainView ) { val topBarScrollBehavior: TopAppBarScrollBehavior? val modifier: Modifier - if (mainState.topAppBarState.isLarge) { + + val showTabs by remember { + derivedStateOf { mainState.topAppBarState.tabsState.hasTabs } + } + val displayLargeTopAppBar by remember { + derivedStateOf { mainState.topAppBarState.isLarge } + } + + if (displayLargeTopAppBar) { topBarScrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) val nestedScrollConnection = remember { topBarScrollBehavior.nestedScrollConnection } modifier = Modifier.nestedScroll(nestedScrollConnection) @@ -107,10 +115,6 @@ fun MainScreen(themeConfigurations: Set, mainView modifier = Modifier } - val showTabs by remember { - derivedStateOf { mainState.topAppBarState.tabsState.hasTabs } - } - Scaffold( modifier = modifier, backgroundColor = OdsTheme.colors.background, diff --git a/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt b/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt index 21bf5a6a6..3e6b56362 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt +++ b/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt @@ -44,11 +44,11 @@ fun rememberMainTopAppBarState( actions: MutableState> = rememberSaveable { mutableStateOf(MainTopAppBarState.DefaultConfiguration.actions) }, navigationIconEnabled: MutableState = rememberSaveable { mutableStateOf(MainTopAppBarState.DefaultConfiguration.isNavigationIconEnabled) }, searchedText: MutableState = remember { mutableStateOf(TextFieldValue("")) }, - extended: MutableState = rememberSaveable { mutableStateOf(false) }, + large: MutableState = rememberSaveable { mutableStateOf(false) }, tabsState: MainTabsState = rememberMainTabsState(), ) = - remember(titleRes, actions, searchedText, navigationIconEnabled, extended, tabsState) { - MainTopAppBarState(titleRes, actions, searchedText, navigationIconEnabled, extended, tabsState) + remember(titleRes, actions, searchedText, navigationIconEnabled, large, tabsState) { + MainTopAppBarState(titleRes, actions, searchedText, navigationIconEnabled, large, tabsState) } @@ -63,7 +63,7 @@ class MainTopAppBarState( companion object { val DefaultConfiguration = TopAppBarConfiguration( - isExtended = false, + isLarge = false, isNavigationIconEnabled = true, actions = listOf(TopAppBarConfiguration.Action.Theme, TopAppBarConfiguration.Action.Mode) ) @@ -84,7 +84,7 @@ class MainTopAppBarState( } override fun updateTopAppBar(topAppBarConfiguration: TopAppBarConfiguration) { - large.value = topAppBarConfiguration.isExtended + large.value = topAppBarConfiguration.isLarge navigationIconEnabled.value = topAppBarConfiguration.isNavigationIconEnabled actions.value = topAppBarConfiguration.actions } @@ -108,7 +108,7 @@ class MainTopAppBarState( } data class TopAppBarConfiguration constructor( - val isExtended: Boolean, + val isLarge: Boolean, val isNavigationIconEnabled: Boolean, val actions: List ) { @@ -158,6 +158,7 @@ data class TopAppBarConfiguration constructor( fun newBuilder() = Builder().apply { navigationIconEnabled(isNavigationIconEnabled) + large(isLarge) actions { clear() addAll(actions) diff --git a/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt b/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt index a49c4b535..033b19735 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt @@ -30,7 +30,6 @@ fun NavGraphBuilder.addComponentsGraph(navigateToElement: (String, Long?, NavBac "${MainDestinations.ComponentDetailRoute}/{${MainDestinations.ComponentIdKey}}", arguments = listOf(navArgument(MainDestinations.ComponentIdKey) { type = NavType.LongType }) ) { from -> - // Lot of recompositions with this line LocalMainTopAppBarManager.current.reset() val arguments = requireNotNull(from.arguments) @@ -58,7 +57,6 @@ fun NavGraphBuilder.addComponentsGraph(navigateToElement: (String, Long?, NavBac "${MainDestinations.ComponentVariantRoute}/{${MainDestinations.ComponentVariantIdKey}}", arguments = listOf(navArgument(MainDestinations.ComponentVariantIdKey) { type = NavType.LongType }) ) { from -> - // Lot of recompositions with this line LocalMainTopAppBarManager.current.reset() val arguments = requireNotNull(from.arguments) @@ -74,7 +72,7 @@ fun NavGraphBuilder.addComponentsGraph(navigateToElement: (String, Long?, NavBac LocalMainTopAppBarManager.current.updateTopAppBarTitle(variant.titleRes) currentVariantId = routeVariantId } - if (variant.largeTopAppBar) LocalMainTopAppBarManager.current.setLargeTopAppBar(true) + LocalMainTopAppBarManager.current.setLargeTopAppBar(variant.largeTopAppBar) variant.screenContent() } } From 2f9ec955f4710602222040e6dcbc0309b4d0577d Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Tue, 4 Jul 2023 15:01:19 +0200 Subject: [PATCH 12/47] Fix top app bar state update not working --- .../com/orange/ods/app/ui/MainBottomNavigation.kt | 14 ++++---------- .../main/java/com/orange/ods/app/ui/MainScreen.kt | 5 +---- .../main/java/com/orange/ods/app/ui/MainState.kt | 4 ++++ .../com/orange/ods/app/ui/MainTopAppBarState.kt | 8 -------- .../com/orange/ods/app/ui/about/AboutNavGraph.kt | 3 ++- .../ods/app/ui/components/ComponentsNavGraph.kt | 15 ++++++--------- .../ods/app/ui/guidelines/GuidelinesNavGraph.kt | 7 ++++--- 7 files changed, 21 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/com/orange/ods/app/ui/MainBottomNavigation.kt b/app/src/main/java/com/orange/ods/app/ui/MainBottomNavigation.kt index 82ab3e380..ba1ba062a 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainBottomNavigation.kt +++ b/app/src/main/java/com/orange/ods/app/ui/MainBottomNavigation.kt @@ -51,29 +51,23 @@ fun NavGraphBuilder.addBottomNavigationGraph(navigateToElement: (String, Long?, val topAppBarConfiguration = MainTopAppBarState.DefaultConfiguration.newBuilder() .prependAction(TopAppBarConfiguration.Action.Search) .build() - with(LocalMainTopAppBarManager.current) { - updateTopAppBar(topAppBarConfiguration) - clearTopAppBarTabs() - } + LocalMainTopAppBarManager.current.updateTopAppBar(topAppBarConfiguration) GuidelinesScreen(onGuidelineClick = { route -> navigateToElement(route, null, from) }) } composable(BottomNavigationSections.Components.route) { from -> val topAppBarConfiguration = MainTopAppBarState.DefaultConfiguration.newBuilder() .prependAction(TopAppBarConfiguration.Action.Search) .build() - with(LocalMainTopAppBarManager.current) { - updateTopAppBar(topAppBarConfiguration) - clearTopAppBarTabs() - } + LocalMainTopAppBarManager.current.updateTopAppBar(topAppBarConfiguration) ComponentsScreen(onComponentClick = { id -> navigateToElement(MainDestinations.ComponentDetailRoute, id, from) }) } composable(BottomNavigationSections.Modules.route) { - LocalMainTopAppBarManager.current.reset() + LocalMainTopAppBarManager.current.updateTopAppBar(MainTopAppBarState.DefaultConfiguration) ModulesScreen() } composable(BottomNavigationSections.About.route) { from -> val context = LocalContext.current - LocalMainTopAppBarManager.current.reset() + LocalMainTopAppBarManager.current.updateTopAppBar(MainTopAppBarState.DefaultConfiguration) AboutScreen(onAboutItemClick = { id -> val aboutItem = aboutItems.firstOrNull { it.id == id } if (aboutItem is UrlAboutItem) { diff --git a/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt b/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt index 73425f87e..94ad7ab7c 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt +++ b/app/src/main/java/com/orange/ods/app/ui/MainScreen.kt @@ -221,10 +221,7 @@ private fun NavGraphBuilder.mainNavGraph(navigateToElement: (String, Long?, NavB composable( route = MainDestinations.SearchRoute ) { from -> - with(LocalMainTopAppBarManager.current) { - clearTopAppBarTabs() - updateTopAppBarTitle(R.string.navigation_item_search) - } + LocalMainTopAppBarManager.current.updateTopAppBarTitle(R.string.navigation_item_search) SearchScreen( searchedText, onResultItemClick = { route, id -> navigateToElement(route, id, from) } diff --git a/app/src/main/java/com/orange/ods/app/ui/MainState.kt b/app/src/main/java/com/orange/ods/app/ui/MainState.kt index f15bf6456..5d15aecd0 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainState.kt +++ b/app/src/main/java/com/orange/ods/app/ui/MainState.kt @@ -84,6 +84,10 @@ class MainState( get() = navController.currentDestination?.route fun upPress() { + with(topAppBarState) { + updateTopAppBar(MainTopAppBarState.DefaultConfiguration) + clearTopAppBarTabs() + } navController.navigateUp() } diff --git a/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt b/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt index 3e6b56362..cc29dd46a 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt +++ b/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt @@ -33,9 +33,6 @@ interface MainTopAppBarManager { fun updateTopAppBarTabs(tabsConfiguration: TabsConfiguration) fun clearTopAppBarTabs() - - /** Restore default values for tabs and top app bar */ - fun reset() } @Composable @@ -100,11 +97,6 @@ class MainTopAppBarState( override fun clearTopAppBarTabs() { tabsState.clearTopAppBarTabs() } - - override fun reset() { - updateTopAppBar(DefaultConfiguration) - clearTopAppBarTabs() - } } data class TopAppBarConfiguration constructor( diff --git a/app/src/main/java/com/orange/ods/app/ui/about/AboutNavGraph.kt b/app/src/main/java/com/orange/ods/app/ui/about/AboutNavGraph.kt index 97dae50d6..b12e4388e 100644 --- a/app/src/main/java/com/orange/ods/app/ui/about/AboutNavGraph.kt +++ b/app/src/main/java/com/orange/ods/app/ui/about/AboutNavGraph.kt @@ -16,13 +16,14 @@ import androidx.navigation.compose.composable import androidx.navigation.navArgument import com.orange.ods.app.ui.LocalMainTopAppBarManager import com.orange.ods.app.ui.MainDestinations +import com.orange.ods.app.ui.MainTopAppBarState fun NavGraphBuilder.addAboutGraph() { composable( "${MainDestinations.AboutItemDetailRoute}/{${MainDestinations.AboutItemIdKey}}", arguments = listOf(navArgument(MainDestinations.AboutItemIdKey) { type = NavType.LongType }) ) { backStackEntry -> - LocalMainTopAppBarManager.current.reset() + LocalMainTopAppBarManager.current.updateTopAppBar(MainTopAppBarState.DefaultConfiguration) val arguments = requireNotNull(backStackEntry.arguments) val aboutItemId = arguments.getLong(MainDestinations.AboutItemIdKey) AboutFileScreen(aboutItemId) diff --git a/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt b/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt index 033b19735..dda3d9648 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/ComponentsNavGraph.kt @@ -30,18 +30,17 @@ fun NavGraphBuilder.addComponentsGraph(navigateToElement: (String, Long?, NavBac "${MainDestinations.ComponentDetailRoute}/{${MainDestinations.ComponentIdKey}}", arguments = listOf(navArgument(MainDestinations.ComponentIdKey) { type = NavType.LongType }) ) { from -> - LocalMainTopAppBarManager.current.reset() - val arguments = requireNotNull(from.arguments) var currentComponentId: Long by remember { mutableStateOf(-1) } val routeComponentId = arguments.getLong(MainDestinations.ComponentIdKey) - val shouldUpdateTitle by remember { + val hasComponentIdChanged by remember { derivedStateOf { currentComponentId != routeComponentId } } val component = remember(routeComponentId) { components.firstOrNull { component -> component.id == routeComponentId } } component?.let { - if (shouldUpdateTitle) { + if (hasComponentIdChanged) { + LocalMainTopAppBarManager.current.updateTopAppBar(MainTopAppBarState.DefaultConfiguration) LocalMainTopAppBarManager.current.updateTopAppBarTitle(component.titleRes) currentComponentId = routeComponentId } @@ -57,22 +56,20 @@ fun NavGraphBuilder.addComponentsGraph(navigateToElement: (String, Long?, NavBac "${MainDestinations.ComponentVariantRoute}/{${MainDestinations.ComponentVariantIdKey}}", arguments = listOf(navArgument(MainDestinations.ComponentVariantIdKey) { type = NavType.LongType }) ) { from -> - LocalMainTopAppBarManager.current.reset() - val arguments = requireNotNull(from.arguments) var currentVariantId: Long by remember { mutableStateOf(-1) } val routeVariantId = arguments.getLong(MainDestinations.ComponentVariantIdKey) val variant = remember(routeVariantId) { components.flatMap { it.variants }.firstOrNull { it.id == routeVariantId } } - val shouldUpdateTitle by remember { + val hasVariantIdChanged by remember { derivedStateOf { currentVariantId != routeVariantId } } variant?.let { - if (shouldUpdateTitle) { + if (hasVariantIdChanged) { LocalMainTopAppBarManager.current.updateTopAppBarTitle(variant.titleRes) + LocalMainTopAppBarManager.current.setLargeTopAppBar(variant.largeTopAppBar) currentVariantId = routeVariantId } - LocalMainTopAppBarManager.current.setLargeTopAppBar(variant.largeTopAppBar) variant.screenContent() } } diff --git a/app/src/main/java/com/orange/ods/app/ui/guidelines/GuidelinesNavGraph.kt b/app/src/main/java/com/orange/ods/app/ui/guidelines/GuidelinesNavGraph.kt index 1d946c6f3..85c391a4e 100644 --- a/app/src/main/java/com/orange/ods/app/ui/guidelines/GuidelinesNavGraph.kt +++ b/app/src/main/java/com/orange/ods/app/ui/guidelines/GuidelinesNavGraph.kt @@ -14,21 +14,22 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.orange.ods.app.ui.LocalMainTopAppBarManager import com.orange.ods.app.ui.MainDestinations +import com.orange.ods.app.ui.MainTopAppBarState import com.orange.ods.app.ui.guidelines.color.GuidelineColorScreen import com.orange.ods.app.ui.guidelines.spacing.GuidelineSpacingScreen import com.orange.ods.app.ui.guidelines.typography.GuidelineTypographyScreen fun NavGraphBuilder.addGuidelinesGraph() { composable(MainDestinations.GuidelineColor) { - LocalMainTopAppBarManager.current.reset() + LocalMainTopAppBarManager.current.updateTopAppBar(MainTopAppBarState.DefaultConfiguration) GuidelineColorScreen() } composable(MainDestinations.GuidelineTypography) { - LocalMainTopAppBarManager.current.reset() + LocalMainTopAppBarManager.current.updateTopAppBar(MainTopAppBarState.DefaultConfiguration) GuidelineTypographyScreen() } composable(MainDestinations.GuidelineSpacing) { - LocalMainTopAppBarManager.current.reset() + LocalMainTopAppBarManager.current.updateTopAppBar(MainTopAppBarState.DefaultConfiguration) GuidelineSpacingScreen() } } From fd6166e3de7d45ff7cab211952541723bce6abbe Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Tue, 4 Jul 2023 16:35:18 +0200 Subject: [PATCH 13/47] Fix display of buttons icons --- .../java/com/orange/ods/app/ui/components/Component.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/orange/ods/app/ui/components/Component.kt b/app/src/main/java/com/orange/ods/app/ui/components/Component.kt index fdc354220..6cd2d1096 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/Component.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/Component.kt @@ -19,6 +19,7 @@ import com.orange.ods.app.ui.components.appbars.top.ComponentTopAppBar import com.orange.ods.app.ui.components.banners.ComponentBanners import com.orange.ods.app.ui.components.bottomnavigation.ComponentBottomNavigation import com.orange.ods.app.ui.components.buttons.ComponentButtons +import com.orange.ods.app.ui.components.buttons.icons.ComponentButtonsIcons import com.orange.ods.app.ui.components.cards.ComponentCard import com.orange.ods.app.ui.components.checkboxes.ComponentCheckboxes import com.orange.ods.app.ui.components.chips.Chip @@ -295,12 +296,15 @@ sealed class Variant( "${OdsComposable.OdsButton.name} with a functional style", { ComponentButtons(variant = ButtonsFunctional) }) - object ButtonsIcon : Variant(R.string.component_buttons_icon, OdsComposable.OdsIconButton.name, { ComponentButtons(variant = ButtonsIcon) }) + object ButtonsIcon : Variant(R.string.component_buttons_icon, OdsComposable.OdsIconButton.name, { ComponentButtonsIcons(variant = ButtonsIcon) }) object ButtonsIconToggle : - Variant(R.string.component_buttons_icon_toggle, OdsComposable.OdsIconToggleButton.name, { ComponentButtons(variant = ButtonsIconToggle) }) + Variant(R.string.component_buttons_icon_toggle, OdsComposable.OdsIconToggleButton.name, { ComponentButtonsIcons(variant = ButtonsIconToggle) }) object ButtonsIconToggleGroup : - Variant(R.string.component_buttons_icon_toggle_group, OdsComposable.OdsIconToggleButtonsRow.name, { ComponentButtons(variant = ButtonsIconToggle) }) + Variant( + R.string.component_buttons_icon_toggle_group, + OdsComposable.OdsIconToggleButtonsRow.name, + { ComponentButtonsIcons(variant = ButtonsIconToggleGroup) }) object CardVerticalImageFirst : Variant(R.string.component_card_vertical_image_first, OdsComposable.OdsVerticalImageFirstCard.name, { ComponentCard(variant = CardVerticalImageFirst) }) From eb5dfa447a3af8be9182d6cecb110e2a45cf406d Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Wed, 5 Jul 2023 15:02:45 +0200 Subject: [PATCH 14/47] Add scroll behavior customization element --- .../com/orange/ods/app/ui/MainTopAppBar.kt | 2 +- .../orange/ods/app/ui/MainTopAppBarState.kt | 19 +- .../appbars/top/ComponentTopAppBar.kt | 288 ++++++++++++------ .../top/TopAppBarCustomizationState.kt | 18 +- app/src/main/res/values/strings.xml | 9 +- .../component/appbar/top/OdsLargeTopAppBar.kt | 20 +- 6 files changed, 243 insertions(+), 113 deletions(-) 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 a4e435daa..fb7b0bd8a 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 @@ -86,7 +86,7 @@ fun MainTopAppBar( navigationIcon = navigationIcon ?: { }, onNavigationIconClick = upPress, actions = actions, - scrollBehavior = scrollBehavior + scrollBehavior = if (topAppBarStateProvider().hasScrollBehavior) scrollBehavior else null ) } else { OdsTopAppBar( diff --git a/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt b/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt index cc29dd46a..a12460037 100644 --- a/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt +++ b/app/src/main/java/com/orange/ods/app/ui/MainTopAppBarState.kt @@ -20,6 +20,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.text.input.TextFieldValue import com.orange.ods.app.R +import com.orange.ods.app.ui.components.appbars.top.TopAppBarCustomizationState import kotlinx.parcelize.Parcelize val LocalMainTopAppBarManager = staticCompositionLocalOf { error("CompositionLocal LocalMainTopAppBarManager not present") } @@ -42,10 +43,11 @@ fun rememberMainTopAppBarState( navigationIconEnabled: MutableState = rememberSaveable { mutableStateOf(MainTopAppBarState.DefaultConfiguration.isNavigationIconEnabled) }, searchedText: MutableState = remember { mutableStateOf(TextFieldValue("")) }, large: MutableState = rememberSaveable { mutableStateOf(false) }, + scrollBehavior: MutableState = rememberSaveable { mutableStateOf(TopAppBarCustomizationState.ScrollBehavior.Collapsible) }, tabsState: MainTabsState = rememberMainTabsState(), ) = - remember(titleRes, actions, searchedText, navigationIconEnabled, large, tabsState) { - MainTopAppBarState(titleRes, actions, searchedText, navigationIconEnabled, large, tabsState) + remember(titleRes, actions, searchedText, navigationIconEnabled, large, scrollBehavior, tabsState) { + MainTopAppBarState(titleRes, actions, searchedText, navigationIconEnabled, large, scrollBehavior, tabsState) } @@ -55,12 +57,14 @@ class MainTopAppBarState( var searchedText: MutableState, private val navigationIconEnabled: MutableState, private var large: MutableState, + private var scrollBehavior: MutableState, val tabsState: MainTabsState, ) : MainTopAppBarManager { companion object { val DefaultConfiguration = TopAppBarConfiguration( isLarge = false, + scrollBehavior = TopAppBarCustomizationState.ScrollBehavior.Collapsible, isNavigationIconEnabled = true, actions = listOf(TopAppBarConfiguration.Action.Theme, TopAppBarConfiguration.Action.Mode) ) @@ -73,6 +77,9 @@ class MainTopAppBarState( val isLarge: Boolean get() = large.value + val hasScrollBehavior: Boolean + get() = scrollBehavior.value != TopAppBarCustomizationState.ScrollBehavior.None + val isNavigationIconEnabled: Boolean get() = navigationIconEnabled.value @@ -82,6 +89,7 @@ class MainTopAppBarState( override fun updateTopAppBar(topAppBarConfiguration: TopAppBarConfiguration) { large.value = topAppBarConfiguration.isLarge + scrollBehavior.value = topAppBarConfiguration.scrollBehavior navigationIconEnabled.value = topAppBarConfiguration.isNavigationIconEnabled actions.value = topAppBarConfiguration.actions } @@ -101,6 +109,7 @@ class MainTopAppBarState( data class TopAppBarConfiguration constructor( val isLarge: Boolean, + val scrollBehavior: TopAppBarCustomizationState.ScrollBehavior, val isNavigationIconEnabled: Boolean, val actions: List ) { @@ -126,11 +135,14 @@ data class TopAppBarConfiguration constructor( class Builder { private var isLarge = false + private var scrollBehavior = TopAppBarCustomizationState.ScrollBehavior.Collapsible private var isNavigationIconEnabled = true private var actions = mutableListOf() fun large(value: Boolean) = apply { isLarge = value } + fun scrollBehavior(value: TopAppBarCustomizationState.ScrollBehavior) = apply { scrollBehavior = value } + fun navigationIconEnabled(enabled: Boolean) = apply { isNavigationIconEnabled = enabled } fun actions(builderAction: MutableList.() -> Unit) = apply { actions.builderAction() } @@ -145,12 +157,13 @@ data class TopAppBarConfiguration constructor( actions { add(action) } } - fun build() = TopAppBarConfiguration(isLarge, isNavigationIconEnabled, actions) + fun build() = TopAppBarConfiguration(isLarge, scrollBehavior, isNavigationIconEnabled, actions) } fun newBuilder() = Builder().apply { navigationIconEnabled(isNavigationIconEnabled) large(isLarge) + scrollBehavior(scrollBehavior) actions { clear() addAll(actions) 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 6526d8be0..2e2e688e4 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 @@ -10,17 +10,25 @@ package com.orange.ods.app.ui.components.appbars.top -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding +import androidx.compose.animation.core.* +import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.Icon import androidx.compose.material.rememberBottomSheetScaffoldState import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.draw.scale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp import com.orange.ods.app.R import com.orange.ods.app.ui.LocalMainTopAppBarManager import com.orange.ods.app.ui.MainTopAppBarState @@ -29,18 +37,15 @@ import com.orange.ods.app.ui.components.Variant import com.orange.ods.app.ui.components.utilities.ComponentCountRow import com.orange.ods.app.ui.components.utilities.ComponentCustomizationBottomSheetScaffold import com.orange.ods.app.ui.utilities.NavigationItem -import com.orange.ods.app.ui.utilities.composable.CodeImplementationColumn -import com.orange.ods.app.ui.utilities.composable.FunctionCallCode -import com.orange.ods.app.ui.utilities.composable.Subtitle +import com.orange.ods.app.ui.utilities.composable.* import com.orange.ods.compose.OdsComposable import com.orange.ods.compose.component.chip.OdsChoiceChip import com.orange.ods.compose.component.chip.OdsChoiceChipsFlowRow import com.orange.ods.compose.component.list.OdsListItem import com.orange.ods.compose.component.list.OdsSwitchTrailing -import com.orange.ods.compose.text.OdsTextH1 -import com.orange.ods.compose.text.OdsTextH2 -import com.orange.ods.compose.text.OdsTextH3 -import com.orange.ods.compose.text.OdsTextH4 +import com.orange.ods.compose.text.OdsTextBody2 +import com.orange.ods.compose.text.OdsTextCaption +import com.orange.ods.compose.theme.OdsTheme import kotlin.math.max @OptIn(ExperimentalMaterialApi::class) @@ -56,6 +61,7 @@ fun ComponentTopAppBar(variant: Variant) { .map { TopAppBarConfiguration.Action.Custom(stringResource(id = it.textResId), it.iconResId) } val topAppBarConfiguration = TopAppBarConfiguration.Builder() .large(isLargeVariant) + .scrollBehavior(scrollBehavior.value) .navigationIconEnabled(isNavigationIconEnabled) .actions { addAll(MainTopAppBarState.DefaultConfiguration.actions.take(actionCount.value)) @@ -66,110 +72,200 @@ fun ComponentTopAppBar(variant: Variant) { with(LocalMainTopAppBarManager.current) { updateTopAppBar(topAppBarConfiguration) - if (isLargeVariant) updateTopAppBarTitle(titleLength.value.titleResId) + if (isLargeVariant) updateTopAppBarTitle(titleLineNum.value.titleResId) } ComponentCustomizationBottomSheetScaffold( bottomSheetScaffoldState = rememberBottomSheetScaffoldState(), - bottomSheetContent = { - OdsListItem( - text = stringResource(id = R.string.component_app_bars_top_element_navigation_icon), - trailing = OdsSwitchTrailing( - checked = navigationIconEnabled - ) - ) - ComponentCountRow( - modifier = Modifier.padding(start = dimensionResource(id = R.dimen.screen_horizontal_margin)), - title = stringResource(id = R.string.component_app_bars_top_actions_count), - count = actionCount, - minusIconContentDescription = stringResource(id = R.string.component_app_bars_top_remove_action), - plusIconContentDescription = stringResource(id = R.string.component_app_bars_top_add_action), - minCount = minActionCount, - maxCount = maxActionCountSelectable - ) - OdsListItem( - text = stringResource(id = R.string.component_app_bars_top_element_overflow_menu), - trailing = OdsSwitchTrailing( - checked = overflowMenuEnabled, - enabled = isOverflowMenuSwitchEnabled - ) - ) + bottomSheetContent = { CustomizationBottomSheetContent(customizationState = customizationState, isLargeVariant = isLargeVariant) } + ) { + val context = LocalContext.current + Column( + modifier = Modifier + .padding(vertical = dimensionResource(id = R.dimen.screen_vertical_margin)) + .verticalScroll(rememberScrollState()), + horizontalAlignment = Alignment.CenterHorizontally + ) { if (isLargeVariant) { - Subtitle(textRes = R.string.component_element_title, horizontalPadding = true) - OdsChoiceChipsFlowRow( - selectedChip = titleLength, + OdsTextBody2(text = stringResource(id = R.string.component_app_bars_top_large_scrolling_upward)) + BlinkingChevronDown( modifier = Modifier - .padding(horizontal = dimensionResource(id = R.dimen.spacing_m)) - .padding(bottom = dimensionResource(id = R.dimen.spacing_s)), - outlinedChips = true - ) { - OdsChoiceChip(textRes = R.string.component_app_bars_top_large_title_one_line, value = TopAppBarCustomizationState.TitleLength.OneLine) - OdsChoiceChip(textRes = R.string.component_app_bars_top_large_title_two_lines, value = TopAppBarCustomizationState.TitleLength.TwoLines) - OdsChoiceChip( - textRes = R.string.component_app_bars_top_large_title_truncated, - value = TopAppBarCustomizationState.TitleLength.Truncated - ) - } + .rotate(180f) + .padding(vertical = dimensionResource(id = R.dimen.spacing_s)) + ) } - }) { - - val context = LocalContext.current - Column(modifier = Modifier.verticalScroll(rememberScrollState())) { - OdsTextH1(text = "test pour scroll") - OdsTextH2(text = "test pour scroll") - OdsTextH3(text = "test pour scroll") - OdsTextH4(text = "test pour scroll") - CodeImplementationColumn(modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.screen_horizontal_margin))) { - FunctionCallCode( - name = if (isLargeVariant) OdsComposable.OdsLargeTopAppBar.name else OdsComposable.OdsTopAppBar.name, - exhaustiveParameters = false, - parameters = { - title(context.getString(R.string.component_app_bars_top_regular)) + CodeImplementationColumn( + modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.screen_horizontal_margin)), + contentBackground = false + ) { + CodeBackgroundColumn { + FunctionCallCode( + name = if (isLargeVariant) OdsComposable.OdsLargeTopAppBar.name else OdsComposable.OdsTopAppBar.name, + exhaustiveParameters = false, + parameters = { + 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)) - } - ) + if (isNavigationIconEnabled) { + composable(name = "navigationIcon") { + FunctionCallCode( + name = "Icon", + parameters = { + simple("imageVector", "") + contentDescription(context.getString(R.string.top_app_bar_back_icon_desc)) + } + ) + } } - } - composable(name = "actions") { - repeat(actionCount.value) { - FunctionCallCode( - name = OdsComposable.OdsTopAppBarActionButton.name, - parameters = { - 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() - } - ) + composable(name = "actions") { + repeat(actionCount.value) { + FunctionCallCode( + name = OdsComposable.OdsTopAppBarActionButton.name, + parameters = { + 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() + } + ) + } } } } + + simple("scrollBehavior", "") } - } + ) + } + OdsTextBody2( + modifier = Modifier.padding(top = dimensionResource(id = R.dimen.spacing_s), bottom = dimensionResource(id = R.dimen.spacing_xs)), + text = stringResource(id = R.string.component_app_bars_top_large_code_collapsing) + ) + OdsTextCaption( + modifier = Modifier.padding(bottom = dimensionResource(id = R.dimen.spacing_xs)), + text = stringResource(id = R.string.component_app_bars_top_large_code_collapsing_step_1) ) + CodeBackgroundColumn { + TechnicalText(text = "val topBarScrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState())") + } + OdsTextCaption( + modifier = Modifier.padding(top = dimensionResource(id = R.dimen.spacing_s), bottom = dimensionResource(id = R.dimen.spacing_xs)), + text = stringResource(id = R.string.component_app_bars_top_large_code_collapsing_step_2) + ) + CodeBackgroundColumn { + TechnicalText(text = "val nestedScrollConnection = remember { topBarScrollBehavior.nestedScrollConnection }") + TechnicalText(text = "Scaffold(modifier = Modifier.nestedScroll(nestedScrollConnection), ...) { ... }") + } + } + + if (isLargeVariant) { + BlinkingChevronDown(modifier = Modifier.padding(vertical = dimensionResource(id = R.dimen.spacing_s))) + OdsTextBody2(text = stringResource(id = R.string.component_app_bars_top_large_scrolling_downward)) } } } } } + +@Composable +private fun CustomizationBottomSheetContent(customizationState: TopAppBarCustomizationState, isLargeVariant: Boolean) { + with(customizationState) { + Subtitle(textRes = R.string.component_app_bars_top_large_scroll_behavior, horizontalPadding = true) + OdsChoiceChipsFlowRow( + selectedChip = scrollBehavior, + modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.spacing_m)), + outlinedChips = true + ) { + OdsChoiceChip(textRes = R.string.component_app_bars_top_large_scroll_behavior_none, value = TopAppBarCustomizationState.ScrollBehavior.None) + OdsChoiceChip( + textRes = R.string.component_app_bars_top_large_scroll_behavior_collapsible, + value = TopAppBarCustomizationState.ScrollBehavior.Collapsible + ) + } + OdsListItem( + text = stringResource(id = R.string.component_app_bars_top_element_navigation_icon), + trailing = OdsSwitchTrailing( + checked = navigationIconEnabled + ) + ) + ComponentCountRow( + modifier = Modifier.padding(start = dimensionResource(id = R.dimen.screen_horizontal_margin)), + title = stringResource(id = R.string.component_app_bars_top_actions_count), + count = actionCount, + minusIconContentDescription = stringResource(id = R.string.component_app_bars_top_remove_action), + plusIconContentDescription = stringResource(id = R.string.component_app_bars_top_add_action), + minCount = minActionCount, + maxCount = maxActionCountSelectable + ) + OdsListItem( + text = stringResource(id = R.string.component_app_bars_top_element_overflow_menu), + trailing = OdsSwitchTrailing( + checked = overflowMenuEnabled, + enabled = isOverflowMenuSwitchEnabled + ) + ) + if (isLargeVariant) { + Subtitle(textRes = R.string.component_element_title, horizontalPadding = true) + OdsChoiceChipsFlowRow( + selectedChip = titleLineNum, + modifier = Modifier + .padding(horizontal = dimensionResource(id = R.dimen.spacing_m)) + .padding(bottom = dimensionResource(id = R.dimen.spacing_s)), + outlinedChips = true + ) { + OdsChoiceChip(textRes = R.string.component_app_bars_top_large_title_one_line, value = TopAppBarCustomizationState.TitleLineNum.OneLine) + OdsChoiceChip(textRes = R.string.component_app_bars_top_large_title_two_lines, value = TopAppBarCustomizationState.TitleLineNum.TwoLines) + OdsChoiceChip( + textRes = R.string.component_app_bars_top_large_title_truncated, + value = TopAppBarCustomizationState.TitleLineNum.ThreeLinesAndMore + ) + } + } + } +} + +@Composable +private fun BlinkingChevronDown(modifier: Modifier = Modifier) { + val infiniteTransition = rememberInfiniteTransition() + + val scale by infiniteTransition.animateFloat( + initialValue = 1f, + targetValue = 1.2f, + animationSpec = infiniteRepeatable( + animation = tween(1000), + repeatMode = RepeatMode.Reverse + ) + ) + + val alpha by infiniteTransition.animateFloat( + initialValue = 0.4f, + targetValue = 1f, + animationSpec = infiniteRepeatable( + animation = tween(1000), + repeatMode = RepeatMode.Reverse + ) + ) + + Icon( + modifier = modifier + .size(30.dp) + .scale(scale) + .alpha(alpha), + painter = painterResource(id = R.drawable.ic_chevron_down), + contentDescription = null, + tint = OdsTheme.colors.onSurface + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/TopAppBarCustomizationState.kt b/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/TopAppBarCustomizationState.kt index cd65498e5..a0bdc7ffe 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/TopAppBarCustomizationState.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/TopAppBarCustomizationState.kt @@ -30,22 +30,28 @@ fun rememberTopAppBarCustomizationState( ) ) }, - titleLength: MutableState = rememberSaveable { mutableStateOf(TopAppBarCustomizationState.TitleLength.OneLine) }, + titleLineNum: MutableState = rememberSaveable { mutableStateOf(TopAppBarCustomizationState.TitleLineNum.OneLine) }, + scrollBehavior: MutableState = rememberSaveable { mutableStateOf(MainTopAppBarState.DefaultConfiguration.scrollBehavior) } ) = - remember(navigationIconEnabled, actionCount, overflowMenuEnabled, titleLength) { - TopAppBarCustomizationState(navigationIconEnabled, actionCount, overflowMenuEnabled, titleLength) + remember(navigationIconEnabled, actionCount, overflowMenuEnabled, titleLineNum, scrollBehavior) { + TopAppBarCustomizationState(navigationIconEnabled, actionCount, overflowMenuEnabled, titleLineNum, scrollBehavior) } class TopAppBarCustomizationState( val navigationIconEnabled: MutableState, val actionCount: MutableState, val overflowMenuEnabled: MutableState, - val titleLength: MutableState + val titleLineNum: MutableState, + val scrollBehavior: MutableState ) { - enum class TitleLength(val titleResId: Int) { + enum class TitleLineNum(val titleResId: Int) { OneLine(R.string.component_app_bars_top_large_title_one_line_value), TwoLines(R.string.component_app_bars_top_large_title_two_lines_value), - Truncated(R.string.component_app_bars_top_large_title_truncated_value) + ThreeLinesAndMore(R.string.component_app_bars_top_large_title_truncated_value) + } + + enum class ScrollBehavior { + None, Collapsible } private val maxActionCount = 3 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4e004c109..56ffa3c3d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -111,7 +111,14 @@ Large: One line title Large: Two lines title allowed in top app bar Large: Title will be truncated if it is too long to fit in the top app bar - + Scrolling upward collapses the top app bar + Scrolling downward expands the top app bar + For collapsing version: + 1 - Pass the following scroll behavior to the top bar + 2 - Connect this behavior to the Scaffold containing the top bar through a Modifier + Scroll behavior + None + Collapsible Bottom navigation 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 20a9c7c25..94eb67e8f 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 @@ -43,11 +43,16 @@ fun OdsLargeTopAppBar( scrollBehavior: TopAppBarScrollBehavior? = null ) { val contentColor = OdsTheme.colors.component.topAppBar.barContent + val expandedTitleStartPadding = 48.dp + val collapsedTitleStartPadding = 8.dp + val expandedTitleAlpha = 1f + val expandedTitleMaxLines = 2 + val collapsedTitleMaxLines = 1 val stateChangeFraction = 0.7 val titleStartPadding by remember { derivedStateOf { - if (scrollBehavior != null && scrollBehavior.state.collapsedFraction >= 0.85) 8.dp else 48.dp + if (scrollBehavior != null && scrollBehavior.state.collapsedFraction >= 0.85) collapsedTitleStartPadding else expandedTitleStartPadding } } @@ -60,13 +65,13 @@ fun OdsLargeTopAppBar( else -> 0 }.toFloat() } else { - 1.0f + expandedTitleAlpha } } } val titleMaxLines by remember { derivedStateOf { - if (scrollBehavior != null && scrollBehavior.state.collapsedFraction >= stateChangeFraction) 1 else 2 + if (scrollBehavior != null && scrollBehavior.state.collapsedFraction >= stateChangeFraction) collapsedTitleMaxLines else expandedTitleMaxLines } } @@ -74,12 +79,15 @@ fun OdsLargeTopAppBar( title = { Text( modifier = Modifier - .padding(start = titleStartPadding, end = dimensionResource(id = R.dimen.spacing_m)) - .alpha(titleAlpha), + .padding( + start = if (scrollBehavior != null) titleStartPadding else expandedTitleStartPadding, + end = dimensionResource(id = R.dimen.spacing_m) + ) + .alpha(if (scrollBehavior != null) titleAlpha else expandedTitleAlpha), text = title, style = OdsTheme.typography.h6, overflow = TextOverflow.Ellipsis, - maxLines = titleMaxLines, + maxLines = if (scrollBehavior != null) titleMaxLines else expandedTitleMaxLines, ) }, modifier = modifier, From 388db41c27058fcce0df0984fd6ef82210c9dc78 Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Wed, 5 Jul 2023 15:10:51 +0200 Subject: [PATCH 15/47] Adjust content display according top app bar type --- .../appbars/top/ComponentTopAppBar.kt | 42 ++++++++++--------- .../top/TopAppBarCustomizationState.kt | 3 ++ app/src/main/res/values/strings.xml | 6 +-- 3 files changed, 28 insertions(+), 23 deletions(-) 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 2e2e688e4..8d8aa3d15 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 @@ -86,7 +86,7 @@ fun ComponentTopAppBar(variant: Variant) { .verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally ) { - if (isLargeVariant) { + if (isLargeVariant && isCollapsible) { OdsTextBody2(text = stringResource(id = R.string.component_app_bars_top_large_scrolling_upward)) BlinkingChevronDown( modifier = Modifier @@ -150,28 +150,30 @@ fun ComponentTopAppBar(variant: Variant) { } ) } - OdsTextBody2( - modifier = Modifier.padding(top = dimensionResource(id = R.dimen.spacing_s), bottom = dimensionResource(id = R.dimen.spacing_xs)), - text = stringResource(id = R.string.component_app_bars_top_large_code_collapsing) - ) - OdsTextCaption( - modifier = Modifier.padding(bottom = dimensionResource(id = R.dimen.spacing_xs)), - text = stringResource(id = R.string.component_app_bars_top_large_code_collapsing_step_1) - ) - CodeBackgroundColumn { - TechnicalText(text = "val topBarScrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState())") - } - OdsTextCaption( - modifier = Modifier.padding(top = dimensionResource(id = R.dimen.spacing_s), bottom = dimensionResource(id = R.dimen.spacing_xs)), - text = stringResource(id = R.string.component_app_bars_top_large_code_collapsing_step_2) - ) - CodeBackgroundColumn { - TechnicalText(text = "val nestedScrollConnection = remember { topBarScrollBehavior.nestedScrollConnection }") - TechnicalText(text = "Scaffold(modifier = Modifier.nestedScroll(nestedScrollConnection), ...) { ... }") + if (isLargeVariant && isCollapsible) { + OdsTextBody2( + modifier = Modifier.padding(top = dimensionResource(id = R.dimen.spacing_s), bottom = dimensionResource(id = R.dimen.spacing_xs)), + text = stringResource(id = R.string.component_app_bars_top_large_code_collapsing) + ) + OdsTextCaption( + modifier = Modifier.padding(bottom = dimensionResource(id = R.dimen.spacing_xs)), + text = stringResource(id = R.string.component_app_bars_top_large_code_collapsing_step_1) + ) + CodeBackgroundColumn { + TechnicalText(text = "val topBarScrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState())") + } + OdsTextCaption( + modifier = Modifier.padding(top = dimensionResource(id = R.dimen.spacing_s), bottom = dimensionResource(id = R.dimen.spacing_xs)), + text = stringResource(id = R.string.component_app_bars_top_large_code_collapsing_step_2) + ) + CodeBackgroundColumn { + TechnicalText(text = "val nestedScrollConnection = remember { topBarScrollBehavior.nestedScrollConnection }") + TechnicalText(text = "Scaffold(modifier = Modifier.nestedScroll(nestedScrollConnection), ...) { ... }") + } } } - if (isLargeVariant) { + if (isLargeVariant && isCollapsible) { BlinkingChevronDown(modifier = Modifier.padding(vertical = dimensionResource(id = R.dimen.spacing_s))) OdsTextBody2(text = stringResource(id = R.string.component_app_bars_top_large_scrolling_downward)) } diff --git a/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/TopAppBarCustomizationState.kt b/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/TopAppBarCustomizationState.kt index a0bdc7ffe..f8862ec88 100644 --- a/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/TopAppBarCustomizationState.kt +++ b/app/src/main/java/com/orange/ods/app/ui/components/appbars/top/TopAppBarCustomizationState.kt @@ -58,6 +58,9 @@ class TopAppBarCustomizationState( val minActionCount = 0 + val isCollapsible: Boolean + get() = scrollBehavior.value == ScrollBehavior.Collapsible + val isNavigationIconEnabled: Boolean get() = navigationIconEnabled.value diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 56ffa3c3d..d576d3b92 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -113,9 +113,9 @@ Large: Title will be truncated if it is too long to fit in the top app bar Scrolling upward collapses the top app bar Scrolling downward expands the top app bar - For collapsing version: - 1 - Pass the following scroll behavior to the top bar - 2 - Connect this behavior to the Scaffold containing the top bar through a Modifier + Define and connect scroll behavior: + Step 1 - Pass the following scroll behavior to the top bar + Step 2 - Connect this behavior to the Scaffold containing the top bar through a Modifier Scroll behavior None Collapsible From 145b7e604709ff86082fed1c0ffed4511746969c Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Wed, 5 Jul 2023 15:27:59 +0200 Subject: [PATCH 16/47] Update documentation --- docs/components/AppBarsTop.md | 144 ++++++++++++++++++++++------------ 1 file changed, 94 insertions(+), 50 deletions(-) diff --git a/docs/components/AppBarsTop.md b/docs/components/AppBarsTop.md index 15ec4ffaa..a6b1ca235 100644 --- a/docs/components/AppBarsTop.md +++ b/docs/components/AppBarsTop.md @@ -10,9 +10,11 @@ description: Top app bars display information and actions relating to the curren * [Specifications references](#specifications-references) * [Accessibility](#accessibility) -* [Implementation](#implementation) +* [Variants](#variants) + * [Regular top app bar](#regular-top-app-bar) + * [Large top app bar](#large-top-app-bar) * [Extras](#extras) - * [Overflow menu](#overflow-menu) + * [Overflow menu](#overflow-menu) * [Component specific tokens](#component-specific-tokens) --- @@ -49,22 +51,23 @@ For action items and items within the overflow menu, the content description needs to be set in the menu: ```xml + - - + + ``` For images within top app bars, set an `android:contentDescription` or use the `setContentDescription` method on the `ImageView`. -## Implementation +## Variants + +### Regular top app bar > **Jetpack Compose implementation** -Add `OdsTopAppBar` composable to your Scaffold topBar as follow: +Add `OdsTopAppBar` composable to your Scaffold topBar: ```kotlin OdsTopAppBar( @@ -96,36 +99,30 @@ Note: By default, the `OdsTopAppBar` is elevated but you can set `elevated` para API and source code: -* `CoordinatorLayout`: [Class definition](https://developer.android.com/reference/androidx/coordinatorlayout/widget/CoordinatorLayout) -* `AppBarLayout`: [Class definition](https://developer.android.com/reference/com/google/android/material/appbar/AppBarLayout), [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/appbar/AppBarLayout.java) -* `MaterialToolbar`: [Class definition](https://developer.android.com/reference/com/google/android/material/appbar/MaterialToolbar), [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/appbar/MaterialToolbar.java) -* `CollapsingToolbarLayout`: [Class definition](https://developer.android.com/reference/com/google/android/material/appbar/CollapsingToolbarLayout), [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/appbar/CollapsingToolbarLayout.java) +* `CoordinatorLayout`: [Class definition](https://developer.android.com/reference/androidx/coordinatorlayout/widget/CoordinatorLayout) +* `AppBarLayout`: [Class definition](https://developer.android.com/reference/com/google/android/material/appbar/AppBarLayout), [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/appbar/AppBarLayout.java) +* `MaterialToolbar`: [Class definition](https://developer.android.com/reference/com/google/android/material/appbar/MaterialToolbar), [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/appbar/MaterialToolbar.java) +* `CollapsingToolbarLayout`: [Class definition](https://developer.android.com/reference/com/google/android/material/appbar/CollapsingToolbarLayout), [Class source](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/appbar/CollapsingToolbarLayout.java) In the layout: ```xml - - - + - @@ -139,28 +136,21 @@ In the layout: In `@menu/top_app_bar.xml`: ```xml + - - + android:contentDescription="@string/content_description_search" app:showAsAction="ifRoom" /> - + ``` @@ -168,9 +158,8 @@ In `@menu/top_app_bar.xml`: In menu/navigation icons: ```xml - - + + ``` In code: @@ -207,13 +196,12 @@ content. Upon scroll, it increases elevation and lets content scroll behind it. In the layout: ```xml + - + - + @@ -225,19 +213,75 @@ _**Raised top app bar**_ If you need to have a top app bar with some elevation you can set the `@style/Widget.Orange.Toolbar.Raised` ```xml + + style="@style/Widget.Orange.Toolbar.Raised" /> ``` +### Large top app bar + +> **Jetpack Compose implementation** + +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 = { + 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. + }, + scrollBehavior = null // See below to attach a scroll behavior and make the top app bar collapsible +) +``` + +If you want a collapsible large top app bar, you can follow these steps: + +1 - Define the scroll behavior to use: + +```kotlin +val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) +``` + +2 - Provide this `scrollBehavior` to the OdsLargeTopAppBar and as a modifier of your Scaffold in order to listen to the scroll event + +```kotlin +Scaffold( + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + OdsLargeTopAppBar( + //... + scrollBehavior = scrollBehavior, + ) + }, + //... +) { + // Scaffold content +} +``` + ## Extras ### Overflow menu @@ -251,13 +295,13 @@ You can easily add an overflow menu to your top app bar by using the `OdsTopAppB OdsTopAppBarOverflowMenuBox(overflowIconContentDescription = "more actions") { OdsDropdownMenuItem( text = "account", - onClick = { + onClick = { // Do something } ) OdsDropdownMenuItem( text = "settings", - onClick = { + onClick = { // Do something } ) From 86a580fcd943a62385286a47696ab19d9ae15c0d Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Wed, 5 Jul 2023 15:29:30 +0200 Subject: [PATCH 17/47] Update changelog --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index 5990d1208..2d3f709e2 100644 --- a/changelog.md +++ b/changelog.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - \[App\] Add component image item ([#473](https://github.com/Orange-OpenSource/ods-android/issues/473)) - \[App\] Display XML version in `ButtonsContained` and `ButtonsOutlined` ([#528](https://github.com/Orange-OpenSource/ods-android/issues/528)) - \[App\] Display XML version in `ButtonsText`, `ButtonsIcon`, `ButtonsIconToggle` and `ButtonsIconToggleGroup` ([#529](https://github.com/Orange-OpenSource/ods-android/issues/529)) +- \[Lib\] Add `OdsLargeTopAppBar` component ([#118](https://github.com/Orange-OpenSource/ods-android/issues/118)) - \[Lib\] Add `OdsImageItem` component ([#473](https://github.com/Orange-OpenSource/ods-android/issues/473)) - \[Lib\] Add `@Parcelize` annotation on `OdsExposedDropdownMenuItem` to allow to save and restore it ([#545](https://github.com/Orange-OpenSource/ods-android/issues/545)) - \[Lib\] Add `themeColors` and `rippleTheme` properties to `OdsDisplaySurface` ([#329](https://github.com/Orange-OpenSource/ods-android/issues/329)) From 1a7d35cde147bb93e19708747d11677f22e8cecf Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Thu, 6 Jul 2023 16:07:45 +0200 Subject: [PATCH 18/47] Add large top app bar previews --- .../component/appbar/top/OdsLargeTopAppBar.kt | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) 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 94eb67e8f..fb4f2655a 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 @@ -12,6 +12,9 @@ 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 @@ -25,10 +28,16 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow +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.utilities.BasicPreviewParameterProvider +import com.orange.ods.compose.component.utilities.Preview +import com.orange.ods.compose.component.utilities.UiModePreviews import com.orange.ods.compose.theme.OdsTheme @OptIn(ExperimentalMaterial3Api::class) @@ -105,4 +114,50 @@ fun OdsLargeTopAppBar( ), scrollBehavior = scrollBehavior ) -} \ No newline at end of file +} + +@OptIn(ExperimentalMaterial3Api::class) +@UiModePreviews.Default +@Composable +private fun PreviewOdsLargeTopAppBar(@PreviewParameter(OdsLargeTopAppBarPreviewParameterProvider::class) parameter: OdsLargeTopAppBarPreviewParameter) = Preview { + 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 = { }) + } + } + ) +} + +internal data class OdsLargeTopAppBarPreviewParameter( + val title: String, + val navigationIcon: @Composable () -> Unit +) + +private class OdsLargeTopAppBarPreviewParameterProvider : + BasicPreviewParameterProvider(*previewParameterValues.toTypedArray()) + +private val previewParameterValues: List + get() { + val navigationIcon = @Composable { + IconButton(onClick = {}) { + Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = null, tint = OdsTheme.colors.component.topAppBar.barContent) + } + } + 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", { }) + ) + } \ No newline at end of file From 9bb27763dfcb87ff9379b5cd6d0a4c9813a58e36 Mon Sep 17 00:00:00 2001 From: Pauline Auvray Date: Wed, 12 Jul 2023 08:58:30 +0200 Subject: [PATCH 19/47] Review: Add LINE_BREAK_AFTER_MULTILINE_WHEN_ENTRY option --- .idea/codeStyles/Project.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index a92b7a9a0..4ef75faf7 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,6 +1,7 @@ +