From 933715c7d944820d761bb6fbb33a68bb421e5f33 Mon Sep 17 00:00:00 2001 From: Till JS Date: Wed, 1 Apr 2026 20:31:36 +0200 Subject: [PATCH] refactor(todo/web): remove edit mode, rename pages, add inline editing & drag reorder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove edit mode entirely (toolbar, context, Layout pill in PillNav) - Remove onAddColumn/add-sheet from all layouts (FokusLayout, Grid, Kanban) - Rename SecondaryPage → TodoPage, secondaryPage → page (i18n keys, CSS) - ViewColumnHeader: always-on inline editing (click name, click color dot) - TodoPage: contenteditable title (invisible, no input styling) - TodoPage: drag handle bar for reorder via HTML5 drag & drop - FokusLayout: add matching drag handle to board sheets - Clean up dead spotlight actions (/today, /upcoming, /kanban) - Remove /statistics reference from QuickInputBar Co-Authored-By: Claude Opus 4.6 (1M context) --- .../board-views/BoardViewRenderer.svelte | 5 - .../components/board-views/FokusLayout.svelte | 86 ++-- .../components/board-views/GridLayout.svelte | 11 - .../board-views/KanbanLayout.svelte | 11 - .../board-views/ViewColumnHeader.svelte | 222 ++++---- .../{SecondaryPage.svelte => TodoPage.svelte} | 105 +++- .../apps/web/src/lib/i18n/locales/de.json | 2 +- .../apps/web/src/lib/i18n/locales/en.json | 2 +- .../apps/web/src/lib/i18n/locales/es.json | 2 +- .../apps/web/src/lib/i18n/locales/fr.json | 2 +- .../apps/web/src/lib/i18n/locales/it.json | 2 +- .../src/lib/stores/minimized-pages.svelte.ts | 51 ++ .../apps/web/src/routes/(app)/+layout.svelte | 256 +++++++-- .../apps/web/src/routes/(app)/+page.svelte | 486 +++++------------- 14 files changed, 603 insertions(+), 640 deletions(-) rename apps/todo/apps/web/src/lib/components/pages/{SecondaryPage.svelte => TodoPage.svelte} (78%) create mode 100644 apps/todo/apps/web/src/lib/stores/minimized-pages.svelte.ts diff --git a/apps/todo/apps/web/src/lib/components/board-views/BoardViewRenderer.svelte b/apps/todo/apps/web/src/lib/components/board-views/BoardViewRenderer.svelte index 5132687d4..b0e02026b 100644 --- a/apps/todo/apps/web/src/lib/components/board-views/BoardViewRenderer.svelte +++ b/apps/todo/apps/web/src/lib/components/board-views/BoardViewRenderer.svelte @@ -16,7 +16,6 @@ onColumnMove?: (colIdx: number, dir: -1 | 1) => void; onColumnDelete?: (colIdx: number) => void; onColumnClose?: (colIdx: number) => void; - onAddColumn?: () => void; trailing?: Snippet; } @@ -28,7 +27,6 @@ onColumnMove, onColumnDelete, onColumnClose, - onAddColumn, trailing, }: Props = $props(); @@ -105,7 +103,6 @@ {onColumnMove} {onColumnDelete} {onColumnClose} - {onAddColumn} {trailing} /> {:else if activeLayout === 'grid'} @@ -119,7 +116,6 @@ {onColumnColorChange} {onColumnMove} {onColumnDelete} - {onAddColumn} /> {:else} {/if} diff --git a/apps/todo/apps/web/src/lib/components/board-views/FokusLayout.svelte b/apps/todo/apps/web/src/lib/components/board-views/FokusLayout.svelte index 3a3878a7d..1d89d29c7 100644 --- a/apps/todo/apps/web/src/lib/components/board-views/FokusLayout.svelte +++ b/apps/todo/apps/web/src/lib/components/board-views/FokusLayout.svelte @@ -9,7 +9,7 @@ import ViewColumnHeader from './ViewColumnHeader.svelte'; import { tasksStore } from '$lib/stores/tasks.svelte'; import { todoSettings } from '$lib/stores/settings.svelte'; - import { X } from '@manacore/shared-icons'; + import { X, DotsSixVertical } from '@manacore/shared-icons'; interface Props { columns: GroupedColumn[]; @@ -22,7 +22,6 @@ onColumnMove?: (colIdx: number, dir: -1 | 1) => void; onColumnDelete?: (colIdx: number) => void; onColumnClose?: (colIdx: number) => void; - onAddColumn?: () => void; trailing?: Snippet; } @@ -37,7 +36,6 @@ onColumnMove, onColumnDelete, onColumnClose, - onAddColumn, trailing, }: Props = $props(); @@ -135,6 +133,11 @@ {#each columns as column, i (column.id)} {@const tasks = localTasksByColumn[column.id] || column.tasks}
+
+ + + +
{/each} - {#if onAddColumn} -
- -
- {/if} - - + {#if trailing} {@render trailing()} {/if} @@ -248,6 +242,36 @@ display: none; } + .drag-handle-bar { + display: flex; + justify-content: center; + padding: 0.25rem 0 0; + } + + .drag-handle { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 14px; + color: #d1d5db; + cursor: grab; + border-radius: 0.25rem; + transition: color 0.15s; + } + .drag-handle:hover { + color: #9ca3af; + } + .drag-handle:active { + cursor: grabbing; + } + :global(.dark) .drag-handle { + color: #3f3b38; + } + :global(.dark) .drag-handle:hover { + color: #6b7280; + } + .fokus-sheet { flex: 0 0 auto; width: var(--sheet-width, min(840px, 85vw)); @@ -337,42 +361,6 @@ background: color-mix(in srgb, var(--color-primary) 4%, transparent); } - /* Add sheet */ - .add-sheet { - border: 2px dashed color-mix(in srgb, var(--color-primary) 30%, transparent) !important; - background: color-mix(in srgb, var(--color-primary) 2%, transparent) !important; - box-shadow: none !important; - } - .add-sheet:hover { - border-color: var(--color-primary) !important; - background: color-mix(in srgb, var(--color-primary) 6%, transparent) !important; - } - - .add-sheet-btn { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 0.5rem; - width: 100%; - height: 100%; - min-height: 200px; - background: transparent; - border: none; - cursor: pointer; - } - .add-sheet-icon { - font-size: 2rem; - font-weight: 300; - color: var(--color-primary); - line-height: 1; - } - .add-sheet-label { - font-size: 0.875rem; - font-weight: 500; - color: var(--color-primary); - } - /* Heute erledigt section */ .completed-today { padding: 0.75rem 1rem 1rem; diff --git a/apps/todo/apps/web/src/lib/components/board-views/GridLayout.svelte b/apps/todo/apps/web/src/lib/components/board-views/GridLayout.svelte index 217fb6d18..33ed39582 100644 --- a/apps/todo/apps/web/src/lib/components/board-views/GridLayout.svelte +++ b/apps/todo/apps/web/src/lib/components/board-views/GridLayout.svelte @@ -13,7 +13,6 @@ onColumnColorChange?: (colIdx: number, color: string) => void; onColumnMove?: (colIdx: number, dir: -1 | 1) => void; onColumnDelete?: (colIdx: number) => void; - onAddColumn?: () => void; } let { @@ -26,7 +25,6 @@ onColumnColorChange, onColumnMove, onColumnDelete, - onAddColumn, }: Props = $props(); @@ -48,15 +46,6 @@ />
{/each} - - {#if onAddColumn} -
- -
- {/if}
diff --git a/apps/todo/apps/web/src/routes/(app)/+page.svelte b/apps/todo/apps/web/src/routes/(app)/+page.svelte index 671a20bc5..c02e44d95 100644 --- a/apps/todo/apps/web/src/routes/(app)/+page.svelte +++ b/apps/todo/apps/web/src/routes/(app)/+page.svelte @@ -1,34 +1,59 @@ - - {pageTitle} - Todo -
- - {#if editMode && activeView} -
- updateView({ name: e.currentTarget.value })} - placeholder="View-Name" - /> - -
- Gruppierung -
- {#each GROUPBY_OPTIONS as opt} - - {/each} -
-
- -
- Breite -
- {#each WIDTH_OPTIONS as opt} - - {/each} -
-
-
- {/if} - - - {#if minimizedPages.length > 0} -
- {#each minimizedPages as page (page.id)} - {@const meta = PAGE_META[page.id] ?? { title: page.id, color: '#6B7280' }} -
handleRestorePage(page.id)} - > - - {meta.title} - -
- {/each} - -
- {/if} - +
{#if activeView} {#snippet trailing()} - + {#each expandedPages as page (page.id)} - handleRemovePage(page.id)} - onMinimize={() => handleMinimizePage(page.id)} - /> + +
handlePageDragStart(e, page.id)} + ondragover={handlePageDragOver} + ondrop={(e) => handlePageDrop(e, page.id)} + ondragend={handlePageDragEnd} + > + handleRemovePage(page.id)} + onMinimize={() => handleMinimizePage(page.id)} + onRename={(name) => handleRenamePage(page.id, name)} + /> +
{/each} - - {#if !editMode} - {#if showPagePicker} -
- (showPagePicker = false)} - activePageIds={openPages.map((p) => p.id)} - /> -
- {:else} - - {/if} + + {#if showPagePicker} +
+ (showPagePicker = false)} + activePageIds={openPages.map((p) => p.id)} + /> +
+ {:else} + {/if} {/snippet}
@@ -291,6 +230,14 @@ flex-direction: column; } + .page-drag-wrapper { + flex: 0 0 auto; + transition: opacity 0.15s; + } + .page-drag-wrapper.dragging { + opacity: 0.4; + } + .neue-seite-card { flex: 0 0 auto; width: 48px; @@ -320,209 +267,10 @@ background: color-mix(in srgb, var(--color-primary, #8b5cf6) 8%, transparent); } - /* ── Minimized Tabs ──────────────────────────────────── */ - .minimized-tabs { - display: flex; - justify-content: center; - gap: 0.375rem; - padding: 0.5rem 1.5rem 0.25rem; - overflow-x: auto; - scrollbar-width: none; - } - .minimized-tabs::-webkit-scrollbar { - display: none; - } - - .minimized-tab { - display: flex; - align-items: center; - gap: 0.375rem; - padding: 0.4rem 0.75rem 0.4rem 0.75rem; - background: #fffef5; - border: 1px solid rgba(0, 0, 0, 0.08); - border-radius: 0.375rem; - cursor: pointer; - transition: all 0.15s; - white-space: nowrap; - flex-shrink: 0; - } - .minimized-tab:hover { - background: #fffdf0; - border-color: rgba(0, 0, 0, 0.12); - } - :global(.dark) .minimized-tab { - background: #252220; - border-color: rgba(255, 255, 255, 0.08); - } - :global(.dark) .minimized-tab:hover { - background: #2a2725; - border-color: rgba(255, 255, 255, 0.14); - } - - .minimized-tab-dot { - width: 0.5rem; - height: 0.5rem; - border-radius: 9999px; - flex-shrink: 0; - } - - .minimized-tab-title { - font-size: 0.75rem; - font-weight: 500; - color: #6b7280; - } - :global(.dark) .minimized-tab-title { - color: #9ca3af; - } - - .minimized-tab-close { - display: flex; - align-items: center; - justify-content: center; - width: 16px; - height: 16px; - border: none; - background: transparent; - color: #d1d5db; - border-radius: 0.125rem; - cursor: pointer; - padding: 0; - transition: all 0.15s; - } - .minimized-tab-close:hover { - color: #6b7280; - background: rgba(0, 0, 0, 0.06); - } - :global(.dark) .minimized-tab-close { - color: #4b5563; - } - :global(.dark) .minimized-tab-close:hover { - color: #9ca3af; - background: rgba(255, 255, 255, 0.08); - } - - .minimized-tab-add { - display: flex; - align-items: center; - justify-content: center; - width: 28px; - height: 28px; - border-radius: 0.375rem; - border: 1px dashed rgba(0, 0, 0, 0.15); - background: transparent; - color: #9ca3af; - cursor: pointer; - flex-shrink: 0; - transition: all 0.15s; - } - .minimized-tab-add:hover { - border-color: var(--color-primary, #8b5cf6); - color: var(--color-primary, #8b5cf6); - background: color-mix(in srgb, var(--color-primary, #8b5cf6) 4%, transparent); - } - :global(.dark) .minimized-tab-add { - border-color: rgba(255, 255, 255, 0.1); - color: #4b5563; - } - :global(.dark) .minimized-tab-add:hover { - border-color: var(--color-primary, #8b5cf6); - color: var(--color-primary, #8b5cf6); - background: color-mix(in srgb, var(--color-primary, #8b5cf6) 8%, transparent); - } - .empty-state { display: flex; align-items: center; justify-content: center; height: 100%; } - - /* ── Edit Toolbar ─────────────────────────────────────── */ - .edit-toolbar { - display: flex; - align-items: center; - gap: 1.5rem; - padding: 0.75rem 1.5rem; - background: rgba(139, 92, 246, 0.06); - border-bottom: 1px solid rgba(139, 92, 246, 0.15); - flex-shrink: 0; - overflow-x: auto; - scrollbar-width: none; - } - .edit-toolbar::-webkit-scrollbar { - display: none; - } - :global(.dark) .edit-toolbar { - background: rgba(139, 92, 246, 0.1); - border-bottom-color: rgba(139, 92, 246, 0.2); - } - - .edit-name-input { - font-size: 1rem; - font-weight: 600; - color: #374151; - background: transparent; - border: none; - border-bottom: 2px solid rgba(139, 92, 246, 0.3); - padding: 0.25rem 0; - outline: none; - min-width: 120px; - max-width: 200px; - } - .edit-name-input:focus { - border-bottom-color: #8b5cf6; - } - :global(.dark) .edit-name-input { - color: #f3f4f6; - } - - .edit-group { - display: flex; - align-items: center; - gap: 0.5rem; - flex-shrink: 0; - } - - .edit-label { - font-size: 0.6875rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.05em; - color: #6b7280; - white-space: nowrap; - } - :global(.dark) .edit-label { - color: #9ca3af; - } - - .edit-pills { - display: flex; - gap: 0.25rem; - } - - .edit-pill { - padding: 0.25rem 0.625rem; - font-size: 0.75rem; - font-weight: 500; - border-radius: 9999px; - border: 1px solid rgba(0, 0, 0, 0.1); - background: rgba(255, 255, 255, 0.8); - color: #374151; - cursor: pointer; - transition: all 0.15s; - white-space: nowrap; - } - :global(.dark) .edit-pill { - background: rgba(255, 255, 255, 0.08); - border-color: rgba(255, 255, 255, 0.1); - color: #d1d5db; - } - .edit-pill:hover { - border-color: rgba(139, 92, 246, 0.4); - } - .edit-pill.active { - background: #8b5cf6; - border-color: #8b5cf6; - color: white; - }