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 6e77b0479..113969561 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 @@ -11,9 +11,20 @@ interface Props { view: LocalBoardView; layoutOverride?: 'kanban' | 'grid' | 'fokus'; + onColumnRename?: (colIdx: number, name: string) => void; + onColumnColorChange?: (colIdx: number, color: string) => void; + onColumnMove?: (colIdx: number, dir: -1 | 1) => void; + onColumnDelete?: (colIdx: number) => void; } - let { view, layoutOverride }: Props = $props(); + let { + view, + layoutOverride, + onColumnRename, + onColumnColorChange, + onColumnMove, + onColumnDelete, + }: Props = $props(); let activeLayout = $derived(layoutOverride || view.layout); @@ -74,6 +85,10 @@ onTaskToggle={handleTaskToggle} onTaskDelete={handleTaskDelete} onTaskUpdate={handleTaskUpdate} + {onColumnRename} + {onColumnColorChange} + {onColumnMove} + {onColumnDelete} /> {:else if activeLayout === 'grid'} {: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 854e06e94..bfdbce2e1 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 @@ -14,15 +14,29 @@ onTaskToggle: (task: Task) => void; onTaskDelete: (taskId: string) => void; onTaskUpdate: (taskId: string, data: Partial) => void; + onColumnRename?: (colIdx: number, name: string) => void; + onColumnColorChange?: (colIdx: number, color: string) => void; + onColumnMove?: (colIdx: number, dir: -1 | 1) => void; + onColumnDelete?: (colIdx: number) => void; } - let { columns, onTaskDrop, onTaskToggle, onTaskDelete, onTaskUpdate }: Props = $props(); + let { + columns, + onTaskDrop, + onTaskToggle, + onTaskDelete, + onTaskUpdate, + onColumnRename, + onColumnColorChange, + onColumnMove, + onColumnDelete, + }: Props = $props(); const PAGE_WIDTH_MAP: Record = { - narrow: 'min(640px, 85vw)', - medium: 'min(840px, 85vw)', - wide: 'min(1024px, 92vw)', - full: '92vw', + narrow: 'min(360px, 85vw)', + medium: 'min(480px, 85vw)', + wide: 'min(640px, 90vw)', + full: 'min(840px, 95vw)', }; let sheetWidth = $derived(PAGE_WIDTH_MAP[todoSettings.pageWidth] || PAGE_WIDTH_MAP.medium); @@ -106,10 +120,20 @@ bind:this={scrollContainer} onscroll={handleScroll} > - {#each columns as column (column.id)} + {#each columns as column, i (column.id)} {@const tasks = localTasksByColumn[column.id] || column.tasks}
- + onColumnRename(i, name) : undefined} + onColorChange={onColumnColorChange ? (c) => onColumnColorChange(i, c) : undefined} + onMove={onColumnMove ? (dir) => onColumnMove(i, dir) : undefined} + onDelete={onColumnDelete ? () => onColumnDelete(i) : undefined} + />
void; onTaskDelete: (taskId: string) => void; onTaskUpdate: (taskId: string, data: Partial) => void; + onColumnRename?: (colIdx: number, name: string) => void; + onColumnColorChange?: (colIdx: number, color: string) => void; + onColumnMove?: (colIdx: number, dir: -1 | 1) => void; + onColumnDelete?: (colIdx: number) => void; } - let { columns, onTaskDrop, onTaskToggle, onTaskDelete, onTaskUpdate }: Props = $props(); + let { + columns, + onTaskDrop, + onTaskToggle, + onTaskDelete, + onTaskUpdate, + onColumnRename, + onColumnColorChange, + onColumnMove, + onColumnDelete, + }: Props = $props();
- {#each columns as column (column.id)} + {#each columns as column, i (column.id)}
onColumnRename(i, name) : undefined} + onColumnColorChange={onColumnColorChange ? (c) => onColumnColorChange(i, c) : undefined} + onColumnMove={onColumnMove ? (dir) => onColumnMove(i, dir) : undefined} + onColumnDelete={onColumnDelete ? () => onColumnDelete(i) : undefined} />
{/each} diff --git a/apps/todo/apps/web/src/lib/components/board-views/KanbanLayout.svelte b/apps/todo/apps/web/src/lib/components/board-views/KanbanLayout.svelte index b6eb2e83f..4cd096408 100644 --- a/apps/todo/apps/web/src/lib/components/board-views/KanbanLayout.svelte +++ b/apps/todo/apps/web/src/lib/components/board-views/KanbanLayout.svelte @@ -9,20 +9,40 @@ onTaskToggle: (task: Task) => void; onTaskDelete: (taskId: string) => void; onTaskUpdate: (taskId: string, data: Partial) => void; + onColumnRename?: (colIdx: number, name: string) => void; + onColumnColorChange?: (colIdx: number, color: string) => void; + onColumnMove?: (colIdx: number, dir: -1 | 1) => void; + onColumnDelete?: (colIdx: number) => void; } - let { columns, onTaskDrop, onTaskToggle, onTaskDelete, onTaskUpdate }: Props = $props(); + let { + columns, + onTaskDrop, + onTaskToggle, + onTaskDelete, + onTaskUpdate, + onColumnRename, + onColumnColorChange, + onColumnMove, + onColumnDelete, + }: Props = $props();
- {#each columns as column (column.id)} + {#each columns as column, i (column.id)}
onColumnRename(i, name) : undefined} + onColumnColorChange={onColumnColorChange ? (c) => onColumnColorChange(i, c) : undefined} + onColumnMove={onColumnMove ? (dir) => onColumnMove(i, dir) : undefined} + onColumnDelete={onColumnDelete ? () => onColumnDelete(i) : undefined} />
{/each} diff --git a/apps/todo/apps/web/src/lib/components/board-views/ViewColumn.svelte b/apps/todo/apps/web/src/lib/components/board-views/ViewColumn.svelte index 5c4009f7f..8f5a2e702 100644 --- a/apps/todo/apps/web/src/lib/components/board-views/ViewColumn.svelte +++ b/apps/todo/apps/web/src/lib/components/board-views/ViewColumn.svelte @@ -9,13 +9,31 @@ interface Props { column: GroupedColumn; + columnIndex?: number; + totalColumns?: number; onTaskDrop: (taskId: string, columnId: string) => void; onTaskToggle: (task: Task) => void; onTaskDelete: (taskId: string) => void; onTaskUpdate: (taskId: string, data: Partial) => void; + onColumnRename?: (name: string) => void; + onColumnColorChange?: (color: string) => void; + onColumnMove?: (dir: -1 | 1) => void; + onColumnDelete?: () => void; } - let { column, onTaskDrop, onTaskToggle, onTaskDelete, onTaskUpdate }: Props = $props(); + let { + column, + columnIndex = 0, + totalColumns = 1, + onTaskDrop, + onTaskToggle, + onTaskDelete, + onTaskUpdate, + onColumnRename, + onColumnColorChange, + onColumnMove, + onColumnDelete, + }: Props = $props(); // Local tasks state for drag and drop let localTasks = $state([]); @@ -102,7 +120,17 @@
- +
+ import { getContext } from 'svelte'; + import { ArrowLeft, ArrowRight, Trash } from '@manacore/shared-icons'; + interface Props { name: string; color: string; taskCount: number; + columnIndex?: number; + totalColumns?: number; + onRename?: (name: string) => void; + onColorChange?: (color: string) => void; + onMove?: (dir: -1 | 1) => void; + onDelete?: () => void; } - let { name, color, taskCount }: Props = $props(); + let { + name, + color, + taskCount, + columnIndex = 0, + totalColumns = 1, + onRename, + onColorChange, + onMove, + onDelete, + }: Props = $props(); + + const editModeCtx: { readonly active: boolean } | undefined = getContext('editMode'); + let editMode = $derived(editModeCtx?.active ?? false); + let editable = $derived(editMode && !!onRename); + + const COLORS = [ + '#EF4444', + '#F59E0B', + '#22C55E', + '#3B82F6', + '#8B5CF6', + '#EC4899', + '#14B8A6', + '#F97316', + '#6B7280', + ]; -
-
- - {name} -
- {taskCount} +
+ {#if editable} + +
+
+ {#each COLORS as c} + + {/each} +
+
+ onRename?.(e.currentTarget.value)} + /> +
+ + + +
+
+
+ {:else} + +
+ + {name} +
+ {taskCount} + {/if}
diff --git a/apps/todo/apps/web/src/routes/(app)/+page.svelte b/apps/todo/apps/web/src/routes/(app)/+page.svelte index 71cc1de63..fad069b46 100644 --- a/apps/todo/apps/web/src/routes/(app)/+page.svelte +++ b/apps/todo/apps/web/src/routes/(app)/+page.svelte @@ -4,7 +4,7 @@ import { BoardViewRenderer } from '$lib/components/board-views'; import { todoSettings, type PageWidth } from '$lib/stores/settings.svelte'; import { boardViewsStore } from '$lib/stores/board-views.svelte'; - import { ArrowLeft, ArrowRight, Plus, Trash } from '@manacore/shared-icons'; + import { Plus } from '@manacore/shared-icons'; // Get active view + edit mode from layout context const activeViewCtx: { readonly value: LocalBoardView | null } = getContext('activeView'); @@ -148,57 +148,11 @@
- {#if columnsEditable} -
- {#each activeView.columns as col, i (col.id)} -
-
- {#each COLUMN_COLORS as c} - - {/each} -
- updateColumn(i, { name: e.currentTarget.value })} - /> -
- - - -
-
- {/each} +
{:else} @@ -208,7 +162,14 @@ {#if activeView} - + updateColumn(i, { name }) : undefined} + onColumnColorChange={columnsEditable ? (i, color) => updateColumn(i, { color }) : undefined} + onColumnMove={columnsEditable ? moveColumn : undefined} + onColumnDelete={columnsEditable ? removeColumn : undefined} + /> {:else}

Views werden geladen...

@@ -319,132 +280,27 @@ color: white; } - /* ── Column Editor ────────────────────────────────────── */ - .column-editor { + /* ── Add Column Bar ───────────────────────────────────── */ + .add-col-bar { display: flex; - gap: 0.75rem; - padding: 0.75rem 1.5rem; - overflow-x: auto; - scrollbar-width: none; + padding: 0.5rem 1.5rem; flex-shrink: 0; - background: rgba(139, 92, 246, 0.03); border-bottom: 1px solid rgba(139, 92, 246, 0.08); } - .column-editor::-webkit-scrollbar { - display: none; - } - :global(.dark) .column-editor { - background: rgba(139, 92, 246, 0.05); - border-bottom-color: rgba(139, 92, 246, 0.12); - } - - .col-edit-card { - display: flex; - flex-direction: column; - gap: 0.375rem; - padding: 0.5rem 0.75rem; - background: rgba(255, 255, 255, 0.9); - border: 1px solid rgba(0, 0, 0, 0.08); - border-radius: 0.5rem; - min-width: 140px; - flex-shrink: 0; - } - :global(.dark) .col-edit-card { - background: rgba(255, 255, 255, 0.06); - border-color: rgba(255, 255, 255, 0.1); - } - - .col-colors { - display: flex; - gap: 0.25rem; - } - - .col-color-dot { - width: 14px; - height: 14px; - border-radius: 50%; - border: 2px solid transparent; - cursor: pointer; - transition: all 0.15s; - } - .col-color-dot:hover { - transform: scale(1.2); - } - .col-color-dot.active { - border-color: white; - box-shadow: 0 0 0 2px currentColor; - transform: scale(1.15); - } - - .col-name-input { - font-size: 0.8125rem; - font-weight: 600; - color: #374151; - background: transparent; - border: none; - border-bottom: 1px solid rgba(0, 0, 0, 0.1); - padding: 0.125rem 0; - outline: none; - width: 100%; - } - .col-name-input:focus { - border-bottom-color: #8b5cf6; - } - :global(.dark) .col-name-input { - color: #f3f4f6; - border-bottom-color: rgba(255, 255, 255, 0.1); - } - - .col-actions { - display: flex; - gap: 0.25rem; - justify-content: flex-end; - } - - .col-action-btn { - padding: 0.25rem; - border-radius: 0.25rem; - color: #6b7280; - cursor: pointer; - transition: all 0.15s; - background: transparent; - border: none; - } - .col-action-btn:hover:not(:disabled) { - color: #374151; - background: rgba(0, 0, 0, 0.05); - } - .col-action-btn:disabled { - opacity: 0.3; - cursor: not-allowed; - } - :global(.dark) .col-action-btn { - color: #9ca3af; - } - :global(.dark) .col-action-btn:hover:not(:disabled) { - color: #f3f4f6; - background: rgba(255, 255, 255, 0.1); - } - - .col-delete-btn:hover:not(:disabled) { - color: #ef4444 !important; - background: rgba(239, 68, 68, 0.1) !important; - } .col-add-btn { display: flex; align-items: center; - justify-content: center; - min-width: 40px; - height: 40px; - border-radius: 0.5rem; - border: 2px dashed rgba(139, 92, 246, 0.3); + gap: 0.375rem; + padding: 0.25rem 0.75rem; + font-size: 0.75rem; + font-weight: 500; + border-radius: 9999px; + border: 1px dashed rgba(139, 92, 246, 0.4); color: #8b5cf6; background: transparent; cursor: pointer; transition: all 0.15s; - flex-shrink: 0; - align-self: center; } .col-add-btn:hover { border-color: #8b5cf6;