Refactor chips

This commit is contained in:
Sad Ellie 2023-09-26 23:29:56 +03:00
parent 533c94db6b
commit 58359a2a2d
2 changed files with 171 additions and 110 deletions

View File

@ -0,0 +1,144 @@
/*
* Unitto is a unit converter for Android
* Copyright (c) 2023 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.core.ui.common
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.AssistChipDefaults
import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.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.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@Composable
fun FilterChip(
modifier: Modifier = Modifier,
selected: Boolean,
onClick: () -> Unit,
label: String,
imageVector: ImageVector,
contentDescription: String,
) {
Row(
modifier = modifier
.background(
color = if (selected) MaterialTheme.colorScheme.primaryContainer else MaterialTheme.colorScheme.surface,
shape = FilterChipDefaults.shape
)
.border(
width = 1.dp,
color = if (selected) Color.Transparent else MaterialTheme.colorScheme.outline,
shape = FilterChipDefaults.shape
)
.height(FilterChipDefaults.Height)
.clickable { onClick() }
.padding(start = 8.dp, end = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
AnimatedVisibility(visible = selected) {
Icon(
modifier = Modifier.height(FilterChipDefaults.IconSize),
imageVector = imageVector,
contentDescription = contentDescription,
tint = MaterialTheme.colorScheme.onPrimaryContainer
)
}
Text(
modifier = Modifier.padding(start = 8.dp),
text = label,
style = MaterialTheme.typography.labelLarge,
color = if (selected) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
@Composable
fun AssistChip(
modifier: Modifier = Modifier,
onClick: () -> Unit,
imageVector: ImageVector,
contentDescription: String,
) {
Row(
modifier = modifier
.background(
color = MaterialTheme.colorScheme.surface,
shape = AssistChipDefaults.shape
)
.border(
width = 1.dp,
color = MaterialTheme.colorScheme.outline,
shape = AssistChipDefaults.shape
)
.height(32.dp)
.clickable { onClick() }
.padding(horizontal = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
modifier = Modifier.height(AssistChipDefaults.IconSize),
imageVector = imageVector,
contentDescription = contentDescription,
tint = MaterialTheme.colorScheme.onPrimaryContainer
)
}
}
@Preview
@Composable
fun PreviewAssistChip() {
AssistChip(
onClick = {},
imageVector = Icons.Default.Settings,
contentDescription = ""
)
}
@Preview
@Composable
fun PreviewFilterChip() {
var isSelected by remember { mutableStateOf(true) }
FilterChip(
selected = isSelected,
onClick = { isSelected = !isSelected },
label = "Label",
imageVector = Icons.Default.Check,
contentDescription = ""
)
}

View File

@ -18,23 +18,17 @@
package com.sadellie.unitto.feature.converter.components package com.sadellie.unitto.feature.converter.components
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateDp import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
@ -43,7 +37,6 @@ import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Check
@ -51,23 +44,20 @@ import androidx.compose.material.icons.filled.ExpandMore
import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.ui.common.AssistChip
import com.sadellie.unitto.core.ui.common.FilterChip
import com.sadellie.unitto.data.model.ALL_UNIT_GROUPS import com.sadellie.unitto.data.model.ALL_UNIT_GROUPS
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.UnitGroup
@ -97,26 +87,14 @@ internal fun ChipsRow(
horizontalArrangement = Arrangement.spacedBy(8.dp) horizontalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
items(items, key = { it.name }) { item -> items(items, key = { it.name }) { item ->
val isSelected: Boolean = item == chosenUnitGroup val selected: Boolean = item == chosenUnitGroup
UnittoFilterChip( FilterChip(
isSelected = isSelected, selected = selected,
selectAction = { selectAction(if (item == chosenUnitGroup) null else item) } onClick = { selectAction(if (selected) null else item) },
) { label = stringResource(item.res),
AnimatedVisibility(visible = isSelected) { imageVector = Icons.Default.Check,
Icon( contentDescription = stringResource(R.string.checked_filter_description)
modifier = Modifier.height(18.dp), )
imageVector = Icons.Default.Check,
contentDescription = stringResource(R.string.checked_filter_description),
tint = MaterialTheme.colorScheme.onPrimaryContainer
)
}
Text(
modifier = Modifier.padding(start = 8.dp),
text = stringResource(item.res),
style = MaterialTheme.typography.labelLarge,
color = if (isSelected) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.onSurfaceVariant
)
}
} }
/** /**
@ -125,17 +103,11 @@ internal fun ChipsRow(
* scrolling right (to the last one). * scrolling right (to the last one).
*/ */
item("settings") { item("settings") {
UnittoFilterChip( AssistChip(
isSelected = false, onClick = navigateToSettingsAction,
selectAction = navigateToSettingsAction, imageVector = Icons.Default.Settings,
paddingValues = PaddingValues(horizontal = 8.dp) contentDescription = stringResource(R.string.open_settings_description)
) { )
Icon(
modifier = Modifier.height(18.dp),
imageVector = Icons.Default.Settings,
contentDescription = stringResource(R.string.open_settings_description)
)
}
} }
} }
} }
@ -175,38 +147,20 @@ fun ChipsFlexRow(
verticalArrangement = Arrangement.spacedBy(8.dp) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
items.forEach { item -> items.forEach { item ->
val isSelected: Boolean = item == chosenUnitGroup val selected: Boolean = item == chosenUnitGroup
UnittoFilterChip( FilterChip(
isSelected = isSelected, selected = selected,
selectAction = { selectAction(if (item == chosenUnitGroup) null else item) } onClick = { selectAction(if (selected) null else item) },
) { label = stringResource(item.res),
AnimatedVisibility(visible = isSelected) { imageVector = Icons.Default.Check,
Icon( contentDescription = stringResource(R.string.checked_filter_description)
modifier = Modifier.height(18.dp),
imageVector = Icons.Default.Check,
contentDescription = stringResource(R.string.checked_filter_description),
tint = MaterialTheme.colorScheme.onPrimaryContainer
)
}
Text(
modifier = Modifier.padding(start = 8.dp),
text = stringResource(item.res),
style = MaterialTheme.typography.labelLarge,
color = if (isSelected) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
UnittoFilterChip(
isSelected = false,
selectAction = navigateToSettingsAction,
paddingValues = PaddingValues(horizontal = 8.dp)
) {
Icon(
modifier = Modifier.height(18.dp),
imageVector = Icons.Default.Settings,
contentDescription = stringResource(R.string.open_settings_description)
) )
} }
AssistChip(
onClick = navigateToSettingsAction,
imageVector = Icons.Default.Settings,
contentDescription = stringResource(R.string.open_settings_description)
)
} }
IconButton( IconButton(
@ -224,43 +178,6 @@ fun ChipsFlexRow(
} }
} }
/**
* Basic chip implementation
*
* @param isSelected Chip state.
* @param selectAction Action to perform when clicking chip.
* @param paddingValues Padding values applied to Row.
* @param content Content of the list. Icon and/or label.
*/
@Composable
private fun UnittoFilterChip(
isSelected: Boolean,
selectAction: () -> Unit,
paddingValues: PaddingValues = PaddingValues(start = 8.dp, end = 16.dp),
content: @Composable RowScope.() -> Unit
) {
val chipShape = RoundedCornerShape(8.dp)
Row(
modifier = Modifier
.height(32.dp)
.clip(chipShape)
.background(
if (isSelected) MaterialTheme.colorScheme.primaryContainer else Color.Transparent
)
// Remove border when selected
.border(
1.dp,
if (isSelected) Color.Transparent else MaterialTheme.colorScheme.outline,
chipShape
)
.clickable { selectAction() }
.padding(paddingValues),
verticalAlignment = Alignment.CenterVertically
) {
content()
}
}
@Preview @Preview
@Composable @Composable
fun PreviewUnittoChips() { fun PreviewUnittoChips() {