Add device visibility and responsive navigation for 1.10.0
CMS quality / php (8.2) (push) Canceled after 0s
CMS quality / php (8.4) (push) Canceled after 0s
CMS quality / browser (push) Canceled after 0s
CMS quality / mysql (push) Canceled after 0s

This commit is contained in:
kantarci committed 2026-09-14 14:46:55 +02:00
1 parent eea6747d9e
commit 37c2942d1d
18 files changed
+493 -26

No files matched your search

+26
View File
@@ -8,6 +8,7 @@ import { preview, rowPreview } from './library-previews.js';
import { decorateContainers } from './preview-containers.js';
import { initMotion } from './motion.js';
import { initSliders } from './slider.js';
import { VISIBILITY, setVisibility } from './editor-model.js';
// Mirrors DocumentValidator::ICONS; the server rejects anything outside this list.
const ICONS = ['app', 'web', 'design', 'network', 'hardware', 'shop', 'support', 'shield', 'clock', 'star', 'phone', 'mail', 'pin', 'check'];
@@ -125,6 +126,12 @@ function renderTree() {
const icon = document.createElement('span'); icon.className = 'tree-icon'; icon.setAttribute('aria-hidden', 'true'); icon.textContent = blockIcon(block);
const text = document.createElement('span'); text.textContent = displayText;
button.append(icon, text); item.append(button);
if (block.layout?.visibility && block.layout.visibility !== 'all') {
const badge = document.createElement('small'); badge.className = 'tree-visibility';
badge.textContent = VISIBILITY[block.layout.visibility];
button.title = badge.textContent; button.append(badge);
button.setAttribute('aria-label', `${blockLabel(block)}: ${displayText} – ${badge.textContent}`);
}
if (block.children?.length && (!collapsed.has(block.id) || query)) item.append(build(block.children));
list.append(item);
}
@@ -177,6 +184,19 @@ function renderProperties() {
panel.append(note);
return;
}
if (block) {
const label = document.createElement('label'); label.htmlFor = 'block-visibility'; label.textContent = 'Sichtbarkeit';
const select = document.createElement('select'); select.id = 'block-visibility';
for (const [value, text] of Object.entries(VISIBILITY)) select.append(new Option(text, value));
select.value = block.layout?.visibility ?? 'all';
select.addEventListener('change', () => {
history.change(doc => setVisibility(doc, block.id, select.value));
renderTree(); controls(); schedulePreview();
});
const help = document.createElement('p'); help.className = 'field-help'; help.id = 'visibility-help';
help.textContent = 'Desktop ab 1025 px · Tablet 768–1024 px · Mobil bis 767 px. Ausgeblendete Elemente bleiben links in der Struktur auswählbar. Ein ausgeblendeter Elternbereich blendet auch seinen Inhalt aus.';
select.setAttribute('aria-describedby', help.id); panel.append(label, select, help);
}
if (block && propertyPanel === 'advanced') { renderAdvanced(block, panel); return; }
if (block && propertyPanel === 'design') { renderDesign(block, panel); return; }
let imagePreview;
@@ -217,6 +237,7 @@ function renderProperties() {
if (key === 'caption') upgradeDocument(doc, 3);
if (key === 'click' || key === 'lightbox') upgradeDocument(doc, 19);
if (key === 'newTab') upgradeDocument(doc, 20);
if (key === 'collapse') upgradeDocument(doc, 22);
if (current.type === 'image' && key === 'src') { delete current.props.sources; delete current.props.mediaId; }
}, options.choices ? null : `${selected || 'page'}:${key}`);
if (key === 'src') imagePreview?.update({ src: input.value });
@@ -297,6 +318,7 @@ function renderProperties() {
if (block.type === 'menu') {
field('Menüplatz', 'slot', block.props.slot, { choices: { header: 'Hauptnavigation', secondary: 'Zweite Navigation', footer: 'Fußzeile', legal: 'Rechtliches' }, help: 'Welche Einträge gezeigt werden. Gepflegt werden sie unter Menüs.' });
field('Menüart', 'layout', block.props.layout || 'horizontal', { choices: { horizontal: 'Waagerecht', vertical: 'Untereinander', burger: 'Aufklappbar (Burger)' }, help: 'Aufklappbar funktioniert ohne JavaScript. Untermenüs klappen in allen Arten per Klick auf, nicht beim Überfahren – das ist auf einem Touchgerät die einzige Art, die funktioniert.' });
field('Automatisch als Hamburger', 'collapse', block.props.collapse || 'tablet', { choices: { tablet: 'Tablet und Mobil (Standard)', mobile: 'Nur Mobil', never: 'Nie' }, help: 'Waagerechte Menüs wechseln standardmäßig bis 1024 px und auf Touchgeräten ohne Maus, auch großen iPads. „Nur Mobil“ gilt bis 767 px. Die Menüarten „Untereinander“ und „Burger“ bleiben unverändert.' });
}
if (block.type === 'partialref') field('Adresse der Sektion', 'partial', block.props.partial, { help: 'Die Adresse aus der Bereichsverwaltung. Die Sektion wird verknüpft, nicht kopiert: eine Änderung dort wirkt auf allen Seiten, die sie einbinden.' });
if (block.type === 'icon') {
@@ -744,6 +766,10 @@ async function updatePreview() {
}
doc.addEventListener('click', event => {
event.preventDefault();
const menuSummary = event.target.closest('.block-menu summary');
if (menuSummary && !busy && !inlineEdit) {
menuSummary.parentElement.open = !menuSummary.parentElement.open;
}
const insert = event.target.closest('[data-editor-target]');
if (insert && !busy && history) {
stopInlineEdit();