/** Datei: plugins/rest/assets.OLD/js/generator/data/table/toolbar.js **/ /** * generator/data/table/toolbar.js * * Erstellt die Toolbar für die Tabelle (Suchfeld, Buttons etc.). * * @author Sparky * @copyright 2025 ARUNOX * @version 1.3.1 * * @since 1.3.0 Erstellt als separate Datei * @since 1.3.1 Fix: Suchfeld filtert `tr` basierend auf `data-search` */ import { generator } from 'plugins/rest/core/generator'; import { sortTable } from './header.js'; generator.extend( 'data/table/toolbar', async ( config, data, tableContainer ) => { if( !config.search && !Array.isArray( config.buttons ) ) return; const toolbarContainer = document.createElement('div'); toolbarContainer.classList.add('table__toolbar'); toolbarContainer.elemets = { tableContainer : tableContainer, buttonContainer : null, searchContainer : null, searchInput : null, resetBtn : null, }; if( config.search || config.filter ) { const searchContainer = document.createElement('div'); const tableResetBtn = await generator.generate( 'components/button', { color : 'danger', type : 'button', block : 'table__search', title : 'Filter zurücksetzen', icon : 'bi bi-funnel', }, {}, null ); searchContainer.classList.add('table__search'); toolbarContainer.appendChild( searchContainer ); toolbarContainer.elemets.searchContainer = searchContainer; toolbarContainer.elemets.resetBtn = tableResetBtn; if( config.search ) { const searchInput = document.createElement('input'); searchInput.type = 'text'; searchInput.placeholder = 'Suchen...'; searchInput.classList.add('table__search__input'); searchInput.addEventListener('input', () => searchTable( searchInput.value, toolbarContainer.elemets )); searchContainer.appendChild( searchInput ); toolbarContainer.elemets.searchInput = searchInput; } tableResetBtn.style.display = 'none'; tableResetBtn.addEventListener( 'click', () => resetTable( config.filter, toolbarContainer.elemets ) ); searchContainer.appendChild( tableResetBtn ); if( config.order.buttons ) { for( const btnConfig of config.order.buttons ) { const button = await generator.generate( btnConfig.generator, btnConfig.config, {}, searchContainer ); button.addEventListener( 'click', () => { const table = tableContainer.querySelector('table'); button.dataset.order = btnConfig.config.order.order == 'asc' ? 'desc' : 'asc'; sortTable( btnConfig.config.order.column, null, button, table ); }); } } if( config.filter ) { tableContainer.addEventListener( 'loaded', () => filterTable( config.filter, toolbarContainer.elemets )); } } if( Array.isArray( config.buttons ) ) { const buttonContainer = document.createElement('div'); buttonContainer.classList.add('table__buttons'); config.buttons.forEach(buttonConfig => { generator.generate(buttonConfig.generator, buttonConfig.config, data, buttonContainer); }); toolbarContainer.appendChild(buttonContainer); toolbarContainer.elemets.buttonContainer = buttonContainer; } tableContainer.appendChild( toolbarContainer ); return toolbarContainer; }); function resetTable( config, elements ) { const tbody = elements.tableContainer.querySelector('tbody'); const rows = elements.tableContainer.querySelectorAll('.table__row'); rows.forEach(row => { row.style.display = null; }); const query = window.location.hash.match( /\?(.*)/ ); const hash = window.location.hash.substring(1).replace( /\?.*/, '' ); const filter = new URLSearchParams( query ? query[1] : '' ); for( const [ index, value ] of config.query.entries() ) { if( filter.has( index ) && filter.get( index ) == value ) { filter.delete( index ); } } if( filter.size ) history.pushState(null, null, `#${hash}?${filter.toString()}`); else history.pushState(null, null, `#${hash}`); delete config.button.dataset.tabFilter; elements.resetBtn.style.display = 'none'; if( elements.searchInput ) elements.searchInput.value = ''; } function filterTable( filter, elements ) { const tbody = elements.tableContainer.querySelector('tbody'); const rows = elements.tableContainer.querySelectorAll('.table__row'); let visibleRows = []; rows.forEach(row => { let isVisible = true; for( const [ column, value ] of filter.query.entries() ) { const filterable = row.hasAttribute(`data-filter-${column}`); if( !filterable ) { filter.query.delete( column ); continue; } const filterData = row.getAttribute(`data-filter-${column}`) || ''; const filterValue = value.toLowerCase(); if( isVisible ) isVisible = filterData == filterValue; } row.style.display = isVisible ? null : 'none'; if( isVisible ) visibleRows.push( row ); }); if( visibleRows.length != rows.length ) elements.resetBtn.style.display = null; visibleRows.forEach( row => tbody.appendChild( row ) ); } function searchTable( searchValue, elements ) { const tbody = elements.tableContainer.querySelector('tbody'); const rows = elements.tableContainer.querySelectorAll('.table__row'); searchValue = searchValue.toLowerCase(); let visibleRows = []; rows.forEach(row => { const searchData = row.getAttribute('data-search') || ''; const isVisible = searchData.includes(searchValue); row.style.display = isVisible ? null : 'none'; if( isVisible ) { visibleRows.push( row ); } }); if( visibleRows.length != rows.length ) elements.resetBtn.style.display = null; visibleRows.forEach( row => tbody.appendChild( row ) ); } /** Datei: plugins/rest/assets.OLD/js/generator/data/table/header.js **/ /** * generator/data/table/header.js * * Erzeugt den Tabellenkopf (``) basierend auf der Konfiguration. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.4.0 * * @since 1.0.0 Erstellt als separate Datei * @since 1.3.1 Fix: Sortieren über `order`-Parameter in `table.dataset.config` * @since 1.3.2 Fix: Bestehendes `tbody` sortieren, statt komplette Tabelle zu erneuern * @since 1.3.3 Verbesserung: `sorting--asc`, `sorting--desc`, `sorting--current` für Sortierrichtung * @since 1.3.4 Fix: Originale Sortierlogik aus `table.js` wiederhergestellt + `dataset.order` * @since 1.4.0 Nutzung von `Promise.all()` für parallele Header-Generierung */ import { generator } from 'plugins/rest/core/generator'; generator.extend('data/table/header', async ( config, data, container ) => { const thead = document.createElement('thead'); const row = document.createElement('tr'); const headerCells = await Promise.all( config.columns.map( async ( column ) => { let cell = document.createElement('th' ); let type = column.type || 'text' if( ! await generator.exists(`data/table/column/${type}-header` ) ) { type = 'text'; } // await generator.generate(`data/table/column/${type}-header`, { column, config }, data, cell ); if( column.title ) { cell.title = column.title; } if( column.icon ) { const icon = document.createElement('i'); icon.classList.add('bi', column.icon || 'bi-info-circle'); cell.appendChild(icon); if( !column.label ) { cell.classList.add(`table__header__cell--type-${column.type}-icon`); column.headerIconOnly = true; } } if( column.label ) { const labelText = document.createTextNode( column.label ); cell.appendChild( labelText ); } cell.classList.add('table__cell', 'table__header__cell', `table__cell--type-${column.type}`); if( column.sortable ) { cell.classList.add('sortable'); cell.dataset.key = column.key; cell.dataset.order = column.order?.order || 'asc'; cell.addEventListener('click', () => sortTable( column.key, null, cell, container )); } if( !column.visible ) { cell.style.display = "none"; // ❌ Unsichtbar, aber existiert } return cell; })); // Alle Header-Zellen auf einmal hinzufügen headerCells.forEach(th => row.appendChild(th)); container.appendChild(row); }); export async function sortTable( column, order, cell, table ) { if (!table) return; const tbody = table.querySelector('tbody'); if (!tbody) return; if( !order ) { order = cell.dataset.order === 'asc' ? 'desc' : 'asc'; } const config = table.RESTconfig; const columns = config.columns || []; const rules = config.order.rules || {}; for( const column of columns ) { const cells = table.querySelectorAll(`[data-key="${column.key}"]`); for( const oldCell of cells ) { if( oldCell.tagName == 'TD') { const newCell = oldCell.cloneNode(true ); const rowData = oldCell.closest('tr').rowData; const value = rowData[ column.key ]; newCell.style.display = column.visible ? '' : 'none'; // 🔄 Sichtbarkeit aus `config` setzen newCell.innerHTML = ''; await generator.generate('data/table/column', { value, column, config }, rowData, newCell, null ); oldCell.parentNode.replaceChild( newCell, oldCell ); if( cell == oldCell ) { cell = newCell; console.log( oldCell ); } } } } if( rules[ column ]?.replace ) { for( const [ oldCol, newCol ] of Object.entries( rules[ column ].replace ) ) { table.querySelectorAll(`td[data-key="${oldCol}"]`).forEach(td => { const rowData = td.closest('tr').rowData; td.innerHTML = rowData[ newCol ] || ''-''; // td.dataset.key = newCol; // Key anpassen }); } } if( rules[ column ]?.show ) { for( const showCol of rules[ column ].show ) { table.querySelectorAll(`td[data-key="${showCol}"], th[data-key="${showCol}"]`).forEach(el => { el.style.display = ''; // Wieder anzeigen }); } } if( rules[ column ]?.hide ) { for( const hideCol of rules[ column ].hide ) { table.querySelectorAll(`td[data-key="${hideCol}"], th[data-key="${hideCol}"]`).forEach(el => { el.style.display = 'none'; // Wieder anzeigen }); } } // Entferne vorherige Sortierklassen table.querySelectorAll('.table__header__cell').forEach(cell => { cell.classList.remove('sorting--asc', 'sorting--desc', 'sorting--current'); }); // Setze neue Sortierklasse if( cell ) { cell.dataset.order = order; cell.classList.add(`sorting--${order}`, 'sorting--current'); } const container = table.closest( '.table__container' ); if( container ) { container.dataset.sortingColumn = column; container.dataset.sortingOrder = order; } // Reihen sortieren const rows = Array.from( tbody.rows ); rows.sort((rowA, rowB) => { let a = rowA.rowData[ column ]?.trim() || ''; let b = rowB.rowData[ column ]?.trim() || ''; // Versuche als Zahl zu interpretieren, falls möglich if (!isNaN(a) && !isNaN(b)) { a = parseFloat(a); b = parseFloat(b); } return order === 'asc' ? String(a).localeCompare(String(b)) : String(b).localeCompare(String(a)); }); // Sortierte Reihen neu einfügen tbody.innerHTML = ''; rows.forEach( row => tbody.append( row ) ); } /** Datei: plugins/rest/assets.OLD/js/generator/data/table/row.js **/ /** * generator/data/table/row.js * * Erzeugt eine Tabellenzeile (``) basierend auf der Konfiguration. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.4.4 * * @since 1.0.0 Erstellt als separate Datei * @since 1.3.0 `column.action` wird ausgeführt * @since 1.3.1 `updateRow()` hinzugefügt für API-gestützte Updates * @since 1.3.2 Fix: `column.action` wird nach `updateRow()` erneut registriert * @since 1.3.3 Fix: `column`-Generator wiederhergestellt, anstatt `generateCell` * @since 1.3.4 `row.js` ruft `column.js` auf und überlässt ihm die `td`-Erstellung * @since 1.3.5 Fix: `updateRow()` Debugging & `await` für `generator.generate()` hinzugefügt * @since 1.3.6 Fix: `updateRow()` exportiert und `config`-Fehler behoben * @since 1.3.7 Fix: `updateRow()` prüft und setzt fehlendes `config.columns` * @since 1.3.8 Fix: `updateRow()` liest `config.columns` aus `table.dataset.config`, falls nicht vorhanden * @since 1.3.9 Fix: `table__row--updated` wird nach erfolgreichem Update gesetzt und nach 200ms entfernt * @since 1.4.0 Fix: `td`-Zellen erhalten `data-key`, um Sortierung zu ermöglichen * @since 1.4.3 Fix: `updateRow()` wiederhergestellt, bleibt jetzt dauerhaft bestehen * @since 1.4.4 Click-Event für `tr`-Zeilen wiederhergestellt. * - Falls `config.action` definiert ist, wird `handleClickAction()` ausgeführt. * - Zeilen reagieren jetzt wieder auf Klicks. */ import { updater } from 'plugins/rest/core/updater'; import { generator } from 'plugins/rest/core/generator'; import { handleAction } from 'plugins/rest/core/helpers'; import { sortTable } from './header.js'; generator.extend('data/table/row', async ( config, rowData, container, context ) => { let tableRow = document.createElement('tr'); if( container.tagName == 'TR') { tableRow = container; container = null; } else { tableRow.dataset.uuid = context.getUUID( context.parent?.route ?? '' ); } // console.log( 'GENERATOR', 'data/table/.row', config, rowData, container ); tableRow.classList.add('table__row'); tableRow.RESTconfig = config; tableRow.rowData = rowData; tableRow.innerHTML = ''; let searchContent = []; for( const column of config.columns || [] ) { let value = '-'; const td = document.createElement('td'); if( rowData[ column.key ] !== undefined) { value = rowData[ column.key ]; } else if( column.key.indexOf('.') > -1 ) { let current = rowData; for( const key of column.key.split('.') ) { current = current[ key ]; } if( current !== undefined ) { value = current; } } await generator.generate('data/table/column', { value, column, config }, rowData, td, tableRow ); td.setAttribute('data-key', column.key); // `data-key` für Sortierung if( value !== null ) { tableRow.setAttribute(`data-filter-${column.key}`, value.toString().toLowerCase() ); } if( column.searchable ) { searchContent.push(td.textContent.trim()); } if( !column.visible ) { td.style.display = "none"; // ❌ Unsichtbar, aber existiert } tableRow.appendChild(td); } tableRow.setAttribute('data-search', searchContent.join(' ').toLowerCase()); if( config.action && !tableRow.classList.contains('js--click-bound') ) { tableRow.classList.add('cursor--pointer'); if( tableRow && tableRow.RESTconfig.api.route && config.action.route ) { let rowSearchParams = tableRow.RESTconfig.api.route.match( /\?(.*)/); let cellSearchParams = config.action.route.match( /\?(.*)/); if( rowSearchParams ) rowSearchParams = new URLSearchParams( rowSearchParams[1] ); if( cellSearchParams ) cellSearchParams = new URLSearchParams( cellSearchParams[1] ); if( rowSearchParams ) { if( rowSearchParams.has('columns') && ( !cellSearchParams || !cellSearchParams.has('columns') ) ) { if( cellSearchParams ) rowSearchParams = '&columns=' + rowSearchParams.get('columns'); else rowSearchParams = '?columns=' + rowSearchParams.get('columns'); config.action.route += rowSearchParams; } // else console.log( cell.RESTconfig.action.route ); } // else console.log( tableRow.RESTconfig.api.route ); } tableRow.addEventListener("click", async ( event ) => { if( ['a'].includes( event.target.tagName.toLowerCase() ) ) return false; await handleAction( config.action, {}, rowData, 'rest' ); }); tableRow.classList.add('js--click-bound'); } if( container ) container.appendChild( tableRow ); return tableRow; }); updater.extend( 'data/table/row', async ( element, config, rowData ) => { if( !element || !rowData ) { console.error('updateRow Fehler: Fehlende Daten', { element, rowData, config }); return; } if( !config.columns ) { console.error('updateRow Fehler: `config.columns` nicht vorhanden', { config }); return; } element.classList.add( 'table__row--updated' ); setTimeout(() => element.classList.remove('table__row--updated' ), 200 ); // console.log( 'UPDATER', 'data/table/row', config, rowData, element ); const newRow = await generator.generate( 'data/table/row', config, rowData, element ); const container = element.closest( '.table__container' ); if( container ) { const table = container.querySelector('table'); const column = container.dataset.sortingColumn; const order = container.dataset.sortingOrder; if( column && order && table ) { sortTable( column, order, null, table ); } } }); /** Datei: plugins/rest/assets.OLD/js/generator/data/table/table.js **/ /** * generator/data/table/table.js * * Hauptmodul für Tabellen, das die anderen Generatoren für Header, Zeilen & Toolbar integriert. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.3.5 * * @since 1.0.0 Erstellt als separater Generator * @since 1.3.2 `config` wird in `table.dataset.config` gespeichert * @since 1.3.3 `reloadTable()` hinzugefügt, um die Tabelle neu zu laden * @since 1.3.4 `toolbar.js` integriert, um Suchfeld & Buttons bereitzustellen * @since 1.3.5 Fix: `toolbar` korrekt als Generator eingebunden */ import { generator } from 'plugins/rest/core/generator'; import { router } from 'plugins/rest/core/router'; import { sortTable } from './header.js'; generator.extend('data/table', async ( config, data, container, context ) => { const tableContainer = document.createElement('div'); tableContainer.classList.add('table__container'); // Toolbar mit Suchfeld und Buttons await generator.generate('data/table/toolbar', config, data, tableContainer ); // Haupt-Tabelle const table = document.createElement('table'); const tbody = document.createElement('tbody'); const tableUUID = context.getUUID(); const generateRows = async ( data, loading = false ) => { const hint = tbody.querySelector('.table__cell--type-hint'); if( hint ) hint.closest('.table__row').remove(); if( !loading && data && data.length ) { for( const rowData of data ) { await context.generate('data/table/row', config, rowData, tbody ); } return; } const tableRow = document.createElement('tr'); const tableCell = document.createElement('td'); tableCell.colSpan = config.columns.length; tableCell.innerHTML = loading ? 'Daten werden geladen ...' : 'Keine Datensätze gefunden'; tableRow.classList.add('table__row'); tableCell.classList.add( 'table__cell', 'table__body__column', 'table__cell--type-hint' ); tableRow.appendChild( tableCell ) tbody.appendChild( tableRow ) }; table.classList.add('table'); table.dataset.uuid = tableUUID; table.RESTconfig = config; tableContainer.appendChild( table ); container.appendChild( tableContainer ); if( config.columns ) { config.columns = config.columns.filter( item => Object.keys(item).length > 0 ); } if( !config.columns ) return; if( config.api ) { if( !config.api.resolved ) { config.api = router.parseRoute( config.api, data ); } router.fetch( config.api ).then( generateRows ); } await generator.generate('data/table/header', config, data, table ); await generateRows( data, true ); table.appendChild(tbody); // Nach dem Generieren der Rows if( config.order && config.order.column ) { const headerCell = table.querySelector(`.table__header__cell[data-key="${config.order.column}"]`); const container = table.closest( '.table__container' ); sortTable( config.order.column, config.order.order, headerCell, table ); // Automatischer Sortieraufruf } tableContainer.dispatchEvent( new Event('loaded', { bubbles: true }) ); }); /** Datei: plugins/rest/assets.OLD/js/generator/data/table/column.js **/ /** * generator/data/table/column.js * * Erzeugt eine einzelne Tabellenzelle (``) durch Aufruf des passenden Spalten-Generators. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.4.0 * * @since 1.0.0 Erstellt als separater Generator * @since 1.1.0 Wiederhergestellt nach Entfernen von `generateCell` * @since 1.2.0 Setzt `td`-Attribute, lädt `column/`, nutzt `column/text` als Fallback * @since 1.3.0 Unterstützung für `date`, `time`, `datetime` hinzugefügt * @since 1.3.1 Fix: Richtige Datei (`date.js`, `time.js`, `datetime.js`) wird geladen * @since 1.4.0 `executeAction()` nutzt jetzt `handleClickAction()` für API-Requests & Routenwechsel. */ import { router } from "plugins/rest/core/router"; import { generator } from 'plugins/rest/core/generator'; import { handleAction } from 'plugins/rest/core/helpers'; generator.extend('data/table/column', async ( config, rowData, cell, tableRow ) => { cell.classList.add('table__cell', 'table__body__column', `table__cell--type-${config.column.type || 'text'}`); cell.RESTconfig = config.column; if( cell.RESTconfig.title ) { cell.title = cell.RESTconfig.title; } if( cell.RESTconfig.action ) { cell.style.cursor = 'pointer'; if( tableRow && tableRow.RESTconfig.api.route && cell.RESTconfig.action.route ) { let rowSearchParams = tableRow.RESTconfig.api.route.match( /\?(.*)/); let cellSearchParams = cell.RESTconfig.action.route.match( /\?(.*)/); if( rowSearchParams ) rowSearchParams = new URLSearchParams( rowSearchParams[1] ); if( cellSearchParams ) cellSearchParams = new URLSearchParams( cellSearchParams[1] ); if( rowSearchParams ) { if( rowSearchParams.has('columns') && ( !cellSearchParams || !cellSearchParams.has('columns') ) ) { if( cellSearchParams ) rowSearchParams = '&columns=' + rowSearchParams.get('columns'); else rowSearchParams = '?columns=' + rowSearchParams.get('columns'); cell.RESTconfig.action.route += rowSearchParams; } // else console.log( cell.RESTconfig.action.route ); } // else console.log( tableRow.RESTconfig.api.route ); } cell.addEventListener('click', async (event) => { event.preventDefault(); event.stopPropagation(); await handleAction( cell.RESTconfig.action, {}, rowData, 'rest' ); }); } // Korrekte Datei basierend auf dem Typ laden await generator.generate(`data/table/column/${cell.RESTconfig.type || 'text'}`, config, rowData, cell ); if( config.column.buttons ) { const children = cell.childNodes; const wrapper = document.createElement('div'); wrapper.classList.add( 'table__cell__inner' ); if( children.length ) { for( const child of children ) { if( child.nodeName === '#text' ) { const text = document.createElement('div'); text.classList.add( 'table__cell__inner__text' ); text.append( child ); wrapper.append( text ); } else wrapper.append( child ); } } for( const buttonConfig of config.column.buttons.values() ) { if( buttonConfig.config.visibility ) { let visible = true; for( let [ key, validValues ] of Object.entries( buttonConfig.config.visibility ) ) { if( !validValues.includes( rowData[ key ] ) ) { visible = false; break; } } if( !visible ) continue; } await generator.generate( buttonConfig.generator, buttonConfig.config, rowData, wrapper ); } cell.append( wrapper ); } }); /** Datei: plugins/rest/assets.OLD/js/generator/data/table/column/date.js **/ /** * generator/data/table/column/date.js * * Zeigt Datumswerte mit anpassbarem Format in Tabellenzellen an. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * * @since 1.0.0 Erstellt als separate Datei */ import { generator } from 'plugins/rest/core/generator'; generator.extend('data/table/column/date', ( config, rowData, cell ) => { const value = new Date(config.value); cell.textContent = new Intl.DateTimeFormat('de-DE', { year: 'numeric', month: '2-digit', day: '2-digit' }).format(value); }); /** Datei: plugins/rest/assets.OLD/js/generator/data/table/column/status-header.js **/ /** * generator/data/table/column/status-header.js * * Header-Spaltengenerator für Status-Anzeigen mit Icons. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.1.0 * * @since 1.0.0 Erstellt als separate Datei für Status-Header-Icons * @since 1.1.0 Unterstützung für `headerIconOnly`, um nur Icons im Header zu zeigen */ import { generator } from 'plugins/rest/core/generator'; generator.extend('data/table/column/status-header', ( config, data, container ) => { config.column.headerIconOnly = false; if( config.column.title ) { container.title = config.column.title; } if( config.column.icon ) { const icon = document.createElement('i'); icon.classList.add('bi', config.column.icon || 'bi-info-circle'); container.appendChild(icon); if( !config.column.label ) { container.classList.add(`table__header__cell--type-${config.column.type}-icon`); config.column.headerIconOnly = true; } } if( config.column.label ) { const labelText = document.createTextNode( config.column.label ); container.appendChild( labelText ); } }); /** Datei: plugins/rest/assets.OLD/js/generator/data/table/column/text.js **/ /** * generator/data/table/column/text.js * * Spaltengenerator für normalen Text. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.1.1 * * @since 1.0.0 Erstellt als separater Generator * @since 1.1.0 Standard-Fallback für alle Spalten * @since 1.1.1 Fix: Kein `` mehr erzeugt, sondern übergebenes `td` manipuliert */ import { generator } from 'plugins/rest/core/generator'; generator.extend('data/table/column/text', async ( config, rowData, container ) => { let option = {}; let icon = null; container.innerHTML = ''; if( config.column.options ) { if( typeof config.column.options === 'object' ) { config.column.options = Object.entries( config.column.options ); } option = config.column.options.find( item => item.value == config.value ); if( option ) { config.value = option.label; if( option.icon ) { icon = await generator.generate( 'components/icon', { name: option.icon }, {}, container ); } } } if( config.column.color || option?.color ) { generator.generate( 'helpers/colorize', config.column?.color ?? {}, option?.color ?? {}, container, icon ); } container.insertAdjacentHTML( 'beforeend', config.value || '-' ); }); /** Datei: plugins/rest/assets.OLD/js/generator/data/table/column/datetime.js **/ /** * generator/data/table/column/datetime.js * * Zeigt Datums- und Zeitwerte mit anpassbarem Format an. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * * @since 1.0.0 Erstellt als separate Datei */ import { generator } from 'plugins/rest/core/generator'; generator.extend('data/table/column/datetime', ( config, rowData, cell ) => { if( !config.value ) return; const value = new Date(config.value); cell.textContent = new Intl.DateTimeFormat('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }).format(value); }); /** Datei: plugins/rest/assets.OLD/js/generator/data/table/column/amount.js **/ /** * generator/data/table/column/amount.js * * Spaltengenerator für normalen Text. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.1.1 * * @since 1.0.0 Erstellt als separater Generator * @since 1.1.0 Standard-Fallback für alle Spalten * @since 1.1.1 Fix: Kein `` mehr erzeugt, sondern übergebenes `td` manipuliert */ import { generator } from 'plugins/rest/core/generator'; generator.extend('data/table/column/amount', async ( config, rowData, container ) => { let option = {}; let icon = null; container.innerHTML = ''; container.removeAttribute('title'); if( config.column.options ) { option = config.column.options.find( item => item.value == config.value ); if( option ) { config.value = option.label; if( option.icon ) { icon = await generator.generate( 'components/icon', { name: option.icon }, {}, container ); } } } if( config.column.color || option?.color ) { generator.generate( 'helpers/colorize', config.column?.color ?? {}, option?.color ?? {}, container, icon ); } container.insertAdjacentHTML( 'beforeend', config.value || '-' ); }); /** Datei: plugins/rest/assets.OLD/js/generator/data/table/column/time.js **/ /** * generator/data/table/column/time.js * * Spaltengenerator für Zeitwerte mit anpassbarem Format. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * * @since 1.0.0 Erstellt als separate Datei */ import { generator } from 'plugins/rest/core/generator'; generator.extend('data/table/column/time', ( config, rowData, cell ) => { const value = new Date(config.value); cell.textContent = new Intl.DateTimeFormat('de-DE', { hour: '2-digit', minute: '2-digit' }).format(value); }); /** Datei: plugins/rest/assets.OLD/js/generator/data/table/column/icon.js **/ /** * generator/data/table/column/icon.js * * Spaltengenerator für Icons mit optionalem Tooltip. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.1.1 * * @since 1.0.0 Erstellt als separater Generator * @since 1.1.1 Fix: Kein `` mehr erzeugt, sondern übergebenes `td` manipuliert */ import { generator } from 'plugins/rest/core/generator'; generator.extend('data/table/column/icon', async ( config, rowData, container ) => { let icon = null; let value = config.value?.value || config.value?.icon || config.value; let iconConfig = config.column.options?.[value] || {}; if( typeof config.value === 'object' ) { iconConfig.icon = config.value.icon; iconConfig.color = config.value.color; } // console.log( typeof config.value, !iconConfig.icon, value, config.value, iconConfig, config.column.options ); if( !iconConfig.icon ) { iconConfig.icon = 'bi bi-question-diamond'; iconConfig.title = `${config.column.title} nicht definiert`; } container.title = iconConfig.title ?? iconConfig.label ?? ''; container.innerHTML = ''; if( config.column.icon ) { icon = await generator.generate( 'components/icon', { name: iconConfig.icon || 'bi bi-question-diamond' }, {}, container ); } generator.generate( 'helpers/colorize', config.column.color, iconConfig.color, container, icon ); }); /** Datei: plugins/rest/assets.OLD/js/generator/data/table/column/status.js **/ /** * generator/data/table/column/status.js * * Spaltengenerator für Status-Anzeigen mit Farb- und Icon-Unterstützung. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.5.0 * * @since 1.0.0 Erstellt als separate Datei * @since 1.3.0 `options.label` wird nur genutzt, wenn definiert * @since 1.3.1 `options.icon` Standard auf `?` gesetzt * @since 1.3.2 `route` als optional für klickbare Spalten * @since 1.3.3 `options.icon` Standard auf `question-diamond`, `color`-Option erweitert * @since 1.3.4 Farbmanagement überarbeitet: `color.name`, Farbwerte/-namen aus `options`, kontrastbasierte Textfarbe * @since 1.3.5 BEM-Klassen für Farbe (`text--`, `icon--`, `bg--`), `color` automatisch erkannt, `background` als Default * @since 1.3.6 Fix: `color.target = text` setzt `.text--`, nicht `.bg--` * @since 1.3.7 Fix: Kein `` mehr erzeugt, sondern übergebenes `td` manipuliert * @since 1.3.8 Fix: Farbe aus `options` wird priorisiert, falls vorhanden * @since 1.3.9 Fix: `title`-Attribut immer auf `td`, nicht auf das Icon * @since 1.4.0 Fix: `color`-Verarbeitung aus `options.color` wiederhergestellt * @since 1.4.1 Unterstützung für Icons ohne Labels in Zellen * @since 1.5.0 Neue Klasse `table__column--type-{type}-icon` für `headerIconOnly` */ import { generator } from 'plugins/rest/core/generator'; generator.extend('data/table/column/status', async ( config, rowData, container ) => { let icon = null; container.innerHTML = ''; const statusConfig = config.column.options?.[config.value] || { icon: 'bi-question-diamond', title: `${config.column.title} nicht definiert` }; container.title = statusConfig.title ?? statusConfig.label; if( config.column.icon ) { icon = await generator.generate( 'components/icon', { name: statusConfig.icon || 'bi bi-question-diamond' }, {}, container ); } if( config.column.headerIconOnly ) { container.classList.add(`table__cell--type-${config.column.type}-icon`); } // Nur Labels hinzufügen, wenn `headerIconOnly` nicht aktiv ist if( !config.column.headerIconOnly && statusConfig.label ) { container.appendChild(document.createTextNode(` ${statusConfig.label}`)); } generator.generate( 'helpers/colorize', config.column.color, statusConfig.color, container, icon ); }); /** Datei: plugins/rest/assets.OLD/js/generator/data/table/column/email.js **/ /** * generator/data/table/column/email.js * * Spaltengenerator für E-Mail-Adressen mit mailto-Links. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.1.0 * * @since 1.0.0 Erstellt als separater Generator */ import { generator } from 'plugins/rest/core/generator'; generator.extend('data/table/column/email', ( config, rowData, container ) => { if (!config.value) { container.textContent = '-'; return; } const link = document.createElement('a'); link.href = `mailto:${config.value}`; link.textContent = config.value; link.classList.add('table__email'); container.appendChild( link ); }); /** Datei: plugins/rest/assets.OLD/js/generator/data/table/column/text-header.js **/ /** * generator/data/table/column/text-header.js * * Standard-Header-Spaltengenerator für Textspalten. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * * @since 1.0.0 Erstellt als separate Datei für generische Text-Header */ import { generator } from 'plugins/rest/core/generator'; generator.extend('data/table/column/text-header', ( config, data, container ) => { if( config.column.title ) { container.title = config.column.title; } if( config.column.label ) { const labelText = document.createTextNode( config.column.label ); container.appendChild( labelText ); } }); /** Datei: plugins/rest/assets.OLD/js/generator/data/table/column/url.js **/ /** * generator/data/table/column/url.js * * Spaltengenerator für Links innerhalb einer Tabellenzelle. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.1.1 * * @since 1.0.0 Erstellt als separater Generator * @since 1.1.0 Standard-Fallback für alle Spalten * @since 1.1.1 Fix: Kein `` mehr erzeugt, sondern übergebenes `td` manipuliert */ import { generator } from 'plugins/rest/core/generator'; generator.extend('data/table/column/url', ( config, rowData, cell ) => { cell.innerHTML = ''; if( !config.value ) { cell.textContent = '-'; return; } const href = config.column.href || config.value; const link = document.createElement('a'); link.href = href.replace('{id}', rowData.id ); link.textContent = config.value; link.target = '_blank'; cell.appendChild(link); }); /** Datei: plugins/rest/assets.OLD/js/generator/data/table/column/actions.js **/ /** * generator/data/table/column/actions.js * * Spaltengenerator für Aktionen/Buttons innerhalb einer Tabellenzelle. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.2.1 * * @since 1.0.0 Erstellt als separater Generator * @since 1.2.1 Fix: Kein `` mehr erzeugt, sondern übergebenes `td` manipuliert */ import { generator } from 'plugins/rest/core/generator'; generator.extend('data/table/column/actions', async ( config, rowData, container ) => { return; const wrapper = document.createElement('div'); wrapper.classList.add( 'table__cell__inner' ); if( config.column.buttons ) { for( const buttonConfig of config.column.buttons.values() ) { // if( action.visibleIf ) { // for( let [key, validValues] of Object.entries(action.visibleIf) ) { // if( !validValues.includes(rowData[key]) ) continue 2; // skip this action // } // } await generator.generate( buttonConfig.generator, buttonConfig.config, rowData, wrapper ); } } container.append( wrapper ); });