asdasd
This commit is contained in:
@@ -20,16 +20,44 @@
|
||||
if (window.__PLACEHOLDER_BLOCKS_LOADED) return;
|
||||
window.__PLACEHOLDER_BLOCKS_LOADED = true;
|
||||
|
||||
const TARGET_CAT_ID = 'placeholders';
|
||||
const PLACEHOLDER_COMPONENT = 'placeholder-block';
|
||||
const ALL_PLACEHOLDER_BLOCK_IDS = [];
|
||||
const TARGET_CAT_ID = 'placeholders';
|
||||
const PLACEHOLDER_COMPONENT = 'placeholder-block';
|
||||
const ALL_PLACEHOLDER_BLOCK_IDS = [];
|
||||
const INLINE_PLACEHOLDER_CLASS = 'bridge-placeholder-inline';
|
||||
|
||||
const placeholderSchemaStore = {
|
||||
promise: null,
|
||||
tables: [],
|
||||
status: null,
|
||||
statusPromise: null,
|
||||
};
|
||||
const placeholderSchemaStore = {
|
||||
promise: null,
|
||||
tables: [],
|
||||
status: null,
|
||||
statusPromise: null,
|
||||
};
|
||||
let inlineStyleInjected = false;
|
||||
|
||||
const ensureInlinePlaceholderStyles = () => {
|
||||
if (inlineStyleInjected || typeof document === 'undefined') return;
|
||||
inlineStyleInjected = true;
|
||||
const style = document.createElement('style');
|
||||
style.id = 'bridge-placeholder-inline-style';
|
||||
style.textContent = `
|
||||
.${INLINE_PLACEHOLDER_CLASS}{
|
||||
display:inline-flex;
|
||||
align-items:center;
|
||||
gap:4px;
|
||||
padding:2px 8px;
|
||||
border:1px dashed #94a3b8;
|
||||
border-radius:6px;
|
||||
background:#f1f5f9;
|
||||
font-family:monospace;
|
||||
font-size:12px;
|
||||
cursor:pointer;
|
||||
user-select:none;
|
||||
}
|
||||
.${INLINE_PLACEHOLDER_CLASS}:hover{
|
||||
background:#e2e8f0;
|
||||
}
|
||||
`;
|
||||
document.head && document.head.appendChild(style);
|
||||
};
|
||||
|
||||
const createEl = (tag, props = {}, children = []) => {
|
||||
const el = document.createElement(tag);
|
||||
@@ -53,7 +81,7 @@
|
||||
return el;
|
||||
};
|
||||
|
||||
const applyInputStyles = (el) => {
|
||||
const applyInputStyles = (el) => {
|
||||
if (!el) return el;
|
||||
el.style.width = '100%';
|
||||
el.style.boxSizing = 'border-box';
|
||||
@@ -65,7 +93,7 @@
|
||||
return el;
|
||||
};
|
||||
|
||||
const syncSelectOptions = (selectEl, options, selected) => {
|
||||
const syncSelectOptions = (selectEl, options, selected) => {
|
||||
if (!selectEl) return;
|
||||
while (selectEl.firstChild) {
|
||||
selectEl.removeChild(selectEl.firstChild);
|
||||
@@ -80,11 +108,20 @@
|
||||
if (selected !== undefined && selected !== null) {
|
||||
selectEl.value = selected;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const refreshPlaceholderComponent = (component) => {
|
||||
if (!component) return;
|
||||
if (typeof component.updatePlaceholderState === 'function') {
|
||||
const safeRemoveComponent = (component) => {
|
||||
if (!component || typeof component.remove !== 'function') return;
|
||||
try {
|
||||
component.remove();
|
||||
} catch (err) {
|
||||
log('PLACEHOLDER WARN', `Komponente konnte nicht entfernt werden: ${err && err.message ? err.message : err}`, '#b45309');
|
||||
}
|
||||
};
|
||||
|
||||
const refreshPlaceholderComponent = (component) => {
|
||||
if (!component) return;
|
||||
if (typeof component.updatePlaceholderState === 'function') {
|
||||
try {
|
||||
component.updatePlaceholderState();
|
||||
} catch (err) {
|
||||
@@ -111,12 +148,14 @@
|
||||
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}"`,
|
||||
];
|
||||
const buildPlaceholderHTML = (payload) => {
|
||||
const type = payload.type === 'database' ? 'database' : 'custom';
|
||||
const attrs = [
|
||||
`data-gjs-type="${PLACEHOLDER_COMPONENT}"`,
|
||||
`data-placeholder-type="${type}"`,
|
||||
`contenteditable="false"`,
|
||||
`class="${INLINE_PLACEHOLDER_CLASS}"`,
|
||||
];
|
||||
if (type === 'database') {
|
||||
attrs.push(`data-placeholder-table="${payload.table || ''}"`);
|
||||
attrs.push(`data-placeholder-column="${payload.column || ''}"`);
|
||||
@@ -181,16 +220,58 @@
|
||||
return true;
|
||||
};
|
||||
|
||||
const captureRteSelection = (rteInstance) => {
|
||||
const doc = rteInstance && rteInstance.doc
|
||||
? rteInstance.doc
|
||||
: (rteInstance && rteInstance.el && rteInstance.el.ownerDocument) || document;
|
||||
if (!doc || !doc.getSelection) return null;
|
||||
const sel = doc.getSelection();
|
||||
if (!sel || !sel.rangeCount) return null;
|
||||
return {
|
||||
doc,
|
||||
range: sel.getRangeAt(0).cloneRange()
|
||||
};
|
||||
};
|
||||
|
||||
const restoreRteSelection = (snapshot) => {
|
||||
if (!snapshot || !snapshot.doc || !snapshot.range) return false;
|
||||
const sel = snapshot.doc.getSelection && snapshot.doc.getSelection();
|
||||
if (!sel) return false;
|
||||
try {
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(snapshot.range);
|
||||
return true;
|
||||
} catch (err) {
|
||||
log('PLACEHOLDER WARN', `Auswahl konnte nicht wiederhergestellt werden: ${err && err.message ? err.message : err}`, '#b45309');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const openPlaceholderModal = (editor, component, opts = {}) => {
|
||||
if (!editor) return;
|
||||
const modal = editor.Modal;
|
||||
if (!modal) return;
|
||||
ensureInlinePlaceholderStyles();
|
||||
|
||||
if (component && (!component.is || !component.is(PLACEHOLDER_COMPONENT))) {
|
||||
log('PLACEHOLDER WARN', 'openPlaceholderModal wurde ohne gültige Placeholder-Komponente aufgerufen.', '#b45309');
|
||||
return;
|
||||
}
|
||||
|
||||
let didSave = false;
|
||||
let didCancel = false;
|
||||
const fireCancel = (reason) => {
|
||||
if (didSave || didCancel) return;
|
||||
didCancel = true;
|
||||
if (typeof opts.onCancel === 'function') {
|
||||
try {
|
||||
opts.onCancel({ reason, component, modal });
|
||||
} catch (err) {
|
||||
log('PLACEHOLDER WARN', `onCancel Fehler: ${err && err.message ? err.message : err}`, '#b45309');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const attrs = component && component.getAttributes ? component.getAttributes() : (opts.initial || {});
|
||||
const initialType = attrs['data-placeholder-type'] || 'custom';
|
||||
const initialKey = attrs['data-placeholder-key'] || 'UEBERSCHRIFT';
|
||||
@@ -348,6 +429,7 @@
|
||||
|
||||
cancelBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
fireCancel('cancel-button');
|
||||
modal.close();
|
||||
});
|
||||
|
||||
@@ -377,6 +459,7 @@
|
||||
});
|
||||
}
|
||||
refreshPlaceholderComponent(component);
|
||||
component.__bridgePlaceholderNew = false;
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -422,6 +505,7 @@
|
||||
return;
|
||||
}
|
||||
}
|
||||
didSave = true;
|
||||
modal.close();
|
||||
});
|
||||
|
||||
@@ -432,6 +516,20 @@
|
||||
|
||||
modal.setTitle('Placeholder konfigurieren');
|
||||
modal.setContent(container);
|
||||
if (typeof modal.onceClose === 'function') {
|
||||
modal.onceClose(() => fireCancel('modal-close'));
|
||||
} else if (modal.getModel && typeof modal.getModel === 'function') {
|
||||
const mdl = modal.getModel();
|
||||
if (mdl && typeof mdl.on === 'function') {
|
||||
const handler = () => {
|
||||
if (!mdl.get('open')) {
|
||||
mdl.off && mdl.off('change:open', handler);
|
||||
fireCancel('modal-close');
|
||||
}
|
||||
};
|
||||
mdl.on && mdl.on('change:open', handler);
|
||||
}
|
||||
}
|
||||
modal.open();
|
||||
if (component && editor.getSelected && editor.getSelected() !== component) {
|
||||
editor.select && editor.select(component);
|
||||
@@ -452,8 +550,20 @@
|
||||
log('PLACEHOLDER INFO', 'Bitte zuerst ein Text-Element auswählen.', '#888');
|
||||
return;
|
||||
}
|
||||
const selectionSnapshot = captureRteSelection(rteInstance);
|
||||
openPlaceholderModal(editor, null, {
|
||||
onCancel: () => {
|
||||
if (selectionSnapshot) {
|
||||
restoreRteSelection(selectionSnapshot);
|
||||
}
|
||||
if (rteInstance && typeof rteInstance.focus === 'function') {
|
||||
setTimeout(() => rteInstance.focus(), 0);
|
||||
}
|
||||
},
|
||||
onSubmit: (payload) => {
|
||||
if (selectionSnapshot) {
|
||||
restoreRteSelection(selectionSnapshot);
|
||||
}
|
||||
const html = buildPlaceholderHTML(payload);
|
||||
let inserted = false;
|
||||
if (insertPlaceholderIntoSelection(rteInstance, html)) {
|
||||
@@ -589,6 +699,8 @@
|
||||
attributes: {
|
||||
'data-placeholder-type': 'custom',
|
||||
'data-placeholder-key': 'UEBERSCHRIFT',
|
||||
'contenteditable': 'false',
|
||||
'class': INLINE_PLACEHOLDER_CLASS,
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
@@ -645,6 +757,7 @@
|
||||
const ensurePlaceholderComponent = (editor) => {
|
||||
const domc = editor.DomComponents;
|
||||
if (domc.getType(PLACEHOLDER_COMPONENT)) return;
|
||||
ensureInlinePlaceholderStyles();
|
||||
|
||||
const baseType = domc.getType('text') || domc.getType('default') || {};
|
||||
const BaseModel = baseType.model || editor.DomComponents.Component;
|
||||
@@ -662,10 +775,12 @@
|
||||
|
||||
updatePlaceholderState() {
|
||||
const attrs = this.getAttributes();
|
||||
const type = attrs['data-placeholder-type'] || 'custom';
|
||||
if (type === 'database' && placeholderSchemaStore.tables.length === 0) {
|
||||
if (!attrs['data-placeholder-type']) {
|
||||
this.addAttributes({ 'data-placeholder-type': 'custom' });
|
||||
}
|
||||
if (attrs['contenteditable'] !== 'false') {
|
||||
this.addAttributes({ 'contenteditable': 'false' });
|
||||
}
|
||||
this.updateTraitVisibility();
|
||||
this.updateSchemaTraits();
|
||||
this.updateLabel();
|
||||
@@ -711,9 +826,12 @@
|
||||
if (columnTrait) {
|
||||
const attrs = this.getAttributes();
|
||||
const tableName = (attrs['data-placeholder-table'] || '').toLowerCase();
|
||||
const table = tables.find(function (tbl) { return tbl.name.toLowerCase() === tableName; });
|
||||
const colOpts = table
|
||||
? table.columns.map(function (col) { return { id: col.name, label: col.name + ' (' + col.type + ')' }; })
|
||||
const table = tables.find(function (tbl) {
|
||||
return (tbl.name || '').toLowerCase() === tableName;
|
||||
});
|
||||
const columns = table && Array.isArray(table.columns) ? table.columns : [];
|
||||
const colOpts = columns.length
|
||||
? columns.map(function (col) { return { id: col.name, label: col.name + (col.type ? ' (' + col.type + ')' : '') }; })
|
||||
: [{ id: '', label: table ? 'Keine Felder' : 'Feld wählen', disabled: !table }];
|
||||
setTraitOptions(columnTrait, colOpts);
|
||||
}
|
||||
@@ -745,7 +863,11 @@
|
||||
render() {
|
||||
BaseView.prototype.render.apply(this, arguments);
|
||||
this.el.classList.add('placeholder-block');
|
||||
this.el.style.display = 'inline-block';
|
||||
this.el.classList.add(INLINE_PLACEHOLDER_CLASS);
|
||||
this.el.setAttribute('contenteditable', 'false');
|
||||
this.el.style.display = 'inline-flex';
|
||||
this.el.style.alignItems = 'center';
|
||||
this.el.style.gap = '4px';
|
||||
this.el.style.padding = '2px 8px';
|
||||
this.el.style.border = '1px dashed #94a3b8';
|
||||
this.el.style.borderRadius = '6px';
|
||||
@@ -848,7 +970,14 @@
|
||||
if (window.__GJS_IS_PARSING) return;
|
||||
if (cmp.__bridgePlaceholderPrompted) return;
|
||||
cmp.__bridgePlaceholderPrompted = true;
|
||||
setTimeout(() => openPlaceholderModal(editor, cmp), 50);
|
||||
cmp.__bridgePlaceholderNew = true;
|
||||
setTimeout(() => openPlaceholderModal(editor, cmp, {
|
||||
onCancel: () => {
|
||||
if (cmp.__bridgePlaceholderNew) {
|
||||
safeRemoveComponent(cmp);
|
||||
}
|
||||
}
|
||||
}), 50);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -865,25 +994,40 @@
|
||||
content: `<span data-gjs-type="${PLACEHOLDER_COMPONENT}" data-placeholder-type="custom" data-placeholder-key="UEBERSCHRIFT">{{UEBERSCHRIFT}}</span>`
|
||||
});
|
||||
|
||||
fetchPlaceholderSchema()
|
||||
.then(tables => {
|
||||
if (!tables || !tables.length) {
|
||||
log('PLACEHOLDER INFO', 'Keine Tabellen – DB Placeholder Block wird nicht angezeigt.', '#888');
|
||||
return;
|
||||
}
|
||||
const firstTable = tables[0] || {};
|
||||
const tableName = firstTable.name || 'tabelle';
|
||||
const columns = Array.isArray(firstTable.columns) ? firstTable.columns : [];
|
||||
const firstColumn = columns.length ? columns[0].name : 'feld';
|
||||
const placeholderLabel = (tableName + '.' + firstColumn).toUpperCase();
|
||||
addOnce(bm, 'cust-placeholder-db', {
|
||||
id: 'cust-placeholder-db',
|
||||
label: '🗄️ Placeholder (DB)',
|
||||
content: `<span data-gjs-type="${PLACEHOLDER_COMPONENT}" data-placeholder-type="database" data-placeholder-table="${tableName}" data-placeholder-column="${firstColumn}">{{${placeholderLabel}}}</span>`
|
||||
const ensureDbBlock = (tables) => {
|
||||
const fallback = { table: 'tabelle', column: 'feld' };
|
||||
let tableName = fallback.table;
|
||||
let columnName = fallback.column;
|
||||
if (Array.isArray(tables) && tables.length) {
|
||||
const tableWithColumns = tables.find(tbl => Array.isArray(tbl.columns) && tbl.columns.length) || tables[0];
|
||||
tableName = tableWithColumns && tableWithColumns.name ? tableWithColumns.name : fallback.table;
|
||||
const firstColumn = tableWithColumns && Array.isArray(tableWithColumns.columns) && tableWithColumns.columns[0];
|
||||
columnName = firstColumn && firstColumn.name ? firstColumn.name : fallback.column;
|
||||
}
|
||||
const payload = { type: 'database', table: tableName, column: columnName };
|
||||
const content = buildPlaceholderHTML(payload);
|
||||
const blockId = 'cust-placeholder-db';
|
||||
const label = '🗄️ Placeholder (DB)';
|
||||
const existing = typeof bm.get === 'function' ? bm.get(blockId) : null;
|
||||
if (existing && typeof existing.set === 'function') {
|
||||
existing.set('content', content);
|
||||
existing.set('label', label);
|
||||
} else {
|
||||
addOnce(bm, blockId, {
|
||||
id: blockId,
|
||||
label,
|
||||
content,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ensureDbBlock();
|
||||
fetchPlaceholderSchema()
|
||||
.then((tables) => {
|
||||
ensureDbBlock(tables);
|
||||
})
|
||||
.catch(() => {
|
||||
log('PLACEHOLDER WARN', 'DB Placeholder Block ausgeblendet (Schemafehler).', '#b45309');
|
||||
log('PLACEHOLDER WARN', 'DB Placeholder Schema konnte nicht geladen werden – Fallback bleibt aktiv.', '#b45309');
|
||||
});
|
||||
|
||||
log('SUCCESS', `Placeholder-Registrierung abgeschlossen. ${ALL_PLACEHOLDER_BLOCK_IDS.length} Blöcke erstellt.`, '#008000', 'info', true);
|
||||
|
||||
Reference in New Issue
Block a user