/* /assets/js/bridge/blocks-api.js (SCHRITT 16: Finaler Stabilitäts-Fix) */ (function (B) { // 🛑 KRITISCHER FIX: Nur minimale Prüfung, um synchrone Initialisierung zu garantieren if (!B || typeof grapesjs === 'undefined') { console.warn("[BRIDGE-API] blocks-api.js: BridgeParts (B) oder GrapesJS fehlt. Exit."); return; } const PluginName = 'bridge-blocks-api'; const qs = new URLSearchParams(location.search); B.EDITOR_MODE = (qs.get('mode') || 'templates').toUpperCase(); console.log(`%c[${PluginName} - INIT] Editor Modus: ${B.EDITOR_MODE} (SCHRITT 16 - FINAL STABLE)`, 'color: #1E90FF; font-weight: bold;'); const TARGET_CAT_ID = 'custom'; const PLACEHOLDER_ID = 'api-placeholder-loading'; const REFERENCE_COMPONENT_TYPE = 'library-reference'; // -------------------------------------------------------- // (1) Kern-Logik: Platzhalter und Kategorien registrieren (SYNCHRON) // -------------------------------------------------------- const preRegisterCategoriesAndPlaceholders = (editor) => { const bm = editor.BlockManager; bm.add(PLACEHOLDER_ID, { label: 'Lade Custom-Blöcke...', category: TARGET_CAT_ID, content: '
⚙️ Custom-Blöcke werden geladen...
', attributes: { class: 'gjs-block__api-placeholder' }, }); const cat = bm.getCategories().get(TARGET_CAT_ID); if (!cat) { bm.addCategory(TARGET_CAT_ID, { label: 'Custom', open: true, order: 1 }); } console.log(`%c[${PluginName}] Platzhalter und Kategorie registriert.`, 'color: #008000;'); }; // -------------------------------------------------------- // (2) Komponenten-Logik (ASYNCHRONER WORKAROUND & FIX) // -------------------------------------------------------- const registerReferenceComponent = (editor) => { const domc = editor.DomComponents; const defaultType = domc.getType('default'); if (!defaultType) return; // KRITISCHER WORKAROUND: Registrierung wird minimal verzögert setTimeout(() => { domc.addType(REFERENCE_COMPONENT_TYPE, { model: defaultType.model.extend({ init() { // Setze die Attribute sicher im init() (Fix für "defaults" TypeError) if (this.get('type') !== REFERENCE_COMPONENT_TYPE) { this.set('type', REFERENCE_COMPONENT_TYPE); this.set('tagName', 'div'); this.set('lib-kind', ''); this.set('lib-id', ''); } this.on('change:lib-kind change:lib-id', this.reloadComponentContent); const editorInstance = this.em.get('Editor'); if (editorInstance && this.get('lib-id')) { // Prüft hier nur auf getApiItem, da es für die Referenz-Komponente essenziell ist if(B.getApiItem) { editorInstance.on('load', this.reloadComponentContent.bind(this), { once: true }); } else { console.warn(`[${PluginName}] B.getApiItem fehlt. Inhalte für 'library-reference' können nicht geladen werden.`); } } }, reloadComponentContent(opts = {}) { const kind = this.get('lib-kind'); const id = this.get('lib-id'); if (!kind || !id) { this.set('content', '
🛑 Fehler: API-Referenz unvollständig.
'); return; } if (!B.getApiItem) { this.set('content', '
🛑 Fehler: API-Kernfunktion getApiItem fehlt.
'); return; } B.getApiItem(kind, id) .then(item => { if (item && item.html) { this.set('content', item.html); console.log(`[${PluginName}] Geladenen Inhalt für ${kind}/${id} gesetzt.`); } else { this.set('content', `
🛑 Fehler: Inhalt für ${kind}/${id} nicht gefunden.
`); } }) .catch(error => { console.error(`[${PluginName}] Fehler beim Abruf von ${kind}/${id}:`, error); this.set('content', `
🛑 Fehler beim Laden von ${kind}/${id}.
`); }); }, }, {}), view: defaultType.view, }); console.log(`%c[${PluginName}] Komponententyp '${REFERENCE_COMPONENT_TYPE}' ASYNCHRON registriert.`, 'color: #008000;'); }, 0); }; // -------------------------------------------------------- // (3) Asynchrone Logik: API-Blöcke registrieren (FINAL CLEAN) // -------------------------------------------------------- const loadAndRegisterApiBlocks = (editor) => { const bm = editor.BlockManager; const targetCatId = TARGET_CAT_ID; // KRITISCHER FIX: Stelle sicher, dass der Platzhalter existiert. if (!bm.get(PLACEHOLDER_ID)) { bm.add(PLACEHOLDER_ID, { label: 'Lade Custom-Blöcke...', category: targetCatId, content: '
⚙️ Custom-Blöcke werden geladen...
', attributes: { class: 'gjs-block__api-placeholder' }, }); console.log(`%c[${PluginName}] Platzhalter erneut hinzugefügt (Überlebens-Check).`, 'color: orange;'); } // 🛑 NEUER CHECK: Prüfe die fetch*-Funktionen erst HIER. if (!B.fetchSections || !B.fetchBlocks || !B.fetchSnippets) { console.error(`%c[${PluginName}] FEHLER: Eine der API-Ladefunktionen (fetchSections/Blocks/Snippets) fehlt.`, 'color: #dc3545; font-weight: bold;'); // Platzhalter bleibt, da die Kategorie sonst verschwindet. return; } // Explizite Promise.all mit allen fetch*-Funktionen Promise.all([ B.fetchSections().then(items => items.map(i => ({ ...i, kind: 'sections' }))), B.fetchBlocks().then(items => items.map(i => ({ ...i, kind: 'blocks' }))), B.fetchSnippets().then(items => items.map(i => ({ ...i, kind: 'snippets' }))) ]) .then(results => { // Führe alle Ergebnisse zu einem flachen Array zusammen const apiItems = results.flat().filter(item => item && item.id); // Array-Ausgabe zur Bestätigung der Daten console.log(`%c[${PluginName}] API-Daten Array:`, 'color: #9400D3; font-weight: bold;', apiItems); console.log(`%c[${PluginName}] API-Daten geladen: ${apiItems.length} Blöcke/Sektionen gefunden.`, 'color: #1E90FF; font-weight: bold;'); if (apiItems.length === 0) { // Platzhalter bleibt, um die leere Kategorie sichtbar zu halten. console.warn(`[${PluginName}] Keine API-Daten gefunden, Platzhalter bleibt (leer) erhalten.`); } else { apiItems.forEach(item => { const blockId = `lib-${item.kind}-${item.id}`; const label = item.name || item.label || 'Unbenannter Block'; const itemKindUpper = item.kind.toUpperCase(); const blockDefinition = { label: label, category: targetCatId, content: { type: REFERENCE_COMPONENT_TYPE, attributes: { 'lib-kind': item.kind, 'lib-id': item.id }, }, attributes: { 'title': itemKindUpper }, media: item.preview_url ? `` : '', }; bm.add(blockId, blockDefinition); }); // Platzhalter entfernen, da Blöcke erfolgreich geladen wurden bm.remove(PLACEHOLDER_ID); console.log(`%c[${PluginName}] ${apiItems.length} API-Blöcke registriert. Platzhalter entfernt.`, 'color: #008000; font-weight: bold;'); } }) .catch(error => { console.error(`%c[${PluginName}] FEHLER beim Laden der API-Blöcke:`, 'color: #dc3545; font-weight: bold;', error); // Platzhalter entfernen, um nicht im ewigen Ladezustand zu bleiben. bm.remove(PLACEHOLDER_ID); }); }; // -------------------------------------------------------- // (4) Plugin-Funktion // -------------------------------------------------------- const plugin = (editor) => { preRegisterCategoriesAndPlaceholders(editor); registerReferenceComponent(editor); editor.on('load', () => { console.log(`%c[${PluginName}] GrapesJS 'load' Event: Starte asynchrones Laden der API-Blöcke.`, 'color: #1E90FF; font-weight: bold;'); loadAndRegisterApiBlocks(editor); }); }; // -------------------------------------------------------- // (5) Export an Bridge Core // -------------------------------------------------------- if (B.registerGrapesJSPlugin) { B.registerGrapesJSPlugin(PluginName, plugin); } })(window.BridgeParts || (window.BridgeParts = {}));