diff --git a/config/current.ver b/config/current.ver
index a54e136..40e7d1a 100644
--- a/config/current.ver
+++ b/config/current.ver
@@ -1 +1 @@
-1.1.69
\ No newline at end of file
+1.1.70
\ 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 a0990a6..5f8dafc 100644
--- a/public/assets/js/bridge/rte-editor.js
+++ b/public/assets/js/bridge/rte-editor.js
@@ -393,6 +393,14 @@
toolbar.className = 'bridge-rte-toolbar';
toolbar.setAttribute('data-bridge-rte', 'toolbar');
+ const toolbarSecondary = doc.createElement('div');
+ toolbarSecondary.style.display = 'flex';
+ toolbarSecondary.style.flexWrap = 'wrap';
+ toolbarSecondary.style.gap = '6px';
+ toolbarSecondary.style.alignItems = 'center';
+ toolbarSecondary.className = 'bridge-rte-toolbar bridge-rte-toolbar-secondary';
+ toolbarSecondary.setAttribute('data-bridge-rte', 'toolbar-secondary');
+
const content = doc.createElement(this.getEditableTag(component));
content.contentEditable = 'true';
content.style.flex = '1';
@@ -681,7 +689,7 @@
} catch {}
};
- const addButton = (labelHtml, title, cmd, valueGetter, handler) => {
+ const addButton = (labelHtml, title, cmd, valueGetter, handler, target) => {
const btn = doc.createElement('button');
btn.type = 'button';
btn.innerHTML = labelHtml;
@@ -689,11 +697,15 @@
btn.setAttribute('aria-label', title);
btn.className = 'bridge-rte-btn';
btn.setAttribute('data-bridge-rte', 'button');
- btn.style.padding = '4px 8px';
+ btn.style.padding = '6px 10px';
btn.style.border = '1px solid #cbd5f5';
btn.style.borderRadius = '4px';
btn.style.background = '#f8fafc';
btn.style.cursor = 'pointer';
+ btn.style.display = 'inline-flex';
+ btn.style.alignItems = 'center';
+ btn.style.justifyContent = 'center';
+ btn.style.gap = '6px';
btn.addEventListener('mousedown', (evt) => {
evt.preventDefault();
saveSelection();
@@ -709,16 +721,16 @@
if (cmd === 'createLink' && !value) return;
exec(cmd, value);
});
- toolbar.appendChild(btn);
+ (target || toolbar).appendChild(btn);
};
- const addSelect = (options, title, onChange) => {
+ const addSelect = (options, title, onChange, target) => {
const select = doc.createElement('select');
select.title = title;
select.setAttribute('aria-label', title);
select.className = 'bridge-rte-select';
select.setAttribute('data-bridge-rte', 'select');
- select.style.padding = '4px 8px';
+ select.style.padding = '6px 10px';
select.style.border = '1px solid #cbd5f5';
select.style.borderRadius = '4px';
select.style.background = '#ffffff';
@@ -735,7 +747,7 @@
const value = select.value;
if (value) onChange(value);
});
- toolbar.appendChild(select);
+ (target || toolbar).appendChild(select);
return select;
};
@@ -780,6 +792,7 @@
component,
modal,
toolbar,
+ toolbarSecondary,
content,
addButton,
addSelect,
@@ -798,7 +811,7 @@
try { ui.buildToolbar(ctx); } catch {}
}
if (!ui.overrideToolbar) {
- const icon = (path) => ``;
+ const icon = (path) => ``;
addButton(
'B',
'Fett',
@@ -1006,7 +1019,7 @@
addButton(icon('M7 4h4v4H7V4zm6 12h4v4h-4v-4zM7 10h10v2H7v-2z'), 'Einzug', 'indent');
addButton(icon('M13 4h4v4h-4V4zM7 16h4v4H7v-4zM7 10h10v2H7v-2z'), 'Ausruecken', 'outdent');
addButton(
- 'Tx',
+ icon('M3 15.5L14.5 4 20 9.5 8.5 21H3v-5.5zm2.5 3H8l9.5-9.5-2.5-2.5L5.5 16v2.5z'),
'Formatierung entfernen',
null,
null,
@@ -1016,6 +1029,40 @@
}
);
+ const buildPlaceholderLabel = (payload) => {
+ const type = payload && payload.type === 'database' ? 'database' : 'custom';
+ if (type === 'database') {
+ const table = (payload.table || 'TABELLE').toUpperCase();
+ const column = (payload.column || 'FELD').toUpperCase();
+ return `${table}.${column}`;
+ }
+ return (payload.key || 'PLATZHALTER').toUpperCase();
+ };
+ const buildPlaceholderText = (payload) => `{{${buildPlaceholderLabel(payload)}}}`;
+ const insertTextAtSelection = (text) => {
+ try {
+ content.focus();
+ restoreSelection();
+ const range = getSelectionRange();
+ if (!range) return false;
+ range.deleteContents();
+ const docRef = content.ownerDocument || document;
+ const node = docRef.createTextNode(text);
+ range.insertNode(node);
+ range.setStartAfter(node);
+ range.setEndAfter(node);
+ const sel = docRef.getSelection();
+ if (sel) {
+ sel.removeAllRanges();
+ sel.addRange(range);
+ }
+ saveSelection();
+ return true;
+ } catch {
+ return false;
+ }
+ };
+
const fontOptions = (this.B.RTE_FONTS && Array.isArray(this.B.RTE_FONTS) && this.B.RTE_FONTS.length)
? this.B.RTE_FONTS
: [
@@ -1028,11 +1075,41 @@
{ label: 'Trebuchet MS', value: 'Trebuchet MS, sans-serif' },
{ label: 'Verdana', value: 'Verdana, sans-serif' },
];
+ addButton(
+ '{}Placeholder',
+ 'Placeholder einfuegen',
+ null,
+ null,
+ () => {
+ saveSelection();
+ const api = window.BridgeBlocksPlaceholder;
+ if (!api || typeof api.openModal !== 'function') return;
+ api.openModal(editor, null, {
+ onCancel: () => {
+ if (content && typeof content.focus === 'function') {
+ setTimeout(() => content.focus(), 0);
+ }
+ },
+ onSubmit: (payload) => {
+ const text = buildPlaceholderText(payload || {});
+ restoreSelection();
+ if (!insertTextAtSelection(text)) {
+ exec('insertText', text);
+ }
+ if (content && typeof content.focus === 'function') {
+ setTimeout(() => content.focus(), 0);
+ }
+ return true;
+ },
+ });
+ },
+ toolbarSecondary
+ );
const fontSelect = addSelect([{ label: 'Schriftart', value: '' }, ...fontOptions], 'Schriftart', (value) => {
if (!value) return;
pendingComponentStyle.fontFamily = value;
applyComponentStyle({ fontFamily: value }, { preview: true });
- });
+ }, toolbarSecondary);
if (existingStyle && existingStyle.fontFamily && fontSelect) {
fontSelect.value = existingStyle.fontFamily;
}
@@ -1067,7 +1144,7 @@
}
pendingComponentStyle.fontSize = `${value}px`;
applyComponentStyle({ fontSize: `${value}px` }, { preview: true });
- });
+ }, toolbarSecondary);
}
const injectedStyle = doc.createElement('style');
@@ -1081,15 +1158,27 @@
} catch {}
const frameCss = this.collectFrameCss(editor);
const rteUiCss = `
- .bridge-rte-toolbar { gap: 8px; }
+ .bridge-rte-toolbar {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ align-items: center;
+ }
+ .bridge-rte-toolbar-secondary {
+ margin-top: 6px;
+ padding-top: 6px;
+ border-top: 1px dashed #e2e8f0;
+ }
.bridge-rte-btn {
font-size: 14px;
line-height: 1;
- min-height: 30px;
- min-width: 30px;
+ min-height: 34px;
+ min-width: 34px;
+ font-weight: 600;
color: #0f172a;
- background: #f1f5f9;
+ background: #f8fafc;
}
+ .bridge-rte-btn svg { width: 16px; height: 16px; }
.bridge-rte-btn:hover { background: #e2e8f0; }
.bridge-rte-btn:active { transform: translateY(1px); }
.bridge-rte-actions .bridge-rte-btn { min-width: 88px; }
@@ -1100,6 +1189,7 @@
}
container.appendChild(toolbar);
+ container.appendChild(toolbarSecondary);
container.appendChild(content);
content.addEventListener('keyup', saveSelection);
content.addEventListener('mouseup', saveSelection);