From 03aa0756c9069683d025a58c650c068c46a81ebc Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Sat, 17 Jan 2026 03:00:22 +0100 Subject: [PATCH] ysdasds --- public/assets/js/bridge/rte-editor.js | 112 +++++++++++++++++++++----- 1 file changed, 90 insertions(+), 22 deletions(-) diff --git a/public/assets/js/bridge/rte-editor.js b/public/assets/js/bridge/rte-editor.js index b1b49a2..d302cee 100644 --- a/public/assets/js/bridge/rte-editor.js +++ b/public/assets/js/bridge/rte-editor.js @@ -188,11 +188,53 @@ || ''; content.innerHTML = initialHtml; + let savedRange = null; + const saveSelection = () => { + try { + const sel = (content.ownerDocument || document).getSelection(); + if (!sel || sel.rangeCount === 0) return; + const range = sel.getRangeAt(0); + if (content.contains(range.commonAncestorContainer)) { + savedRange = range.cloneRange(); + } + } catch {} + }; + const restoreSelection = () => { + try { + const sel = (content.ownerDocument || document).getSelection(); + if (!sel || !savedRange) return; + sel.removeAllRanges(); + sel.addRange(savedRange); + } catch {} + }; const exec = (cmd, value) => { try { content.focus(); + restoreSelection(); const docRef = content.ownerDocument || document; docRef.execCommand(cmd, false, value); + saveSelection(); + } catch {} + }; + 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 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); + saveSelection(); } catch {} }; @@ -201,11 +243,16 @@ btn.type = 'button'; btn.innerHTML = labelHtml; btn.title = title; + btn.setAttribute('aria-label', title); btn.style.padding = '4px 8px'; btn.style.border = '1px solid #cbd5f5'; btn.style.borderRadius = '4px'; btn.style.background = '#f8fafc'; btn.style.cursor = 'pointer'; + btn.addEventListener('mousedown', (evt) => { + evt.preventDefault(); + saveSelection(); + }); btn.addEventListener('click', () => { const value = typeof valueGetter === 'function' ? valueGetter() : valueGetter; if (value === null || value === undefined) return; @@ -218,6 +265,7 @@ const addSelect = (options, title, onChange) => { const select = doc.createElement('select'); select.title = title; + select.setAttribute('aria-label', title); select.style.padding = '4px 8px'; select.style.border = '1px solid #cbd5f5'; select.style.borderRadius = '4px'; @@ -228,6 +276,10 @@ optEl.textContent = opt.label; select.appendChild(optEl); }); + select.addEventListener('mousedown', (evt) => { + evt.preventDefault(); + saveSelection(); + }); select.addEventListener('change', () => { const value = select.value; if (value) onChange(value); @@ -237,23 +289,22 @@ }; const icon = (path) => ``; - addButton('B', 'Fett', 'bold'); - addButton('I', 'Kursiv', 'italic'); - addButton('U', 'Unterstrichen', 'underline'); - addButton('S', 'Durchgestrichen', 'strikethrough'); + 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('M4 7h10v2H4zM4 11h16v2H4zM4 15h10v2H4z'), 'Liste (ungeordnet)', 'insertUnorderedList'); - addButton(icon('M4 7h14v2H4zM4 11h14v2H4zM4 15h14v2H4z') + '1.', 'Liste (geordnet)', 'insertOrderedList'); + addButton(icon('M4 6h4v2H4V6zm0 4h4v2H4v-2zm0 4h4v2H4v-2zm6-8h10v2H10V6zm0 4h10v2H10v-2zm0 4h10v2H10v-2z'), 'Liste (geordnet)', 'insertOrderedList'); addButton(icon('M4 7h10v2H4zM4 11h16v2H4zM4 15h12v2H4z'), 'Linksbundig', 'justifyLeft'); addButton(icon('M5 7h14v2H5zM4 11h16v2H4zM5 15h14v2H5z'), 'Zentriert', 'justifyCenter'); - addButton(icon('M10 7h10v2H10zM4 11h16v2H4zM8 15h12v2H8z'), 'Rechtsbundig', 'justifyRight'); + addButton(icon('M10 7h10v2H10zM4 11h16v2H4zM8 15h12v2H8z'), 'Rechtsbuendig', 'justifyRight'); addButton(icon('M4 7h16v2H4zM4 11h16v2H4zM4 15h16v2H4z'), 'Blocksatz', 'justifyFull'); - addButton('Link', 'Link einfuegen', 'createLink', () => prompt('Link-URL eingeben', 'https://')); - addButton('Unlink', 'Link entfernen', 'unlink'); - addButton('Sub', 'Tiefgestellt', 'subscript'); - addButton('Sup', 'Hochgestellt', 'superscript'); - addButton('Einr.', 'Einzug', 'indent'); - addButton('Aus.', 'Ausruecken', 'outdent'); - addButton('Clear', 'Formatierung entfernen', 'removeFormat'); + addButton(icon('M7 7h10l-1.5 1.5-3-3-5.5 5.5v5h5l5.5-5.5-3-3L17 7z'), 'Link einfuegen', 'createLink', () => prompt('Link-URL eingeben', 'https://')); + addButton(icon('M7 7h4v2H7v4H5V9a2 2 0 0 1 2-2zm10 0a2 2 0 0 1 2 2v4h-2V9h-4V7h4zm0 10h-4v-2h4v-4h2v4a2 2 0 0 1-2 2zm-10 0a2 2 0 0 1-2-2v-4h2v4h4v2H7z'), 'Link entfernen', 'unlink'); + addButton(icon('M7 6h10v2H7zM9 10h6v2H9zM10 14h4v2h-4z'), 'Tiefgestellt', 'subscript'); + addButton(icon('M7 6h10v2H7zM9 10h6v2H9zM8 14h8v2H8z'), 'Hochgestellt', 'superscript'); + addButton(icon('M7 4h4v4H7V4zm6 12h4v4h-4v-4zM7 10h10v2H7v-2z'), 'Einzug', 'indent'); + addButton(icon('M13 4h4v4h-4V4zM7 16h4v4H7v-4zM7 10h10v2H7v-2z'), 'Ausruecken', 'outdent'); + addButton(icon('M5 5h14v2H5zM5 9h10v2H5zM5 13h14v2H5zM5 17h10v2H5z'), 'Formatierung entfernen', 'removeFormat'); const fontOptions = (B.RTE_FONTS && Array.isArray(B.RTE_FONTS) && B.RTE_FONTS.length) ? B.RTE_FONTS @@ -267,17 +318,27 @@ { label: 'Trebuchet MS', value: 'Trebuchet MS, sans-serif' }, { label: 'Verdana', value: 'Verdana, sans-serif' }, ]; - addSelect([{ label: 'Schriftart', value: '' }, ...fontOptions], 'Schriftart', (value) => exec('fontName', value)); + addSelect([{ label: 'Schriftart', value: '' }, ...fontOptions], 'Schriftart', (value) => applyInlineStyle('fontFamily', value)); addSelect([ { label: 'Groesse', value: '' }, - { label: '10px', value: '1' }, - { label: '12px', value: '2' }, - { label: '14px', value: '3' }, - { label: '16px', value: '4' }, - { label: '18px', value: '5' }, - { label: '24px', value: '6' }, - { label: '32px', value: '7' }, - ], 'Schriftgroesse', (value) => exec('fontSize', value)); + { label: '10px', value: '10' }, + { label: '12px', value: '12' }, + { label: '14px', value: '14' }, + { label: '16px', value: '16' }, + { label: '18px', value: '18' }, + { label: '24px', value: '24' }, + { label: '32px', value: '32' }, + { label: 'Custom…', value: '__custom__' }, + ], 'Schriftgroesse', (value) => { + if (value === '__custom__') { + const raw = prompt('Schriftgroesse in px', '14'); + const num = Number(raw || 0); + if (!Number.isFinite(num) || num <= 0) return; + applyInlineStyle('fontSize', `${num}px`); + return; + } + applyInlineStyle('fontSize', `${value}px`); + }); const emojiBtn = doc.createElement('button'); emojiBtn.type = 'button'; @@ -288,6 +349,10 @@ emojiBtn.style.borderRadius = '4px'; emojiBtn.style.background = '#f8fafc'; emojiBtn.style.cursor = 'pointer'; + emojiBtn.addEventListener('mousedown', (evt) => { + evt.preventDefault(); + saveSelection(); + }); emojiBtn.addEventListener('click', () => { const pick = prompt('Emoticon eingeben', ':)'); if (pick) exec('insertText', pick); @@ -296,6 +361,9 @@ container.appendChild(toolbar); container.appendChild(content); + content.addEventListener('keyup', saveSelection); + content.addEventListener('mouseup', saveSelection); + content.addEventListener('focus', saveSelection); const actions = doc.createElement('div'); actions.style.display = 'flex';