diff --git a/app/src/main/java/org/mozilla/fenix/compose/Chip.kt b/app/src/main/java/org/mozilla/fenix/compose/Chip.kt new file mode 100644 index 000000000000..17a9ddbd01f5 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/compose/Chip.kt @@ -0,0 +1,78 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.compose + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.material.ChipDefaults.chipColors +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import org.mozilla.fenix.theme.FirefoxTheme + +/** + * Default layout for a clickable chip. + * + * @param text [String] displayed in this chip. + * @param modifier [Modifier] used to be applied to the layout of the chip. + * @param textColor Optional text [Color] for the chip. + * @param backgroundColor Optional background [Color] for the chip. + * @param enabled [Boolean] used to determine if chip responds to user input + * @param onClick Optional Callback for when the user taps this chip. + */ +@OptIn(ExperimentalMaterialApi::class) +@Composable +fun Chip( + text: String, + modifier: Modifier = Modifier, + textColor: Color = FirefoxTheme.colors.textActionPrimary, + backgroundColor: Color = FirefoxTheme.colors.actionPrimary, + enabled: Boolean = true, + onClick: () -> Unit, +) { + androidx.compose.material.Chip( + onClick = onClick, + enabled = enabled, + modifier = modifier, + colors = chipColors( + backgroundColor = backgroundColor, + contentColor = textColor, + ), + ) { + Text( + text = text, + style = FirefoxTheme.typography.body2, + color = textColor, + ) + } +} + +@Composable +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) +private fun ChipPreview() { + FirefoxTheme { + Column( + modifier = Modifier + .background(FirefoxTheme.colors.layer1), + verticalArrangement = Arrangement.SpaceEvenly, + ) { + Chip( + text = "Chirp", + onClick = {}, + ) + Chip( + text = "NoChirp", + enabled = false, + onClick = {}, + ) + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/compose/SelectableChip.kt b/app/src/main/java/org/mozilla/fenix/compose/SelectableChip.kt index fc715d55e64a..d2f63d5525d4 100644 --- a/app/src/main/java/org/mozilla/fenix/compose/SelectableChip.kt +++ b/app/src/main/java/org/mozilla/fenix/compose/SelectableChip.kt @@ -8,71 +8,82 @@ import android.content.res.Configuration.UI_MODE_NIGHT_NO import android.content.res.Configuration.UI_MODE_NIGHT_YES import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.selection.selectable -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.capitalize -import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import org.mozilla.fenix.theme.FirefoxTheme /** * Default layout of a selectable chip. * - * @param text [String] displayed in this chip. Ideally should only be one word. + * @param text [String] displayed in this chip. * @param isSelected Whether this should be shown as selected. - * @param selectedTextColor Optional text [Color] when the chip is selected. - * @param unselectedTextColor Optional text [Color] when the chip is not selected. - * @param selectedBackgroundColor Optional background [Color] when the chip is selected. - * @param unselectedBackgroundColor Optional background [Color] when the chip is not selected. + * @param selectableChipColors The color set defined by [SelectableChipColors] used to style the chip. * @param onClick Callback for when the user taps this. */ @Composable fun SelectableChip( text: String, isSelected: Boolean, - selectedTextColor: Color? = null, - unselectedTextColor: Color? = null, - selectedBackgroundColor: Color? = null, - unselectedBackgroundColor: Color? = null, + selectableChipColors: SelectableChipColors = SelectableChipColors.buildColors(), onClick: () -> Unit, ) { - Box( - modifier = Modifier - .selectable(isSelected) { onClick() } - .clip(MaterialTheme.shapes.small) - .background( - color = if (isSelected) { - selectedBackgroundColor ?: FirefoxTheme.colors.actionPrimary - } else { - unselectedBackgroundColor ?: FirefoxTheme.colors.actionTertiary - }, - ) - .padding(horizontal = 16.dp, vertical = 10.dp), + var selected by remember { mutableStateOf(isSelected) } + + Chip( + text = text, + textColor = if (selected) selectableChipColors.selectedTextColor else selectableChipColors.unselectedTextColor, + backgroundColor = if (selected) selectableChipColors.selectedBackgroundColor else selectableChipColors.unselectedBackgroundColor, ) { - Text( - text = text.capitalize(Locale.current), - style = TextStyle(fontSize = 14.sp), - color = if (isSelected) { - selectedTextColor ?: FirefoxTheme.colors.textActionPrimary - } else { - unselectedTextColor ?: FirefoxTheme.colors.textActionTertiary - }, + selected = !selected + onClick() + } +} + +/** + * Wrapper for the color parameters of [SelectableChip]. + * + * @param selectedTextColor Text [Color] when the chip is selected. + * @param unselectedTextColor Text [Color] when the chip is not selected. + * @param selectedBackgroundColor Background [Color] when the chip is selected. + * @param unselectedBackgroundColor Background [Color] when the chip is not selected. + */ +data class SelectableChipColors( + val selectedBackgroundColor: Color, + val unselectedBackgroundColor: Color, + val selectedTextColor: Color, + val unselectedTextColor: Color, +) { + companion object { + + /** + * Builder function used to construct an instance of [SelectableChipColors]. + */ + @Composable + fun buildColors( + selectedBackgroundColor: Color = FirefoxTheme.colors.actionPrimary, + unselectedBackgroundColor: Color = FirefoxTheme.colors.actionTertiary, + selectedTextColor: Color = FirefoxTheme.colors.textActionPrimary, + unselectedTextColor: Color = FirefoxTheme.colors.textActionTertiary, + ) = SelectableChipColors( + selectedBackgroundColor = selectedBackgroundColor, + unselectedBackgroundColor = unselectedBackgroundColor, + selectedTextColor = selectedTextColor, + unselectedTextColor = unselectedTextColor, ) } } +/** + * An example of a selectable chip. + */ @Composable @Preview(uiMode = UI_MODE_NIGHT_YES) @Preview(uiMode = UI_MODE_NIGHT_NO) @@ -84,12 +95,15 @@ private fun SelectableChipPreview() { .background(FirefoxTheme.colors.layer1), horizontalArrangement = Arrangement.SpaceEvenly, ) { - SelectableChip(text = "Chirp", isSelected = false) { } - SelectableChip(text = "Chirp", isSelected = true) { } + SelectableChip(text = "ChirpOne", isSelected = false) {} + SelectableChip(text = "ChirpTwo", isSelected = true) {} } } } +/** + * An example of a [SelectableChip] with custom colors + */ @Composable @Preview(uiMode = UI_MODE_NIGHT_YES) @Preview(uiMode = UI_MODE_NIGHT_NO) @@ -102,17 +116,25 @@ private fun SelectableChipWithCustomColorsPreview() { horizontalArrangement = Arrangement.SpaceEvenly, ) { SelectableChip( - text = "Chirp", + text = "Yellow", isSelected = false, - unselectedTextColor = FirefoxTheme.colors.textSecondary, - unselectedBackgroundColor = Color.Cyan, - ) { } + selectableChipColors = SelectableChipColors( + selectedBackgroundColor = Color.Yellow, + unselectedBackgroundColor = Color.DarkGray, + selectedTextColor = Color.Black, + unselectedTextColor = Color.Gray, + ), + ) {} SelectableChip( - text = "Chirp", + text = "Cyan", isSelected = true, - selectedTextColor = Color.Black, - selectedBackgroundColor = Color.Yellow, - ) { } + selectableChipColors = SelectableChipColors( + selectedBackgroundColor = Color.Cyan, + unselectedBackgroundColor = Color.DarkGray, + selectedTextColor = Color.Red, + unselectedTextColor = Color.Gray, + ), + ) {} } } } diff --git a/app/src/main/java/org/mozilla/fenix/home/pocket/PocketStoriesComposables.kt b/app/src/main/java/org/mozilla/fenix/home/pocket/PocketStoriesComposables.kt index e90d0c529199..b4e83fa7ada2 100644 --- a/app/src/main/java/org/mozilla/fenix/home/pocket/PocketStoriesComposables.kt +++ b/app/src/main/java/org/mozilla/fenix/home/pocket/PocketStoriesComposables.kt @@ -68,6 +68,7 @@ import org.mozilla.fenix.compose.ListItemTabLarge import org.mozilla.fenix.compose.ListItemTabLargePlaceholder import org.mozilla.fenix.compose.ListItemTabSurface import org.mozilla.fenix.compose.SelectableChip +import org.mozilla.fenix.compose.SelectableChipColors import org.mozilla.fenix.compose.StaggeredHorizontalGrid import org.mozilla.fenix.compose.TabSubtitleWithInterdot import org.mozilla.fenix.compose.inComposePreview @@ -432,6 +433,13 @@ fun PocketStoriesCategories( categoryColors: PocketStoriesCategoryColors = PocketStoriesCategoryColors.buildColors(), onCategoryClick: (PocketRecommendedStoriesCategory) -> Unit, ) { + val selectableChipColors = SelectableChipColors( + selectedTextColor = categoryColors.selectedTextColor, + unselectedTextColor = categoryColors.unselectedTextColor, + selectedBackgroundColor = categoryColors.selectedBackgroundColor, + unselectedBackgroundColor = categoryColors.unselectedBackgroundColor, + ) + Box( modifier = modifier.semantics { testTagsAsResourceId = true @@ -443,13 +451,12 @@ fun PocketStoriesCategories( verticalItemsSpacing = 16.dp, ) { categories.filter { it.name != POCKET_STORIES_DEFAULT_CATEGORY_NAME }.forEach { category -> + val isSelected = selections.map { it.name }.contains(category.name) + SelectableChip( text = category.name, - isSelected = selections.map { it.name }.contains(category.name), - selectedTextColor = categoryColors.selectedTextColor, - unselectedTextColor = categoryColors.unselectedTextColor, - selectedBackgroundColor = categoryColors.selectedBackgroundColor, - unselectedBackgroundColor = categoryColors.unselectedBackgroundColor, + isSelected = isSelected, + selectableChipColors = selectableChipColors, ) { onCategoryClick(category) } @@ -479,10 +486,10 @@ data class PocketStoriesCategoryColors( */ @Composable fun buildColors( - selectedBackgroundColor: Color = FirefoxTheme.colors.actionPrimary, - unselectedBackgroundColor: Color = FirefoxTheme.colors.actionTertiary, - selectedTextColor: Color = FirefoxTheme.colors.textActionPrimary, - unselectedTextColor: Color = FirefoxTheme.colors.textActionTertiary, + selectedBackgroundColor: Color = SelectableChipColors.buildColors().selectedBackgroundColor, + unselectedBackgroundColor: Color = SelectableChipColors.buildColors().unselectedBackgroundColor, + selectedTextColor: Color = SelectableChipColors.buildColors().selectedTextColor, + unselectedTextColor: Color = SelectableChipColors.buildColors().unselectedTextColor, ) = PocketStoriesCategoryColors( selectedBackgroundColor = selectedBackgroundColor, unselectedBackgroundColor = unselectedBackgroundColor,