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,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Added
- The slide-out panel now has a "View" section to switch between Month,
Week, and Day, mirroring the top-bar switcher pill — tapping a view
selects it and closes the drawer. The current view is highlighted
### Fixed ### Fixed
- Typing in the event title, location, and description fields no longer - Typing in the event title, location, and description fields no longer
makes the cursor jump around: the form state's round-trip to the UI was makes the cursor jump around: the form state's round-trip to the UI was

View File

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

View File

@@ -1,5 +1,13 @@
package de.jeanlucmakiola.calendula.ui.common 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). * The top-level calendar views the user can switch between (spec M1).
* Day is declared but not yet implemented (v0.5) — see [IMPLEMENTED_VIEWS]. * Day is declared but not yet implemented (v0.5) — see [IMPLEMENTED_VIEWS].
@@ -10,6 +18,23 @@ enum class CalendarView {
Day, 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 * Views that actually have a screen today. The view-switcher pill cycles
* through these in order. * through these in order.

View File

@@ -6,7 +6,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource 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 * Top-bar pill that shows the current view and cycles to the next one on tap
@@ -18,16 +17,11 @@ fun ViewSwitcherPill(
onCycle: () -> Unit, onCycle: () -> Unit,
modifier: Modifier = Modifier, 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( FilledTonalButton(
onClick = onCycle, onClick = onCycle,
shape = MaterialTheme.shapes.large, shape = MaterialTheme.shapes.large,
modifier = modifier, modifier = modifier,
) { ) {
Text(stringResource(labelRes)) Text(stringResource(current.labelRes))
} }
} }

View File

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

View File

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

View File

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

View File

@@ -187,6 +187,7 @@
<string name="view_month">Monat</string> <string name="view_month">Monat</string>
<string name="view_week">Woche</string> <string name="view_week">Woche</string>
<string name="view_day">Tag</string> <string name="view_day">Tag</string>
<string name="view_section">Ansicht</string>
<!-- Kalender-Filter (M3) --> <!-- Kalender-Filter (M3) -->
<string name="filter_title">Kalender</string> <string name="filter_title">Kalender</string>

View File

@@ -188,6 +188,7 @@
<string name="view_month">Month</string> <string name="view_month">Month</string>
<string name="view_week">Week</string> <string name="view_week">Week</string>
<string name="view_day">Day</string> <string name="view_day">Day</string>
<string name="view_section">View</string>
<!-- Calendar filter (M3) --> <!-- Calendar filter (M3) -->
<string name="filter_title">Calendars</string> <string name="filter_title">Calendars</string>