From 0893e47ad39c000d7742e6c733d764730bf10f06 Mon Sep 17 00:00:00 2001 From: Till JS Date: Mon, 23 Mar 2026 21:56:58 +0100 Subject: [PATCH] feat(todo-web): add right-click context menu to task list Use shared ContextMenu component for quick actions: edit, toggle complete, change priority, move to project, and delete. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../web/src/lib/components/TaskList.svelte | 155 +++++++++++++++--- 1 file changed, 133 insertions(+), 22 deletions(-) diff --git a/apps/todo/apps/web/src/lib/components/TaskList.svelte b/apps/todo/apps/web/src/lib/components/TaskList.svelte index db5fb1564..739c99e74 100644 --- a/apps/todo/apps/web/src/lib/components/TaskList.svelte +++ b/apps/todo/apps/web/src/lib/components/TaskList.svelte @@ -3,6 +3,100 @@ import type { Task, UpdateTaskInput } from '@todo/shared'; import TaskItem from './TaskItem.svelte'; import { tasksStore } from '$lib/stores/tasks.svelte'; + import { projectsStore } from '$lib/stores/projects.svelte'; + import { ContextMenu, type ContextMenuItem } from '@manacore/shared-ui'; + + // Context menu state + let contextMenuVisible = $state(false); + let contextMenuX = $state(0); + let contextMenuY = $state(0); + let contextMenuTask = $state(null); + + function handleContextMenu(e: MouseEvent, task: Task) { + e.preventDefault(); + e.stopPropagation(); + contextMenuX = e.clientX; + contextMenuY = e.clientY; + contextMenuTask = task; + contextMenuVisible = true; + } + + function getContextMenuItems(): ContextMenuItem[] { + if (!contextMenuTask) return []; + const task = contextMenuTask; + + const items: ContextMenuItem[] = [ + { + id: 'edit', + label: 'Bearbeiten', + action: () => handleExpandTask(task.id), + }, + { + id: 'toggle-complete', + label: task.isCompleted ? 'Als offen markieren' : 'Als erledigt markieren', + action: () => handleToggleComplete(task), + }, + { id: 'divider-1', label: '', type: 'divider' }, + { + id: 'priority-low', + label: 'Niedrig', + action: () => handleSetPriority(task.id, 'low'), + disabled: task.priority === 'low', + }, + { + id: 'priority-medium', + label: 'Mittel', + action: () => handleSetPriority(task.id, 'medium'), + disabled: task.priority === 'medium', + }, + { + id: 'priority-high', + label: 'Hoch', + action: () => handleSetPriority(task.id, 'high'), + disabled: task.priority === 'high', + }, + { + id: 'priority-urgent', + label: 'Dringend', + action: () => handleSetPriority(task.id, 'urgent'), + disabled: task.priority === 'urgent', + }, + ]; + + // Add project move options if there are projects + const projects = projectsStore.activeProjects; + if (projects.length > 0) { + items.push({ id: 'divider-2', label: '', type: 'divider' }); + items.push({ + id: 'move-inbox', + label: 'In Inbox verschieben', + action: () => tasksStore.moveTask(task.id, null), + disabled: !task.projectId, + }); + for (const project of projects) { + items.push({ + id: `move-${project.id}`, + label: project.name, + action: () => tasksStore.moveTask(task.id, project.id), + disabled: task.projectId === project.id, + }); + } + } + + items.push({ id: 'divider-3', label: '', type: 'divider' }); + items.push({ + id: 'delete', + label: 'Löschen', + variant: 'danger', + action: () => handleDelete(task.id), + }); + + return items; + } + + async function handleSetPriority(taskId: string, priority: string) { + await tasksStore.updateTask(taskId, { priority: priority as Task['priority'] }); + } interface Props { tasks: Task[]; @@ -152,17 +246,20 @@ onfinalize={handleDndFinalize} > {#each items.filter((t) => t.id !== SHADOW_PLACEHOLDER_ITEM_ID) as task (task.id)} - handleToggleComplete(task)} - onDelete={() => handleDelete(task.id)} - onExpand={() => handleExpandTask(task.id)} - onCollapse={handleCollapseTask} - onSave={(data) => handleSaveTask(task.id, data)} - /> + +
handleContextMenu(e, task)}> + handleToggleComplete(task)} + onDelete={() => handleDelete(task.id)} + onExpand={() => handleExpandTask(task.id)} + onCollapse={handleCollapseTask} + onSave={(data) => handleSaveTask(task.id, data)} + /> +
{/each} {#if items.length === 0}
@@ -173,21 +270,35 @@ {:else}
{#each tasks as task (task.id)} - handleToggleComplete(task)} - onDelete={() => handleDelete(task.id)} - onExpand={() => handleExpandTask(task.id)} - onCollapse={handleCollapseTask} - onSave={(data) => handleSaveTask(task.id, data)} - /> + +
handleContextMenu(e, task)}> + handleToggleComplete(task)} + onDelete={() => handleDelete(task.id)} + onExpand={() => handleExpandTask(task.id)} + onCollapse={handleCollapseTask} + onSave={(data) => handleSaveTask(task.id, data)} + /> +
{/each}
{/if} + { + contextMenuVisible = false; + contextMenuTask = null; + }} +/> +