v0.2.2: support dragging a device between front and rear elevations
Front and rear panels already share one coordinate system (same UNIT_HEIGHT, RACK_X, ...), just placed side by side on screen, so the existing delta-based drag math is equally valid for a drop on the other panel - only needed to detect which face the pointer ended up over (elementFromPoint at drop time) and include it as `face` in the submitted move; the reorder endpoint already supported changing it. Also fixed a bug this surfaced: without pointer-events: none on the dragged block during the drag, elementFromPoint() at drop time always hit the dragged block itself (it's what's rendered under the cursor), never the panel actually underneath - cross-face drops would never have been detected. Pointer capture keeps delivering move/up events to the block regardless of its own pointer-events value, so this is safe. Added overflow: visible on the elevation SVGs so a device being dragged toward the other panel doesn't just vanish at its own SVG's edge mid-drag. Updated the German translation for the changed drag-hint string. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,10 @@
|
||||
height: auto;
|
||||
user-select: none;
|
||||
touch-action: none;
|
||||
/* A device being dragged toward the other face's panel shouldn't just vanish the
|
||||
moment it crosses this SVG's own edge - let it render past the boundary while
|
||||
it's mid-drag, into whatever sits next to it (the other panel, side by side). */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.concept-elevation .unit-slot {
|
||||
|
||||
@@ -113,10 +113,18 @@
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
|
||||
/* Which .concept-elevation panel (front or rear) the pointer is currently over -
|
||||
used at drop time to tell whether a device was dragged onto the other face. */
|
||||
function panelAt(clientX, clientY) {
|
||||
const el = document.elementFromPoint(clientX, clientY);
|
||||
return el ? el.closest('.concept-elevation') : null;
|
||||
}
|
||||
|
||||
container.querySelectorAll('.concept-elevation').forEach(function (panel) {
|
||||
const svg = panel.querySelector('svg');
|
||||
const geo = geometry(svg);
|
||||
if (!geo) return;
|
||||
const sourceFace = panel.dataset.face;
|
||||
|
||||
panel.querySelectorAll('.device-block:not(.opposite)').forEach(function (block) {
|
||||
block.addEventListener('pointerdown', onPointerDown);
|
||||
@@ -139,6 +147,11 @@
|
||||
let dy = 0;
|
||||
|
||||
block.classList.add('dragging');
|
||||
// Without this, elementFromPoint() at drop time would always hit the
|
||||
// dragged block itself (it's what's rendered under the cursor), never
|
||||
// whatever panel is actually underneath it - pointer capture (below)
|
||||
// keeps move/up events coming to `block` regardless, so this is safe.
|
||||
block.style.pointerEvents = 'none';
|
||||
block.setPointerCapture(event.pointerId);
|
||||
|
||||
function onMove(moveEvent) {
|
||||
@@ -152,11 +165,12 @@
|
||||
block.setAttribute('transform', 'translate(' + dx + ' ' + snappedY + ')');
|
||||
}
|
||||
|
||||
function onUp() {
|
||||
function onUp(upEvent) {
|
||||
block.removeEventListener('pointermove', onMove);
|
||||
block.removeEventListener('pointerup', onUp);
|
||||
block.removeEventListener('pointercancel', onUp);
|
||||
block.classList.remove('dragging');
|
||||
block.style.pointerEvents = '';
|
||||
|
||||
if (!moved) {
|
||||
// Treat it as a click: follow the device link.
|
||||
@@ -166,6 +180,14 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Front and rear share one coordinate system (same UNIT_HEIGHT,
|
||||
// RACK_X, ...), just placed side by side on screen - the delta
|
||||
// computed against the source panel is equally valid whichever
|
||||
// panel the pointer ends up over, so dropping onto the other face
|
||||
// only needs to swap which face gets submitted.
|
||||
const targetPanel = panelAt(upEvent.clientX, upEvent.clientY);
|
||||
const targetFace = (targetPanel && targetPanel.dataset.face) || sourceFace;
|
||||
|
||||
const unitDelta = Math.round(dy / geo.unitHeight) * (descUnits ? 1 : -1);
|
||||
const lowest = startingUnit;
|
||||
const highest = startingUnit + rackHeight - deviceUnits;
|
||||
@@ -180,7 +202,8 @@
|
||||
);
|
||||
}
|
||||
|
||||
submit(block, newPosition, fraction > 1 ? horizontal : null);
|
||||
submit(block, newPosition, fraction > 1 ? horizontal : null,
|
||||
targetFace !== sourceFace ? targetFace : null);
|
||||
}
|
||||
|
||||
block.addEventListener('pointermove', onMove);
|
||||
@@ -190,7 +213,7 @@
|
||||
});
|
||||
});
|
||||
|
||||
function submit(block, position, horizontalPosition) {
|
||||
function submit(block, position, horizontalPosition, face) {
|
||||
const move = {
|
||||
id: parseInt(block.dataset.deviceId, 10),
|
||||
position: position
|
||||
@@ -198,6 +221,9 @@
|
||||
if (horizontalPosition !== null) {
|
||||
move.horizontal_position = horizontalPosition;
|
||||
}
|
||||
if (face) {
|
||||
move.face = face;
|
||||
}
|
||||
|
||||
setStatus('Saving…', false);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user