From 3ea2c03ab26fdf42234de9e5390c0654ade5e9d8 Mon Sep 17 00:00:00 2001 From: Till JS Date: Tue, 31 Mar 2026 19:06:55 +0200 Subject: [PATCH] feat(todo): inline title editing + detail modal button on task items - Title already had contenteditable; now shows clear focus ring (primary tint) when active so it's obvious the title is being edited - Added ArrowsOutSimple icon button at the right end of each task row (appears on hover) that opens the full TaskEditModal - Modal wired up with save/delete/close handlers - Fixed all remaining hardcoded colors in TaskItem to use CSS vars (checkbox checked/animating, drag handle, due date, meta, form inputs, expanded wrapper border, btn-danger, assignee dot) Co-Authored-By: Claude Sonnet 4.6 --- .../web/src/lib/components/TaskItem.svelte | 220 +++++++++--------- 1 file changed, 109 insertions(+), 111 deletions(-) diff --git a/apps/todo/apps/web/src/lib/components/TaskItem.svelte b/apps/todo/apps/web/src/lib/components/TaskItem.svelte index 208128768..50d89b9e2 100644 --- a/apps/todo/apps/web/src/lib/components/TaskItem.svelte +++ b/apps/todo/apps/web/src/lib/components/TaskItem.svelte @@ -9,7 +9,8 @@ import { contactsStore } from '$lib/stores/contacts.svelte'; import { ContactAvatar, ContactSelector } from '@manacore/shared-ui'; import SubtaskList from './SubtaskList.svelte'; - import { Check, CheckSquare, DotsSixVertical } from '@manacore/shared-icons'; + import { Check, CheckSquare, DotsSixVertical, ArrowsOutSimple } from '@manacore/shared-icons'; + import TaskEditModal from './TaskEditModal.svelte'; import { PrioritySelector, StorypointsSelector, @@ -46,6 +47,27 @@ // Toggle for showing created date on completed tasks let showCreatedDate = $state(false); + // Detail modal + let showModal = $state(false); + + function handleOpenModal(e: MouseEvent) { + e.stopPropagation(); + showModal = true; + } + + function handleModalClose() { + showModal = false; + } + + function handleModalSave(data: Partial) { + onSave?.(data as UpdateTaskInput); + } + + function handleModalDelete(_taskId: string) { + onDelete(); + showModal = false; + } + // Shared form state const form = useTaskForm(); @@ -402,6 +424,17 @@ {dueDateText()} {/if} + + + @@ -422,6 +455,15 @@ {/if} + + + {#if isExpanded}
@@ -601,20 +643,13 @@ .task-item-wrapper.expanded { position: relative; z-index: 9990; - background: rgba(255, 255, 255, 0.95); - backdrop-filter: blur(12px); - -webkit-backdrop-filter: blur(12px); - border: 1px solid rgba(139, 92, 246, 0.3); + background: var(--color-surface-elevated-2); + border: 1px solid color-mix(in srgb, var(--color-primary) 35%, transparent); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15); border-radius: 0.75rem; margin-bottom: 0.75rem; } - :global(.dark) .task-item-wrapper.expanded { - background: rgba(30, 30, 30, 0.95); - border-color: rgba(139, 92, 246, 0.4); - } - .task-item { display: flex; align-items: center; @@ -637,39 +672,20 @@ box-shadow: none; backdrop-filter: none; border-radius: 0.75rem 0.75rem 0 0; - border-bottom: 1px solid rgba(0, 0, 0, 0.08); - } - - :global(.dark) .task-item-wrapper.expanded .task-item { - border-bottom-color: rgba(255, 255, 255, 0.1); - } - - :global(.dark) .task-item { - background: transparent; - border: none; + border-bottom: 1px solid var(--color-border); } .task-item:hover { background: transparent; } - :global(.dark) .task-item:hover { - background: transparent; - } - .task-item.completed { opacity: 0.6; } /* Completing animation */ .task-item.completing { - background: rgba(34, 197, 94, 0.08); - border-bottom-color: rgba(34, 197, 94, 0.2); - } - - :global(.dark) .task-item.completing { - background: rgba(34, 197, 94, 0.12); - border-bottom-color: rgba(34, 197, 94, 0.3); + background: color-mix(in srgb, var(--color-success) 8%, transparent); } /* Drag handle — sticks out left beyond the content area */ @@ -694,11 +710,7 @@ .drag-handle:hover { opacity: 0.8 !important; - background: rgba(0, 0, 0, 0.05); - } - - :global(.dark) .drag-handle:hover { - background: rgba(255, 255, 255, 0.1); + background: var(--color-surface-hover); } .drag-handle:active { @@ -715,10 +727,39 @@ /* During drag, disable pointer events on interactive elements */ :global([aria-grabbed='true']) .task-checkbox, :global([aria-grabbed='true']) .task-content, - :global([aria-grabbed='true']) .expand-btn { + :global([aria-grabbed='true']) .expand-btn, + :global([aria-grabbed='true']) .detail-btn { pointer-events: none; } + /* Detail modal button */ + .detail-btn { + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + width: 1.5rem; + height: 1.5rem; + border: none; + background: transparent; + color: var(--color-muted-foreground); + cursor: pointer; + border-radius: 0.375rem; + opacity: 0; + transition: all 0.15s; + padding: 0; + } + + .task-item:hover .detail-btn { + opacity: 1; + } + + .detail-btn:hover { + color: var(--color-primary); + background: color-mix(in srgb, var(--color-primary) 10%, transparent); + opacity: 1; + } + /* Checkbox with priority color fill */ .task-checkbox { margin-top: 0.1875rem; @@ -775,8 +816,8 @@ } .task-checkbox.checked { - background: #8b5cf6; - border-color: #8b5cf6; + background: var(--color-primary); + border-color: var(--color-primary); border-style: solid; } @@ -785,8 +826,8 @@ } .task-checkbox.animating { - background: #22c55e; - border-color: #22c55e; + background: var(--color-success); + border-color: var(--color-success); border-style: solid; transform: scale(1.2); } @@ -840,7 +881,7 @@ .task-title { font-size: 0.875rem; font-weight: 500; - color: #374151; + color: var(--color-foreground); white-space: normal; word-break: break-word; cursor: text; @@ -848,23 +889,17 @@ padding: 0.125rem 0.25rem; margin: -0.125rem -0.25rem; outline: none; + transition: background 0.1s; } - .task-title:hover { - background: transparent; - } - - :global(.dark) .task-title:hover { - background: transparent; - } - - :global(.dark) .task-title { - color: #f3f4f6; + .task-title:focus { + background: color-mix(in srgb, var(--color-primary) 6%, transparent); + outline: 1px solid color-mix(in srgb, var(--color-primary) 30%, transparent); } .task-title.line-through { text-decoration: line-through; - color: #9ca3af; + color: var(--color-muted-foreground); } /* Meta info */ @@ -880,11 +915,7 @@ align-items: center; gap: 0.25rem; font-size: 0.75rem; - color: #6b7280; - } - - :global(.dark) .meta-item { - color: #9ca3af; + color: var(--color-muted-foreground); } .meta-icon { @@ -920,13 +951,9 @@ right: -1px; width: 6px; height: 6px; - background: #8b5cf6; + background: var(--color-primary); border-radius: 50%; - border: 1px solid white; - } - - :global(.dark) .assignee-avatar::after { - border-color: rgba(30, 30, 30, 1); + border: 1px solid var(--color-surface-elevated-2); } .involved-avatars { @@ -944,34 +971,26 @@ .more-contacts { font-size: 0.625rem; - color: #6b7280; + color: var(--color-muted-foreground); margin-left: 0.25rem; font-weight: 500; } - :global(.dark) .more-contacts { - color: #9ca3af; - } - /* Due date */ .due-date { font-size: 0.75rem; - color: #6b7280; + color: var(--color-muted-foreground); flex-shrink: 0; white-space: nowrap; margin-top: 0.25rem; } - :global(.dark) .due-date { - color: #9ca3af; - } - .due-date.overdue { - color: #ef4444; + color: var(--color-error); } .due-date.today { - color: #f97316; + color: var(--color-warning); } /* Completed date toggle */ @@ -1017,20 +1036,16 @@ padding: 0.25rem; border: none; background: transparent; - color: #9ca3af; + color: var(--color-muted-foreground); cursor: pointer; border-radius: 9999px; transition: all 0.15s; flex-shrink: 0; } - .task-item:hover .expand-btn { - color: #6b7280; - } - .expand-btn:hover { - color: #8b5cf6; - background: rgba(139, 92, 246, 0.1); + color: var(--color-primary); + background: color-mix(in srgb, var(--color-primary) 10%, transparent); } .expand-icon { @@ -1072,18 +1087,14 @@ .form-label { font-size: 0.75rem; font-weight: 600; - color: #374151; + color: var(--color-foreground); text-transform: uppercase; letter-spacing: 0.025em; } - :global(.dark) .form-label { - color: #d1d5db; - } - .form-sublabel { font-size: 0.6875rem; - color: #6b7280; + color: var(--color-muted-foreground); } .form-row { @@ -1124,30 +1135,21 @@ .form-input-sm { width: 100%; padding: 0.5rem 0.75rem; - border: 1px solid rgba(0, 0, 0, 0.15); + border: 1px solid var(--color-border); border-radius: 0.5rem; - background: rgba(255, 255, 255, 0.8); + background: var(--color-surface); font-size: 0.875rem; - color: #374151; + color: var(--color-foreground); transition: all 0.15s; } - :global(.dark) .form-input, - :global(.dark) .form-textarea, - :global(.dark) .form-select, - :global(.dark) .form-input-sm { - background: rgba(255, 255, 255, 0.1); - border-color: rgba(255, 255, 255, 0.15); - color: #f3f4f6; - } - .form-input:focus, .form-textarea:focus, .form-select:focus, .form-input-sm:focus { outline: none; - border-color: #8b5cf6; - box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.1); + border-color: var(--color-primary); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-primary) 10%, transparent); } .form-input-sm { @@ -1166,14 +1168,10 @@ align-items: center; justify-content: space-between; padding-top: 0.75rem; - border-top: 1px solid rgba(0, 0, 0, 0.08); + border-top: 1px solid var(--color-border); margin-top: 0.5rem; } - :global(.dark) .form-actions { - border-top-color: rgba(255, 255, 255, 0.1); - } - .btn { display: flex; align-items: center; @@ -1194,12 +1192,12 @@ } .btn-danger { - background: rgba(239, 68, 68, 0.1); - color: #ef4444; + background: color-mix(in srgb, var(--color-error) 10%, transparent); + color: var(--color-error); } .btn-danger:hover:not(:disabled) { - background: #ef4444; + background: var(--color-error); color: white; }