From eebb9cfa690a42de37c8feca9d6ae2f40046b6e4 Mon Sep 17 00:00:00 2001 From: Etienne Delvarre Date: Fri, 29 May 2026 14:55:33 +0200 Subject: [PATCH] feat: moodboard card reordering via drag handle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grip handle (⠿) appears on hover, drag to reorder cards. Separate from image repositioning (which uses direct mousedown on image). Co-Authored-By: Claude Opus 4.6 --- public/index.html | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/public/index.html b/public/index.html index ba9bfd2..b5a7975 100644 --- a/public/index.html +++ b/public/index.html @@ -299,6 +299,11 @@ window.dispatchEvent(new Event('tiptap-loaded')); .mood-card-caption { padding: 8px 10px; font-size: 13px; color: var(--text); min-height: 32px; } .mood-card-remove { position: absolute; top: 4px; right: 4px; display: none; background: rgba(139,48,48,0.8); color: #fff; border: none; border-radius: 50%; width: 20px; height: 20px; font-size: 12px; cursor: pointer; align-items: center; justify-content: center; z-index: 2; } .mood-card:hover .mood-card-remove { display: flex; } + .mood-card-handle { position: absolute; top: 4px; left: 4px; display: none; background: rgba(0,0,0,0.5); color: #fff; border: none; border-radius: 4px; width: 22px; height: 22px; font-size: 14px; cursor: grab; align-items: center; justify-content: center; z-index: 2; user-select: none; } + .mood-card:hover .mood-card-handle { display: flex; } + .mood-card-handle:active { cursor: grabbing; } + .mood-card.drag-over { outline: 2px solid var(--accent); outline-offset: -2px; } + .mood-card.dragging-card { opacity: 0.4; } .mood-add { border: 1px dashed var(--border); background: transparent; display: flex; flex-direction: column; align-items: center; justify-content: center; cursor: pointer; min-height: 120px; border-radius: 10px; } .mood-add:hover { background: var(--bg-alt); } .mood-add-icon { font-size: 24px; color: var(--text-hint); } @@ -1287,7 +1292,44 @@ function renderMoodboard() { removeBtn.innerHTML = '×'; removeBtn.onclick = (e) => { e.stopPropagation(); removeMoodItem(i); }; + // Drag handle for reordering + const handle = document.createElement('div'); + handle.className = 'mood-card-handle'; + handle.innerHTML = '⠿'; + if (hasImg) { + card.draggable = false; + handle.addEventListener('mousedown', () => { card.draggable = true; }); + window.addEventListener('mouseup', () => { card.draggable = false; }); + card.addEventListener('dragstart', (e) => { + moodDragIndex = i; + card.classList.add('dragging-card'); + e.dataTransfer.effectAllowed = 'move'; + }); + card.addEventListener('dragend', () => { + card.classList.remove('dragging-card'); + card.draggable = false; + moodDragIndex = -1; + container.querySelectorAll('.mood-card').forEach(c => c.classList.remove('drag-over')); + }); + card.addEventListener('dragover', (e) => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + card.classList.add('drag-over'); + }); + card.addEventListener('dragleave', () => { card.classList.remove('drag-over'); }); + card.addEventListener('drop', (e) => { + e.preventDefault(); + card.classList.remove('drag-over'); + if (moodDragIndex < 0 || moodDragIndex === i) return; + const moved = items.splice(moodDragIndex, 1)[0]; + items.splice(i, 0, moved); + saveVisionSection('moodboard', JSON.stringify(items)); + renderMoodboard(); + }); + } + card.appendChild(removeBtn); + card.appendChild(handle); card.appendChild(imgDiv); card.appendChild(caption); container.appendChild(card); @@ -1305,6 +1347,7 @@ function renderMoodboard() { } let moodUploadTarget = -1; +let moodDragIndex = -1; document.addEventListener('DOMContentLoaded', () => { const moodInput = document.getElementById('mood-file-input'); if (moodInput) moodInput.addEventListener('change', function() {