mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-16 16:59:41 +02:00
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) <noreply@anthropic.com>
This commit is contained in:
parent
1926c6b1f2
commit
dc4ba0a39c
1 changed files with 194 additions and 85 deletions
|
|
@ -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',
|
||||
];
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
{#if showColorPicker}
|
||||
<div class="picker-backdrop" onclick={() => (showColorPicker = false)}></div>
|
||||
{/if}
|
||||
|
||||
<div class="column-header" class:editing={editable}>
|
||||
{#if editable}
|
||||
<!-- Edit mode: color dots + name input + actions -->
|
||||
<div class="edit-header">
|
||||
<div class="color-row">
|
||||
{#each COLORS as c}
|
||||
<button
|
||||
class="color-pick"
|
||||
class:active={color === c}
|
||||
style="background-color: {c}"
|
||||
onclick={() => onColorChange?.(c)}
|
||||
></button>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="edit-row">
|
||||
<input
|
||||
class="name-input"
|
||||
type="text"
|
||||
value={name}
|
||||
oninput={(e) => onRename?.(e.currentTarget.value)}
|
||||
/>
|
||||
<div class="edit-actions">
|
||||
<button
|
||||
class="act-btn"
|
||||
onclick={() => onMove?.(-1)}
|
||||
disabled={columnIndex === 0}
|
||||
title="Nach links"
|
||||
>
|
||||
<ArrowLeft size={12} />
|
||||
</button>
|
||||
<button
|
||||
class="act-btn"
|
||||
onclick={() => onMove?.(1)}
|
||||
disabled={columnIndex >= totalColumns - 1}
|
||||
title="Nach rechts"
|
||||
>
|
||||
<ArrowRight size={12} />
|
||||
</button>
|
||||
<button
|
||||
class="act-btn del-btn"
|
||||
onclick={() => onDelete?.()}
|
||||
disabled={totalColumns <= 1}
|
||||
title="Spalte löschen"
|
||||
>
|
||||
<Trash size={12} />
|
||||
</button>
|
||||
</div>
|
||||
<!-- Edit mode: same layout, color dot is clickable, name is input -->
|
||||
<div class="header-left">
|
||||
<div class="color-dot-wrapper">
|
||||
<button
|
||||
class="color-dot editable"
|
||||
style="background-color: {color}"
|
||||
onclick={() => (showColorPicker = !showColorPicker)}
|
||||
title="Farbe ändern"
|
||||
></button>
|
||||
|
||||
{#if showColorPicker}
|
||||
<div class="color-picker" onclick={(e) => e.stopPropagation()}>
|
||||
<div class="picker-grid">
|
||||
{#each PRESET_COLORS as c}
|
||||
<button
|
||||
class="picker-swatch"
|
||||
class:active={color === c}
|
||||
style="background-color: {c}"
|
||||
onclick={() => {
|
||||
onColorChange?.(c);
|
||||
showColorPicker = false;
|
||||
}}
|
||||
></button>
|
||||
{/each}
|
||||
</div>
|
||||
<label class="custom-color-row">
|
||||
<span class="custom-label">Eigene:</span>
|
||||
<input
|
||||
type="color"
|
||||
value={color}
|
||||
oninput={(e) => onColorChange?.(e.currentTarget.value)}
|
||||
class="custom-color-input"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<input
|
||||
class="name-input"
|
||||
type="text"
|
||||
value={name}
|
||||
oninput={(e) => onRename?.(e.currentTarget.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="edit-actions">
|
||||
<button
|
||||
class="act-btn"
|
||||
onclick={() => onMove?.(-1)}
|
||||
disabled={columnIndex === 0}
|
||||
title="Nach links"
|
||||
>
|
||||
<ArrowLeft size={12} />
|
||||
</button>
|
||||
<button
|
||||
class="act-btn"
|
||||
onclick={() => onMove?.(1)}
|
||||
disabled={columnIndex >= totalColumns - 1}
|
||||
title="Nach rechts"
|
||||
>
|
||||
<ArrowRight size={12} />
|
||||
</button>
|
||||
<button
|
||||
class="act-btn del-btn"
|
||||
onclick={() => onDelete?.()}
|
||||
disabled={totalColumns <= 1}
|
||||
title="Spalte löschen"
|
||||
>
|
||||
<Trash size={12} />
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Normal mode -->
|
||||
|
|
@ -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%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue