Ergänzt Prozentregler für die responsive Bildbreite
This commit is contained in:
1 parent
d78a58f714
commit
0920e3cb83
8 files changed
+191
-4
No files matched your search
@@ -311,3 +311,6 @@ dialog p{font-size:14px;line-height:1.7;color:var(--muted)}
|
||||
@media(max-width:560px){.left-panel,.right-panel{width:100%;height:620px;max-height:620px}.canvas-toolbar{padding-inline:8px}.canvas-label{display:none}}
|
||||
|
||||
.canvas-toolbar{flex-wrap:wrap;gap:8px}.canvas-toolbar #preview-motion{white-space:nowrap;min-height:36px}
|
||||
.image-width-control{padding:14px;margin-bottom:20px;border:1px solid var(--line);border-radius:10px;background:#f5f8f1}
|
||||
.image-width-control>label{margin-top:0}.image-width-inputs{display:grid;grid-template-columns:minmax(0,1fr) 76px;align-items:center;gap:12px}
|
||||
.image-width-inputs input[type=range]{width:100%;min-width:0;margin:0;padding:0;accent-color:var(--accent);min-height:36px}.image-width-control .field-help{margin:8px 0}.image-width-control .text-button{margin-top:4px}.image-width-error{color:#a62424}
|
||||
+45
-2
@@ -333,15 +333,16 @@ function renderProperties() {
|
||||
}
|
||||
if (block.type === 'button') field('Linkziel', 'href', block.props.href, { help: 'Lokaler Pfad (/kontakt), Sprungmarke (#abschnitt) oder HTTPS-Adresse.' });
|
||||
if (block.type === 'image') {
|
||||
renderImageWidth(block, panel);
|
||||
field('Beim Klick', 'click', block.props?.click || (block.props?.href ? 'link' : 'keine'), { choices: { keine: 'Nichts tun', vergroessern: 'Bild vergrößern', link: 'Zu einer Adresse führen' }, help: 'Vergrößern öffnet das Bild über der Seite. Ohne JavaScript öffnet derselbe Klick die große Bilddatei – die Seite bleibt also in jedem Fall benutzbar.' });
|
||||
if ((block.props?.click || (block.props?.href ? 'link' : 'keine')) === 'link') field('Linkziel', 'href', block.props.href || '', { help: 'Lokaler Pfad (/kontakt), Sprungmarke (#abschnitt) oder HTTPS-Adresse. Für ein Logo im Kopfbereich meist /.' });
|
||||
const picker = document.createElement('button'); picker.className = 'secondary'; picker.textContent = 'Bild aus Mediathek wählen';
|
||||
picker.addEventListener('click', () => chooseMedia(false)); panel.append(picker);
|
||||
field('Bildadresse', 'src', block.props.src, { help: 'Vorhandene lokale Datei oder HTTPS-Adresse. Eine Mediathek folgt später.' });
|
||||
field('Bildadresse', 'src', block.props.src, { help: 'Lokale Datei oder HTTPS-Adresse. Über die Mediathek wird die passende Bildvariante automatisch gewählt.' });
|
||||
field('Dekoratives Bild', 'decorative', block.props.decorative, { checkbox: true });
|
||||
if (!block.props.decorative) field('Alternativtext', 'alt', block.props.alt, { multiline: true });
|
||||
field('Originalbreite (px)', 'width', block.props.width, { number: true });
|
||||
field('Originalhöhe (px)', 'height', block.props.height, { number: true });
|
||||
field('Originalhöhe (px)', 'height', block.props.height, { number: true, help: 'Abmessungen der Bilddatei. Die Anzeigegröße stellst du oben unter Bildbreite ein.' });
|
||||
field('Bildunterschrift', 'caption', block.props.caption || '', { multiline: true, maxLength: 500 });
|
||||
}
|
||||
if (block.type === 'gallery') {
|
||||
@@ -397,6 +398,48 @@ function renderAdvanced(block, panel) {
|
||||
}
|
||||
}
|
||||
|
||||
function renderImageWidth(block, panel) {
|
||||
const group = document.createElement('div'); group.className = 'image-width-control';
|
||||
const label = document.createElement('label'); label.htmlFor = 'image-width-percent'; label.textContent = 'Bildbreite (%)';
|
||||
const row = document.createElement('div'); row.className = 'image-width-inputs';
|
||||
const slider = document.createElement('input'); slider.type = 'range'; slider.setAttribute('aria-label', 'Bildbreite Schieberegler');
|
||||
const exact = document.createElement('input'); exact.type = 'number'; exact.id = label.htmlFor;
|
||||
for (const input of [slider, exact]) { input.min = '1'; input.max = '100'; input.step = '0.1'; input.setAttribute('aria-describedby', 'image-width-help image-width-state'); }
|
||||
const state = document.createElement('p'); state.id = 'image-width-state'; state.className = 'field-help';
|
||||
const help = document.createElement('p'); help.id = 'image-width-help'; help.className = 'field-help';
|
||||
help.textContent = 'Anteil an der verfügbaren Breite der Sektion oder Spalte. Die Originalmaße der Datei bleiben erhalten.';
|
||||
const error = document.createElement('p'); error.className = 'field-help image-width-error'; error.setAttribute('role', 'alert'); error.hidden = true;
|
||||
error.textContent = 'Bitte 1 bis 100 eingeben. Die bisherige Breite bleibt bis dahin erhalten.';
|
||||
const reset = document.createElement('button'); reset.type = 'button'; reset.id = 'image-width-reset'; reset.className = 'text-button'; reset.textContent = 'Breite zurücksetzen';
|
||||
const sync = (keepInput = false) => {
|
||||
const current = locate(history.document.blocks, block.id)?.block;
|
||||
const { value, source } = effectiveStyle(current, device, 'width');
|
||||
const percent = typeof value === 'string' && /^\d+(?:\.\d+)?%$/.test(value) ? parseFloat(value) : null;
|
||||
if (!keepInput) exact.value = percent ?? '';
|
||||
exact.placeholder = value && percent === null ? value : '100';
|
||||
slider.value = percent ?? 100;
|
||||
slider.setAttribute('aria-valuetext', percent !== null ? `${percent} Prozent` : `Aktuell ${value || 'automatisch'}. Prozentwert wählen.`);
|
||||
state.textContent = `${devices[device]} · ${source === device ? 'Eigener Wert' : source ? `Von ${devices[source]} übernommen` : 'Automatische Breite'}${value ? ` (${value})` : ''}`;
|
||||
const maximum = effectiveStyle(current, device, 'maxWidth').value;
|
||||
if (maximum && maximum !== 'none') state.textContent += `. Maximale Breite: ${maximum} (Design → Größe).`;
|
||||
};
|
||||
const apply = (value, keepInput = false) => {
|
||||
error.hidden = true; exact.removeAttribute('aria-invalid');
|
||||
history.change(doc => setStyle(doc, block.id, device, 'width', value), `image-width:${block.id}:${device}`);
|
||||
controls(); schedulePreview(); sync(keepInput);
|
||||
};
|
||||
slider.addEventListener('input', () => apply(`${slider.value}%`));
|
||||
exact.addEventListener('input', () => {
|
||||
if (exact.validity.badInput || (exact.value !== '' && !exact.checkValidity())) { error.hidden = false; exact.setAttribute('aria-invalid', 'true'); return; }
|
||||
apply(exact.value === '' ? '' : `${Number(exact.value)}%`, true);
|
||||
});
|
||||
// A whole drag or keyboard edit is one undo step, independent of preview requests.
|
||||
slider.addEventListener('change', () => { history.mergeKey = null; });
|
||||
exact.addEventListener('blur', () => { history.mergeKey = null; if (error.hidden) sync(); });
|
||||
reset.addEventListener('click', () => { history.mergeKey = null; apply(''); history.mergeKey = null; });
|
||||
row.append(slider, exact); group.append(label, row, state, help, error, reset); panel.append(group); sync();
|
||||
}
|
||||
|
||||
function renderDesign(block, panel) {
|
||||
if (designSchema && designPanel) {
|
||||
designPanel(panel);
|
||||
|
||||
Reference in new issue
Block a user