dasds
This commit is contained in:
@@ -31,6 +31,287 @@
|
||||
statusPromise: null,
|
||||
};
|
||||
|
||||
const createEl = (tag, props = {}, children = []) => {
|
||||
const el = document.createElement(tag);
|
||||
Object.entries(props || {}).forEach(([key, value]) => {
|
||||
if (value === undefined || value === null) return;
|
||||
if (key === 'className') {
|
||||
el.className = value;
|
||||
} else if (key === 'text') {
|
||||
el.textContent = value;
|
||||
} else if (key === 'html') {
|
||||
el.innerHTML = value;
|
||||
} else if (key in el) {
|
||||
el[key] = value;
|
||||
} else {
|
||||
el.setAttribute(key, value);
|
||||
}
|
||||
});
|
||||
(Array.isArray(children) ? children : [children]).forEach(child => {
|
||||
if (child) el.appendChild(child);
|
||||
});
|
||||
return el;
|
||||
};
|
||||
|
||||
const applyInputStyles = (el) => {
|
||||
if (!el) return el;
|
||||
el.style.width = '100%';
|
||||
el.style.boxSizing = 'border-box';
|
||||
el.style.padding = '6px 8px';
|
||||
el.style.border = '1px solid #cbd5f5';
|
||||
el.style.borderRadius = '4px';
|
||||
el.style.fontSize = '13px';
|
||||
el.style.fontFamily = 'inherit';
|
||||
return el;
|
||||
};
|
||||
|
||||
const syncSelectOptions = (selectEl, options, selected) => {
|
||||
if (!selectEl) return;
|
||||
while (selectEl.firstChild) {
|
||||
selectEl.removeChild(selectEl.firstChild);
|
||||
}
|
||||
(options || []).forEach(opt => {
|
||||
const optionEl = document.createElement('option');
|
||||
optionEl.value = opt.id ?? opt.value ?? '';
|
||||
optionEl.textContent = opt.label ?? opt.text ?? '';
|
||||
if (opt.disabled) optionEl.disabled = true;
|
||||
selectEl.appendChild(optionEl);
|
||||
});
|
||||
if (selected !== undefined && selected !== null) {
|
||||
selectEl.value = selected;
|
||||
}
|
||||
};
|
||||
|
||||
const buildField = (labelText, control) => {
|
||||
const controlId = `bridge-placeholder-field-${Math.random().toString(36).slice(2)}`;
|
||||
control.id = controlId;
|
||||
const label = createEl('label', { htmlFor: controlId, text: labelText });
|
||||
label.style.display = 'block';
|
||||
label.style.fontWeight = '600';
|
||||
label.style.fontSize = '13px';
|
||||
label.style.marginBottom = '4px';
|
||||
const wrapper = createEl('div', { className: 'bridge-placeholder-field' }, [label, control]);
|
||||
wrapper.style.marginBottom = '12px';
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
const openPlaceholderModal = (editor, component) => {
|
||||
if (!editor || !component || !component.is || !component.is(PLACEHOLDER_COMPONENT)) return;
|
||||
const modal = editor.Modal;
|
||||
if (!modal) return;
|
||||
|
||||
const attrs = component.getAttributes ? component.getAttributes() : {};
|
||||
const initialType = attrs['data-placeholder-type'] || 'custom';
|
||||
const initialKey = attrs['data-placeholder-key'] || 'UEBERSCHRIFT';
|
||||
const initialTable = attrs['data-placeholder-table'] || '';
|
||||
const initialColumn = attrs['data-placeholder-column'] || '';
|
||||
|
||||
const container = createEl('div', { className: 'bridge-placeholder-modal' });
|
||||
container.style.padding = '8px 4px';
|
||||
|
||||
const form = createEl('form', { className: 'bridge-placeholder-form' });
|
||||
form.style.display = 'flex';
|
||||
form.style.flexDirection = 'column';
|
||||
|
||||
const typeSelect = applyInputStyles(createEl('select'));
|
||||
[
|
||||
{ id: 'custom', label: 'Placeholder (Text)' },
|
||||
{ id: 'database', label: 'Placeholder (DB)' },
|
||||
].forEach(opt => {
|
||||
const optionEl = createEl('option', { value: opt.id, text: opt.label });
|
||||
typeSelect.appendChild(optionEl);
|
||||
});
|
||||
typeSelect.value = initialType;
|
||||
|
||||
const keyInput = applyInputStyles(createEl('input', { type: 'text', value: initialKey }));
|
||||
keyInput.placeholder = 'z.B. UEBERSCHRIFT';
|
||||
|
||||
const tableSelect = applyInputStyles(createEl('select'));
|
||||
const columnSelect = applyInputStyles(createEl('select'));
|
||||
tableSelect.disabled = true;
|
||||
columnSelect.disabled = true;
|
||||
|
||||
const dbMessage = createEl('p', { className: 'bridge-placeholder-hint', text: '' });
|
||||
dbMessage.style.fontSize = '12px';
|
||||
dbMessage.style.margin = '4px 0 0';
|
||||
dbMessage.style.color = '#475569';
|
||||
|
||||
const columnMessage = createEl('p', { className: 'bridge-placeholder-hint', text: '' });
|
||||
columnMessage.style.fontSize = '12px';
|
||||
columnMessage.style.margin = '4px 0 0';
|
||||
columnMessage.style.color = '#475569';
|
||||
|
||||
const customWrap = createEl('div', { className: 'bridge-placeholder-section' }, [
|
||||
buildField('Bezeichner', keyInput),
|
||||
]);
|
||||
|
||||
const dbWrap = createEl('div', { className: 'bridge-placeholder-section' }, [
|
||||
buildField('Tabelle', tableSelect),
|
||||
dbMessage,
|
||||
buildField('Feld', columnSelect),
|
||||
columnMessage,
|
||||
]);
|
||||
|
||||
const errorBox = createEl('p', { className: 'bridge-placeholder-error', text: '' });
|
||||
errorBox.style.fontSize = '12px';
|
||||
errorBox.style.color = '#b91c1c';
|
||||
errorBox.style.margin = '4px 0 0';
|
||||
|
||||
form.appendChild(buildField('Typ', typeSelect));
|
||||
form.appendChild(customWrap);
|
||||
form.appendChild(dbWrap);
|
||||
form.appendChild(errorBox);
|
||||
|
||||
const actions = createEl('div', { className: 'bridge-placeholder-actions' });
|
||||
actions.style.marginTop = '16px';
|
||||
actions.style.display = 'flex';
|
||||
actions.style.justifyContent = 'flex-end';
|
||||
actions.style.gap = '8px';
|
||||
|
||||
const cancelBtn = createEl('button', { type: 'button', text: 'Abbrechen' });
|
||||
cancelBtn.style.padding = '6px 12px';
|
||||
cancelBtn.style.border = '1px solid #cbd5f5';
|
||||
cancelBtn.style.borderRadius = '4px';
|
||||
cancelBtn.style.background = '#f8fafc';
|
||||
cancelBtn.style.cursor = 'pointer';
|
||||
|
||||
const saveBtn = createEl('button', { type: 'submit', text: 'Speichern' });
|
||||
saveBtn.style.padding = '6px 16px';
|
||||
saveBtn.style.border = '1px solid #0ea5e9';
|
||||
saveBtn.style.background = '#0ea5e9';
|
||||
saveBtn.style.color = '#fff';
|
||||
saveBtn.style.borderRadius = '4px';
|
||||
saveBtn.style.cursor = 'pointer';
|
||||
|
||||
actions.appendChild(cancelBtn);
|
||||
actions.appendChild(saveBtn);
|
||||
form.appendChild(actions);
|
||||
container.appendChild(form);
|
||||
|
||||
let tablesCache = placeholderSchemaStore.tables || [];
|
||||
|
||||
const toggleSections = () => {
|
||||
const type = typeSelect.value || 'custom';
|
||||
customWrap.style.display = type === 'custom' ? '' : 'none';
|
||||
dbWrap.style.display = type === 'database' ? '' : 'none';
|
||||
};
|
||||
|
||||
const populateColumns = (tableName, preferredColumn) => {
|
||||
const table = (tablesCache || []).find(tbl => (tbl.name || '').toLowerCase() === (tableName || '').toLowerCase());
|
||||
const columns = table && Array.isArray(table.columns) ? table.columns : [];
|
||||
if (!columns.length) {
|
||||
syncSelectOptions(columnSelect, [{ id: '', label: columns.length ? '– Feld wählen –' : 'Keine Felder verfügbar' }], '');
|
||||
columnSelect.disabled = true;
|
||||
columnMessage.textContent = table ? 'Diese Tabelle enthält keine Felder.' : 'Bitte zuerst eine Tabelle wählen.';
|
||||
return;
|
||||
}
|
||||
const opts = columns.map(col => ({
|
||||
id: col.name,
|
||||
label: col.name + (col.type ? ` (${col.type})` : ''),
|
||||
}));
|
||||
syncSelectOptions(columnSelect, opts, preferredColumn || initialColumn || opts[0].id);
|
||||
columnSelect.disabled = false;
|
||||
columnMessage.textContent = '';
|
||||
};
|
||||
|
||||
const populateTables = (tables) => {
|
||||
tablesCache = Array.isArray(tables) ? tables : [];
|
||||
if (!tablesCache.length) {
|
||||
syncSelectOptions(tableSelect, [{ id: '', label: 'Keine Tabellen gefunden' }], '');
|
||||
tableSelect.disabled = true;
|
||||
columnSelect.disabled = true;
|
||||
dbMessage.textContent = 'Kein Tabellen-Schema verfügbar. Bitte Bridge-Setup prüfen.';
|
||||
columnMessage.textContent = '';
|
||||
return;
|
||||
}
|
||||
const opts = tablesCache.map(tbl => ({ id: tbl.name, label: tbl.name }));
|
||||
syncSelectOptions(tableSelect, opts, initialTable || opts[0].id);
|
||||
tableSelect.disabled = false;
|
||||
dbMessage.textContent = '';
|
||||
populateColumns(tableSelect.value, initialColumn);
|
||||
};
|
||||
|
||||
const loadTables = () => {
|
||||
if (placeholderSchemaStore.tables && placeholderSchemaStore.tables.length) {
|
||||
populateTables(placeholderSchemaStore.tables);
|
||||
return;
|
||||
}
|
||||
dbMessage.textContent = 'Tabellen werden geladen …';
|
||||
fetchPlaceholderSchema()
|
||||
.then(populateTables)
|
||||
.catch(() => {
|
||||
populateTables([]);
|
||||
});
|
||||
};
|
||||
|
||||
tableSelect.addEventListener('change', () => {
|
||||
populateColumns(tableSelect.value);
|
||||
});
|
||||
|
||||
typeSelect.addEventListener('change', () => {
|
||||
toggleSections();
|
||||
if (typeSelect.value === 'database' && (!tablesCache || !tablesCache.length)) {
|
||||
loadTables();
|
||||
}
|
||||
});
|
||||
|
||||
cancelBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
modal.close();
|
||||
});
|
||||
|
||||
form.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
errorBox.textContent = '';
|
||||
const type = typeSelect.value || 'custom';
|
||||
if (type === 'custom') {
|
||||
const key = (keyInput.value || '').trim();
|
||||
if (!key) {
|
||||
errorBox.textContent = 'Bitte einen Bezeichner eingeben.';
|
||||
keyInput.focus();
|
||||
return;
|
||||
}
|
||||
component.addAttributes({
|
||||
'data-placeholder-type': 'custom',
|
||||
'data-placeholder-key': key,
|
||||
'data-placeholder-table': '',
|
||||
'data-placeholder-column': '',
|
||||
});
|
||||
} else {
|
||||
const table = tableSelect.value || '';
|
||||
const column = columnSelect.value || '';
|
||||
if (!table || !column) {
|
||||
errorBox.textContent = 'Bitte Tabelle und Feld auswählen.';
|
||||
if (!table) {
|
||||
tableSelect.focus();
|
||||
} else {
|
||||
columnSelect.focus();
|
||||
}
|
||||
return;
|
||||
}
|
||||
component.addAttributes({
|
||||
'data-placeholder-type': 'database',
|
||||
'data-placeholder-key': '',
|
||||
'data-placeholder-table': table,
|
||||
'data-placeholder-column': column,
|
||||
});
|
||||
}
|
||||
modal.close();
|
||||
});
|
||||
|
||||
toggleSections();
|
||||
if (typeSelect.value === 'database') {
|
||||
loadTables();
|
||||
}
|
||||
|
||||
modal.setTitle('Placeholder bearbeiten');
|
||||
modal.setContent(container);
|
||||
modal.open();
|
||||
if (editor.getSelected() !== component) {
|
||||
editor.select && editor.select(component);
|
||||
}
|
||||
};
|
||||
|
||||
function addOnce(bm, id, def, category = TARGET_CAT_ID) {
|
||||
try {
|
||||
bm.add(id, { ...def, category });
|
||||
@@ -173,6 +454,12 @@
|
||||
changeProp: true,
|
||||
},
|
||||
];
|
||||
const baseToolbar = Array.isArray(placeholderDefaults.toolbar) ? placeholderDefaults.toolbar.slice() : [];
|
||||
baseToolbar.push({
|
||||
attributes: { class: 'fa fa-edit', title: 'Placeholder bearbeiten' },
|
||||
command: 'bridge-placeholder:edit',
|
||||
});
|
||||
placeholderDefaults.toolbar = baseToolbar;
|
||||
|
||||
const PlaceholderModel = BaseModel.extend({
|
||||
init() {
|
||||
@@ -308,6 +595,39 @@
|
||||
const bm = editor.BlockManager;
|
||||
ensurePlaceholderComponent(editor);
|
||||
|
||||
if (!editor.Commands.get('bridge-placeholder:edit')) {
|
||||
editor.Commands.add('bridge-placeholder:edit', {
|
||||
run(ed, sender, opts = {}) {
|
||||
const component = opts.component || ed.getSelected();
|
||||
if (component && component.is && component.is(PLACEHOLDER_COMPONENT)) {
|
||||
openPlaceholderModal(ed, component);
|
||||
} else {
|
||||
log('PLACEHOLDER INFO', 'Kein Placeholder ausgewählt.', '#888');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (!editor.__bridgePlaceholderEventsBound) {
|
||||
editor.__bridgePlaceholderEventsBound = true;
|
||||
|
||||
editor.on('component:dblclick', (cmp) => {
|
||||
if (cmp && cmp.is && cmp.is(PLACEHOLDER_COMPONENT)) {
|
||||
openPlaceholderModal(editor, cmp);
|
||||
return false;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
editor.on('component:add', (cmp) => {
|
||||
if (!cmp || !cmp.is || !cmp.is(PLACEHOLDER_COMPONENT)) return;
|
||||
if (window.__GJS_IS_PARSING) return;
|
||||
if (cmp.__bridgePlaceholderPrompted) return;
|
||||
cmp.__bridgePlaceholderPrompted = true;
|
||||
setTimeout(() => openPlaceholderModal(editor, cmp), 50);
|
||||
});
|
||||
}
|
||||
|
||||
addOnce(bm, 'cust-placeholder-custom', {
|
||||
id: 'cust-placeholder-custom',
|
||||
label: '🔖 Placeholder (Text)',
|
||||
@@ -340,7 +660,8 @@
|
||||
|
||||
window.BridgeBlocksPlaceholder = {
|
||||
IDS: ALL_PLACEHOLDER_BLOCK_IDS,
|
||||
register
|
||||
register,
|
||||
openModal: openPlaceholderModal
|
||||
};
|
||||
|
||||
if (B && B.registerGrapesJSPlugin && typeof register === 'function') {
|
||||
|
||||
Reference in New Issue
Block a user