You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
276 lines
9.2 KiB
276 lines
9.2 KiB
2 months ago
|
/*
|
||
|
* Copyright 2021 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package com.example.jetsnack.ui.home
|
||
|
|
||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||
|
//import androidx.compose.desktop.ui.tooling.preview.Preview
|
||
|
import androidx.compose.foundation.layout.Column
|
||
|
import androidx.compose.foundation.layout.Row
|
||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||
|
import androidx.compose.foundation.layout.padding
|
||
|
import androidx.compose.foundation.rememberScrollState
|
||
|
import androidx.compose.foundation.selection.selectable
|
||
|
import androidx.compose.foundation.verticalScroll
|
||
|
import androidx.compose.material.ContentAlpha
|
||
|
import androidx.compose.material.Icon
|
||
|
import androidx.compose.material.IconButton
|
||
|
import androidx.compose.material.LocalContentAlpha
|
||
|
import androidx.compose.material.MaterialTheme
|
||
|
import androidx.compose.material.Slider
|
||
|
import androidx.compose.material.SliderDefaults
|
||
|
import androidx.compose.material.Text
|
||
|
import androidx.compose.material.TopAppBar
|
||
|
import androidx.compose.material.icons.Icons
|
||
|
import androidx.compose.material.icons.filled.Close
|
||
|
import androidx.compose.material.icons.filled.Done
|
||
|
import androidx.compose.runtime.Composable
|
||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||
|
import androidx.compose.runtime.getValue
|
||
|
import androidx.compose.runtime.mutableStateOf
|
||
|
import androidx.compose.runtime.remember
|
||
|
import androidx.compose.runtime.setValue
|
||
|
import androidx.compose.ui.Modifier
|
||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||
|
import androidx.compose.ui.text.style.TextAlign
|
||
|
import androidx.compose.ui.unit.dp
|
||
|
import com.example.jetsnack.*
|
||
|
import com.example.jetsnack.flowlayout.FlowMainAxisAlignment
|
||
|
import com.example.jetsnack.flowlayout.FlowRow
|
||
|
import com.example.jetsnack.model.Filter
|
||
|
import com.example.jetsnack.model.SnackRepo
|
||
|
import com.example.jetsnack.ui.components.FilterChip
|
||
|
import com.example.jetsnack.ui.components.JetsnackScaffold
|
||
|
import com.example.jetsnack.ui.theme.JetsnackTheme
|
||
|
|
||
|
@OptIn(ExperimentalAnimationApi::class)
|
||
|
@Composable
|
||
|
fun FilterScreen(
|
||
|
onDismiss: () -> Unit
|
||
|
) {
|
||
|
var sortState by remember { mutableStateOf(SnackRepo.getSortDefault()) }
|
||
|
var maxCalories by remember { mutableStateOf(0f) }
|
||
|
val defaultFilter = SnackRepo.getSortDefault()
|
||
|
|
||
|
SnackDialog(onCloseRequest = onDismiss) {
|
||
|
val priceFilters = remember { SnackRepo.getPriceFilters() }
|
||
|
val categoryFilters = remember { SnackRepo.getCategoryFilters() }
|
||
|
val lifeStyleFilters = remember { SnackRepo.getLifeStyleFilters() }
|
||
|
JetsnackScaffold(
|
||
|
topBar = {
|
||
|
TopAppBar(
|
||
|
navigationIcon = {
|
||
|
IconButton(onClick = onDismiss) {
|
||
|
Icon(
|
||
|
imageVector = Icons.Filled.Close,
|
||
|
contentDescription = stringResource(id = MppR.string.close)
|
||
|
)
|
||
|
}
|
||
|
},
|
||
|
title = {
|
||
|
Text(
|
||
|
text = stringResource(id = MppR.string.label_filters),
|
||
|
modifier = Modifier.fillMaxWidth(),
|
||
|
textAlign = TextAlign.Center,
|
||
|
style = MaterialTheme.typography.h6
|
||
|
)
|
||
|
},
|
||
|
actions = {
|
||
|
var resetEnabled = sortState != defaultFilter
|
||
|
IconButton(
|
||
|
onClick = { /* TODO: Open search */ },
|
||
|
enabled = resetEnabled
|
||
|
) {
|
||
|
val alpha = if (resetEnabled) {
|
||
|
ContentAlpha.high
|
||
|
} else {
|
||
|
ContentAlpha.disabled
|
||
|
}
|
||
|
CompositionLocalProvider(LocalContentAlpha provides alpha) {
|
||
|
Text(
|
||
|
text = stringResource(id = MppR.string.reset),
|
||
|
style = MaterialTheme.typography.body2
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
backgroundColor = JetsnackTheme.colors.uiBackground
|
||
|
)
|
||
|
}
|
||
|
) {
|
||
|
Column(
|
||
|
Modifier
|
||
|
.fillMaxSize()
|
||
|
.verticalScroll(rememberScrollState())
|
||
|
.padding(horizontal = 24.dp, vertical = 16.dp),
|
||
|
) {
|
||
|
SortFiltersSection(
|
||
|
sortState = sortState,
|
||
|
onFilterChange = { filter ->
|
||
|
sortState = filter.name
|
||
|
}
|
||
|
)
|
||
|
FilterChipSection(
|
||
|
title = stringResource(id = MppR.string.price),
|
||
|
filters = priceFilters
|
||
|
)
|
||
|
FilterChipSection(
|
||
|
title = stringResource(id = MppR.string.category),
|
||
|
filters = categoryFilters
|
||
|
)
|
||
|
|
||
|
MaxCalories(
|
||
|
sliderPosition = maxCalories,
|
||
|
onValueChanged = { newValue ->
|
||
|
maxCalories = newValue
|
||
|
}
|
||
|
)
|
||
|
FilterChipSection(
|
||
|
title = stringResource(id = MppR.string.lifestyle),
|
||
|
filters = lifeStyleFilters
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Composable
|
||
|
expect fun SnackDialog(onCloseRequest: () -> Unit, content: @Composable () -> Unit)
|
||
|
|
||
|
|
||
|
@Composable
|
||
|
fun FilterChipSection(title: String, filters: List<Filter>) {
|
||
|
FilterTitle(text = title)
|
||
|
FlowRow(
|
||
|
mainAxisAlignment = FlowMainAxisAlignment.Center,
|
||
|
modifier = Modifier
|
||
|
.fillMaxWidth()
|
||
|
.padding(top = 12.dp, bottom = 16.dp)
|
||
|
.padding(horizontal = 4.dp)
|
||
|
) {
|
||
|
filters.forEach { filter ->
|
||
|
FilterChip(
|
||
|
filter = filter,
|
||
|
modifier = Modifier.padding(end = 4.dp, bottom = 8.dp)
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Composable
|
||
|
fun SortFiltersSection(sortState: String, onFilterChange: (Filter) -> Unit) {
|
||
|
FilterTitle(text = stringResource(id = MppR.string.sort))
|
||
|
Column(Modifier.padding(bottom = 24.dp)) {
|
||
|
SortFilters(
|
||
|
sortState = sortState,
|
||
|
onChanged = onFilterChange
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Composable
|
||
|
fun SortFilters(
|
||
|
sortFilters: List<Filter> = SnackRepo.getSortFilters(),
|
||
|
sortState: String,
|
||
|
onChanged: (Filter) -> Unit
|
||
|
) {
|
||
|
|
||
|
sortFilters.forEach { filter ->
|
||
|
SortOption(
|
||
|
text = filter.name,
|
||
|
icon = filter.icon,
|
||
|
selected = sortState == filter.name,
|
||
|
onClickOption = {
|
||
|
onChanged(filter)
|
||
|
}
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Composable
|
||
|
fun MaxCalories(sliderPosition: Float, onValueChanged: (Float) -> Unit) {
|
||
|
FlowRow {
|
||
|
FilterTitle(text = stringResource(id = MppR.string.max_calories))
|
||
|
Text(
|
||
|
text = stringResource(id = MppR.string.per_serving),
|
||
|
style = MaterialTheme.typography.body2,
|
||
|
color = JetsnackTheme.colors.brand,
|
||
|
modifier = Modifier.padding(top = 5.dp, start = 10.dp)
|
||
|
)
|
||
|
}
|
||
|
Slider(
|
||
|
value = sliderPosition,
|
||
|
onValueChange = { newValue ->
|
||
|
onValueChanged(newValue)
|
||
|
},
|
||
|
valueRange = 0f..300f,
|
||
|
steps = 5,
|
||
|
modifier = Modifier
|
||
|
.fillMaxWidth(),
|
||
|
colors = SliderDefaults.colors(
|
||
|
thumbColor = JetsnackTheme.colors.brand,
|
||
|
activeTrackColor = JetsnackTheme.colors.brand
|
||
|
)
|
||
|
)
|
||
|
}
|
||
|
|
||
|
@Composable
|
||
|
fun FilterTitle(text: String) {
|
||
|
Text(
|
||
|
text = text,
|
||
|
style = MaterialTheme.typography.h6,
|
||
|
color = JetsnackTheme.colors.brand,
|
||
|
modifier = Modifier.padding(bottom = 8.dp)
|
||
|
)
|
||
|
}
|
||
|
@Composable
|
||
|
fun SortOption(
|
||
|
text: String,
|
||
|
icon: ImageVector?,
|
||
|
onClickOption: () -> Unit,
|
||
|
selected: Boolean
|
||
|
) {
|
||
|
Row(
|
||
|
modifier = Modifier
|
||
|
.padding(top = 14.dp)
|
||
|
.selectable(selected) { onClickOption() }
|
||
|
) {
|
||
|
if (icon != null) {
|
||
|
Icon(imageVector = icon, contentDescription = null)
|
||
|
}
|
||
|
Text(
|
||
|
text = text,
|
||
|
style = MaterialTheme.typography.subtitle1,
|
||
|
modifier = Modifier
|
||
|
.padding(start = 10.dp)
|
||
|
.weight(1f)
|
||
|
)
|
||
|
if (selected) {
|
||
|
Icon(
|
||
|
imageVector = Icons.Filled.Done,
|
||
|
contentDescription = null,
|
||
|
tint = JetsnackTheme.colors.brand
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//@Preview
|
||
|
@Composable
|
||
|
fun FilterScreenPreview() {
|
||
|
FilterScreen(onDismiss = {})
|
||
|
}
|