feat(drawer): add View section to switch Month/Week/Day

The slide-out panel gains a "View" section mirroring the top-bar switcher
pill: three NavigationDrawerItems (Month/Week/Day) with the current view
highlighted; tapping one selects that view and closes the drawer. The pill
stays as-is for quick cycling.

Centralise each view's label + icon as labelRes/icon extensions on
CalendarView so the pill and the drawer share one mapping. The drawer's
"Today" jump is dropped — the top-bar Today action and error-state retry
still cover it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 21:48:49 +02:00
parent 31163da868
commit 21e7b1ff91
9 changed files with 65 additions and 25 deletions

View File

@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.Today
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
@@ -27,16 +26,19 @@ import de.jeanlucmakiola.calendula.ui.filter.CalendarFilterList
* Visual language (kept deliberately small so sizes don't drift):
* - Drawer title — `titleLarge`
* - Section headers (e.g. "Calendars") — `titleSmall`, primary, text only
* - Nav items (Today / Settings) — Material `NavigationDrawerItem`
* - Nav items (the views, Today / Settings) — Material `NavigationDrawerItem`
* (`labelLarge` label + a single 24dp leading icon)
*
* Hosts the per-calendar visibility filter (M3) inline — the calendar list with
* its checkboxes lives here rather than in a separate sheet — plus the "today"
* jump and a Settings entry (M4). The host screen owns the drawer state.
* The "View" section mirrors the top-bar switcher pill: tapping a view here
* selects it (and closes the drawer) rather than cycling. Also hosts the
* per-calendar visibility filter (M3) inline — the calendar list with its
* checkboxes lives here rather than in a separate sheet — plus a Settings
* entry (M4). The host screen owns the drawer state.
*/
@Composable
fun CalendarDrawer(
onToday: () -> Unit,
currentView: CalendarView,
onSelectView: (CalendarView) -> Unit,
onSettings: () -> Unit,
) {
ModalDrawerSheet {
@@ -47,14 +49,17 @@ fun CalendarDrawer(
modifier = Modifier.padding(horizontal = 28.dp, vertical = 24.dp),
)
HorizontalDivider()
Spacer(Modifier.height(8.dp))
NavigationDrawerItem(
icon = { Icon(Icons.Filled.Today, contentDescription = null) },
label = { Text(stringResource(R.string.month_today_action)) },
selected = false,
onClick = onToday,
modifier = Modifier.padding(horizontal = 12.dp),
)
DrawerSectionHeader(stringResource(R.string.view_section))
IMPLEMENTED_VIEWS.forEach { view ->
NavigationDrawerItem(
icon = { Icon(view.icon, contentDescription = null) },
label = { Text(stringResource(view.labelRes)) },
selected = view == currentView,
onClick = { onSelectView(view) },
modifier = Modifier.padding(horizontal = 12.dp),
)
}
Spacer(Modifier.height(8.dp))
HorizontalDivider()

View File

@@ -1,5 +1,13 @@
package de.jeanlucmakiola.calendula.ui.common
import androidx.annotation.StringRes
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CalendarViewDay
import androidx.compose.material.icons.filled.CalendarViewMonth
import androidx.compose.material.icons.filled.CalendarViewWeek
import androidx.compose.ui.graphics.vector.ImageVector
import de.jeanlucmakiola.calendula.R
/**
* The top-level calendar views the user can switch between (spec M1).
* Day is declared but not yet implemented (v0.5) — see [IMPLEMENTED_VIEWS].
@@ -10,6 +18,23 @@ enum class CalendarView {
Day,
}
/** Switcher label, shared by the top-bar pill and the drawer's View section. */
@get:StringRes
val CalendarView.labelRes: Int
get() = when (this) {
CalendarView.Month -> R.string.view_month
CalendarView.Week -> R.string.view_week
CalendarView.Day -> R.string.view_day
}
/** Leading icon for the view in the drawer's View section. */
val CalendarView.icon: ImageVector
get() = when (this) {
CalendarView.Month -> Icons.Filled.CalendarViewMonth
CalendarView.Week -> Icons.Filled.CalendarViewWeek
CalendarView.Day -> Icons.Filled.CalendarViewDay
}
/**
* Views that actually have a screen today. The view-switcher pill cycles
* through these in order.

View File

@@ -6,7 +6,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import de.jeanlucmakiola.calendula.R
/**
* Top-bar pill that shows the current view and cycles to the next one on tap
@@ -18,16 +17,11 @@ fun ViewSwitcherPill(
onCycle: () -> Unit,
modifier: Modifier = Modifier,
) {
val labelRes = when (current) {
CalendarView.Month -> R.string.view_month
CalendarView.Week -> R.string.view_week
CalendarView.Day -> R.string.view_day
}
FilledTonalButton(
onClick = onCycle,
shape = MaterialTheme.shapes.large,
modifier = modifier,
) {
Text(stringResource(labelRes))
Text(stringResource(current.labelRes))
}
}

View File

@@ -157,7 +157,11 @@ fun DayScreen(
gesturesEnabled = drawerState.isOpen,
drawerContent = {
CalendarDrawer(
onToday = { jumpToToday(); scope.launch { drawerState.close() } },
currentView = selectedView,
onSelectView = { view ->
onSelectView(view)
scope.launch { drawerState.close() }
},
onSettings = {
onOpenSettings()
scope.launch { drawerState.close() }

View File

@@ -130,8 +130,9 @@ fun MonthScreen(
gesturesEnabled = drawerState.isOpen,
drawerContent = {
CalendarDrawer(
onToday = {
jumpToToday()
currentView = selectedView,
onSelectView = { view ->
onSelectView(view)
scope.launch { drawerState.close() }
},
onSettings = {

View File

@@ -162,7 +162,11 @@ fun WeekScreen(
gesturesEnabled = drawerState.isOpen,
drawerContent = {
CalendarDrawer(
onToday = { jumpToToday(); scope.launch { drawerState.close() } },
currentView = selectedView,
onSelectView = { view ->
onSelectView(view)
scope.launch { drawerState.close() }
},
onSettings = {
onOpenSettings()
scope.launch { drawerState.close() }