Fassung 1.6.2: sichtbare leere Layouts und direktes Befuellen im Editor
This commit is contained in:
1 parent
149e1fe520
commit
6a044e59e9
14 files changed
+314
-21
No files matched your search
@@ -4,9 +4,9 @@ import { blockLabel, locate, dropList, isContainer } from './editor-model.js';
|
||||
// The layer above the preview.
|
||||
//
|
||||
// The preview is a sandboxed iframe without scripts of its own, and every change replaces its
|
||||
// whole document. Anything drawn inside it would therefore be wiped on the next keystroke and
|
||||
// would also end up in the markup we are supposed to be showing faithfully. So the outline, the
|
||||
// element toolbar, the insert points and the drop marker all live in the editor document, in an
|
||||
// whole document. The selection tools therefore live outside it and get rebound after a refresh.
|
||||
// Empty-container hit areas are rebuilt separately inside the frame by preview-containers.js.
|
||||
// The outline, element toolbar, insert points and drop marker live in the editor document, in an
|
||||
// absolutely positioned layer that sits exactly on top of the frame.
|
||||
//
|
||||
// Geometry: the layer is the same box as the frame and carries the same scale transform, so a
|
||||
@@ -50,10 +50,11 @@ export function createCanvasOverlay({ stage, frame, getScale, getDocument, getSe
|
||||
toolbar.setAttribute('role', 'toolbar');
|
||||
toolbar.setAttribute('aria-label', 'Aktionen für das gewählte Element');
|
||||
const drop = element('div', 'ov-drop');
|
||||
const dropLabel = element('div', 'ov-drop-label');
|
||||
const insertBefore = insertButton('before', 'Element davor einfügen');
|
||||
const insertAfter = insertButton('after', 'Element dahinter einfügen');
|
||||
toolbar.append(selectName);
|
||||
layer.append(hover, select, toolbar, insertBefore, insertAfter, drop);
|
||||
layer.append(hover, select, toolbar, insertBefore, insertAfter, drop, dropLabel);
|
||||
stage.append(layer);
|
||||
|
||||
for (const tool of TOOLS) {
|
||||
@@ -206,7 +207,10 @@ export function createCanvasOverlay({ stage, frame, getScale, getDocument, getSe
|
||||
positionToolbar(selectedBox, Boolean(sibling));
|
||||
const hoverBox = hovered && hovered !== selectedId ? rectOf(hovered) : null;
|
||||
if (place(hover, hoverBox)) paintLabel(hoverLabel, hovered);
|
||||
if (dragging) paintDrop();
|
||||
if (dragging) {
|
||||
dropPlan = dragPoint ? planDrop(dragPoint.x, dragPoint.y, dragging.block) : null;
|
||||
paintDrop();
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
@@ -259,8 +263,9 @@ export function createCanvasOverlay({ stage, frame, getScale, getDocument, getSe
|
||||
const parentId = item?.parent?.id ?? null;
|
||||
if (id && !ownBranch && dropList(document_, parentId, block)) {
|
||||
const box = rectOf(id);
|
||||
const after = box ? y > box.top + box.height / 2 : false;
|
||||
return { parentId, index: item.index + (after ? 1 : 0), line: box ? { box, edge: after ? 'bottom' : 'top' } : null };
|
||||
const horizontal = item.parent?.type === 'row' && indexInside(parentId, x, y).sideBySide;
|
||||
const after = box ? (horizontal ? x > box.left + box.width / 2 : y > box.top + box.height / 2) : false;
|
||||
return { parentId, index: item.index + (after ? 1 : 0), line: box ? { box, edge: horizontal ? (after ? 'right' : 'left') : (after ? 'bottom' : 'top') } : null };
|
||||
}
|
||||
if (!node) break;
|
||||
node = item?.parent ? doc()?.getElementById(item.parent.id) : null;
|
||||
@@ -276,11 +281,19 @@ export function createCanvasOverlay({ stage, frame, getScale, getDocument, getSe
|
||||
|
||||
function paintDrop() {
|
||||
const line = dropPlan?.line;
|
||||
if (!line?.box) { drop.hidden = true; return; }
|
||||
if (!line?.box) { drop.hidden = true; dropLabel.hidden = true; return; }
|
||||
drop.hidden = false;
|
||||
const scale = getScale();
|
||||
const thickness = 3 / scale;
|
||||
const { box, edge } = line;
|
||||
const target = dropPlan.parentId ? doc()?.getElementById(dropPlan.parentId) : null;
|
||||
const label = target?.dataset.editorLabel || (dropPlan.parentId ? blockLabel(locate(getDocument().blocks, dropPlan.parentId)?.block) : 'Seite');
|
||||
dropLabel.textContent = `${edge === 'inside' ? 'Einfügen in' : 'Einfügen ·'} ${label}`;
|
||||
dropLabel.hidden = false;
|
||||
dropLabel.style.left = `${Math.max(0, box.left + 8)}px`;
|
||||
dropLabel.style.top = `${Math.max(2, box.top + 8)}px`;
|
||||
counterScale(dropLabel, 'left', 'top');
|
||||
drop.style.marginLeft = drop.style.marginTop = '0';
|
||||
drop.classList.toggle('ov-drop-inside', edge === 'inside');
|
||||
if (edge === 'inside') {
|
||||
drop.style.left = `${box.left}px`; drop.style.top = `${box.top}px`;
|
||||
@@ -317,15 +330,20 @@ export function createCanvasOverlay({ stage, frame, getScale, getDocument, getSe
|
||||
document_.addEventListener('dragover', event => {
|
||||
if (!dragging) return;
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = dragging.kind === 'move' ? 'move' : 'copy';
|
||||
dragPoint = { x: event.clientX, y: event.clientY };
|
||||
dropPlan = planDrop(event.clientX, event.clientY, dragging.block);
|
||||
event.dataTransfer.dropEffect = dropPlan ? (dragging.kind === 'move' ? 'move' : 'copy') : 'none';
|
||||
paintDrop();
|
||||
if (!scrollFrame) scrollFrame = requestAnimationFrame(scrollDrag);
|
||||
});
|
||||
document_.addEventListener('dragleave', event => {
|
||||
if (!event.relatedTarget) { dragPoint = null; dropPlan = null; paintDrop(); }
|
||||
});
|
||||
document_.addEventListener('drop', event => {
|
||||
if (!dragging) return;
|
||||
event.preventDefault();
|
||||
// Both are read before the clean-up clears them.
|
||||
const payload = dragging, plan = dropPlan;
|
||||
const payload = dragging, plan = planDrop(event.clientX, event.clientY, dragging.block);
|
||||
endDrag();
|
||||
if (plan) onDrop(payload, plan);
|
||||
});
|
||||
@@ -335,17 +353,35 @@ export function createCanvasOverlay({ stage, frame, getScale, getDocument, getSe
|
||||
function startDrag(payload) {
|
||||
dragging = payload;
|
||||
layer.classList.add('is-dragging');
|
||||
doc()?.documentElement.classList.add('editor-dragging');
|
||||
hovered = null;
|
||||
sync();
|
||||
}
|
||||
|
||||
function endDrag() {
|
||||
dragging = null;
|
||||
dragPoint = null;
|
||||
cancelAnimationFrame(scrollFrame); scrollFrame = null;
|
||||
dropPlan = null;
|
||||
drop.hidden = true;
|
||||
dropLabel.hidden = true;
|
||||
layer.classList.remove('is-dragging');
|
||||
doc()?.documentElement.classList.remove('editor-dragging');
|
||||
sync();
|
||||
}
|
||||
|
||||
let dragPoint = null, scrollFrame = null;
|
||||
function scrollDrag() {
|
||||
scrollFrame = null;
|
||||
if (!dragging || !dragPoint) return;
|
||||
const edge = 60 / getScale();
|
||||
const amount = dragPoint.y < edge ? -14 : dragPoint.y > frame.clientHeight - edge ? 14 : 0;
|
||||
if (amount) {
|
||||
doc()?.defaultView?.scrollBy(0, amount / getScale());
|
||||
sync();
|
||||
scrollFrame = requestAnimationFrame(scrollDrag);
|
||||
}
|
||||
}
|
||||
|
||||
return { sync, attach, startDrag, endDrag, get dragPayload() { return dragging; } };
|
||||
}
|
||||
Reference in new issue
Block a user