From ecda4535d826f95f73f1b399fe6102817a003bea Mon Sep 17 00:00:00 2001 From: Till JS Date: Mon, 23 Mar 2026 22:51:07 +0100 Subject: [PATCH] feat: add right-click context menus to calendar agenda, chat, contacts, and storage - Calendar AgendaView: edit, duplicate, delete events (reuses WeekView i18n) - Chat ConversationList: rename, archive, delete conversations - Contacts ContactGridView: open, favorite, call, email, delete - Storage FileCard: replace custom dropdown with shared ContextMenu Co-Authored-By: Claude Opus 4.6 (1M context) --- .../lib/components/calendar/AgendaView.svelte | 78 +++++++++++++- .../components/chat/ConversationList.svelte | 58 +++++++++- .../components/views/ContactGridView.svelte | 57 ++++++++++ .../src/lib/components/files/FileCard.svelte | 101 +++++++----------- 4 files changed, 231 insertions(+), 63 deletions(-) diff --git a/apps/calendar/apps/web/src/lib/components/calendar/AgendaView.svelte b/apps/calendar/apps/web/src/lib/components/calendar/AgendaView.svelte index fd04c2662..75f2471d1 100644 --- a/apps/calendar/apps/web/src/lib/components/calendar/AgendaView.svelte +++ b/apps/calendar/apps/web/src/lib/components/calendar/AgendaView.svelte @@ -7,7 +7,9 @@ import { format, parseISO, isToday, isTomorrow, startOfDay } from 'date-fns'; import { de } from 'date-fns/locale'; import { toDate } from '$lib/utils/eventDateHelpers'; - import type { CalendarEvent } from '@calendar/shared'; + import type { CalendarEvent, CreateEventInput } from '@calendar/shared'; + import { ContextMenu, type ContextMenuItem } from '@manacore/shared-ui'; + import { _ } from 'svelte-i18n'; interface Props { /** Optional date override for carousel navigation (uses viewStore.currentDate if not provided) */ @@ -85,6 +87,59 @@ onEventClick(event); } } + + // Context menu state + let contextMenuVisible = $state(false); + let contextMenuX = $state(0); + let contextMenuY = $state(0); + let contextMenuEvent = $state(null); + + function handleContextMenu(event: CalendarEvent, e: MouseEvent) { + contextMenuX = e.clientX; + contextMenuY = e.clientY; + contextMenuEvent = event; + contextMenuVisible = true; + } + + function getContextMenuItems(): ContextMenuItem[] { + if (!contextMenuEvent) return []; + const event = contextMenuEvent; + + return [ + { + id: 'edit', + label: $_('calendar.contextMenu.edit'), + action: () => { + handleEventClick(event); + }, + }, + { + id: 'duplicate', + label: $_('calendar.contextMenu.duplicate'), + action: async () => { + await eventsStore.createEvent({ + calendarId: event.calendarId, + title: `${event.title} (${$_('calendar.contextMenu.copy')})`, + description: event.description ?? undefined, + location: event.location ?? undefined, + startTime: event.startTime, + endTime: event.endTime, + isAllDay: event.isAllDay, + timezone: event.timezone ?? undefined, + color: event.color ?? undefined, + status: event.status ?? undefined, + }); + }, + }, + { id: 'divider-1', label: '', type: 'divider' }, + { + id: 'delete', + label: $_('calendar.contextMenu.delete'), + variant: 'danger', + action: () => eventsStore.deleteEvent(event.id), + }, + ]; + }
@@ -110,7 +165,15 @@
{#each group.events as event} -