/** Datei: plugins/rest/assets/js/components/button.js **/ /** * /arunox/components/button.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'button', { hooks : { config : function( config ) { if( config.type ) config.attributes.type ??= config.type; if( config.name ) config.attributes.name ??= config.name; if( config.value ) config.attributes.value ??= config.value; if( config.title ) config.attributes.title ??= config.title; if( config.target ) config.attributes.target ??= config.target; return config; }, wrapper : function( root ) { const config = this.instance.config; if( !config.dropdowns ) return root; const wrapper = ARUNOX.Templates.render( 'div', { classes : 'button__wrapper', }) this.instance.nodes.wrapper = wrapper; this.instance.apply('dropdown'); wrapper.root.append( root ); return wrapper.root; }, dropdown : function() { const config = this.instance.config; const wrapper = this.instance.nodes.wrapper; if( !wrapper || !config.dropdowns ) return; const menu = wrapper.render('div', { nodeName : 'menu', classes : 'button__dropdown' }); for( const item of config.dropdowns ) { const itemContainer = menu.render( 'div', { classes : 'button__dropdown__item' }); itemContainer.render( 'button', Object.assign({ color : 'secondary', type : 'button', block : 'button__dropdown' }, item.config ), this.instance.data); } this.instance.root.addEventListener( 'click', ( event ) => { event.stopPropagation(); wrapper.root.classList.toggle('is--open'); }); wrapper.root.addEventListener( 'mouseleave', () => { wrapper.root.classList.remove( 'is--open'); const focusedElement = wrapper.root.querySelector(':focus'); if( focusedElement ) focusedElement.blur(); }); }, }, htmlTag : function( config, data, ctx ) { return config.href ? 'a' : 'button'; }, builder : function( config, data ) { const className = config?.name || 'button'; const block = config.block ? `${className} ${config.block}__${className}` : className; this.root.classList.add( ...block.split(' ') ); if( config.label ) { this.root.classList.add( 'has--label' ); this.render( 'span', { classes : `${block}__label label`, content : config.label, }); } if( config.href ) { this.root.href = ARUNOX.Router.parseUrl( config.href, data ); this.root.addEventListener( 'click', ( event ) => { event.stopPropagation(); }); return; } if( config.type ) { const form = this.root.closest( 'form' ); if( form ) { if( !form.id ) form.id = crypto.randomUUID(); this.root.setAttribute( 'form', form.id ); } } if( config.action && !config.dropdowns ) { this.root.addEventListener( 'click', ( event ) => { event.preventDefault(); event.stopPropagation(); if( this.root.name && this.root.value !== '' && this.root.value !== 'undefined' ) { ARUNOX.Router.handle( config.action, { [`${this.name}`] : this.value }, data, 'rest' ); } else ARUNOX.Router.handle( config.action, {}, data, 'rest' ); }); } }, }); ARUNOX.Templates.register( 'buttons', { htmlTag : 'div', hooks : { config : function( config ) { config.datasets.arx.node = 'buttons'; config.content ??= config.buttons || []; return config; }, }, builder : function( config, data ) {} }); /** Datei: plugins/rest/assets/js/components/tabs.js **/ /** * /arunox/components/tabs.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'tabs', { htmlTag : 'div', hooks : { config : function( config, data ) { config.classes.push( 'tabs' ); return config; }, buildRootAfter : function() { const config = this.instance.config; config.query = window.location.hash.match( /\?(.*)/ ); config.hash = window.location.hash.substring(1).replace( /\?.*/, '' ); config.filter = new URLSearchParams( config.query ? config.query[1] : '' ); config.isNested = this.instance.root.closest( '.dialog, .tabs__content' ) !== null; if( config.isNested ) { let match = ARUNOX.Router.history.match( /.*(?:#([a-zA-Z0-9]+)).*/ ); config.hash = this.instance.root.dataset.activeTab || match ? match[1] : ''; config.filter = new URLSearchParams(); } }, }, builder : function( config, data ) { this.render( 'tabs.buttons', { nodeName : 'buttons', tabs : config.tabs, }); this.render( 'tabs.contents', { nodeName : 'contents', tabs : config.tabs, }); }, }); ARUNOX.Templates.register( 'tabs.buttons', { extends : 'buttons', hooks : { config : function( config, data ) { config.classes.push( 'tabs__list' ); return config; }, content : function( content, config, data ) { for( let [ index, tabConfig ] of config.tabs.entries() ) { tabConfig = Object.assign( { index }, tabConfig ); delete tabConfig.content; content.push( ARUNOX.Templates.config( 'tabs.button', tabConfig, data ) ); } return content; }, }, }); ARUNOX.Templates.register( 'tabs.button', { extends : 'button', hooks : { config : function( config, data ) { config.classes.push( 'tabs__item' ); config.color = 'secondary'; config.arx.tab = config.id; return config; }, select : function() { const tabs = this.instance.closest( 'tabs' ); const tabsConfig = tabs.config; const config = this.instance.config; const buttons = tabs.nodes.buttons; const contents = tabs.nodes.contents; for( const button of buttons.root.childNodes ) { button.classList.remove('tabs__item--active' ); if( button.dataset.arxTab == config.arx.tab ) { this.instance.root.classList.add( 'tabs__item--active' ); } } for( const content of Object.values( contents.root.childNodes ) ) { if( content.dataset.arxTab == config.arx.tab ) { content.classList.add('tabs__content--active' ); content.style.display = ''; content.ARX.apply('show'); } else { content.classList.remove('tabs__content--active' ); content.style.display = 'none'; content.ARX.apply('hide'); } } if( tabsConfig.isNested ) return; if( this.instance.root.dataset.tabFilter ) history.pushState(null, null, `#${config.arx.tab}?${config.arx.filter}` ); else if( config.arx.tab ) history.pushState(null, null, `#${config.arx.tab}` ); else history.pushState(null, null, '' ); }, afterRender : function( config, data ) { const tabs = this.instance.closest( 'tabs' ); const tabsConfig = tabs.config; if( config.action ) { this.instance.root.classList.add( 'tabs__item--action' ); this.instance.root.addEventListener( 'click', () => ARUNOX.Router.handle( config.action, {}, data, 'rest' ) ); return; } if( config.type === 'submit' ) { this.instance.root.classList.add( 'tabs__item--action' ); return; } this.instance.root.addEventListener( 'click', () => this.instance.apply('select') ); if( ( tabsConfig.hash && tabsConfig.hash === config.arx.tab ) || ( !tabsConfig.hash && ( config.active || ( config.index === 0 && !tabsConfig.tabs.some( t => t.active ) ) ) ) ) { queueMicrotask( () => this.instance.apply('select') ); } }, }, }); ARUNOX.Templates.register( 'tabs.contents', { htmlTag : 'div', hooks : { config : function( config, data ) { config.classes.push( 'tabs__content', 'scroll__viewport' ); config.arx.tab = config.id; return config; }, }, builder : function( config, data ) { for( const tabConfig of config.tabs ) { if( tabConfig.action || tabConfig.type === 'submit' ) continue; this.render( 'tabs.content', tabConfig ); } }, }); ARUNOX.Templates.register( 'tabs.content', { htmlTag : 'div', hooks : { config : function( config, data ) { config.classes.push( 'tabs__content__item' ); config.arx.tab = config.id; config.styles.display = 'none'; delete config.icon; delete config.label; return config; }, }, builder : function( config, data ) {}, }); /** Datei: plugins/rest/assets/js/components/table.js **/ /** * /arunox/components/table.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'table', { hooks : { config : function( config, data ) { config.classes.push( 'table' ); return config; }, wrapper : function( root ) { const wrapper = ARUNOX.Templates.render( 'div', { classes : 'table__wrapper', }) this.instance.nodes.wrapper = wrapper; this.instance.apply('toolbar'); wrapper.root.append( root ); return wrapper.root; }, toolbar : function() { const config = this.instance.config; const filters = this.instance.filter( 'filters', config.filter, config.columns ); const wrapper = this.instance.nodes.wrapper; const toolbar = wrapper.render( 'div', { nodeName : 'toolbar', classes : 'table__toolbar', }); if( config.search ) { toolbar.render( 'div', { nodeName : 'searchContainer', classes : 'table__search', content : [ config.search ? ARUNOX.Templates.config( 'input', { nodeName : 'searchInput', type : 'text', classes : 'table__search__input', attributes : { placeholder : 'Suchen...', }, onInput : () => this.instance.apply( 'search', toolbar.nodes.searchInput.root.value ), }) : null, ARUNOX.Templates.config( 'button', { nodeName : 'searchReset', color : 'danger', type : 'button', block : 'table__search', title : 'Suche zurücksetzen', icon : 'bi bi-funnel', apply : [ 'active', false ], onClick : () => this.instance.apply( 'reset', 'search', config ), }), ], }); } if( filters ) { toolbar.render( 'buttons', { nodeName : 'filterContainer', classes : 'table__filter', content : [ ...filters, ARUNOX.Templates.config( 'button', { nodeName : 'filterReset', color : 'danger', type : 'button', block : 'table__filter', label : 'Filter zurücksetzen', icon : 'bi bi-funnel', apply : [ 'active', false ], onClick : () => this.instance.apply( 'reset', 'filter', config ), }) ], }); } if( config.buttons ) { const buttons = toolbar.render( 'buttons', { content : config.buttons, nodeName : 'buttons', classes : 'table__buttons', }); } }, filters : function( filters, columns ) { const buttons = []; if( !Array.isArray( filters ) ) filters = [ filters ]; for( const column of filters ) { const config = columns.find( item => item.key == column ); if( !config?.filterable || !config?.options ) continue; for( const option of config.options ) { buttons.push( ARUNOX.Templates.config( 'button', { nodeName : `filter${config.key.toUpperCaseFirst()}-${option.value}`, color : option?.color, type : 'button', block : 'table__filter', label : option?.label, icon : option?.icon, apply : [ 'active', false ], onClick : () => this.instance.apply( 'filter', config.key, option?.value ), }) ); } } return buttons; }, filter : function( column, filterValue ) { // console.log( 'FILTER', column, filterValue ); const toolbar = this.instance.nodes.toolbar; const tableBody = this.instance.nodes.body; const tableRows = tableBody.root.querySelectorAll('tr'); filterValue = filterValue.toString().toLowerCase(); let visibleRows = []; tableRows.forEach(row => { const filterData = row.dataset?.[`arxFilter${column.toUpperCaseFirst()}`] || ''; const isVisible = filterData == filterValue; row.style.display = isVisible ? null : 'none'; if( isVisible ) { visibleRows.push( row ); } }); toolbar.nodes.filterContainer.root.childNodes.forEach( element => { element.ARX.apply( 'active', element.ARX.config.nodeName == `filter${column.toUpperCaseFirst()}-${filterValue}` ); }); toolbar.nodes.filterReset.apply( 'active', visibleRows.length != tableRows.length ); visibleRows.forEach( row => tableBody.root.append( row ) ); }, search : function( searchValue ) { // console.log( 'SEARCH', searchValue ); const toolbar = this.instance.nodes.toolbar; const tableBody = this.instance.nodes.body; const tableRows = tableBody.root.querySelectorAll('tr'); searchValue = searchValue.toLowerCase(); let visibleRows = []; tableRows.forEach(row => { const searchData = row.dataset?.arxSearch || ''; const isVisible = searchData.includes( searchValue ); row.style.display = isVisible ? null : 'none'; if( isVisible ) { visibleRows.push( row ); } }); toolbar.nodes.searchReset.apply( 'active', visibleRows.length != tableRows.length ); visibleRows.forEach( row => tableBody.root.append( row ) ); }, reset : function( config, type ) { // console.log( 'RESET', type ); const toolbar = this.instance.nodes.toolbar; const tableBody = this.instance.nodes.body; const tableRows = tableBody.root.querySelectorAll('tr'); tableRows.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] : '' ); if( config?.query ) { for( const [ index, value ] of config.query.entries() ) { if( filter.has( index ) && filter.get( index ) == value ) { filter.delete( index ); } } } if( hash && filter?.size ) history.pushState(null, null, `#${hash}?${filter.toString()}`); else if( filter?.size ) history.pushState(null, null, `?${filter.toString()}`); else if( hash ) history.pushState(null, null, `#${hash}`); else history.pushState(null, null, ); toolbar.nodes.filterContainer.root.childNodes.forEach( element => { element.ARX.apply( 'active', false ); }); if( toolbar.nodes.filterReset ) toolbar.nodes.filterReset.apply( 'active', false ); if( toolbar.nodes.searchReset ) toolbar.nodes.searchReset.apply( 'active', false ); if( toolbar.nodes.searchInput ) toolbar.nodes.searchInput.root.value = ''; }, sort : function( column, order ) { // console.log( 'SORT', column, order ); const config = this.instance.config; const thead = this.instance.nodes.header; const tbody = this.instance.nodes.body; const tabelCell = thead.root.querySelector( `[data-arx-column="${column}"]` ); const tableRows = Array.from( tbody.root.querySelectorAll( 'tr' ) ); if( !tabelCell || !tableRows ) return; if( !order ) order = tabelCell.dataset.arxOrder === 'asc' ? 'desc' : 'asc'; const columns = config.columns || []; const rules = config.order.rules || {}; if( rules[ column ]?.replace ) { for( const [ oldCol, newCol ] of Object.entries( rules[ column ].replace ) ) { this.instance.root.querySelectorAll(`td[data-arx-column="${oldCol}"]`).forEach(td => { const rowData = td.closest('tr').rowData; td.innerHTML = rowData[ newCol ] || ''-''; // td.dataset.arxColumn = newCol; // Key anpassen }); } } if( rules[ column ]?.show ) { for( const showCol of rules[ column ].show ) { this.instance.root.querySelectorAll(`td[data-arx-column="${showCol}"], th[data-arx-column="${showCol}"]`).forEach(el => { el.style.display = ''; // Wieder anzeigen }); } } if( rules[ column ]?.hide ) { for( const hideCol of rules[ column ].hide ) { this.instance.root.querySelectorAll(`td[data-arx-column="${hideCol}"], th[data-arx-column="${hideCol}"]`).forEach(el => { el.style.display = 'none'; // Wieder anzeigen }); } } // Entferne vorherige Sortierklassen this.instance.root.querySelectorAll('.table__header__cell').forEach(cell => { cell.classList.remove('sorting--asc', 'sorting--desc', 'sorting--current'); }); // Setze neue Sortierklasse if( tabelCell ) { tabelCell.dataset.arxOrder = order; tabelCell.classList.add( `sorting--${order}`, 'sorting--current' ); } this.instance.root.dataset.arxSortingColumn = column; this.instance.root.dataset.arxSortingOrder = order; // Reihen sortieren tableRows.sort( (rowA, rowB) => { let a = rowA.ARX.data[ column ]?.trim() || ''; let b = rowB.ARX.data[ 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.root.innerHTML = ''; tableRows.forEach( row => tbody.root.append( row ) ); }, loaded : function() { const config = this.instance.config; if( config.order && config.order.column ) { this.instance.apply( 'sort', config.order.column, config.order.order ); } }, }, builder : function( config, data ) { this.render( 'thead', { nodeName : 'header', columns : config.columns, }); const body = this.render( 'tbody', { nodeName : 'body', columns : config.columns, apply : [ 'generate', data, true ], }, data ); if( config.api ) { if( !config.api.resolved ) { config.api = ARUNOX.Router.parseRoute( config.api, data ); } ARUNOX.Router.fetch( config.api ).then( data => body.apply( 'generate', data, false ) ); } }, }); ARUNOX.Templates.register( 'thead', { hooks : { config : function( config, data ) { config.classes.push( 'table__header' ); return config; }, }, builder : function( config, data ) { const headerRow = this.render( 'tr', { classes : [ 'table__header__row' ], columns : config.columns, }); } }); ARUNOX.Templates.register( 'tbody', { hooks : { config : function( config, data ) { config.classes.push( 'table__body' ); return config; }, generate : async function( data, loading ) { const table = this.instance.closest( 'table' ); this.instance.root.innerHTML = ''; if( data && data.length ) { for( const rowData of data ) { this.instance.render( 'tr', { classes : [ 'table__body__row' ], columns : this.instance.config.columns, }, rowData ); } table.apply( 'loaded' ); return; } this.instance.render( 'tr', { classes : [ 'table__body__row' ], columns : [ { key : 'hint', attributes : { colSpan : this.instance.config.columns.length, }, innerText : loading ? 'Daten werden geladen ...' : 'Keine Datensätze gefunden', } ], }); }, }, }); ARUNOX.Templates.register( 'tr', { hooks : { config : function( config, data ) { config.arx.filter ??= {}; config.arx.search ??= []; config.classes.push( 'table_row' ); return config; }, afterRender : function( config, data ) { const table = this.instance.closest( 'table' ); this.instance.setUUID( data?.hashid ); if( table.config.action && data ) { this.instance.root.classList.add('cursor--pointer'); this.instance.root.addEventListener('click', async ( event ) => { if( event.target.tagName.toLowerCase() === 'a' ) return; await ARUNOX.Router.handle( table.config.action, {}, data, 'rest' ); }); } }, afterContent : function( config, data ) { this.instance.config.arx.search = this.instance.config.arx.search.join( ' ' ).toLowerCase(); }, update : function( data, status ) { const updatedRow = this.instance.replace( 'tr', { columns : this.instance.config.columns, classes : status ? 'table__body__row--updated' : 'table__body__row--failed', }, data ); setTimeout(() => updatedRow.root.classList.remove( 'table__body__row--updated', 'table__body__row--failed' ), 200 ); }, }, builder : function( config, data ) { const cellTag = this.parent.template == 'thead' ? 'th' : 'td'; for( const column of config.columns ) { this.render( cellTag, column, data ); } }, }); ARUNOX.Templates.register( 'th', { htmlTag : 'th', hooks : { config : function( config, data ) { config.type ??= 'text'; config.innerText ??= config?.label || null; config.arx.column ??= config?.key || null; config.classes.push( 'table__header__cell' ); config.classes.push( `table__header__cell--type-${config.type}` ); config.classes.push( 'table__cell' ); config.classes.push( `table__cell--type-${config.type}` ); if( config.sortable ) { config.classes.push( 'sortable' ); config.arx.column = config.key; config.arx.order = config.order?.order || 'asc'; config.events.click = () => this.instance.apply( 'sort', null ); } if( !config.visible ) { config.styles.display = 'none'; } return config; }, sort : function( order ) { const table = this.instance.closest( 'table' ); const column = this.instance.root.dataset?.arxColumn || null; table.apply( 'sort', column, order ); }, }, builder : function( config, data ) {} }); ARUNOX.Templates.register( 'td', { hooks : { config : function( config, data ) { config.type ??= 'text'; config.arx.column ??= config?.key || null; config.classes.push( 'table__cell' ); config.classes.push( `table__cell--type-${config.type}` ); if( !config.visible ) { config.styles.display = 'none'; } return config; }, data : function( data, config ) { if( data === undefined ) return data; let value = '-'; if( typeof config.output === 'object' ) { let keys = Array.isArray( config.output.keys ) ? config.output.keys : [ config.output.keys ]; let values = []; for( const key of keys ) { values.push( data[ key ] || null ); } if( config.output.format == 'string' ) { value = values.join( config.output.seperator || ' ' ); } else value = values.join(' '); data[ config.key ] = value; } if( data[ config.key ] !== undefined ) { value = data[ config.key ]; } else if( config.key.indexOf('.') > -1 ) { let current = data; for( const key of config.key.split('.') ) { current = current[ key ]; } if( current !== undefined ) { value = current; } } return value; }, beforeRender : function( config, data ) { this.instance.config.innerText ??= data || '-'; if( data !== null && data !== undefined ) { if( config?.filterable ) this.instance.parent.config.arx.filter[ config.key ] = data.toString().toLowerCase(); if( config?.searchable ) this.instance.parent.config.arx.search.push( data.trim() ); } }, afterRender : function( config, data ) { this.instance.apply( `type.${config.type}` ); }, }, }); ARUNOX.Hooks.register( 'td->type.icon', function() { const data = this.instance.data; const config = this.instance.config; const option = config.options[ data ] || {}; this.instance.root.textContent = null; const icon = this.instance.render( 'icon', { name : option?.icon || 'bi bi-question-diamond', color : { target : 'text', prefix : null, color : option?.color || 'default', }, attributes : { title : option?.label || `${config.title} nicht definiert`, }, }); }); ARUNOX.Hooks.register( 'td->type.status', function() { this.instance.apply( 'type.icon' ); const table = this.instance.closest( 'table' ); const config = this.instance.config; const data = this.instance.parent.data; const action = Object.assign( {}, { query : table.config.api?.query || {}, }, config.action ); this.instance.root.classList.add( 'js--click-ownbound' ); this.instance.root.addEventListener( 'click', async event => { event.preventDefault(); event.stopPropagation(); await ARUNOX.Router.handle( action, {}, data, 'rest' ); }); }); /** Datei: plugins/rest/assets/js/components/headline.js **/ /** * /components/headline.js * * Headline-Component mit Icon-Support * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File created */ ARUNOX.Templates.register( 'headline', { htmlTag : function( config, data, ctx ) { return `h${config.size || 2}`; }, builder : function( config, data ) { const className = 'headline'; const block = config.block ? `${className} ${config.block}__${className}` : className; this.root.classList.add( ...block.split(' ') ); // Title als Text-Node if( config.title ) { const textNode = document.createTextNode( config.title ); this.root.appendChild( textNode ); } } }); /** Datei: plugins/rest/assets/js/components/icon.js **/ /** * /arunox/components/icon.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'icon', { htmlTag : 'i', builder : function( config, data ) { const className = 'icon'; const block = config.block ? `${className} ${config.block}__${className}` : className; this.root.classList.add( ...config.name.split(' ') ); this.root.classList.add( ...block.split(' ') ); this.parent.root?.classList.add( 'has--icon' ); }, }); /** Datei: plugins/rest/assets/js/components/form.js **/ /** * rest/arunox/templates/form.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'form', { htmlTag : 'form', hooks : { config : function( config ) { config.attributes.id = crypto.randomUUID(); config.datasets.arx.node = this.instance.htmlTag; config.classes.push( 'form__container' ); return config; }, content : function( content, config, data ) { if( config.buttons ) { content.push( ARUNOX.Templates.config( 'buttons', { classes : [ 'form__buttons' ], content : config.buttons.map( button => { button.config.attributes ??= {}; button.config.attributes.form = this.instance.config.attributes.id; return button; }), }, data ) ); } return content; }, afterRender : function( config, data ) { if( !config.action ) return; let lastSearchParams = ARUNOX.Router.history?.match( /\?(.*)/) | {}; let formSearchParams = config.action.route.match( /\?(.*)/); if( lastSearchParams ) lastSearchParams = new URLSearchParams( lastSearchParams[1] ); if( formSearchParams ) formSearchParams = new URLSearchParams( formSearchParams[1] ); if( lastSearchParams ) { if( lastSearchParams.has('columns') && ( !formSearchParams || !formSearchParams.has('columns') ) ) { if( formSearchParams ) lastSearchParams = '&columns=' + lastSearchParams.get('columns'); else lastSearchParams = '?columns=' + lastSearchParams.get('columns'); config.action.route += lastSearchParams; } } this.instance.noValidate = true; this.instance.actionConfig = ARUNOX.Router.parseRoute( config.action, data, {}, 'rest' ); } }, }); ARUNOX.Templates.register( 'control', { htmlTag : 'div', hooks : { config : function( config ) { config.input = Object.assignRecursive( {}, config || {} ); config.template = `input/${config.type}`; config.datasets.arx.node = 'control'; config.datasets.arx.control = config.type; if( !ARUNOX.Templates.exists( config.template ) ) { if( config.type == 'select' ) config.template = 'select' else if( config.type == 'textarea' ) config.template = 'textarea' else if( config.type == 'datalist' ) config.template = 'datalist' else config.template = 'input'; } config.classes.push( 'form__group', `form__control--${config.type}` ); return config; }, }, builder : function( config, data ) { if( config.label ) { const label = this.render( 'label', { classes : [ 'form__label' ], content : config.label, attributes : { for : `control_${config.key}`, }, }); } const element = this.render( config.template, Object.assignRecursive( { classes : [ 'form__control' ], datasets : { arx : { node : 'input', }, }, }, config.input || {} ), data ); }, }); ARUNOX.Hooks.register( 'input->beforeRender', function( config, data ) { config.attributes = { name : config.key, type : config.type, required : !!config.required, }; }); ARUNOX.Hooks.register( 'select->beforeRender', function( config, data ) { config.content = []; config.attributes = { name : config.key, required : !!config.required, multiple : !!config.multiple, }; // console.log( config, data ); if( config.choose ) { config.content.push( ARUNOX.Templates.config( 'option', { content : config.choose, attributes : { selected : true, }, } ) ); } for( const option of config.options ) { config.content.push( ARUNOX.Templates.config( 'option', { content : option.label, attributes : { value : option.value, selected : data?.[ config.key ] == option.value, }, } ) ); } }); ARUNOX.Hooks.register( 'textarea->beforeRender', function( config, data ) { config.attributes = { name : config.key, required : !!config.required, }; }); if( !window.formSubmitListener ) { document.addEventListener('submit', async event => { if( !event.target.matches('form') ) return; event.preventDefault(); const form = event.target; const inputs = form.querySelectorAll('input, select, textarea'); const promises = []; console.log( inputs ); inputs.forEach(input => { if( typeof input.ARX.validate === 'function' ) { promises.push(new Promise((resolve) => { input.addEventListener('validationComplete', () => { resolve( !!input.closest('.is-valid') ); }, { once: true }); input.dispatchEvent( new Event('validate', { bubbles: true } ) ); })); } }); const results = await Promise.all( promises ); const isValid = results.every( result => result ); if( isValid ) { ARUNOX.Router.handle( form.ARX.actionConfig, new FormData( form ), {}, 'rest' ); } }); window.formSubmitListener = true; } /** Datei: plugins/rest/assets/js/components/notifications.js **/ /** * /arunox/components/notifications.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'notifications', { htmlTag : 'div', hooks : { shouldRebuild : function( shouldRebuild, config, data ) { return document.querySelector('.notification__wrapper'); }, config : function( config, data ) { config.classes.push( 'notification__wrapper', 'notification--show-on-hover', 'notification--stacked' ); return config }, }, addNotification : function( config, data ) { this.render( 'notification', config, data ); } }); ARUNOX.Templates.register( 'notification', { htmlTag : 'div', hooks : { config : function( config, data ) { return { type : config.type, color : config.color || config.type, duration : config.duration, message : config.message || config.title, classes : [ 'notification' ], content : [ config.icon ? ARUNOX.Templates.config( 'icon', { name: config.icon, block: 'notification' }) : null, ARUNOX.Templates.config( 'div', { classes : [ 'notification__inner' ], content : [ config.title ? ARUNOX.Templates.config( 'div', { innerText : config.title, classes : 'notification__title', }) : null, config.message ? ARUNOX.Templates.config( 'div', { innerText : config.message, classes : 'notification__message', }) : null, ], }), ARUNOX.Templates.config( 'button', { classes : [ 'notification__close' ], innerHTML : '×', onClick : () => this.apply( 'close' ), }), ], }; }, close : function() { this.instance.root.classList.add( 'is-closing' ); setTimeout(() => this.instance.root.remove(), 300 ); }, afterRender : function( config, data ) { if( config.duration === 0 ) return; setTimeout(() => this.instance.apply( 'close' ), config.duration || 5000 ); } }, });