diff --git a/public/assets/js/bridge/rte-editor.js b/public/assets/js/bridge/rte-editor.js index 35fcaa8..049d3ff 100644 --- a/public/assets/js/bridge/rte-editor.js +++ b/public/assets/js/bridge/rte-editor.js @@ -206,6 +206,15 @@ || (component.view && component.view.el && component.view.el.innerHTML) || ''; content.innerHTML = initialHtml; + const existingStyle = component && component.get && component.get('style') ? component.get('style') : null; + if (existingStyle && typeof existingStyle === 'object') { + if (existingStyle.fontFamily) { + content.style.fontFamily = existingStyle.fontFamily; + } + if (existingStyle.fontSize) { + content.style.fontSize = existingStyle.fontSize; + } + } let savedRange = null; const saveSelection = () => { @@ -235,29 +244,42 @@ saveSelection(); } catch {} }; + const getSelectionRange = () => { + try { + const docRef = content.ownerDocument || document; + const sel = docRef.getSelection(); + if (!sel || sel.rangeCount === 0) return null; + const range = sel.getRangeAt(0); + if (!content.contains(range.commonAncestorContainer)) return null; + return range; + } catch { + return null; + } + }; const applyInlineStyle = (styleProp, value) => { try { content.focus(); restoreSelection(); const docRef = content.ownerDocument || document; - const sel = docRef.getSelection(); - if (!sel || sel.rangeCount === 0) return; - const range = sel.getRangeAt(0); - if (range.collapsed) return; + const range = getSelectionRange(); + if (!range || range.collapsed) return; const wrapper = docRef.createElement('span'); wrapper.style[styleProp] = value; const fragment = range.extractContents(); wrapper.appendChild(fragment); range.insertNode(wrapper); - sel.removeAllRanges(); - const newRange = docRef.createRange(); - newRange.selectNodeContents(wrapper); - sel.addRange(newRange); + const sel = docRef.getSelection(); + if (sel) { + sel.removeAllRanges(); + const newRange = docRef.createRange(); + newRange.selectNodeContents(wrapper); + sel.addRange(newRange); + } saveSelection(); } catch {} }; - const addButton = (labelHtml, title, cmd, valueGetter) => { + const addButton = (labelHtml, title, cmd, valueGetter, handler) => { const btn = doc.createElement('button'); btn.type = 'button'; btn.innerHTML = labelHtml; @@ -273,6 +295,10 @@ saveSelection(); }); btn.addEventListener('click', () => { + if (typeof handler === 'function') { + handler(); + return; + } const value = typeof valueGetter === 'function' ? valueGetter() : valueGetter; if (value === null || value === undefined) return; if (cmd === 'createLink' && !value) return; @@ -307,9 +333,27 @@ }; const icon = (path) => ``; - addButton(icon('M6 4h5a3 3 0 0 1 0 6H6V4zm0 8h6a3 3 0 0 1 0 6H6v-6z'), 'Fett', 'bold'); - addButton(icon('M10 4h8v2h-3l-4 12h3v2H6v-2h3l4-12h-3V4z'), 'Kursiv', 'italic'); - addButton(icon('M5 4h14v2h-6v3h4a4 4 0 0 1 0 8H7v-2h10a2 2 0 0 0 0-4h-4V6H5V4z'), 'Unterstrichen', 'underline'); + addButton( + icon('M6 4h5a3 3 0 0 1 0 6H6V4zm0 8h6a3 3 0 0 1 0 6H6v-6z'), + 'Fett', + 'bold', + null, + () => applyInlineStyle('fontWeight', '700') + ); + addButton( + icon('M10 4h8v2h-3l-4 12h3v2H6v-2h3l4-12h-3V4z'), + 'Kursiv', + 'italic', + null, + () => applyInlineStyle('fontStyle', 'italic') + ); + addButton( + icon('M5 4h14v2h-6v3h4a4 4 0 0 1 0 8H7v-2h10a2 2 0 0 0 0-4h-4V6H5V4z'), + 'Unterstrichen', + 'underline', + null, + () => applyInlineStyle('textDecoration', 'underline') + ); addButton(icon('M4 7h10v2H4zM4 11h16v2H4zM4 15h10v2H4z'), 'Liste (ungeordnet)', 'insertUnorderedList'); addButton(icon('M4 6h4v2H4V6zm0 4h4v2H4v-2zm0 4h4v2H4v-2zm6-8h10v2H10V6zm0 4h10v2H10v-2zm0 4h10v2H10v-2z'), 'Liste (geordnet)', 'insertOrderedList'); addButton(icon('M4 7h10v2H4zM4 11h16v2H4zM4 15h12v2H4z'), 'Linksbundig', 'justifyLeft'); @@ -356,10 +400,15 @@ } catch {} }; - addSelect([{ label: 'Schriftart', value: '' }, ...fontOptions], 'Schriftart', (value) => { + const pendingComponentStyle = {}; + const fontSelect = addSelect([{ label: 'Schriftart', value: '' }, ...fontOptions], 'Schriftart', (value) => { if (!value) return; + pendingComponentStyle.fontFamily = value; applyComponentStyle({ fontFamily: value }, { preview: true }); }); + if (existingStyle && existingStyle.fontFamily && fontSelect) { + fontSelect.value = existingStyle.fontFamily; + } addSelect([ { label: 'Groesse', value: '' }, { label: '10px', value: '10' }, @@ -375,10 +424,22 @@ const raw = prompt('Schriftgroesse in px', '14'); const num = Number(raw || 0); if (!Number.isFinite(num) || num <= 0) return; - applyInlineStyle('fontSize', `${num}px`); + const range = getSelectionRange(); + if (range && !range.collapsed) { + applyInlineStyle('fontSize', `${num}px`); + } else { + pendingComponentStyle.fontSize = `${num}px`; + applyComponentStyle({ fontSize: `${num}px` }, { preview: true }); + } return; } - applyInlineStyle('fontSize', `${value}px`); + const range = getSelectionRange(); + if (range && !range.collapsed) { + applyInlineStyle('fontSize', `${value}px`); + return; + } + pendingComponentStyle.fontSize = `${value}px`; + applyComponentStyle({ fontSize: `${value}px` }, { preview: true }); }); // Emoji-Picker entfernt (auf Wunsch) – kann spaeter als echter Picker wiederkommen. @@ -419,6 +480,9 @@ component.__bridgeRteLastContent = html; logConsoleSnapshot(editor, component, 'before-save'); const forceApply = () => { + if (Object.keys(pendingComponentStyle).length) { + applyComponentStyle(pendingComponentStyle); + } applyContentToComponent(editor, component, html); logConsoleSnapshot(editor, component, 'after-save'); };