diff --git a/public/assets/js/bridge/blocks-placeholder.js b/public/assets/js/bridge/blocks-placeholder.js
index 25787a5..cad8477 100644
--- a/public/assets/js/bridge/blocks-placeholder.js
+++ b/public/assets/js/bridge/blocks-placeholder.js
@@ -82,6 +82,51 @@
}
};
+ const refreshPlaceholderComponent = (component) => {
+ if (!component) return;
+ if (typeof component.updatePlaceholderState === 'function') {
+ try {
+ component.updatePlaceholderState();
+ } catch (err) {
+ log('PLACEHOLDER WARN', `updatePlaceholderState Fehler: ${err && err.message ? err.message : err}`, '#b45309');
+ }
+ } else if (typeof component.trigger === 'function') {
+ component.trigger('change:attributes');
+ }
+ if (component.view && typeof component.view.render === 'function') {
+ component.view.render();
+ }
+ if (component.em && typeof component.em.trigger === 'function') {
+ component.em.trigger('component:update', component);
+ }
+ };
+
+ const buildPlaceholderLabel = (payload) => {
+ const type = 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 buildPlaceholderHTML = (payload) => {
+ const type = payload.type === 'database' ? 'database' : 'custom';
+ const attrs = [
+ `data-gjs-type="${PLACEHOLDER_COMPONENT}"`,
+ `data-placeholder-type="${type}"`,
+ ];
+ if (type === 'database') {
+ attrs.push(`data-placeholder-table="${payload.table || ''}"`);
+ attrs.push(`data-placeholder-column="${payload.column || ''}"`);
+ } else {
+ attrs.push(`data-placeholder-key="${payload.key || ''}"`);
+ }
+ const label = buildPlaceholderLabel(payload);
+ return `{{${label}}}`;
+ };
+
const buildField = (labelText, control) => {
const controlId = `bridge-placeholder-field-${Math.random().toString(36).slice(2)}`;
control.id = controlId;
@@ -95,12 +140,17 @@
return wrapper;
};
- const openPlaceholderModal = (editor, component) => {
- if (!editor || !component || !component.is || !component.is(PLACEHOLDER_COMPONENT)) return;
+ const openPlaceholderModal = (editor, component, opts = {}) => {
+ if (!editor) return;
const modal = editor.Modal;
if (!modal) return;
- const attrs = component.getAttributes ? component.getAttributes() : {};
+ if (component && (!component.is || !component.is(PLACEHOLDER_COMPONENT))) {
+ log('PLACEHOLDER WARN', 'openPlaceholderModal wurde ohne gültige Placeholder-Komponente aufgerufen.', '#b45309');
+ return;
+ }
+
+ const attrs = component && component.getAttributes ? component.getAttributes() : (opts.initial || {});
const initialType = attrs['data-placeholder-type'] || 'custom';
const initialKey = attrs['data-placeholder-key'] || 'UEBERSCHRIFT';
const initialTable = attrs['data-placeholder-table'] || '';
@@ -260,6 +310,35 @@
modal.close();
});
+ const applyPayload = (payload) => {
+ if (typeof opts.onSubmit === 'function') {
+ const res = opts.onSubmit(payload, { component, modal });
+ return res !== false;
+ }
+ if (!component || typeof component.addAttributes !== 'function') {
+ log('PLACEHOLDER INFO', 'Keine Ziel-Komponente gefunden – Placeholder-Daten werden verworfen.', '#888');
+ return true;
+ }
+
+ if (payload.type === 'custom') {
+ component.addAttributes({
+ 'data-placeholder-type': 'custom',
+ 'data-placeholder-key': payload.key,
+ 'data-placeholder-table': '',
+ 'data-placeholder-column': '',
+ });
+ } else {
+ component.addAttributes({
+ 'data-placeholder-type': 'database',
+ 'data-placeholder-key': '',
+ 'data-placeholder-table': payload.table,
+ 'data-placeholder-column': payload.column,
+ });
+ }
+ refreshPlaceholderComponent(component);
+ return true;
+ };
+
form.addEventListener('submit', (e) => {
e.preventDefault();
errorBox.textContent = '';
@@ -271,12 +350,15 @@
keyInput.focus();
return;
}
- component.addAttributes({
- 'data-placeholder-type': 'custom',
- 'data-placeholder-key': key,
- 'data-placeholder-table': '',
- 'data-placeholder-column': '',
- });
+ const payload = {
+ type: 'custom',
+ key,
+ table: '',
+ column: '',
+ };
+ if (!applyPayload(payload)) {
+ return;
+ }
} else {
const table = tableSelect.value || '';
const column = columnSelect.value || '';
@@ -289,12 +371,15 @@
}
return;
}
- component.addAttributes({
- 'data-placeholder-type': 'database',
- 'data-placeholder-key': '',
- 'data-placeholder-table': table,
- 'data-placeholder-column': column,
- });
+ const payload = {
+ type: 'database',
+ key: '',
+ table,
+ column,
+ };
+ if (!applyPayload(payload)) {
+ return;
+ }
}
modal.close();
});
@@ -304,14 +389,50 @@
loadTables();
}
- modal.setTitle('Placeholder bearbeiten');
+ modal.setTitle('Placeholder konfigurieren');
modal.setContent(container);
modal.open();
- if (editor.getSelected() !== component) {
+ if (component && editor.getSelected && editor.getSelected() !== component) {
editor.select && editor.select(component);
}
};
+ const ensureRtePlaceholderButton = (editor) => {
+ const rte = editor && editor.RichTextEditor;
+ if (!rte || rte.__bridgePlaceholderButton) return;
+
+ rte.__bridgePlaceholderButton = true;
+ rte.add('bridge-placeholder', {
+ icon: '',
+ attributes: { title: 'Placeholder einfügen' },
+ result: (rteInstance) => {
+ const target = editor && editor.getSelected && editor.getSelected();
+ if (!target || !target.is || !target.is('text')) {
+ log('PLACEHOLDER INFO', 'Bitte zuerst ein Text-Element auswählen.', '#888');
+ return;
+ }
+ openPlaceholderModal(editor, null, {
+ onSubmit: (payload) => {
+ const html = buildPlaceholderHTML(payload);
+ if (rteInstance && typeof rteInstance.insertHTML === 'function') {
+ rteInstance.insertHTML(html);
+ } else if (typeof document !== 'undefined' && document.execCommand) {
+ document.execCommand('insertHTML', false, html);
+ } else {
+ log('PLACEHOLDER ERROR', 'Placeholder konnte nicht eingefügt werden (kein RTE).', 'red', 'error');
+ return false;
+ }
+ if (rteInstance && typeof rteInstance.focus === 'function') {
+ rteInstance.focus();
+ }
+ return true;
+ }
+ });
+ },
+ });
+ log('RTE', 'Placeholder-Button im RichTextEditor registriert.', '#DAA520');
+ };
+
function addOnce(bm, id, def, category = TARGET_CAT_ID) {
try {
bm.add(id, { ...def, category });
@@ -628,6 +749,13 @@
});
}
+ const bindRteButton = () => ensureRtePlaceholderButton(editor);
+ if (editor.RichTextEditor) {
+ bindRteButton();
+ } else if (typeof editor.on === 'function') {
+ editor.on('load', bindRteButton, { once: true });
+ }
+
addOnce(bm, 'cust-placeholder-custom', {
id: 'cust-placeholder-custom',
label: '🔖 Placeholder (Text)',