init
This commit is contained in:
@@ -2,25 +2,26 @@ package cc.n0th1ng.tripmoney.navigation
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
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.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldColors
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -33,12 +34,17 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavHostController
|
||||
import cc.n0th1ng.tripmoney.Filter
|
||||
import cc.n0th1ng.tripmoney.R
|
||||
import cc.n0th1ng.tripmoney.data.entity.Category
|
||||
import cc.n0th1ng.tripmoney.screens.addexpense.categoriesToPreview
|
||||
import cc.n0th1ng.tripmoney.theme.TripMoneyTheme
|
||||
import cc.n0th1ng.tripmoney.utils.AllPreviews
|
||||
import com.composables.icons.materialsymbols.outlined.R.drawable
|
||||
@@ -50,21 +56,27 @@ fun TopBar(
|
||||
onDrawerClick: () -> Unit,
|
||||
title: String = "",
|
||||
isSearchable: Boolean = false,
|
||||
onFilterChange: (String) -> Unit
|
||||
onSearchChange: (String) -> Unit,
|
||||
filter: Filter,
|
||||
onFilterChange: (Filter) -> Unit,
|
||||
categories: List<Category>
|
||||
) {
|
||||
var isSearch by remember { mutableStateOf(false) }
|
||||
var showSearch by remember { mutableStateOf(false) }
|
||||
var showFilter by remember { mutableStateOf(false) }
|
||||
var value by remember { mutableStateOf("") }
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
TopAppBar(
|
||||
title = {
|
||||
if (isSearch && isSearchable) {
|
||||
if (showSearch && isSearchable) {
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
OutlinedTextField(
|
||||
textStyle = MaterialTheme.typography.bodyMedium,
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
modifier = Modifier.fillMaxWidth(0.9f).focusRequester(focusRequester),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(0.9f)
|
||||
.focusRequester(focusRequester),
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = Color.Transparent,
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
@@ -79,9 +91,9 @@ fun TopBar(
|
||||
trailingIcon = {
|
||||
Icon(
|
||||
modifier = Modifier.clickable(onClick = {
|
||||
isSearch = false
|
||||
showSearch = false
|
||||
value = ""
|
||||
onFilterChange("")
|
||||
onSearchChange("")
|
||||
}),
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = null
|
||||
@@ -90,7 +102,7 @@ fun TopBar(
|
||||
)
|
||||
LaunchedEffect(key1 = value) {
|
||||
delay(1000)
|
||||
onFilterChange(value)
|
||||
onSearchChange(value)
|
||||
}
|
||||
} else {
|
||||
Text(title)
|
||||
@@ -102,7 +114,7 @@ fun TopBar(
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
if (!isSearch && isSearchable) {
|
||||
if (!showSearch && isSearchable) {
|
||||
Row(
|
||||
modifier = Modifier.padding(end = 13.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(15.dp)
|
||||
@@ -111,14 +123,16 @@ fun TopBar(
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
painter = painterResource(drawable.materialsymbols_ic_filter_alt_outlined),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.clickable(onClick = {})
|
||||
modifier = Modifier.clickable(onClick = {
|
||||
showFilter = true
|
||||
})
|
||||
)
|
||||
Icon(
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
painter = painterResource(drawable.materialsymbols_ic_search_outlined),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.clickable(onClick = {
|
||||
isSearch = true
|
||||
showSearch = true
|
||||
})
|
||||
)
|
||||
|
||||
@@ -127,13 +141,96 @@ fun TopBar(
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
if (showFilter) {
|
||||
FilterDialog(
|
||||
onDismiss = { showFilter = false },
|
||||
onSave = { newFilter ->
|
||||
onFilterChange(newFilter)
|
||||
showFilter = false
|
||||
},
|
||||
categories = categories,
|
||||
filter = filter
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FilterDialog(
|
||||
onDismiss: () -> Unit,
|
||||
onSave: (Filter) -> Unit,
|
||||
categories: List<Category>,
|
||||
filter: Filter
|
||||
) {
|
||||
var filter by remember { mutableStateOf(filter) }
|
||||
var fromAmountString by remember { mutableStateOf(filter.startAmount.toString()) }
|
||||
var toAmountString by remember { mutableStateOf(filter.endAmount.toString()) }
|
||||
AlertDialog(
|
||||
onDismiss, {
|
||||
Button(
|
||||
enabled = true,
|
||||
onClick = {
|
||||
onSave(
|
||||
filter.withStartAmount(fromAmountString.safeToDouble())
|
||||
.withEndAmount( toAmountString.safeToDouble())
|
||||
)
|
||||
}) { Text(stringResource(R.string.save)) }
|
||||
}, title = { Text("Filter") },
|
||||
text = {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
Text(text = "Categories")
|
||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
categories.forEach {
|
||||
FilterChip(selected = filter.categories.contains(it), onClick = {
|
||||
filter = if (filter.categories.contains(it)) {
|
||||
filter.without(it)
|
||||
} else {
|
||||
filter.with(it)
|
||||
}
|
||||
}, label = { Text(text = it.name) })
|
||||
}
|
||||
}
|
||||
AmountTextField(label = "from", onValueChange = { newText ->
|
||||
fromAmountString = newText
|
||||
}, value = fromAmountString)
|
||||
AmountTextField(label = "to", onValueChange = { newText ->
|
||||
toAmountString = newText
|
||||
}, value = toAmountString)
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AmountTextField(label: String, onValueChange: (String) -> Unit, value: String) {
|
||||
var value by remember { mutableStateOf(value) }
|
||||
OutlinedTextField(
|
||||
label = { Text(label) },
|
||||
value = if (value == Double.MAX_VALUE.toString()) "∞" else value,
|
||||
onValueChange = { newText ->
|
||||
if (newText == Double.MAX_VALUE.toString()) {
|
||||
value = "∞"
|
||||
return@OutlinedTextField
|
||||
}
|
||||
val regex = Regex("^\\d*\\.?\\d{0,2}$")
|
||||
if (regex.matches(newText)) {
|
||||
value = newText
|
||||
onValueChange(value)
|
||||
}
|
||||
},
|
||||
placeholder = { Text("0.00") },
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Decimal,
|
||||
imeAction = ImeAction.Done
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun TopBarSettings(navController: NavHostController) {
|
||||
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.settings)) },
|
||||
navigationIcon = {
|
||||
@@ -151,7 +248,30 @@ fun PreviewTopBar() {
|
||||
TopBar(
|
||||
onDrawerClick = {},
|
||||
title = "Essa",
|
||||
onFilterChange = {}
|
||||
onSearchChange = {},
|
||||
onFilterChange = {},
|
||||
isSearchable = true,
|
||||
categories = categoriesToPreview,
|
||||
filter = Filter()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@AllPreviews
|
||||
@Composable
|
||||
fun PreviewFilterDialog() {
|
||||
TripMoneyTheme {
|
||||
FilterDialog(
|
||||
onDismiss = {},
|
||||
onSave = {},
|
||||
categories = categoriesToPreview,
|
||||
filter = Filter()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.safeToDouble(): Double {
|
||||
if(this == "∞") return Double.MAX_VALUE
|
||||
if(this.isEmpty()) return 0.0
|
||||
return this.toDouble()
|
||||
}
|
||||
Reference in New Issue
Block a user