mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 21:21:10 +02:00
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:
parent
2777447ae8
commit
c710f43391
4 changed files with 226 additions and 18 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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} />
|
||||
|
|
@ -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;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue