From a1c3eafee60bae03616d777ead8b9d60f6283e9b Mon Sep 17 00:00:00 2001 From: Till-JS <101404291+Till-JS@users.noreply.github.com> Date: Sat, 13 Dec 2025 14:34:22 +0100 Subject: [PATCH] feat(calendar): add context menu support to MonthView and MultiDayView MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add oncontextmenu handler to event-pill in MonthView - Add oncontextmenu handler to event-card in MultiDayView - Move EventContextMenu to layout level for proper z-index stacking - Context menu now appears above DateStrip and other fixed elements 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../src/lib/components/calendar/MonthView.svelte | 10 ++++++++++ .../lib/components/calendar/MultiDayView.svelte | 10 ++++++++++ .../apps/web/src/routes/(app)/+layout.svelte | 15 ++++++++++++--- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/apps/calendar/apps/web/src/lib/components/calendar/MonthView.svelte b/apps/calendar/apps/web/src/lib/components/calendar/MonthView.svelte index 14d5abc4e..81a13963b 100644 --- a/apps/calendar/apps/web/src/lib/components/calendar/MonthView.svelte +++ b/apps/calendar/apps/web/src/lib/components/calendar/MonthView.svelte @@ -5,6 +5,7 @@ import { settingsStore } from '$lib/stores/settings.svelte'; import { searchStore } from '$lib/stores/search.svelte'; import { todosStore } from '$lib/stores/todos.svelte'; + import { eventContextMenuStore } from '$lib/stores/eventContextMenu.svelte'; import TodoDayCell from './TodoDayCell.svelte'; import { goto } from '$app/navigation'; import { @@ -241,6 +242,14 @@ viewStore.setDate(day); viewStore.setViewType('day'); } + + function handleEventContextMenu(event: CalendarEvent, e: MouseEvent) { + e.preventDefault(); + e.stopPropagation(); + // Don't show context menu for draft events + if (eventsStore.isDraftEvent(event.id)) return; + eventContextMenuStore.show(event, e.clientX, e.clientY); + }
@@ -297,6 +306,7 @@ style="background-color: {calendarsStore.getColor(event.calendarId)}" onpointerdown={(e) => startDrag(event, e)} onclick={(e) => !isDraft && handleEventClick(event, e)} + oncontextmenu={(e) => handleEventContextMenu(event, e)} role="button" tabindex="0" > diff --git a/apps/calendar/apps/web/src/lib/components/calendar/MultiDayView.svelte b/apps/calendar/apps/web/src/lib/components/calendar/MultiDayView.svelte index ce5246266..52e9a3c65 100644 --- a/apps/calendar/apps/web/src/lib/components/calendar/MultiDayView.svelte +++ b/apps/calendar/apps/web/src/lib/components/calendar/MultiDayView.svelte @@ -5,6 +5,7 @@ import { settingsStore } from '$lib/stores/settings.svelte'; import { searchStore } from '$lib/stores/search.svelte'; import { todosStore, type Task } from '$lib/stores/todos.svelte'; + import { eventContextMenuStore } from '$lib/stores/eventContextMenu.svelte'; import TaskBlock from './TaskBlock.svelte'; import { goto } from '$app/navigation'; import { @@ -299,6 +300,14 @@ } } + function handleEventContextMenu(event: CalendarEvent, e: MouseEvent) { + e.preventDefault(); + e.stopPropagation(); + // Don't show context menu for draft events + if (eventsStore.isDraftEvent(event.id)) return; + eventContextMenuStore.show(event, e.clientX, e.clientY); + } + // ========== Drag & Drop Functions ========== function getDayFromX(clientX: number): Date | null { @@ -935,6 +944,7 @@ tabindex="0" onpointerdown={(e) => startDrag(event, e)} onclick={(e) => !isDraft && handleEventClick(event, e)} + oncontextmenu={(e) => handleEventContextMenu(event, e)} onkeydown={(e) => !isDraft && e.key === 'Enter' && goto(`/?event=${event.id}`)} title={`${formatEventTime(event.startTime)} - ${formatEventTime(event.endTime)}: ${event.title || (isDraft ? '(Neuer Termin)' : '')}`} > diff --git a/apps/calendar/apps/web/src/routes/(app)/+layout.svelte b/apps/calendar/apps/web/src/routes/(app)/+layout.svelte index aea016b1d..138420cce 100644 --- a/apps/calendar/apps/web/src/routes/(app)/+layout.svelte +++ b/apps/calendar/apps/web/src/routes/(app)/+layout.svelte @@ -50,6 +50,8 @@ import CalendarToolbar from '$lib/components/calendar/CalendarToolbar.svelte'; import CalendarToolbarContent from '$lib/components/calendar/CalendarToolbarContent.svelte'; import DateStrip from '$lib/components/calendar/DateStrip.svelte'; + import EventContextMenu from '$lib/components/event/EventContextMenu.svelte'; + import { eventContextMenuStore } from '$lib/stores/eventContextMenu.svelte'; // App switcher items const appItems = getPillAppItems('calendar'); @@ -275,6 +277,11 @@ goto('/login'); } + // Context menu edit handler - navigate to event + function handleContextMenuEdit(event: { id: string }) { + goto(`/?event=${event.id}`); + } + onMount(async () => { // Redirect to login if not authenticated if (!authStore.isAuthenticated) { @@ -408,7 +415,6 @@ onParseCreate={handleParseCreate} createText="Erstellen" appIcon="calendar" - primaryColor="#3b82f6" autoFocus={true} bottomOffset={isSidebarMode ? '0px' @@ -420,6 +426,9 @@
+ + +