From dc4ba0a39c11a64269d0d013615ecc15dc738b0f Mon Sep 17 00:00:00 2001 From: Till JS Date: Tue, 31 Mar 2026 13:49:16 +0200 Subject: [PATCH] feat(todo): color picker popup on column dot instead of inline swatches In edit mode, the color dot stays in its normal position but gets a purple ring to indicate it's clickable. Clicking opens a popup with: - 16 preset color swatches in a 4x4 grid - Native color picker for custom colors - Click outside to dismiss Co-Authored-By: Claude Opus 4.6 (1M context) --- .../board-views/ViewColumnHeader.svelte | 279 ++++++++++++------ 1 file changed, 194 insertions(+), 85 deletions(-) diff --git a/apps/todo/apps/web/src/lib/components/board-views/ViewColumnHeader.svelte b/apps/todo/apps/web/src/lib/components/board-views/ViewColumnHeader.svelte index bae8922e8..c26faad7c 100644 --- a/apps/todo/apps/web/src/lib/components/board-views/ViewColumnHeader.svelte +++ b/apps/todo/apps/web/src/lib/components/board-views/ViewColumnHeader.svelte @@ -30,67 +30,106 @@ let editMode = $derived(editModeCtx?.active ?? false); let editable = $derived(editMode && !!onRename); - const COLORS = [ + let showColorPicker = $state(false); + + const PRESET_COLORS = [ '#EF4444', - '#F59E0B', - '#22C55E', - '#3B82F6', - '#8B5CF6', - '#EC4899', - '#14B8A6', '#F97316', + '#F59E0B', + '#EAB308', + '#22C55E', + '#14B8A6', + '#06B6D4', + '#3B82F6', + '#6366F1', + '#8B5CF6', + '#A855F7', + '#EC4899', + '#F43F5E', + '#78716C', '#6B7280', + '#334155', ]; + +{#if showColorPicker} +
(showColorPicker = false)}>
+{/if} +
{#if editable} - -
-
- {#each COLORS as c} - - {/each} -
-
- onRename?.(e.currentTarget.value)} - /> -
- - - -
+ +
+
+ + + {#if showColorPicker} +
e.stopPropagation()}> +
+ {#each PRESET_COLORS as c} + + {/each} +
+ +
+ {/if}
+ + onRename?.(e.currentTarget.value)} + /> +
+ +
+ + +
{:else} @@ -110,15 +149,12 @@ padding: 0.75rem 1rem; } - .column-header.editing { - padding: 0.5rem 0.75rem; - } - .header-left { display: flex; align-items: center; gap: 0.5rem; min-width: 0; + flex: 1; } .color-dot { @@ -157,45 +193,29 @@ } /* ── Edit mode ────────────────────────────────────────── */ - .edit-header { - display: flex; - flex-direction: column; - gap: 0.375rem; - width: 100%; + + .color-dot-wrapper { + position: relative; + flex-shrink: 0; } - .color-row { - display: flex; - gap: 0.2rem; - } - - .color-pick { - width: 12px; - height: 12px; - border-radius: 50%; - border: 2px solid transparent; + .color-dot.editable { + width: 0.875rem; + height: 0.875rem; cursor: pointer; - transition: all 0.15s; + border: none; padding: 0; + box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.5); + transition: all 0.15s; } - .color-pick:hover { - transform: scale(1.25); - } - .color-pick.active { - border-color: white; - box-shadow: 0 0 0 1.5px currentColor; + .color-dot.editable:hover { + box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.7); transform: scale(1.15); } - .edit-row { - display: flex; - align-items: center; - gap: 0.375rem; - } - .name-input { flex: 1; - font-size: 0.8125rem; + font-size: 0.875rem; font-weight: 600; color: #374151; background: transparent; @@ -216,6 +236,7 @@ display: flex; gap: 0.125rem; flex-shrink: 0; + margin-left: 0.375rem; } .act-btn { @@ -244,4 +265,92 @@ color: #ef4444 !important; background: rgba(239, 68, 68, 0.1) !important; } + + /* ── Color Picker Popup ──────────────────────────────── */ + + .picker-backdrop { + position: fixed; + inset: 0; + z-index: 50; + } + + .color-picker { + position: absolute; + top: calc(100% + 0.5rem); + left: 0; + z-index: 51; + background: rgba(255, 255, 255, 0.97); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 0.75rem; + box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.15); + padding: 0.625rem; + min-width: 170px; + } + :global(.dark) .color-picker { + background: rgba(30, 30, 30, 0.97); + border-color: rgba(255, 255, 255, 0.12); + } + + .picker-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 0.375rem; + margin-bottom: 0.5rem; + } + + .picker-swatch { + width: 28px; + height: 28px; + border-radius: 50%; + border: 2px solid transparent; + cursor: pointer; + transition: all 0.15s; + padding: 0; + } + .picker-swatch:hover { + transform: scale(1.15); + } + .picker-swatch.active { + border-color: white; + box-shadow: 0 0 0 2px currentColor; + } + + .custom-color-row { + display: flex; + align-items: center; + gap: 0.5rem; + padding-top: 0.375rem; + border-top: 1px solid rgba(0, 0, 0, 0.06); + } + :global(.dark) .custom-color-row { + border-top-color: rgba(255, 255, 255, 0.08); + } + + .custom-label { + font-size: 0.6875rem; + font-weight: 500; + color: #6b7280; + } + :global(.dark) .custom-label { + color: #9ca3af; + } + + .custom-color-input { + width: 28px; + height: 28px; + padding: 0; + border: none; + border-radius: 50%; + cursor: pointer; + background: transparent; + } + .custom-color-input::-webkit-color-swatch-wrapper { + padding: 0; + } + .custom-color-input::-webkit-color-swatch { + border: 2px solid rgba(0, 0, 0, 0.1); + border-radius: 50%; + }