From d7cac55df53657f5af6ea50ecca49ed7b5727210 Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Fri, 16 Jan 2026 03:11:04 +0100 Subject: [PATCH] asdasd --- public/assets/js/bridge/table-builder.js | 230 +++++++++++++++++++++++ public/editor/bridge-core.js | 228 ++-------------------- 2 files changed, 241 insertions(+), 217 deletions(-) create mode 100644 public/assets/js/bridge/table-builder.js diff --git a/public/assets/js/bridge/table-builder.js b/public/assets/js/bridge/table-builder.js new file mode 100644 index 0000000..fc9f772 --- /dev/null +++ b/public/assets/js/bridge/table-builder.js @@ -0,0 +1,230 @@ +/* /assets/js/bridge/table-builder.js */ +(function () { + const PluginName = 'bridge-table-builder'; + const B = window.BridgeParts || (window.BridgeParts = {}); + + if (B.__tableBuilderLoaded) return; + B.__tableBuilderLoaded = true; + + const log = (type, message, color = '#94a3b8', logType = 'info', force = false) => { + if (typeof B.log === 'function') { + B.log(PluginName, `[${type}] ${message}`, color, logType, force); + } + }; + + B.setupTableBuilder = (editor) => { + const domc = editor && editor.DomComponents; + if (!domc) return; + const icon = (path) => ``; + const tableIcon = icon('M3 4h18v16H3V4zm2 2v3h6V6H5zm8 0v3h6V6h-6zM5 11v3h6v-3H5zm8 0v3h6v-3h-6zM5 16v2h6v-2H5zm8 0v2h6v-2h-6z'); + + const ensureTableTags = () => { + const baseType = domc.getType('default'); + if (!baseType) return; + const BaseModel = baseType.model; + const BaseView = baseType.view; + const tags = ['table', 'tbody', 'thead', 'tfoot', 'tr', 'td', 'th']; + tags.forEach((tag) => { + if (domc.getType(tag)) return; + domc.addType(tag, { + model: BaseModel, + view: BaseView, + isComponent: (el) => { + if (el.tagName && el.tagName.toLowerCase() === tag) { + return { type: tag }; + } + return ''; + }, + }); + }); + }; + + const collectTableCells = (component) => { + const el = component?.view?.el; + if (!el) return []; + return Array.from(el.querySelectorAll('tr')).map(row => + Array.from(row.children || []).map(cell => cell.innerHTML || '') + ); + }; + + const buildTableHtml = (rows, cols, existing) => { + const safeRows = Math.max(1, Math.min(20, Number(rows) || 1)); + const safeCols = Math.max(2, Math.min(2, Number(cols) || 2)); + const cellStyle = "padding:8px;border:1px solid #e2e8f0;font-size:13px"; + const headStyle = "text-align:left;padding:8px;border:1px solid #e2e8f0;background-color:#f8fafc;font-size:13px"; + let html = ''; + for (let r = 0; r < safeRows; r++) { + html += ''; + for (let c = 0; c < safeCols; c++) { + const existingVal = existing?.[r]?.[c] || ''; + const label = existingVal || (r === 0 ? `Spalte ${String.fromCharCode(65 + c)}` : `Zeile ${r} / ${c + 1}`); + if (r === 0) { + html += `${label}`; + } else { + html += `${label}`; + } + } + html += ''; + } + return html; + }; + + const openTableModal = (component) => { + if (!component) return; + const modal = editor.Modal; + if (!modal) return; + const attrs = component.getAttributes ? component.getAttributes() : {}; + const rows = Number(attrs['data-bridge-rows'] || 3) || 3; + + const container = document.createElement('div'); + container.style.display = 'flex'; + container.style.flexDirection = 'column'; + container.style.gap = '12px'; + container.style.minWidth = '280px'; + + const label = document.createElement('label'); + label.textContent = 'Anzahl Zeilen'; + label.style.fontSize = '13px'; + label.style.fontWeight = '600'; + + const input = document.createElement('input'); + input.type = 'number'; + input.min = '1'; + input.max = '20'; + input.value = String(rows); + input.style.width = '100%'; + input.style.padding = '6px 8px'; + input.style.border = '1px solid #cbd5f5'; + input.style.borderRadius = '4px'; + + label.appendChild(input); + container.appendChild(label); + + const actions = document.createElement('div'); + actions.style.display = 'flex'; + actions.style.justifyContent = 'flex-end'; + actions.style.gap = '8px'; + + const cancelBtn = document.createElement('button'); + cancelBtn.type = 'button'; + cancelBtn.textContent = 'Abbrechen'; + cancelBtn.className = 'btn'; + cancelBtn.addEventListener('click', () => modal.close()); + + const saveBtn = document.createElement('button'); + saveBtn.type = 'button'; + saveBtn.textContent = 'Uebernehmen'; + saveBtn.className = 'btn'; + saveBtn.addEventListener('click', () => { + const nextRows = Math.max(1, Math.min(20, Number(input.value) || 1)); + const existing = collectTableCells(component); + const html = buildTableHtml(nextRows, 2, existing); + component.addAttributes && component.addAttributes({ + 'data-bridge-rows': String(nextRows), + 'data-bridge-cols': '2', + }); + if (component.components) { + component.components(html); + } + if (component.view && component.view.render) { + component.view.render(); + } + modal.close(); + }); + + actions.appendChild(cancelBtn); + actions.appendChild(saveBtn); + container.appendChild(actions); + + modal.setTitle('Tabelle konfigurieren'); + modal.setContent(container); + const mdl = modal.getModel && modal.getModel(); + if (mdl && typeof mdl.set === 'function') { + mdl.set('closeOnEsc', false); + mdl.set('closeOnClick', false); + } + modal.open(); + }; + + if (editor.Commands && editor.Commands.add) { + editor.Commands.add('bridge-table:edit', { + run(ed, sender, opts = {}) { + if (sender && sender.set) sender.set('active', 0); + const component = opts.component || ed.getSelected(); + if (component && component.is && component.is('bridge-table')) { + openTableModal(component); + } + }, + }); + } + + if (!domc.getType('bridge-table')) { + const baseType = domc.getType('table') || domc.getType('default'); + const BaseModel = baseType.model; + const BaseView = baseType.view; + domc.addType('bridge-table', { + model: BaseModel.extend({ + initialize(props = {}, opts = {}) { + if (BaseModel.prototype.initialize) { + BaseModel.prototype.initialize.apply(this, [props, opts]); + } + const attrs = this.getAttributes ? this.getAttributes() : {}; + const nextAttrs = Object.assign({ + 'data-bridge-table': '1', + 'data-bridge-rows': '3', + 'data-bridge-cols': '2', + role: 'presentation', + }, attrs || {}); + if (this.addAttributes) { + this.addAttributes(nextAttrs); + } else if (this.set) { + this.set('attributes', nextAttrs, { silent: true }); + } + const toolbar = this.get && this.get('toolbar'); + if (!Array.isArray(toolbar) || !toolbar.some(btn => btn && btn.command === 'bridge-table:edit')) { + this.set && this.set('toolbar', [ + ...(Array.isArray(toolbar) ? toolbar : []), + { + label: tableIcon, + command: 'bridge-table:edit', + attributes: { title: 'Tabelle bearbeiten' }, + }, + ]); + } + }, + }), + view: BaseView, + isComponent: (el) => { + if (el.tagName === 'TABLE' && el.getAttribute('data-bridge-table')) { + return { type: 'bridge-table' }; + } + return ''; + }, + }); + } + + editor.on('component:selected', (model) => { + if (model && model.is && model.is('bridge-table')) { + const toolbar = model.get('toolbar') || []; + const exists = toolbar.some(btn => btn && btn.command === 'bridge-table:edit'); + if (!exists) { + toolbar.push({ + label: tableIcon, + command: 'bridge-table:edit', + attributes: { title: 'Tabelle bearbeiten' }, + }); + model.set('toolbar', toolbar); + } + } + }); + + editor.on('component:dblclick', (model) => { + if (model && model.is && model.is('bridge-table')) { + openTableModal(model); + } + }); + + ensureTableTags(); + log('INIT', 'Table-Builder geladen.', '#10b981'); + }; +})(); diff --git a/public/editor/bridge-core.js b/public/editor/bridge-core.js index ea047c4..af479ca 100644 --- a/public/editor/bridge-core.js +++ b/public/editor/bridge-core.js @@ -150,8 +150,11 @@         ];         const initialLoadList = B.ENABLE_EDITOR_EXTENSIONS === false -             ? [base + 'general-functions.js'] -             : [...coreFiles]; +            ? [base + 'general-functions.js'] +            : [...coreFiles]; + if (B.ENABLE_EDITOR_EXTENSIONS !== false && B.ENABLE_TABLE_BUILDER) { + initialLoadList.push(base + 'table-builder.js'); + }                  function recursiveLoader(list, index = 0) {             if (index >= list.length) { @@ -881,220 +884,7 @@ editor.on('component:add', (model) => rememberIfPresent(model)); }; - const setupTableBuilder = (editor) => { - const domc = editor.DomComponents; - if (!domc) return; - const icon = (path) => ``; - const tableIcon = icon('M3 4h18v16H3V4zm2 2v3h6V6H5zm8 0v3h6V6h-6zM5 11v3h6v-3H5zm8 0v3h6v-3h-6zM5 16v2h6v-2H5zm8 0v2h6v-2h-6z'); - - const ensureTableTags = () => { - const baseType = domc.getType('default'); - if (!baseType) return; - const BaseModel = baseType.model; - const BaseView = baseType.view; - const tags = ['table', 'tbody', 'thead', 'tfoot', 'tr', 'td', 'th']; - tags.forEach((tag) => { - if (domc.getType(tag)) return; - domc.addType(tag, { - model: BaseModel, - view: BaseView, - isComponent: el => { - if (el.tagName && el.tagName.toLowerCase() === tag) { - return { type: tag }; - } - return ''; - }, - }); - }); - }; - - const collectTableCells = (component) => { - const el = component?.view?.el; - if (!el) return []; - return Array.from(el.querySelectorAll('tr')).map(row => - Array.from(row.children || []).map(cell => cell.innerHTML || '') - ); - }; - - const buildTableHtml = (rows, cols, existing) => { - const safeRows = Math.max(1, Math.min(20, Number(rows) || 1)); - const safeCols = Math.max(2, Math.min(2, Number(cols) || 2)); - const cellStyle = "padding:8px;border:1px solid #e2e8f0;font-size:13px"; - const headStyle = "text-align:left;padding:8px;border:1px solid #e2e8f0;background-color:#f8fafc;font-size:13px"; - let html = ''; - for (let r = 0; r < safeRows; r++) { - html += ''; - for (let c = 0; c < safeCols; c++) { - const existingVal = existing?.[r]?.[c] || ''; - const label = existingVal || (r === 0 ? `Spalte ${String.fromCharCode(65 + c)}` : `Zeile ${r} / ${c + 1}`); - if (r === 0) { - html += `${label}`; - } else { - html += `${label}`; - } - } - html += ''; - } - return html; - }; - - const openTableModal = (component) => { - if (!component) return; - const modal = editor.Modal; - if (!modal) return; - const attrs = component.getAttributes ? component.getAttributes() : {}; - const rows = Number(attrs['data-bridge-rows'] || 3) || 3; - - const container = document.createElement('div'); - container.style.display = 'flex'; - container.style.flexDirection = 'column'; - container.style.gap = '12px'; - container.style.minWidth = '280px'; - - const label = document.createElement('label'); - label.textContent = 'Anzahl Zeilen'; - label.style.fontSize = '13px'; - label.style.fontWeight = '600'; - - const input = document.createElement('input'); - input.type = 'number'; - input.min = '1'; - input.max = '20'; - input.value = String(rows); - input.style.width = '100%'; - input.style.padding = '6px 8px'; - input.style.border = '1px solid #cbd5f5'; - input.style.borderRadius = '4px'; - - label.appendChild(input); - container.appendChild(label); - - const actions = document.createElement('div'); - actions.style.display = 'flex'; - actions.style.justifyContent = 'flex-end'; - actions.style.gap = '8px'; - - const cancelBtn = document.createElement('button'); - cancelBtn.type = 'button'; - cancelBtn.textContent = 'Abbrechen'; - cancelBtn.className = 'btn'; - cancelBtn.addEventListener('click', () => modal.close()); - - const saveBtn = document.createElement('button'); - saveBtn.type = 'button'; - saveBtn.textContent = 'Uebernehmen'; - saveBtn.className = 'btn'; - saveBtn.addEventListener('click', () => { - const nextRows = Math.max(1, Math.min(20, Number(input.value) || 1)); - const existing = collectTableCells(component); - const html = buildTableHtml(nextRows, 2, existing); - component.addAttributes && component.addAttributes({ - 'data-bridge-rows': String(nextRows), - 'data-bridge-cols': '2', - }); - if (component.components) { - component.components(html); - } - if (component.view && component.view.render) { - component.view.render(); - } - modal.close(); - }); - - actions.appendChild(cancelBtn); - actions.appendChild(saveBtn); - container.appendChild(actions); - - modal.setTitle('Tabelle konfigurieren'); - modal.setContent(container); - const mdl = modal.getModel && modal.getModel(); - if (mdl && typeof mdl.set === 'function') { - mdl.set('closeOnEsc', false); - mdl.set('closeOnClick', false); - } - modal.open(); - }; - - if (editor.Commands && editor.Commands.add) { - editor.Commands.add('bridge-table:edit', { - run(ed, sender, opts = {}) { - if (sender && sender.set) sender.set('active', 0); - const component = opts.component || ed.getSelected(); - if (component && component.is && component.is('bridge-table')) { - openTableModal(component); - } - }, - }); - } - - if (!domc.getType('bridge-table')) { - const baseType = domc.getType('table') || domc.getType('default'); - const BaseModel = baseType.model; - const BaseView = baseType.view; - domc.addType('bridge-table', { - model: BaseModel.extend({ - initialize(props = {}, opts = {}) { - if (BaseModel.prototype.initialize) { - BaseModel.prototype.initialize.apply(this, [props, opts]); - } - const attrs = this.getAttributes ? this.getAttributes() : {}; - const nextAttrs = Object.assign({ - 'data-bridge-table': '1', - 'data-bridge-rows': '3', - 'data-bridge-cols': '2', - role: 'presentation', - }, attrs || {}); - if (this.addAttributes) { - this.addAttributes(nextAttrs); - } else if (this.set) { - this.set('attributes', nextAttrs, { silent: true }); - } - const toolbar = this.get && this.get('toolbar'); - if (!Array.isArray(toolbar) || !toolbar.some(btn => btn && btn.command === 'bridge-table:edit')) { - this.set && this.set('toolbar', [ - ...(Array.isArray(toolbar) ? toolbar : []), - { - label: tableIcon, - command: 'bridge-table:edit', - attributes: { title: 'Tabelle bearbeiten' }, - }, - ]); - } - }, - }), - view: BaseView, - isComponent: el => { - if (el.tagName === 'TABLE' && el.getAttribute('data-bridge-table')) { - return { type: 'bridge-table' }; - } - return ''; - }, - }); - } - - editor.on('component:selected', (model) => { - if (model && model.is && model.is('bridge-table')) { - const toolbar = model.get('toolbar') || []; - const exists = toolbar.some(btn => btn && btn.command === 'bridge-table:edit'); - if (!exists) { - toolbar.push({ - label: tableIcon, - command: 'bridge-table:edit', - attributes: { title: 'Tabelle bearbeiten' }, - }); - model.set('toolbar', toolbar); - } - } - }); - - editor.on('component:dblclick', (model) => { - if (model && model.is && model.is('bridge-table')) { - openTableModal(model); - } - }); - - ensureTableTags(); - }; + // Table-Builder ausgelagert nach /assets/js/bridge/table-builder.js const setupBlurLogger = (editor) => { if (!editor || !editor.Canvas || !editor.Canvas.getBody) return; @@ -1243,7 +1033,11 @@ } if (B.ENABLE_EDITOR_EXTENSIONS !== false && (B.ENABLE_EDITOR_BEHAVIOR !== false || B.ENABLE_TABLE_BUILDER)) { - setupTableBuilder(ed); + if (typeof B.setupTableBuilder === 'function') { + B.setupTableBuilder(ed); + } else { + log('TABLE WARN', 'Table-Builder nicht geladen.', 'orange', 'warn'); + } } if (B.ENABLE_EDITOR_EXTENSIONS !== false && B.ENABLE_EDITOR_BEHAVIOR !== false) { setupPlainTextPreserver(ed);