refactor(ui): one selection check and one motion spec across the pickers
Three inconsistencies the recurrence-picker review surfaced, all of the same kind: the redesigned picker had quietly diverged from the family. SelectedCheck existed as five copies app-side plus a private one in the kit, and they had drifted — the kit drew Icons.Rounded.Check, every app copy drew Icons.Default.Check. Delete all five against the kit's now public primitive (floret-kit!2), which settles the glyph on Rounded. The recurrence picker was the only full-screen picker whose selected row carried no check, relying on the tonal highlight alone. Add it to all six rows, matching OptionPicker, reminder, agenda range, timezone, calendar and Settings. Its two new AnimatedVisibility blocks were the only ones in the app using Compose's bare defaults rather than expandEnter()/collapseExit(). Those helpers honour rememberReduceMotion(), so the weekday and count cards were ignoring the system "remove animations" setting — an accessibility regression, not just a style drift. Bumps the floret-kit pointer; needs floret-kit!2 merged first. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.height
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
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.Cloud
|
import androidx.compose.material.icons.filled.Cloud
|
||||||
import androidx.compose.material.icons.filled.PhoneAndroid
|
import androidx.compose.material.icons.filled.PhoneAndroid
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@@ -33,6 +32,7 @@ import de.jeanlucmakiola.calendula.R
|
|||||||
import de.jeanlucmakiola.calendula.domain.CalendarSource
|
import de.jeanlucmakiola.calendula.domain.CalendarSource
|
||||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||||
import de.jeanlucmakiola.floret.components.Position
|
import de.jeanlucmakiola.floret.components.Position
|
||||||
|
import de.jeanlucmakiola.floret.components.SelectedCheck
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The app's single "which calendar" selection list, shared by the event editor
|
* The app's single "which calendar" selection list, shared by the event editor
|
||||||
@@ -99,13 +99,7 @@ private fun CalendarPickerGroup(
|
|||||||
selected = isSelected,
|
selected = isSelected,
|
||||||
leading = { CalendarColorChip(calendar.color) },
|
leading = { CalendarColorChip(calendar.color) },
|
||||||
trailing = if (isSelected) {
|
trailing = if (isSelected) {
|
||||||
{
|
{ SelectedCheck() }
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Check,
|
|
||||||
contentDescription = null,
|
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Check
|
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -26,6 +25,7 @@ import de.jeanlucmakiola.floret.components.CustomAmountEditor
|
|||||||
import de.jeanlucmakiola.floret.components.FullScreenPicker
|
import de.jeanlucmakiola.floret.components.FullScreenPicker
|
||||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||||
import de.jeanlucmakiola.floret.components.Position
|
import de.jeanlucmakiola.floret.components.Position
|
||||||
|
import de.jeanlucmakiola.floret.components.SelectedCheck
|
||||||
import de.jeanlucmakiola.floret.components.positionOf
|
import de.jeanlucmakiola.floret.components.positionOf
|
||||||
import de.jeanlucmakiola.floret.identity.collapseExit
|
import de.jeanlucmakiola.floret.identity.collapseExit
|
||||||
import de.jeanlucmakiola.floret.identity.expandEnter
|
import de.jeanlucmakiola.floret.identity.expandEnter
|
||||||
@@ -207,14 +207,6 @@ private fun PickerDescription(text: String) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun SelectedCheck() {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Check,
|
|
||||||
contentDescription = null,
|
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Agenda-range picker, full-screen. Two grouped lists — the calendar-aligned
|
* Agenda-range picker, full-screen. Two grouped lists — the calendar-aligned
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
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.Close
|
import androidx.compose.material.icons.filled.Close
|
||||||
import androidx.compose.material.icons.filled.Public
|
import androidx.compose.material.icons.filled.Public
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
@@ -41,6 +40,7 @@ import de.jeanlucmakiola.floret.components.FullScreenPicker
|
|||||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||||
import de.jeanlucmakiola.floret.components.InlineTextField
|
import de.jeanlucmakiola.floret.components.InlineTextField
|
||||||
import de.jeanlucmakiola.floret.components.Position
|
import de.jeanlucmakiola.floret.components.Position
|
||||||
|
import de.jeanlucmakiola.floret.components.SelectedCheck
|
||||||
import de.jeanlucmakiola.floret.components.positionOf
|
import de.jeanlucmakiola.floret.components.positionOf
|
||||||
import de.jeanlucmakiola.floret.locale.currentLocale
|
import de.jeanlucmakiola.floret.locale.currentLocale
|
||||||
|
|
||||||
@@ -223,14 +223,6 @@ private fun SectionHeader(text: String) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun SelectedCheck() {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Check,
|
|
||||||
contentDescription = null,
|
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ZoneRow(
|
private fun ZoneRow(
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ import de.jeanlucmakiola.floret.components.InlineTextField
|
|||||||
import de.jeanlucmakiola.floret.components.OptionCard
|
import de.jeanlucmakiola.floret.components.OptionCard
|
||||||
import de.jeanlucmakiola.floret.components.OptionPicker
|
import de.jeanlucmakiola.floret.components.OptionPicker
|
||||||
import de.jeanlucmakiola.floret.components.Position
|
import de.jeanlucmakiola.floret.components.Position
|
||||||
|
import de.jeanlucmakiola.floret.components.SelectedCheck
|
||||||
import de.jeanlucmakiola.floret.components.groupedShape as floretGroupedShape
|
import de.jeanlucmakiola.floret.components.groupedShape as floretGroupedShape
|
||||||
import de.jeanlucmakiola.floret.components.positionOf
|
import de.jeanlucmakiola.floret.components.positionOf
|
||||||
import de.jeanlucmakiola.calendula.ui.common.REMINDER_PRESETS
|
import de.jeanlucmakiola.calendula.ui.common.REMINDER_PRESETS
|
||||||
@@ -1379,6 +1380,11 @@ private fun RecurrencePickerDialog(
|
|||||||
title = stringResource(R.string.event_edit_recurrence_none),
|
title = stringResource(R.string.event_edit_recurrence_none),
|
||||||
position = positionOf(0, rowCount),
|
position = positionOf(0, rowCount),
|
||||||
selected = current == null,
|
selected = current == null,
|
||||||
|
trailing = if (current == null) {
|
||||||
|
{ SelectedCheck() }
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
onClick = { onSelect(null) },
|
onClick = { onSelect(null) },
|
||||||
)
|
)
|
||||||
RecurrenceFreq.entries.forEachIndexed { index, entry ->
|
RecurrenceFreq.entries.forEachIndexed { index, entry ->
|
||||||
@@ -1386,6 +1392,11 @@ private fun RecurrencePickerDialog(
|
|||||||
title = stringResource(recurrencePresetLabel(entry)),
|
title = stringResource(recurrencePresetLabel(entry)),
|
||||||
position = positionOf(index + 1, rowCount),
|
position = positionOf(index + 1, rowCount),
|
||||||
selected = isPlainPreset && parsed?.freq == entry,
|
selected = isPlainPreset && parsed?.freq == entry,
|
||||||
|
trailing = if (isPlainPreset && parsed?.freq == entry) {
|
||||||
|
{ SelectedCheck() }
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
onClick = { onSelect(SimpleRecurrence(entry).toRRule()) },
|
onClick = { onSelect(SimpleRecurrence(entry).toRRule()) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1393,6 +1404,11 @@ private fun RecurrencePickerDialog(
|
|||||||
title = stringResource(R.string.event_edit_recurrence_custom),
|
title = stringResource(R.string.event_edit_recurrence_custom),
|
||||||
position = positionOf(rowCount - 1, rowCount),
|
position = positionOf(rowCount - 1, rowCount),
|
||||||
selected = current != null && !isPlainPreset,
|
selected = current != null && !isPlainPreset,
|
||||||
|
trailing = if (current != null && !isPlainPreset) {
|
||||||
|
{ SelectedCheck() }
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
onClick = { customMode = true },
|
onClick = { customMode = true },
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@@ -1477,7 +1493,11 @@ private fun RecurrencePickerDialog(
|
|||||||
|
|
||||||
// Weekday picks — only meaningful on a weekly rule. The 16dp gap
|
// Weekday picks — only meaningful on a weekly rule. The 16dp gap
|
||||||
// sits inside the animated block so it grows/shrinks with the card.
|
// sits inside the animated block so it grows/shrinks with the card.
|
||||||
AnimatedVisibility(visible = freq == RecurrenceFreq.Weekly) {
|
AnimatedVisibility(
|
||||||
|
visible = freq == RecurrenceFreq.Weekly,
|
||||||
|
enter = expandEnter(),
|
||||||
|
exit = collapseExit(),
|
||||||
|
) {
|
||||||
GroupedSurface(
|
GroupedSurface(
|
||||||
position = Position.Alone,
|
position = Position.Alone,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -1507,6 +1527,11 @@ private fun RecurrencePickerDialog(
|
|||||||
title = stringResource(R.string.event_edit_recurrence_end_never),
|
title = stringResource(R.string.event_edit_recurrence_end_never),
|
||||||
position = Position.Top,
|
position = Position.Top,
|
||||||
selected = endMode == RecurrenceEndMode.Never,
|
selected = endMode == RecurrenceEndMode.Never,
|
||||||
|
trailing = if (endMode == RecurrenceEndMode.Never) {
|
||||||
|
{ SelectedCheck() }
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
onClick = { endMode = RecurrenceEndMode.Never },
|
onClick = { endMode = RecurrenceEndMode.Never },
|
||||||
)
|
)
|
||||||
GroupedRow(
|
GroupedRow(
|
||||||
@@ -1514,6 +1539,11 @@ private fun RecurrencePickerDialog(
|
|||||||
summary = untilSummary,
|
summary = untilSummary,
|
||||||
position = Position.Middle,
|
position = Position.Middle,
|
||||||
selected = endMode == RecurrenceEndMode.Until,
|
selected = endMode == RecurrenceEndMode.Until,
|
||||||
|
trailing = if (endMode == RecurrenceEndMode.Until) {
|
||||||
|
{ SelectedCheck() }
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
endMode = RecurrenceEndMode.Until
|
endMode = RecurrenceEndMode.Until
|
||||||
showUntilPicker = true
|
showUntilPicker = true
|
||||||
@@ -1527,12 +1557,21 @@ private fun RecurrencePickerDialog(
|
|||||||
Position.Bottom
|
Position.Bottom
|
||||||
},
|
},
|
||||||
selected = endMode == RecurrenceEndMode.Count,
|
selected = endMode == RecurrenceEndMode.Count,
|
||||||
|
trailing = if (endMode == RecurrenceEndMode.Count) {
|
||||||
|
{ SelectedCheck() }
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
onClick = { endMode = RecurrenceEndMode.Count },
|
onClick = { endMode = RecurrenceEndMode.Count },
|
||||||
)
|
)
|
||||||
// Animated for the same reason the weekday card is: this is the
|
// Animated for the same reason the weekday card is: this is the
|
||||||
// picker's other collapsible block, and a bare `if` here made
|
// picker's other collapsible block, and a bare `if` here made
|
||||||
// the ends group snap while the card above it glided.
|
// the ends group snap while the card above it glided.
|
||||||
AnimatedVisibility(visible = endMode == RecurrenceEndMode.Count) {
|
AnimatedVisibility(
|
||||||
|
visible = endMode == RecurrenceEndMode.Count,
|
||||||
|
enter = expandEnter(),
|
||||||
|
exit = collapseExit(),
|
||||||
|
) {
|
||||||
GroupedSurface(
|
GroupedSurface(
|
||||||
position = Position.Bottom,
|
position = Position.Bottom,
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ import androidx.compose.material.icons.filled.BugReport
|
|||||||
import androidx.compose.material.icons.filled.Cake
|
import androidx.compose.material.icons.filled.Cake
|
||||||
import androidx.compose.material.icons.filled.CalendarMonth
|
import androidx.compose.material.icons.filled.CalendarMonth
|
||||||
import androidx.compose.material.icons.filled.Dashboard
|
import androidx.compose.material.icons.filled.Dashboard
|
||||||
import androidx.compose.material.icons.filled.Check
|
|
||||||
import androidx.compose.material.icons.filled.DragHandle
|
import androidx.compose.material.icons.filled.DragHandle
|
||||||
import androidx.compose.material.icons.filled.SwapVert
|
import androidx.compose.material.icons.filled.SwapVert
|
||||||
import androidx.compose.material.icons.filled.ExpandLess
|
import androidx.compose.material.icons.filled.ExpandLess
|
||||||
@@ -131,6 +130,7 @@ import de.jeanlucmakiola.floret.components.Position
|
|||||||
import de.jeanlucmakiola.calendula.ui.common.REMINDER_PRESETS
|
import de.jeanlucmakiola.calendula.ui.common.REMINDER_PRESETS
|
||||||
import de.jeanlucmakiola.calendula.ui.common.ReminderDefaultPicker
|
import de.jeanlucmakiola.calendula.ui.common.ReminderDefaultPicker
|
||||||
import de.jeanlucmakiola.calendula.ui.common.TimePickerAlert
|
import de.jeanlucmakiola.calendula.ui.common.TimePickerAlert
|
||||||
|
import de.jeanlucmakiola.floret.components.SelectedCheck
|
||||||
import de.jeanlucmakiola.floret.components.positionOf
|
import de.jeanlucmakiola.floret.components.positionOf
|
||||||
import de.jeanlucmakiola.calendula.ui.common.reminderLeadTimeLabel
|
import de.jeanlucmakiola.calendula.ui.common.reminderLeadTimeLabel
|
||||||
import de.jeanlucmakiola.calendula.ui.common.SnoozeDurationPicker
|
import de.jeanlucmakiola.calendula.ui.common.SnoozeDurationPicker
|
||||||
@@ -1093,13 +1093,7 @@ private fun NotificationsScreen(
|
|||||||
},
|
},
|
||||||
position = Position.Top,
|
position = Position.Top,
|
||||||
trailing = if (batteryExempt) {
|
trailing = if (batteryExempt) {
|
||||||
{
|
{ SelectedCheck() }
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Check,
|
|
||||||
contentDescription = null,
|
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
},
|
},
|
||||||
@@ -1845,13 +1839,7 @@ private fun FontOptionRow(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
trailing = if (selected) {
|
trailing = if (selected) {
|
||||||
{
|
{ SelectedCheck() }
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Check,
|
|
||||||
contentDescription = null,
|
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
},
|
},
|
||||||
|
|||||||
Submodule floret-kit updated: df8bdbaf73...b4954713ff
Reference in New Issue
Block a user