diff --git a/config/current.ver b/config/current.ver index 8c1a6bb..29c1447 100644 --- a/config/current.ver +++ b/config/current.ver @@ -1 +1 @@ -1.1.58 \ No newline at end of file +1.1.59 \ No newline at end of file diff --git a/public/assets/js/bridge/rte-editor.js b/public/assets/js/bridge/rte-editor.js index f6d0150..18ca6f6 100644 --- a/public/assets/js/bridge/rte-editor.js +++ b/public/assets/js/bridge/rte-editor.js @@ -376,6 +376,7 @@ container.style.gap = '10px'; container.style.height = '100%'; container.style.minHeight = '360px'; + container.style.position = 'relative'; container.className = 'bridge-rte-container'; container.setAttribute('data-bridge-rte', 'container'); @@ -847,28 +848,46 @@ addButton(icon('M5 7h14v2H5zM4 11h16v2H4zM5 15h14v2H5z'), 'Zentriert', 'justifyCenter'); addButton(icon('M10 7h10v2H10zM4 11h16v2H4zM8 15h12v2H8z'), 'Rechtsbuendig', 'justifyRight'); addButton(icon('M4 7h16v2H4zM4 11h16v2H4zM4 15h16v2H4z'), 'Blocksatz', 'justifyFull'); + const linkModal = doc.createElement('div'); + linkModal.style.display = 'none'; + linkModal.style.position = 'absolute'; + linkModal.style.inset = '0'; + linkModal.style.background = 'rgba(15,23,42,0.35)'; + linkModal.style.alignItems = 'center'; + linkModal.style.justifyContent = 'center'; + linkModal.style.zIndex = '20'; + linkModal.className = 'bridge-rte-link-modal'; + linkModal.setAttribute('data-bridge-rte', 'link-modal'); + const linkPanel = doc.createElement('div'); - linkPanel.style.display = 'none'; - linkPanel.style.alignItems = 'center'; - linkPanel.style.gap = '6px'; - linkPanel.style.padding = '6px'; + linkPanel.style.display = 'flex'; + linkPanel.style.flexDirection = 'column'; + linkPanel.style.gap = '10px'; + linkPanel.style.padding = '12px'; linkPanel.style.border = '1px solid #cbd5f5'; - linkPanel.style.borderRadius = '6px'; + linkPanel.style.borderRadius = '8px'; linkPanel.style.background = '#ffffff'; - linkPanel.style.boxShadow = '0 2px 10px rgba(15,23,42,0.08)'; - linkPanel.className = 'bridge-rte-link-panel'; - linkPanel.setAttribute('data-bridge-rte', 'link-panel'); + linkPanel.style.boxShadow = '0 6px 24px rgba(15,23,42,0.2)'; + linkPanel.style.minWidth = '320px'; + + const linkTitle = doc.createElement('div'); + linkTitle.textContent = 'Link einfuegen'; + linkTitle.style.fontWeight = '600'; + linkTitle.style.fontSize = '14px'; const linkInput = doc.createElement('input'); linkInput.type = 'text'; linkInput.placeholder = 'https://...'; - linkInput.style.flex = '1'; - linkInput.style.minWidth = '220px'; linkInput.style.padding = '6px 8px'; linkInput.style.border = '1px solid #cbd5f5'; linkInput.style.borderRadius = '4px'; linkInput.style.fontSize = '14px'; + const linkActions = doc.createElement('div'); + linkActions.style.display = 'flex'; + linkActions.style.justifyContent = 'flex-end'; + linkActions.style.gap = '8px'; + const linkApply = doc.createElement('button'); linkApply.type = 'button'; linkApply.textContent = 'OK'; @@ -887,19 +906,24 @@ linkCancel.className = 'bridge-rte-btn'; linkCancel.style.padding = '6px 10px'; + linkActions.appendChild(linkCancel); + linkActions.appendChild(linkRemove); + linkActions.appendChild(linkApply); + + linkPanel.appendChild(linkTitle); linkPanel.appendChild(linkInput); - linkPanel.appendChild(linkApply); - linkPanel.appendChild(linkRemove); - linkPanel.appendChild(linkCancel); - toolbar.appendChild(linkPanel); + linkPanel.appendChild(linkActions); + linkModal.appendChild(linkPanel); + container.appendChild(linkModal); const hideLinkPanel = () => { - linkPanel.style.display = 'none'; + linkModal.style.display = 'none'; }; const showLinkPanel = () => { + saveSelection(); const linkEl = findLinkAtSelection(); linkInput.value = linkEl && linkEl.getAttribute ? (linkEl.getAttribute('href') || '') : ''; - linkPanel.style.display = 'flex'; + linkModal.style.display = 'flex'; linkInput.focus(); linkInput.select(); }; @@ -910,7 +934,6 @@ null, null, () => { - saveSelection(); showLinkPanel(); } ); @@ -918,7 +941,7 @@ linkApply.addEventListener('click', () => { const url = String(linkInput.value || '').trim(); if (!url) { - linkRemove.click(); + hideLinkPanel(); return; } try { @@ -929,13 +952,24 @@ return; } } catch {} - const range = getSelectionRange(); + const range = savedRange ? savedRange.cloneRange() : getSelectionRange(); if (!range || range.collapsed) { hideLinkPanel(); return; } - restoreSelection(); - exec('createLink', url); + try { + const docRef = content.ownerDocument || document; + const anchor = docRef.createElement('a'); + anchor.setAttribute('href', url); + anchor.setAttribute('target', '_blank'); + anchor.setAttribute('rel', 'noopener'); + const fragment = range.extractContents(); + anchor.appendChild(fragment); + range.insertNode(anchor); + } catch { + restoreSelection(); + exec('createLink', url); + } hideLinkPanel(); }); linkRemove.addEventListener('click', () => {