Fassung 1.6.1: flexible Editorflaeche und passende Blockeigenschaften
This commit is contained in:
1 parent
05ea703655
commit
b783d9d1e5
13 files changed
+276
-11
No files matched your search
+66
-2
@@ -23,6 +23,7 @@ let device = 'desktop', propertyPanel = 'content', designTokens, designSchema, d
|
||||
// same factor, so it is kept here instead of being recomputed from the transform string.
|
||||
let previewScale = 1, overlay = null, pendingInsert = null;
|
||||
const collapsed = new Set();
|
||||
let renderedProperties = null;
|
||||
|
||||
function notify(message = '', recover = false) {
|
||||
$('#message').textContent = message;
|
||||
@@ -72,6 +73,7 @@ function controls() {
|
||||
document.querySelectorAll('[data-panel],[data-viewport],#tree-search,#library-search,#expand-all,#collapse-all').forEach(input => input.disabled = !ready || busy);
|
||||
$('[data-panel="design"]').disabled ||= !current;
|
||||
$('#select-page').disabled = !ready || busy;
|
||||
$('#add-root-section').disabled = !ready || busy;
|
||||
$('#open-media-library').disabled = !ready || busy;
|
||||
}
|
||||
|
||||
@@ -131,6 +133,9 @@ function highlight(scroll = false) {
|
||||
function renderProperties() {
|
||||
const block = locate(history.document.blocks, selected)?.block;
|
||||
if (!block) propertyPanel = 'content';
|
||||
const context = `${selected ?? 'page'}:${propertyPanel}`;
|
||||
if (context !== renderedProperties) $('.right-panel').scrollTop = 0;
|
||||
renderedProperties = context;
|
||||
$('#properties-title').textContent = block ? blockLabel(block) : 'Seiteneinstellungen';
|
||||
const panel = $('#properties'); panel.replaceChildren();
|
||||
document.querySelectorAll('[data-panel]').forEach(button => button.setAttribute('aria-pressed', String(button.dataset.panel === propertyPanel)));
|
||||
@@ -325,7 +330,7 @@ function renderProperties() {
|
||||
}
|
||||
if (block.type === 'section') {
|
||||
field('Farbwelt', 'tone', block.layout?.tone || 'paper', { layout: true, choices: { paper: 'Hell · Papier', mint: 'Hellgrün · Akzent', ink: 'Dunkel · Tinte' } });
|
||||
field('Inhaltsbreite', 'width', block.layout?.width || 'normal', { layout: true, choices: { normal: 'Normal (1180 px)', narrow: 'Schmal (800 px)', wide: 'Breit (1600 px)', full: 'Randlos (volle Breite)' }, help: 'Randlos heißt ohne Rand und ohne Begrenzung – für ein Bild oder eine Farbfläche von Kante zu Kante.' });
|
||||
field('Inhaltsbreite', 'width', block.layout?.width || 'normal', { layout: true, choices: { normal: 'Websitebreite (aus dem Theme)', narrow: 'Schmal (800 px)', wide: 'Breit (1600 px)', full: 'Randlos (volle Breite)' }, help: 'Randlos nutzt die gesamte Sektion. Text und Überschriften füllen ihren Container; eine eigene Begrenzung stellst du unter Design → Größe ein.' });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,9 +550,9 @@ function stopInlineEdit() {
|
||||
|
||||
function resizePreview() {
|
||||
const container = $('.canvas-scroll'), frame = $('#preview'), stage = $('#preview-stage');
|
||||
const width = { desktop: 1240, tablet: 768, mobile: 390 }[device];
|
||||
const padding = parseFloat(getComputedStyle(container).paddingLeft) * 2;
|
||||
const available = Math.max(100, container.clientWidth - padding);
|
||||
const width = { desktop: Math.max(1240, available), tablet: 768, mobile: 390 }[device];
|
||||
const scale = Math.min(1, available / width);
|
||||
const height = Math.max(550, (container.clientHeight - padding) / scale);
|
||||
previewScale = scale;
|
||||
@@ -830,6 +835,13 @@ $('#library').addEventListener('click', event => {
|
||||
addElement(button.dataset.add);
|
||||
});
|
||||
$('#cancel-insert').addEventListener('click', () => setPendingInsert(null));
|
||||
$('#add-root-section').addEventListener('click', () => {
|
||||
if (busy || !history) return;
|
||||
stopInlineEdit();
|
||||
setPendingInsert(null);
|
||||
selected = history.change(doc => insertBlock(doc, 'section', selected));
|
||||
propertyPanel = 'content'; changed();
|
||||
});
|
||||
$('#presets').addEventListener('click', event => {
|
||||
const name = event.target.closest('[data-preset]')?.dataset.preset;
|
||||
if (!name || busy || !history) return;
|
||||
@@ -1003,6 +1015,58 @@ function setViewport(name) {
|
||||
}
|
||||
document.querySelectorAll('[data-viewport]').forEach(button => button.addEventListener('click', () => setViewport(button.dataset.viewport)));
|
||||
new ResizeObserver(resizePreview).observe($('.canvas-scroll'));
|
||||
// Panel sizes are workspace preferences, independent of document history and publication.
|
||||
const panelSizes = { left: 280, right: 320 };
|
||||
try {
|
||||
const saved = JSON.parse(localStorage.getItem('kantsites.panel-widths'));
|
||||
for (const side of ['left', 'right']) if (Number.isFinite(saved?.[side])) panelSizes[side] = saved[side];
|
||||
} catch { /* Storage can be unavailable; resizing still works for this session. */ }
|
||||
const panelMinimum = { left: 240, right: 280 };
|
||||
function layoutPanels() {
|
||||
let left = Math.max(240, Math.min(560, panelSizes.left));
|
||||
let right = Math.max(280, Math.min(560, panelSizes.right));
|
||||
const budget = Math.max(520, $('.editor-layout').clientWidth - 292);
|
||||
const excess = Math.max(0, left + right - budget);
|
||||
const spare = left + right - 520;
|
||||
if (excess && spare) { left -= excess * (left - 240) / spare; right -= excess * (right - 280) / spare; }
|
||||
for (const [side, size] of Object.entries({ left, right })) {
|
||||
root.style.setProperty(`--panel-${side}`, `${size}px`);
|
||||
const handle = $(`[data-resize="${side}"]`);
|
||||
handle.setAttribute('aria-valuenow', String(Math.round(size)));
|
||||
handle.setAttribute('aria-valuemax', String(Math.floor(Math.min(560, budget - (side === 'left' ? right : left)))));
|
||||
handle.setAttribute('aria-valuetext', `${Math.round(size)} Pixel`);
|
||||
}
|
||||
}
|
||||
const persistPanels = () => { try { localStorage.setItem('kantsites.panel-widths', JSON.stringify(panelSizes)); } catch {} };
|
||||
for (const handle of document.querySelectorAll('[data-resize]')) {
|
||||
const side = handle.dataset.resize, direction = side === 'left' ? 1 : -1;
|
||||
let drag = null;
|
||||
const setSize = value => {
|
||||
panelSizes[side] = Math.max(panelMinimum[side], Math.min(Number(handle.getAttribute('aria-valuemax')), value));
|
||||
layoutPanels();
|
||||
};
|
||||
handle.addEventListener('pointerdown', event => {
|
||||
if (event.button !== 0) return;
|
||||
event.preventDefault(); handle.focus();
|
||||
drag = { x: event.clientX, size: Number(handle.getAttribute('aria-valuenow')) };
|
||||
handle.setPointerCapture(event.pointerId); root.classList.add('resizing-panels');
|
||||
});
|
||||
handle.addEventListener('pointermove', event => { if (drag) setSize(drag.size + direction * (event.clientX - drag.x)); });
|
||||
const stop = () => { drag = null; root.classList.remove('resizing-panels'); persistPanels(); };
|
||||
handle.addEventListener('pointerup', stop);
|
||||
handle.addEventListener('pointercancel', stop);
|
||||
handle.addEventListener('lostpointercapture', stop);
|
||||
handle.addEventListener('dblclick', () => { panelSizes[side] = side === 'left' ? 280 : 320; layoutPanels(); persistPanels(); });
|
||||
handle.addEventListener('keydown', event => {
|
||||
if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return;
|
||||
event.preventDefault();
|
||||
const current = Number(handle.getAttribute('aria-valuenow'));
|
||||
setSize(event.key === 'Home' ? panelMinimum[side] : event.key === 'End' ? 560 : current + (event.key === 'ArrowRight' ? 1 : -1) * direction * (event.shiftKey ? 40 : 10));
|
||||
persistPanels();
|
||||
});
|
||||
}
|
||||
new ResizeObserver(layoutPanels).observe($('.editor-layout'));
|
||||
layoutPanels();
|
||||
window.addEventListener('keydown', event => {
|
||||
if (!history || busy || !(event.ctrlKey || event.metaKey)) return;
|
||||
const key = event.key.toLowerCase();
|
||||
|
||||
Reference in new issue
Block a user