feat: moodboard card reordering via drag handle
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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-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-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: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 { 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:hover { background: var(--bg-alt); }
|
||||||
.mood-add-icon { font-size: 24px; color: var(--text-hint); }
|
.mood-add-icon { font-size: 24px; color: var(--text-hint); }
|
||||||
@@ -1287,7 +1292,44 @@ function renderMoodboard() {
|
|||||||
removeBtn.innerHTML = '×';
|
removeBtn.innerHTML = '×';
|
||||||
removeBtn.onclick = (e) => { e.stopPropagation(); removeMoodItem(i); };
|
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(removeBtn);
|
||||||
|
card.appendChild(handle);
|
||||||
card.appendChild(imgDiv);
|
card.appendChild(imgDiv);
|
||||||
card.appendChild(caption);
|
card.appendChild(caption);
|
||||||
container.appendChild(card);
|
container.appendChild(card);
|
||||||
@@ -1305,6 +1347,7 @@ function renderMoodboard() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let moodUploadTarget = -1;
|
let moodUploadTarget = -1;
|
||||||
|
let moodDragIndex = -1;
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const moodInput = document.getElementById('mood-file-input');
|
const moodInput = document.getElementById('mood-file-input');
|
||||||
if (moodInput) moodInput.addEventListener('change', function() {
|
if (moodInput) moodInput.addEventListener('change', function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user