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)',