asdasd
This commit is contained in:
@@ -54,6 +54,73 @@
|
|||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
// (2) Komponenten-Logik (ASYNCHRONER WORKAROUND & FIX)
|
// (2) Komponenten-Logik (ASYNCHRONER WORKAROUND & FIX)
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
|
const rehydrateLegacyReferences = (editor) => {
|
||||||
|
try {
|
||||||
|
const wrapper = editor.DomComponents?.getWrapper?.();
|
||||||
|
if (!wrapper) return;
|
||||||
|
|
||||||
|
const candidates = wrapper.find('[data-lib-kind][data-lib-id]');
|
||||||
|
if (!candidates || !candidates.length) return;
|
||||||
|
|
||||||
|
let patched = 0;
|
||||||
|
|
||||||
|
candidates.forEach((component) => {
|
||||||
|
const attrs = (typeof component.getAttributes === 'function') ? (component.getAttributes() || {}) : {};
|
||||||
|
const attrKind = component.get('lib-kind') || attrs['data-lib-kind'] || '';
|
||||||
|
const attrId = component.get('lib-id') || attrs['data-lib-id'] || '';
|
||||||
|
if (!attrKind || !attrId) return;
|
||||||
|
|
||||||
|
if (typeof component.syncReferenceAttributes === 'function') {
|
||||||
|
component.syncReferenceAttributes();
|
||||||
|
} else if (component.get && component.set && component.get('type') !== REFERENCE_COMPONENT_TYPE) {
|
||||||
|
const parent = component.parent && component.parent();
|
||||||
|
if (!parent || typeof parent.components !== 'function') return;
|
||||||
|
|
||||||
|
const atIndex = parent.components().indexOf(component);
|
||||||
|
const startContent = typeof component.toHTML === 'function' ? component.toHTML() : '';
|
||||||
|
const classes = component.get && typeof component.get === 'function'
|
||||||
|
? (component.get('classes') || [])
|
||||||
|
: [];
|
||||||
|
const normalizedClasses = Array.isArray(classes)
|
||||||
|
? classes
|
||||||
|
: (classes.models || classes.collection || []);
|
||||||
|
|
||||||
|
const newComponent = {
|
||||||
|
type: REFERENCE_COMPONENT_TYPE,
|
||||||
|
'lib-kind': attrKind,
|
||||||
|
'lib-id': attrId,
|
||||||
|
startContent,
|
||||||
|
attributes: {
|
||||||
|
...attrs,
|
||||||
|
'data-lib-kind': attrKind,
|
||||||
|
'data-lib-id': attrId,
|
||||||
|
'data-lib-ref': attrs['data-lib-ref'] || '1',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (normalizedClasses && normalizedClasses.length) {
|
||||||
|
newComponent.classes = normalizedClasses.map((cls) => {
|
||||||
|
if (typeof cls === 'string') return { name: cls };
|
||||||
|
if (cls && typeof cls.get === 'function') return { name: cls.get('name') };
|
||||||
|
if (cls && cls.name) return { name: cls.name };
|
||||||
|
return null;
|
||||||
|
}).filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
component.remove();
|
||||||
|
parent.components().add(newComponent, { at: atIndex });
|
||||||
|
patched++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (patched) {
|
||||||
|
log(`REHYDRATE`, `${patched} Legacy-Referenzen in Referenz-Komponenten umgewandelt.`, '#228B22');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
log('REF REHYDRATE ERROR', error?.message || String(error), '#dc3545', 'error', true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const registerReferenceComponent = (editor) => {
|
const registerReferenceComponent = (editor) => {
|
||||||
const domc = editor.DomComponents;
|
const domc = editor.DomComponents;
|
||||||
const defaultType = domc.getType('default');
|
const defaultType = domc.getType('default');
|
||||||
@@ -62,79 +129,15 @@
|
|||||||
|
|
||||||
log(`Starte Registrierung des Komponententyps '${REFERENCE_COMPONENT_TYPE}'.`, '#1E90FF');
|
log(`Starte Registrierung des Komponententyps '${REFERENCE_COMPONENT_TYPE}'.`, '#1E90FF');
|
||||||
|
|
||||||
setTimeout(() => {
|
const ReferenceModel = defaultType.model.extend({
|
||||||
domc.addType(REFERENCE_COMPONENT_TYPE, {
|
|
||||||
model: defaultType.model.extend({
|
|
||||||
|
|
||||||
getCachedApiItem(kind, id) {
|
|
||||||
const key = `${kind}-${id}`;
|
|
||||||
const item = B.ApiItemCache?.[key];
|
|
||||||
return item;
|
|
||||||
},
|
|
||||||
|
|
||||||
init() {
|
|
||||||
const id = this.get('lib-id');
|
|
||||||
const kind = this.get('lib-kind');
|
|
||||||
const startContent = this.get('startContent');
|
|
||||||
|
|
||||||
log(`INIT LÄUFT. lib-kind: ${kind}, lib-id: ${id}. (Bestätigung des Element-Drops/Load)`, '#8A2BE2');
|
|
||||||
|
|
||||||
if (startContent) {
|
|
||||||
// 💡 NEUER FIX: Beim Drop nur die 'content'-Eigenschaft setzen, NICHT als Unterkomponenten parsen
|
|
||||||
this.set('content', startContent);
|
|
||||||
this.unset('startContent');
|
|
||||||
log(`INHALT erfolgreich als REINES HTML aus 'startContent' gesetzt: ${kind}/${id}`, '#008000');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.on('change:lib-kind change:lib-id', this.reloadComponentContent);
|
|
||||||
|
|
||||||
if (!startContent && kind && id) {
|
|
||||||
this.reloadComponentContent({ forced: true, reason: 'INIT_LOAD_FROM_CACHE' });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
reloadComponentContent(opts = {}) {
|
|
||||||
const kind = this.get('lib-kind');
|
|
||||||
const id = this.get('lib-id');
|
|
||||||
const reason = opts.reason || (opts.forced ? 'FORCED_INTERNAL' : 'EVENT_CHANGE');
|
|
||||||
log(`RELOAD START (${reason}). Kind: ${kind}, ID: ${id}.`, '#8A2BE2');
|
|
||||||
|
|
||||||
if (!kind || !id) {
|
|
||||||
log('RELOAD FEHLER: lib-kind oder lib-id fehlt. Setze Fehler-Placeholder.', '#dc3545', 'error', true);
|
|
||||||
// 💡 FIX: Setze reinen HTML-String als content
|
|
||||||
this.set('content', '<div style="padding: 10px; color: #dc3545; background-color: #fce7f3; border: 1px solid #fbcfe8; text-align: center;">🛑 Fehler: API-Referenz unvollständig.</div>');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const item = this.getCachedApiItem(kind, id);
|
|
||||||
|
|
||||||
if (item && (item.html || item.content)) {
|
|
||||||
const content = item.html || item.content;
|
|
||||||
// 💡 FIX: Verwende set('content', ...) statt components(...)
|
|
||||||
// Dadurch wird der Inhalt als reiner HTML-String in die Komponente gesetzt
|
|
||||||
// und nicht als neue, bearbeitbare GrapesJS-Komponenten geparst.
|
|
||||||
this.set('content', content);
|
|
||||||
log(`INHALT erfolgreich für ${kind}/${id} geladen und als REINER HTML-STRING gesetzt.`, '#008000');
|
|
||||||
} else {
|
|
||||||
log(`RELOAD FEHLER: Inhalt für ${kind}/${id} NICHT im Cache gefunden.`, '#dc3545', 'error', true);
|
|
||||||
// 💡 FIX: Setze reinen HTML-HTML-String als content
|
|
||||||
this.set('content', `<div style="padding: 10px; color: #dc3545; background-color: #fce7f3; border: 1px solid #fbcfe8; text-align: center;">🛑 Fehler: Inhalt für ${kind}/${id} nicht im Cache gefunden.</div>`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
isComponent: el => el && el.nodeType === 1 && el.hasAttribute('lib-id'),
|
|
||||||
extend: 'default',
|
|
||||||
model: {
|
|
||||||
defaults: {
|
defaults: {
|
||||||
...defaultType.model.prototype.defaults,
|
...defaultType.model.prototype.defaults,
|
||||||
// 🛑 KRITISCHE FIXES FÜR REFERENZEN
|
components: [],
|
||||||
components: '', // Darf keine Unterkomponenten haben, die geparst werden
|
editable: false,
|
||||||
editable: false, // ❌ Nicht bearbeitbar (Inline-Editierung verhindern)
|
|
||||||
removable: true,
|
removable: true,
|
||||||
draggable: true,
|
draggable: true,
|
||||||
copyable: true,
|
copyable: true,
|
||||||
droppable: false, // ❌ Darf keine anderen Komponenten aufnehmen
|
droppable: false,
|
||||||
// ---------------------------------
|
|
||||||
traits: [
|
traits: [
|
||||||
{ type: 'text', name: 'lib-id', label: 'Library ID', changeProp: true },
|
{ type: 'text', name: 'lib-id', label: 'Library ID', changeProp: true },
|
||||||
{ type: 'text', name: 'lib-kind', label: 'Library Kind', changeProp: true },
|
{ type: 'text', name: 'lib-kind', label: 'Library Kind', changeProp: true },
|
||||||
@@ -142,16 +145,182 @@
|
|||||||
'lib-id': '',
|
'lib-id': '',
|
||||||
'lib-kind': '',
|
'lib-kind': '',
|
||||||
startContent: '',
|
startContent: '',
|
||||||
content: '', // Inhalt, der das gerenderte HTML hält
|
rawHtml: '',
|
||||||
}
|
},
|
||||||
}
|
|
||||||
}),
|
initialize(props = {}, opts = {}) {
|
||||||
// 💡 WICHTIG: Die View muss den Content als reinen HTML-Inhalt rendern (defaultType macht das).
|
defaultType.model.prototype.initialize.apply(this, [props, opts]);
|
||||||
view: defaultType.view,
|
|
||||||
|
this.on('change:lib-kind change:lib-id', () => {
|
||||||
|
this.ensureReferenceMetadata();
|
||||||
|
this.reloadComponentContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
log(`Komponententyp '${REFERENCE_COMPONENT_TYPE}' ASYNCHRON registriert.`, '#008000');
|
this.ensureReferenceMetadata();
|
||||||
}, 0);
|
const id = this.get('lib-id');
|
||||||
|
const kind = this.get('lib-kind');
|
||||||
|
const startContent = this.get('startContent');
|
||||||
|
|
||||||
|
log(`INIT LÄUFT. lib-kind: ${kind}, lib-id: ${id}.`, '#8A2BE2');
|
||||||
|
|
||||||
|
if (startContent) {
|
||||||
|
this.setPreviewHtml(startContent);
|
||||||
|
this.unset('startContent');
|
||||||
|
} else if (kind && id) {
|
||||||
|
this.reloadComponentContent({ forced: true, reason: 'INIT_LOAD' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ensureReferenceMetadata() {
|
||||||
|
const attrsCurrent = this.get('attributes') || {};
|
||||||
|
let attrs = Array.isArray(attrsCurrent) ? {} : { ...attrsCurrent };
|
||||||
|
const kind = this.get('lib-kind') || attrs['data-lib-kind'] || '';
|
||||||
|
const id = this.get('lib-id') || attrs['data-lib-id'] || '';
|
||||||
|
let changed = false;
|
||||||
|
|
||||||
|
if (!this.get('lib-kind') && kind) {
|
||||||
|
this.set('lib-kind', kind, { silent: true });
|
||||||
|
}
|
||||||
|
if (!this.get('lib-id') && id) {
|
||||||
|
this.set('lib-id', id, { silent: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attrs['data-lib-kind'] !== kind) {
|
||||||
|
attrs['data-lib-kind'] = kind;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (attrs['data-lib-id'] !== id) {
|
||||||
|
attrs['data-lib-id'] = id;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (attrs['data-lib-ref'] !== '1') {
|
||||||
|
attrs['data-lib-ref'] = '1';
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
this.set('attributes', attrs);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getCachedApiItem(kind, id) {
|
||||||
|
const key = `${kind}-${id}`;
|
||||||
|
const item = B.ApiItemCache?.[key];
|
||||||
|
return item || null;
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchReference(kind, id) {
|
||||||
|
if (!kind || !id) return Promise.resolve(null);
|
||||||
|
const key = `${kind}-${id}`;
|
||||||
|
const cached = this.getCachedApiItem(kind, id);
|
||||||
|
if (cached && cached.html) return Promise.resolve(cached);
|
||||||
|
if (typeof B.getApiItem !== 'function') return Promise.resolve(cached);
|
||||||
|
return B.getApiItem(kind, id)
|
||||||
|
.then((data) => {
|
||||||
|
if (!data) return cached;
|
||||||
|
const normalized = {
|
||||||
|
html: data.html || data.item?.html || '',
|
||||||
|
content: data.content || data.item?.content || '',
|
||||||
|
};
|
||||||
|
B.ApiItemCache = B.ApiItemCache || {};
|
||||||
|
B.ApiItemCache[key] = { ...(B.ApiItemCache[key] || {}), ...normalized };
|
||||||
|
return B.ApiItemCache[key];
|
||||||
|
})
|
||||||
|
.catch(() => cached);
|
||||||
|
},
|
||||||
|
|
||||||
|
renderError(message) {
|
||||||
|
return `<div class="lib-ref-placeholder">${message}</div>`;
|
||||||
|
},
|
||||||
|
|
||||||
|
setPreviewHtml(html) {
|
||||||
|
const safeHtml = html || this.renderError('Referenz lädt …');
|
||||||
|
this.set('rawHtml', safeHtml);
|
||||||
|
const comps = this.components();
|
||||||
|
if (comps && comps.length) comps.reset([]);
|
||||||
|
this.trigger('preview:update');
|
||||||
|
},
|
||||||
|
|
||||||
|
reloadComponentContent(opts = {}) {
|
||||||
|
const kind = this.get('lib-kind');
|
||||||
|
const id = this.get('lib-id');
|
||||||
|
const reason = opts.reason || (opts.forced ? 'FORCED' : 'AUTO');
|
||||||
|
log(`RELOAD START (${reason}). Kind: ${kind}, ID: ${id}.`, '#8A2BE2');
|
||||||
|
|
||||||
|
if (!kind || !id) {
|
||||||
|
log('RELOAD FEHLER: lib-kind oder lib-id fehlt.', '#dc3545', 'error', true);
|
||||||
|
this.setPreviewHtml(this.renderError('🛑 Fehler: Referenz unvollständig.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cached = this.getCachedApiItem(kind, id);
|
||||||
|
if (cached && cached.html) {
|
||||||
|
this.setPreviewHtml(cached.html);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fetchReference(kind, id)
|
||||||
|
.then((item) => {
|
||||||
|
if (item && item.html) {
|
||||||
|
this.setPreviewHtml(item.html);
|
||||||
|
log(`INHALT erfolgreich für ${kind}/${id} geladen.`, '#008000');
|
||||||
|
} else {
|
||||||
|
log(`RELOAD FEHLER: Inhalt ${kind}/${id} nicht gefunden.`, '#dc3545', 'error', true);
|
||||||
|
this.setPreviewHtml(
|
||||||
|
this.renderError(`🛑 Fehler: Inhalt für ${kind}/${id} nicht gefunden.`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
log('RELOAD FETCH ERROR', error?.message || String(error), '#dc3545', 'error', true);
|
||||||
|
this.setPreviewHtml(this.renderError('🛑 Fehler beim Laden der Referenz.'));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
toHTML(opts = {}) {
|
||||||
|
const raw = this.get('rawHtml');
|
||||||
|
if (raw) return raw;
|
||||||
|
return defaultType.model.prototype.toHTML.call(this, opts);
|
||||||
|
},
|
||||||
|
|
||||||
|
syncReferenceAttributes() {
|
||||||
|
this.ensureReferenceMetadata();
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
isComponent: (el) => el && el.nodeType === 1 && el.hasAttribute('lib-id'),
|
||||||
|
});
|
||||||
|
|
||||||
|
const ReferenceView = defaultType.view.extend({
|
||||||
|
initialize(opts = {}) {
|
||||||
|
defaultType.view.prototype.initialize.apply(this, [opts]);
|
||||||
|
this.listenTo(this.model, 'preview:update', this.renderPreview);
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
defaultType.view.prototype.render.apply(this, arguments);
|
||||||
|
this.el.classList.add('lib-ref');
|
||||||
|
this.renderPreview();
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
renderPreview() {
|
||||||
|
const html = this.model.get('rawHtml') || this.model.renderError('Referenz lädt …');
|
||||||
|
this.el.innerHTML = '';
|
||||||
|
const wrap = document.createElement('div');
|
||||||
|
wrap.className = 'lib-ref-inner';
|
||||||
|
wrap.innerHTML = html;
|
||||||
|
wrap.setAttribute('contenteditable', 'false');
|
||||||
|
wrap.style.pointerEvents = 'none';
|
||||||
|
wrap.style.userSelect = 'none';
|
||||||
|
this.el.appendChild(wrap);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
domc.addType(REFERENCE_COMPONENT_TYPE, {
|
||||||
|
model: ReferenceModel,
|
||||||
|
view: ReferenceView,
|
||||||
|
});
|
||||||
|
|
||||||
|
log(`Komponententyp '${REFERENCE_COMPONENT_TYPE}' registriert.`, '#008000');
|
||||||
};
|
};
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
@@ -260,6 +429,7 @@
|
|||||||
registerSaveCommand(editor); // HINZUGEFÜGT: Speichern-Logik
|
registerSaveCommand(editor); // HINZUGEFÜGT: Speichern-Logik
|
||||||
|
|
||||||
editor.on('load', () => {
|
editor.on('load', () => {
|
||||||
|
rehydrateLegacyReferences(editor);
|
||||||
log("GrapesJS 'load' Event: Delegiere asynchrones Laden der API-Blöcke an library-api.", '#1E90FF');
|
log("GrapesJS 'load' Event: Delegiere asynchrones Laden der API-Blöcke an library-api.", '#1E90FF');
|
||||||
if (B.loadAndRegisterApiBlocks) {
|
if (B.loadAndRegisterApiBlocks) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
@@ -203,16 +203,21 @@
|
|||||||
const blockDefinition = {
|
const blockDefinition = {
|
||||||
label: label,
|
label: label,
|
||||||
category: TARGET_CAT_ID,
|
category: TARGET_CAT_ID,
|
||||||
// 💡 KORREKTUR: Immer die library-reference-Komponente verwenden, um die Referenz-Logik
|
// 💡 KORREKTUR: Immer die library-reference-Komponente verwenden, um die Referenz-Logik
|
||||||
// (mit editable: false) aus blocks-api.js zu erzwingen.
|
// (mit editable: false) aus blocks-api.js zu erzwingen.
|
||||||
content: {
|
content: {
|
||||||
type: REFERENCE_COMPONENT_TYPE,
|
type: REFERENCE_COMPONENT_TYPE,
|
||||||
'lib-kind': item.kind,
|
'lib-kind': item.kind,
|
||||||
'lib-id': item.id,
|
'lib-id': item.id,
|
||||||
// NEU: startContent wird nur als reines HTML übergeben.
|
attributes: {
|
||||||
// Die Logik in blocks-api.js (init/reloadComponentContent) kümmert sich um die Anzeige.
|
'data-lib-kind': item.kind,
|
||||||
|
'data-lib-id': item.id,
|
||||||
|
'data-lib-ref': '1',
|
||||||
|
},
|
||||||
|
// NEU: startContent wird nur als reines HTML übergeben.
|
||||||
|
// Die Logik in blocks-api.js (init/reloadComponentContent) kümmert sich um die Anzeige.
|
||||||
startContent: item.html || item.content || '<div style="padding: 10px; color: #dc3545; background-color: #fce7f3; border: 1px solid #fbcfe8; text-align: center;">🛑 Fehler: Inhalt fehlte beim Laden.</div>',
|
startContent: item.html || item.content || '<div style="padding: 10px; color: #dc3545; background-color: #fce7f3; border: 1px solid #fbcfe8; text-align: center;">🛑 Fehler: Inhalt fehlte beim Laden.</div>',
|
||||||
content: '', // Wichtig: Beim Drop keinen GrapesJS-Content setzen
|
content: '', // Wichtig: Beim Drop keinen GrapesJS-Content setzen
|
||||||
},
|
},
|
||||||
attributes: { 'title': itemKindUpper },
|
attributes: { 'title': itemKindUpper },
|
||||||
media: item.preview_url ? `<img src="${item.preview_url}">` : '',
|
media: item.preview_url ? `<img src="${item.preview_url}">` : '',
|
||||||
|
|||||||
Reference in New Issue
Block a user