feat(calendar): add DateStrip context menu with display settings

- Add context menu for DateStrip with toggle switches for:
  - Moon phases visibility
  - Event indicators visibility
  - Weekday names visibility
  - Weekend highlighting (subtle background color)
  - Month divider lines (spacing always present, line toggleable)
  - Compact mode
- Add glass effect to "Today" button matching PillNav styling
- Add toggle switch support to shared ContextMenu component
- Persist all settings to localStorage with cloud sync support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2025-12-13 15:06:41 +01:00
parent 2777447ae8
commit c710f43391
4 changed files with 226 additions and 18 deletions

View file

@ -262,8 +262,11 @@
{@const isFirstOfMonth = day.getDate() === 1}
{@const moonPhase = isSignificantMoonPhase(day)}
{@const eventCount = getEventCount(day)}
{#if isFirstOfMonth && settingsStore.dateStripShowMonthDividers}
<div class="month-divider"></div>
{#if isFirstOfMonth}
<div
class="month-divider"
class:show-line={settingsStore.dateStripShowMonthDividers}
></div>
{/if}
<button
class="day-item"
@ -277,12 +280,14 @@
onclick={() => handleDayClick(day)}
class:is-today={dayIsToday}
>
{#if moonPhase.significant}
{#if moonPhase.significant && settingsStore.dateStripShowMoonPhases}
<span class="moon-indicator">{moonPhase.emoji}</span>
{/if}
<span class="day-weekday">{format(day, 'EE', { locale: de })}</span>
{#if settingsStore.dateStripShowWeekday}
<span class="day-weekday">{format(day, 'EE', { locale: de })}</span>
{/if}
<span class="day-number">{format(day, 'd')}</span>
{#if eventCount > 0}
{#if eventCount > 0 && settingsStore.dateStripShowEventIndicators}
<div class="event-dots">
{#each Array(eventCount) as _, i}
<span class="event-dot"></span>
@ -295,6 +300,8 @@
</div>
</div>
<DateStripContextMenu bind:this={contextMenu} />
<style>
.date-strip-wrapper {
position: fixed;
@ -333,25 +340,23 @@
flex-direction: column;
align-items: center;
padding: 0.375rem 0.875rem;
background: hsl(var(--color-primary) / 0.1);
border: 1px solid hsl(var(--color-primary) / 0.3);
background: hsl(var(--color-surface) / 0.85);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid hsl(var(--color-border));
border-radius: 9999px;
cursor: pointer;
color: hsl(var(--color-primary));
pointer-events: auto;
transition: all 0.2s ease;
box-shadow: 0 2px 8px hsl(var(--color-foreground) / 0.08);
}
.today-button:hover {
background: hsl(var(--color-primary));
border-color: hsl(var(--color-primary));
color: hsl(var(--color-primary-foreground, 0 0% 100%));
background: hsl(var(--color-surface) / 0.95);
border-color: hsl(var(--color-primary) / 0.3);
transform: translateY(-50%) scale(1.02);
box-shadow: 0 4px 12px hsl(var(--color-primary) / 0.3);
}
.today-button:hover .today-date {
color: rgba(255, 255, 255, 0.85);
box-shadow: 0 4px 12px hsl(var(--color-foreground) / 0.12);
}
.today-label {
@ -364,7 +369,7 @@
.today-date {
font-size: 0.75rem;
font-weight: 500;
color: hsl(var(--color-primary) / 0.8);
color: hsl(var(--color-muted-foreground));
}
.date-strip-container {
@ -402,11 +407,15 @@
.month-divider {
width: 1px;
height: 40px;
background: var(--color-border, #e5e7eb);
background: transparent;
margin: 0 0.5rem;
flex-shrink: 0;
}
.month-divider.show-line {
background: hsl(var(--color-border));
}
.days-scroll {
display: flex;
align-items: center;
@ -479,7 +488,11 @@
}
.day-item.weekend {
color: var(--color-muted-foreground, #6b7280);
background: hsl(var(--color-muted) / 0.5);
}
.day-item.weekend:hover {
background: hsl(var(--color-muted) / 0.8);
}
.day-item.selected {
@ -590,4 +603,62 @@
margin: 0 0.375rem;
}
}
/* Compact mode */
.date-strip-wrapper.compact .date-strip-container {
padding: 0.25rem 0;
}
.date-strip-wrapper.compact .month-header {
padding: 0.125rem 0.5rem 0.25rem;
}
.date-strip-wrapper.compact .month-label {
font-size: 0.875rem;
}
.date-strip-wrapper.compact .day-item {
min-width: 40px;
height: 44px;
padding: 0.25rem;
}
.date-strip-wrapper.compact .day-weekday {
font-size: 0.625rem;
}
.date-strip-wrapper.compact .day-number {
font-size: 0.875rem;
}
.date-strip-wrapper.compact .moon-indicator {
font-size: 0.875rem;
top: -12px;
}
.date-strip-wrapper.compact .month-divider {
height: 28px;
}
.date-strip-wrapper.compact .event-dot {
width: 3px;
height: 3px;
}
.date-strip-wrapper.compact .month-header {
padding-top: 0.375rem;
}
.date-strip-wrapper.compact .today-button {
padding: 0.25rem 0.625rem;
margin-right: 1rem;
}
.date-strip-wrapper.compact .today-label {
font-size: 0.5625rem;
}
.date-strip-wrapper.compact .today-date {
font-size: 0.625rem;
}
</style>

View file

@ -0,0 +1,98 @@
<script lang="ts">
import { ContextMenu, type ContextMenuItem } from '@manacore/shared-ui';
import { Moon, Calendar, Eye, Columns, ArrowsIn } from '@manacore/shared-icons';
import { settingsStore } from '$lib/stores/settings.svelte';
// Context menu state
let visible = $state(false);
let x = $state(0);
let y = $state(0);
// Build menu items based on current settings
let menuItems = $derived.by((): ContextMenuItem[] => {
return [
{
id: 'moon-phases',
label: 'Mondphasen',
icon: Moon,
toggle: true,
checked: settingsStore.dateStripShowMoonPhases,
action: () => toggleSetting('dateStripShowMoonPhases'),
},
{
id: 'event-indicators',
label: 'Termin-Punkte',
icon: Eye,
toggle: true,
checked: settingsStore.dateStripShowEventIndicators,
action: () => toggleSetting('dateStripShowEventIndicators'),
},
{
id: 'weekday',
label: 'Wochentag',
icon: Calendar,
toggle: true,
checked: settingsStore.dateStripShowWeekday,
action: () => toggleSetting('dateStripShowWeekday'),
},
{
id: 'divider-1',
label: '',
type: 'divider',
},
{
id: 'highlight-weekends',
label: 'Wochenenden hervorheben',
icon: Calendar,
toggle: true,
checked: settingsStore.dateStripHighlightWeekends,
action: () => toggleSetting('dateStripHighlightWeekends'),
},
{
id: 'month-dividers',
label: 'Monatstrennlinien',
icon: Columns,
toggle: true,
checked: settingsStore.dateStripShowMonthDividers,
action: () => toggleSetting('dateStripShowMonthDividers'),
},
{
id: 'divider-2',
label: '',
type: 'divider',
},
{
id: 'compact',
label: 'Kompakte Ansicht',
icon: ArrowsIn,
toggle: true,
checked: settingsStore.dateStripCompact,
action: () => toggleSetting('dateStripCompact'),
},
];
});
function toggleSetting(key: keyof typeof settingsStore.settings) {
const currentValue = settingsStore.settings[key];
if (typeof currentValue === 'boolean') {
settingsStore.set(key, !currentValue);
}
}
function handleClose() {
visible = false;
}
// Export show function to be called from parent
export function show(clientX: number, clientY: number) {
x = clientX;
y = clientY;
visible = true;
}
export function hide() {
visible = false;
}
</script>
<ContextMenu {visible} {x} {y} items={menuItems} onClose={handleClose} />

View file

@ -26,6 +26,14 @@ export interface CalendarAppSettings {
dayEndHour: number; // Last visible hour (0-23)
allDayDisplayMode: AllDayDisplayMode; // How to display all-day events
// DateStrip settings
dateStripShowMoonPhases: boolean; // Show moon phase indicators
dateStripShowEventIndicators: boolean; // Show event dot indicators
dateStripShowWeekday: boolean; // Show weekday names (Mo, Di, Mi...)
dateStripHighlightWeekends: boolean; // Visually highlight weekend days
dateStripShowMonthDividers: boolean; // Show vertical dividers between months
dateStripCompact: boolean; // Use compact/smaller DateStrip
// UI settings
sidebarCollapsed: boolean;
@ -44,6 +52,14 @@ const DEFAULT_SETTINGS: CalendarAppSettings = {
dayStartHour: 6,
dayEndHour: 20,
allDayDisplayMode: 'header',
// DateStrip defaults
dateStripShowMoonPhases: true,
dateStripShowEventIndicators: true,
dateStripShowWeekday: true,
dateStripHighlightWeekends: true,
dateStripShowMonthDividers: true,
dateStripCompact: false,
// UI defaults
sidebarCollapsed: false,
defaultEventDuration: 60,
defaultReminder: 15,
@ -142,6 +158,25 @@ export const settingsStore = {
get allDayDisplayMode() {
return settings.allDayDisplayMode;
},
// DateStrip settings
get dateStripShowMoonPhases() {
return settings.dateStripShowMoonPhases;
},
get dateStripShowEventIndicators() {
return settings.dateStripShowEventIndicators;
},
get dateStripShowWeekday() {
return settings.dateStripShowWeekday;
},
get dateStripHighlightWeekends() {
return settings.dateStripHighlightWeekends;
},
get dateStripShowMonthDividers() {
return settings.dateStripShowMonthDividers;
},
get dateStripCompact() {
return settings.dateStripCompact;
},
get defaultEventDuration() {
return settings.defaultEventDuration;
},

View file

@ -20,6 +20,10 @@ export interface ContextMenuItem {
action?: () => void;
/** Additional data attached to the item */
data?: unknown;
/** Show a toggle switch (for boolean settings) */
toggle?: boolean;
/** Current toggle state (only used when toggle is true) */
checked?: boolean;
}
export interface ContextMenuState<T = unknown> {