Redesign Date calculator pages

This commit is contained in:
Sad Ellie 2023-11-14 17:46:59 +03:00
parent b83401e05a
commit dde5033784
4 changed files with 206 additions and 136 deletions

View File

@ -25,20 +25,23 @@ import android.content.Intent
import android.provider.CalendarContract
import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxHeight
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.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Event
@ -47,6 +50,7 @@ import androidx.compose.material.icons.outlined.Remove
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SegmentedButton
import androidx.compose.material3.SegmentedButtonDefaults
@ -142,6 +146,16 @@ private fun AddSubtractView(
focusedTextFieldValue = null
}
val showResult = remember(uiState.start, uiState.result) { uiState.start != uiState.result }
val inputWidth = animateFloatAsState(
targetValue = if (showResult) 0.5f else 1f,
label = "inputWidth"
)
val resultWidth = animateFloatAsState(
targetValue = if (showResult) 1f else 0f,
label = "resultWidth"
)
Column(Modifier.fillMaxSize()) {
Scaffold(
modifier = Modifier
@ -159,24 +173,23 @@ private fun AddSubtractView(
)
}
},
contentWindowInsets = WindowInsets(0,0,0,0)
contentWindowInsets = WindowInsets(0, 0, 0, 0)
) {
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(2.dp),
.padding(horizontal = 16.dp, vertical = 16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
FlowRow(
modifier = Modifier.padding(vertical = 16.dp),
maxItemsInEachRow = 2,
verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp)
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
DateTimeSelectorBlock(
modifier = Modifier
.weight(1f)
.fillMaxWidth(),
.fillMaxWidth(inputWidth.value),
containerColor = MaterialTheme.colorScheme.secondaryContainer,
title = stringResource(R.string.date_calculator_start),
dateTime = uiState.start,
onLongClick = { updateStart(ZonedDateTime.now()) },
@ -187,8 +200,8 @@ private fun AddSubtractView(
DateTimeSelectorBlock(
modifier = Modifier
.weight(1f)
.fillMaxWidth(),
.fillMaxWidth(resultWidth.value),
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
title = stringResource(R.string.date_calculator_end),
dateTime = uiState.result,
)
@ -212,74 +225,110 @@ private fun AddSubtractView(
shape = SegmentedButtonDefaults.itemShape(index = 1, count = 2),
icon = {}
) {
Icon(Icons.Outlined.Remove, stringResource(R.string.date_calculator_subtract))
Icon(
Icons.Outlined.Remove,
stringResource(R.string.date_calculator_subtract)
)
}
}
TimeUnitTextField(
modifier = Modifier.onFocusEvent {
if (it.hasFocus) {
addSymbol = updateYears
focusedTextFieldValue = uiState.years
}
},
value = uiState.years,
onValueChange = updateYears,
label = stringResource(R.string.date_calculator_years),
formatterSymbols = uiState.formatterSymbols
)
Column(
modifier = Modifier
.fillMaxWidth()
.background(
MaterialTheme.colorScheme.secondaryContainer,
RoundedCornerShape(32.dp)
)
.padding(16.dp, 24.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
TimeUnitTextField(
modifier = Modifier
.onFocusEvent {
if (it.hasFocus) {
addSymbol = updateYears
focusedTextFieldValue = uiState.years
}
}
.fillMaxWidth(),
value = uiState.years,
onValueChange = updateYears,
label = stringResource(R.string.date_calculator_years),
formatterSymbols = uiState.formatterSymbols
)
TimeUnitTextField(
modifier = Modifier.onFocusEvent {
if (it.hasFocus) {
addSymbol = updateMonths
focusedTextFieldValue = uiState.months
}
},
value = uiState.months,
onValueChange = updateMonths,
label = stringResource(R.string.date_calculator_months),
formatterSymbols = uiState.formatterSymbols
)
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
TimeUnitTextField(
modifier = Modifier
.onFocusEvent {
if (it.hasFocus) {
addSymbol = updateMonths
focusedTextFieldValue = uiState.months
}
}
.weight(1f),
value = uiState.months,
onValueChange = updateMonths,
label = stringResource(R.string.date_calculator_months),
formatterSymbols = uiState.formatterSymbols
)
TimeUnitTextField(
modifier = Modifier.onFocusEvent {
if (it.hasFocus) {
addSymbol = updateDays
focusedTextFieldValue = uiState.days
}
},
value = uiState.days,
onValueChange = updateDays,
label = stringResource(R.string.date_calculator_days),
formatterSymbols = uiState.formatterSymbols
)
TimeUnitTextField(
modifier = Modifier
.onFocusEvent {
if (it.hasFocus) {
addSymbol = updateDays
focusedTextFieldValue = uiState.days
}
}
.weight(1f),
value = uiState.days,
onValueChange = updateDays,
label = stringResource(R.string.date_calculator_days),
formatterSymbols = uiState.formatterSymbols
)
}
TimeUnitTextField(
modifier = Modifier.onFocusEvent {
if (it.hasFocus) {
addSymbol = updateHours
focusedTextFieldValue = uiState.hours
}
},
value = uiState.hours,
onValueChange = updateHours,
label = stringResource(R.string.date_calculator_hours),
formatterSymbols = uiState.formatterSymbols
)
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
TimeUnitTextField(
modifier = Modifier
.onFocusEvent {
if (it.hasFocus) {
addSymbol = updateHours
focusedTextFieldValue = uiState.hours
}
}
.weight(1f),
value = uiState.hours,
onValueChange = updateHours,
label = stringResource(R.string.date_calculator_hours),
formatterSymbols = uiState.formatterSymbols
)
TimeUnitTextField(
modifier = Modifier.onFocusEvent {
if (it.hasFocus) {
addSymbol = updateMinutes
focusedTextFieldValue = uiState.minutes
}
},
value = uiState.minutes,
onValueChange = updateMinutes,
label = stringResource(R.string.date_calculator_minutes),
formatterSymbols = uiState.formatterSymbols
)
TimeUnitTextField(
modifier = Modifier
.onFocusEvent {
if (it.hasFocus) {
addSymbol = updateMinutes
focusedTextFieldValue = uiState.minutes
}
}
.weight(1f),
value = uiState.minutes,
onValueChange = updateMinutes,
label = stringResource(R.string.date_calculator_minutes),
formatterSymbols = uiState.formatterSymbols
)
}
}
}
}
AnimatedVisibility(

View File

@ -35,14 +35,17 @@ import androidx.compose.foundation.layout.width
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.sadellie.unitto.core.ui.LocalLocale
import com.sadellie.unitto.core.ui.common.ProvideColor
import com.sadellie.unitto.core.ui.common.squashable
import com.sadellie.unitto.core.ui.datetime.formatDateWeekDayMonthYear
import com.sadellie.unitto.core.ui.datetime.formatTimeAmPm
@ -52,6 +55,7 @@ import java.time.ZonedDateTime
@Composable
internal fun DateTimeSelectorBlock(
modifier: Modifier = Modifier,
containerColor: Color,
title: String,
dateTime: ZonedDateTime,
onClick: () -> Unit = {},
@ -62,44 +66,35 @@ internal fun DateTimeSelectorBlock(
val locale = LocalLocale.current
val is24Hour = DateFormat.is24HourFormat(LocalContext.current)
Column(
modifier = modifier
.squashable(
onClick = onClick,
onLongClick = onLongClick,
interactionSource = remember { MutableInteractionSource() },
cornerRadiusRange = 8.dp..32.dp
)
.background(MaterialTheme.colorScheme.secondaryContainer)
.padding(16.dp),
horizontalAlignment = Alignment.Start
ProvideColor(
MaterialTheme.colorScheme.contentColorFor(containerColor)
) {
Text(title, style = MaterialTheme.typography.labelMedium)
Column(
modifier = Modifier.clickable(
indication = rememberRipple(),
interactionSource = remember { MutableInteractionSource() },
onClick = onTimeClick
)
) {
AnimatedContent(
targetState = dateTime,
transitionSpec = {
slideInVertically { height -> height } + fadeIn() togetherWith
slideOutVertically { height -> -height } + fadeOut() using
SizeTransform()
},
label = "Animated time",
) { time ->
Text(
text = time.formatTimeShort(locale, is24Hour),
style = MaterialTheme.typography.displaySmall,
maxLines = 1
modifier = Modifier
.squashable(
onClick = onClick,
onLongClick = onLongClick,
interactionSource = remember { MutableInteractionSource() },
cornerRadiusRange = 8.dp..32.dp
)
}
.background(containerColor)
.then(modifier)
.padding(16.dp),
horizontalAlignment = Alignment.Start
) {
Text(
text = title,
style = MaterialTheme.typography.labelMedium,
maxLines = 1,
)
if (!is24Hour) {
Column(
modifier = Modifier.clickable(
indication = rememberRipple(),
interactionSource = remember { MutableInteractionSource() },
onClick = onTimeClick
)
) {
AnimatedContent(
targetState = dateTime,
transitionSpec = {
@ -107,35 +102,54 @@ internal fun DateTimeSelectorBlock(
slideOutVertically { height -> -height } + fadeOut() using
SizeTransform()
},
label = "Animated am/pm",
label = "Animated time",
) { time ->
Text(
text = time.formatTimeAmPm(locale),
style = MaterialTheme.typography.bodyLarge,
text = time.formatTimeShort(locale, is24Hour),
style = MaterialTheme.typography.displaySmall,
maxLines = 1
)
}
}
}
AnimatedContent(
targetState = dateTime,
transitionSpec = {
slideInVertically { height -> height } + fadeIn() togetherWith
slideOutVertically { height -> -height } + fadeOut() using
SizeTransform()
},
label = "Animated date",
) { date ->
Text(
modifier = Modifier.clickable(
indication = rememberRipple(),
interactionSource = remember { MutableInteractionSource() },
onClick = onDateClick
),
text = date.formatDateWeekDayMonthYear(locale),
style = MaterialTheme.typography.bodySmall
)
if (!is24Hour) {
AnimatedContent(
targetState = dateTime,
transitionSpec = {
slideInVertically { height -> height } + fadeIn() togetherWith
slideOutVertically { height -> -height } + fadeOut() using
SizeTransform()
},
label = "Animated am/pm",
) { time ->
Text(
text = time.formatTimeAmPm(locale),
style = MaterialTheme.typography.bodyLarge,
maxLines = 1
)
}
}
}
AnimatedContent(
targetState = dateTime,
transitionSpec = {
slideInVertically { height -> height } + fadeIn() togetherWith
slideOutVertically { height -> -height } + fadeOut() using
SizeTransform()
},
label = "Animated date",
) { date ->
Text(
modifier = Modifier.clickable(
indication = rememberRipple(),
interactionSource = remember { MutableInteractionSource() },
onClick = onDateClick
),
text = date.formatDateWeekDayMonthYear(locale),
style = MaterialTheme.typography.bodySmall,
maxLines = 1
)
}
}
}
}
@ -144,8 +158,10 @@ internal fun DateTimeSelectorBlock(
@Composable
fun DateTimeSelectorBlockPreview() {
DateTimeSelectorBlock(
modifier = Modifier
.width(224.dp),
containerColor = MaterialTheme.colorScheme.secondaryContainer,
title = "End",
dateTime = ZonedDateTime.now(),
modifier = Modifier.width(224.dp)
)
}

View File

@ -21,7 +21,6 @@ package com.sadellie.unitto.feature.datecalculator.components
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Clear
import androidx.compose.material3.Icon
@ -46,7 +45,7 @@ internal fun TimeUnitTextField(
formatterSymbols: FormatterSymbols
) = CompositionLocalProvider(LocalTextInputService provides null) {
OutlinedTextField(
modifier = modifier.fillMaxWidth(),
modifier = modifier,
value = value,
onValueChange = { newValue ->
onValueChange(newValue.copy(newValue.text.filter { it.isDigit() }))

View File

@ -21,12 +21,14 @@ package com.sadellie.unitto.feature.datecalculator.difference
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -72,11 +74,12 @@ private fun DateDifferenceView(
.fillMaxSize()
.padding(16.dp),
maxItemsInEachRow = 2,
verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp)
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
DateTimeSelectorBlock(
modifier = Modifier
.background(MaterialTheme.colorScheme.secondaryContainer)
.weight(1f)
.fillMaxWidth(),
title = stringResource(R.string.date_calculator_start),
@ -85,10 +88,12 @@ private fun DateDifferenceView(
onLongClick = { setStartDate(ZonedDateTime.now()) },
onTimeClick = { dialogState = DialogState.FROM_TIME },
onDateClick = { dialogState = DialogState.FROM_DATE },
containerColor = MaterialTheme.colorScheme.secondaryContainer
)
DateTimeSelectorBlock(
modifier = Modifier
.background(MaterialTheme.colorScheme.secondaryContainer)
.weight(1f)
.fillMaxWidth(),
title = stringResource(R.string.date_calculator_end),
@ -97,6 +102,7 @@ private fun DateDifferenceView(
onLongClick = { setStartDate(ZonedDateTime.now()) },
onTimeClick = { dialogState = DialogState.TO_TIME },
onDateClick = { dialogState = DialogState.TO_DATE },
containerColor = MaterialTheme.colorScheme.secondaryContainer
)
AnimatedVisibility(