Freies Layout: Board-Drag als Free-Move neu (widgets.js-Vorbild), .board.dragging auf z-index umgewidmet, Reorder-CSS (placeholder/ghost) raus
This commit is contained in:
+2
-13
@@ -893,14 +893,8 @@ body.show-desc .bm-desc { display: block; }
|
||||
.modal-body { padding: 14px 16px; }
|
||||
.modal-footer { padding: 10px 16px 14px; display: flex; justify-content: flex-end; }
|
||||
|
||||
.board.dragging { opacity: 0.35; }
|
||||
|
||||
.board-placeholder {
|
||||
border: 2px dashed var(--border-accent);
|
||||
border-radius: var(--radius);
|
||||
background: var(--accent-dim);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
/* Free-Move: das gezogene Board nach vorne heben (frueher Reorder-Opacity). */
|
||||
.board.dragging { z-index: 50; cursor: grabbing; }
|
||||
}
|
||||
|
||||
|
||||
@@ -2484,11 +2478,6 @@ body.show-desc .bm-desc { display: block; }
|
||||
.hidden { display: none; }
|
||||
.accent-text { color: var(--accent); }
|
||||
.dim { opacity: 0.4; }
|
||||
.drag-ghost {
|
||||
position: fixed; opacity: 0.75; pointer-events: none; z-index: 9999;
|
||||
transform: rotate(1.5deg) scale(1.02);
|
||||
box-shadow: 0 12px 40px rgba(0,0,0,0.6);
|
||||
}
|
||||
.bm-item.drag-over { background: rgba(255,160,50,0.07); }
|
||||
.bm-item.dragging-source { opacity: 0.4; }
|
||||
.about-info-label-block { display: block; margin-bottom: 6px; }
|
||||
|
||||
+38
-72
@@ -5,95 +5,61 @@
|
||||
Bookmarks: Reihenfolge innerhalb eines Boards
|
||||
============================================= */
|
||||
|
||||
// ---- BOARD DRAG (Pointer Events) ----
|
||||
// ---- BOARD FREE-MOVE (Pointer Events) ----
|
||||
// Neugebaut fuer v2.3 (frueher Reorder mit Ghost/Placeholder). Vorbild:
|
||||
// widgets.js _initDrag — setPointerCapture, offX/offY, onMove mit Clamping
|
||||
// gegen window.innerWidth/Height, onUp schreibt board.pos + saveBoards().
|
||||
// Gebunden am .board-drag-handle, NICHT am ganzen .board, damit Bookmark-Drag,
|
||||
// Klick-Delegation und Action-Buttons frei bleiben.
|
||||
function initBoardDragDrop() {
|
||||
const wrapper = document.getElementById('boardsWrapper');
|
||||
let dragging = null;
|
||||
let placeholder = null;
|
||||
|
||||
function getInsertTarget(clientX, clientY) {
|
||||
const boardEls = Array.from(wrapper.querySelectorAll('.board:not(.dragging)'));
|
||||
for (const b of boardEls) {
|
||||
const r = b.getBoundingClientRect();
|
||||
if (clientX >= r.left && clientX <= r.right && clientY >= r.top && clientY <= r.bottom) {
|
||||
return { el: b, before: clientX < r.left + r.width / 2 };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
wrapper.querySelectorAll('.board').forEach(boardEl => {
|
||||
const handle = boardEl.querySelector('.board-drag-handle');
|
||||
if (!handle) return;
|
||||
|
||||
handle.style.cursor = 'grab';
|
||||
|
||||
handle.addEventListener('pointerdown', e => {
|
||||
handle.addEventListener('pointerdown', function onDown(e) {
|
||||
// Auf Mobil ist .board position:static (Stapel) -> kein Free-Move.
|
||||
if (getComputedStyle(boardEl).position !== 'absolute') return;
|
||||
e.preventDefault();
|
||||
handle.setPointerCapture(e.pointerId);
|
||||
handle.style.cursor = 'grabbing';
|
||||
|
||||
const rect = boardEl.getBoundingClientRect();
|
||||
|
||||
// Ghost
|
||||
const ghost = boardEl.cloneNode(true);
|
||||
ghost.className += ' drag-ghost';
|
||||
ghost.style.left = rect.left + 'px';
|
||||
ghost.style.top = rect.top + 'px';
|
||||
ghost.style.width = rect.width + 'px';
|
||||
ghost.style.height = rect.height + 'px';
|
||||
document.body.appendChild(ghost);
|
||||
|
||||
// Placeholder
|
||||
placeholder = document.createElement('div');
|
||||
placeholder.className = 'board-placeholder';
|
||||
placeholder.style.cssText = `width:${rect.width}px; height:${rect.height}px;`;
|
||||
boardEl.parentNode.insertBefore(placeholder, boardEl);
|
||||
// .board.dragging hebt das Board per CSS nach vorne (z-index) UND ist das Signal fuer den
|
||||
// Live-Sync-Guard in app.js (bindStorageSync verwirft ein onChanged-Re-Render, das diesen
|
||||
// Drag sonst abreissen wuerde). Der Guard prueft genau diese Klasse (Phase-4-Review 2a).
|
||||
boardEl.classList.add('dragging');
|
||||
|
||||
dragging = { el: boardEl, ghost,
|
||||
offsetX: e.clientX - rect.left,
|
||||
offsetY: e.clientY - rect.top
|
||||
};
|
||||
});
|
||||
const rect = boardEl.getBoundingClientRect();
|
||||
const offX = e.clientX - rect.left;
|
||||
const offY = e.clientY - rect.top;
|
||||
|
||||
handle.addEventListener('pointermove', e => {
|
||||
if (!dragging || dragging.el !== boardEl) return;
|
||||
e.preventDefault();
|
||||
dragging.ghost.style.left = (e.clientX - dragging.offsetX) + 'px';
|
||||
dragging.ghost.style.top = (e.clientY - dragging.offsetY) + 'px';
|
||||
|
||||
const target = getInsertTarget(e.clientX, e.clientY);
|
||||
if (target && target.el !== boardEl) {
|
||||
target.before
|
||||
? target.el.parentNode.insertBefore(placeholder, target.el)
|
||||
: target.el.parentNode.insertBefore(placeholder, target.el.nextSibling);
|
||||
function onMove(ev) {
|
||||
const maxX = window.innerWidth - boardEl.offsetWidth;
|
||||
const maxY = window.innerHeight - boardEl.offsetHeight;
|
||||
const x = Math.max(0, Math.min(maxX, ev.clientX - offX));
|
||||
const y = Math.max(48, Math.min(maxY, ev.clientY - offY)); // 48px = Header-Hoehe
|
||||
boardEl.style.setProperty('--board-x', x + 'px');
|
||||
boardEl.style.setProperty('--board-y', y + 'px');
|
||||
}
|
||||
});
|
||||
|
||||
handle.addEventListener('pointerup', async () => {
|
||||
if (!dragging || dragging.el !== boardEl) return;
|
||||
handle.style.cursor = 'grab';
|
||||
placeholder.parentNode.insertBefore(boardEl, placeholder);
|
||||
placeholder.remove(); placeholder = null;
|
||||
boardEl.classList.remove('dragging');
|
||||
dragging.ghost.remove();
|
||||
dragging = null;
|
||||
async function onUp() {
|
||||
handle.releasePointerCapture(e.pointerId);
|
||||
handle.removeEventListener('pointermove', onMove);
|
||||
handle.removeEventListener('pointerup', onUp);
|
||||
boardEl.classList.remove('dragging'); // z-index zurueck + Live-Sync-Guard freigeben
|
||||
|
||||
// Neue Reihenfolge aus DOM ablesen
|
||||
const newOrder = Array.from(wrapper.querySelectorAll('.board'))
|
||||
.map(el => el.dataset.boardId).filter(Boolean);
|
||||
boards.sort((a, b) => newOrder.indexOf(a.id) - newOrder.indexOf(b.id));
|
||||
const id = boardEl.dataset.boardId;
|
||||
const board = boards.find(b => b.id === id);
|
||||
if (board) {
|
||||
board.pos = {
|
||||
x: parseFloat(boardEl.style.getPropertyValue('--board-x')),
|
||||
y: parseFloat(boardEl.style.getPropertyValue('--board-y'))
|
||||
};
|
||||
await saveBoards();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handle.addEventListener('pointercancel', () => {
|
||||
if (!dragging) return;
|
||||
dragging.ghost.remove();
|
||||
if (placeholder) { placeholder.remove(); placeholder = null; }
|
||||
boardEl.classList.remove('dragging');
|
||||
dragging = null;
|
||||
handle.style.cursor = 'grab';
|
||||
handle.addEventListener('pointermove', onMove);
|
||||
handle.addEventListener('pointerup', onUp);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user