/** Datei: plugins/rest/assets.OLD/js/generator/layouts/tabs.js **/ /** * generator/layouts/tabs.js * * Erstellt eine tab-basierte Navigation mit dynamischen Inhalten. * Unterstützt `active: true`, um ein Standard-Tab zu setzen. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.2.1 * * @since 1.0.0 Tab-Generator erstellt * @since 1.1.0 Fix: Active-Tab Logik integriert * @since 1.2.0 Version angepasst für erneutes Hochladen * @since 1.2.1 Fix: Korrekte BEM-Klasse `tabs__item--active` */ import { generator } from 'plugins/rest/core/generator'; import { handleAction } from 'plugins/rest/core/helpers'; generator.extend('layouts/tabs', async ( config, data, container ) => { const contentCache = []; const query = window.location.hash.match( /\?(.*)/ ); const tabsWrapper = document.createElement('div'); const tabList = document.createElement('div'); const tabContent = document.createElement('div'); const isNested = container.closest('.dialog__content, .tabs__content') !== null; let hash = window.location.hash.substring(1).replace( /\?.*/, '' ); let filter = new URLSearchParams( query ? query[1] : '' ); if( isNested ) { let match = handleRoute.match( /.*(?:#([a-zA-Z0-9]+)).*/ ); hash = container.dataset.activeTab || match ? match[1] : ''; filter = new URLSearchParams(); } tabList.classList.add('tabs__list'); tabContent.classList.add( 'tabs__content', 'scroll__viewport'); tabsWrapper.classList.add('tabs'); for( const [ index, tab ] of config.content.entries() ) { const tabItem = document.createElement('button'); const tabLabel = document.createTextNode( tab.label ); const tabId = tab.id; tabItem.classList.add( 'tabs__item', `tabs__item--${tab.color || 'none'}` ); tabItem.type = tab.type ?? 'button'; tabItem.dataset.tabId = tabId; if( tab.title ) { tabItem.title = tab.title; } // Falls ein Icon definiert ist, generiere es if( tab.icon ) { await generator.generate("components/icon", { name: tab.icon, block: "tabs" }, data, tabItem ); } tabItem.append( tabLabel ); // Falls es sich um eine Aktion handelt, speichere sie für später if( tab.action ) { tabItem.classList.add('tabs__item--action'); tabItem.addEventListener('click', () => handleAction( tab.action, {}, data, 'rest' ) ); tabList.appendChild(tabItem); continue; } if( tab.type === 'submit' ) { tabItem.classList.add('tabs__item--action'); tabList.appendChild(tabItem); continue; } const contentContainer = document.createElement('div'); contentContainer.classList.add('tabs__content__item'); contentContainer.style.display = 'none'; contentCache[index] = contentContainer; if( ( hash && hash === tabId ) || ( !hash && ( tab.active || ( index === 0 && !config.content.some( t => t.active ) ) ) ) ) { tabItem.classList.add('tabs__item--active'); // 🔥 Setzt das erste oder aktive Tab als Default contentContainer.style.display = ''; tab.active = true; } else tab.active = false; if( filter && hash && hash === tabId ) { tabItem.dataset.tabFilter = filter.toString(); } if( tab.content ) { for( const component of tab.content ) { component.config.filter = { button : tabItem, query : new URLSearchParams( tabItem.dataset.tabFilter ), }; await generator.generate( component.generator, component.config, data, contentContainer ); } } tabItem.addEventListener('click', () => { tabList.querySelectorAll('.tabs__item').forEach(el => el.classList.remove('tabs__item--active')); tabItem.classList.add('tabs__item--active'); contentCache.forEach(c => (c.style.display = 'none')); contentContainer.style.display = ''; if( isNested ) return; if( tabItem.dataset.tabFilter ) { history.pushState(null, null, `#${tabItem.dataset.tabId}?${tabItem.dataset.tabFilter}`); } else history.pushState(null, null, `#${tabItem.dataset.tabId}`); }); tabList.appendChild(tabItem); tabContent.appendChild(contentContainer); } tabsWrapper.appendChild(tabList); tabsWrapper.appendChild(tabContent); container.appendChild(tabsWrapper); }); /** Datei: plugins/rest/assets.OLD/js/generator/layouts/menu.js **/ /** * generator/layouts/menu.js * * Erstellt eine dynamische Navigation basierend auf der Konfigurationsstruktur. * Unterstützt Haupt- und Untermenüs mit aktiver Markierung. * * @author Sparky * @copyright 2025 ARUNOX * @version 0.0.1 * * @since 1.0.0 Erste Implementierung des Menü-Generators * @since 1.1.0 Unterstützung für Submenüs hinzugefügt */ import { generator } from 'plugins/rest/core/generator'; generator.extend("layouts/menu", async ( config, data, container ) => { if( !container ) return; if( !container.classList.contains('menu__sub') ) { container.classList.add('menu'); } const routes = data.filter( route => route.menu == config.name ); for( const route of routes ) { const item = document.createElement('div'); const subRoutes = data.filter( subRoute => subRoute.menu === ( route.name ?? 'XXX' ) ); container.appendChild( item ); if( !route.path && subRoutes.length > 0 ) { const category = document.createElement('div'); item.classList.add('menu__category'); if( route.icon ) await generator.generate("components/icon", { name: route.icon, block: 'menu' }, {}, category ); if( route.label ) { category.insertAdjacentHTML( 'beforeend', route.label ); } category.classList.add('menu__label'); item.appendChild( category ); } if( route.path ) { item.classList.add('menu__item'); const link = await generator.generate( 'components/link', { label : route.label, title : route.title, path : route.path, icon : route.icon, block : 'menu' }); item.appendChild( link ); if( route.badge ) { const badge = document.createElement('span'); badge.textContent = route.badge; badge.classList.add('menu__badge'); link.appendChild( badge ); } if( route.path != '/' && config.path.startsWith( route.path ) ) { if( route.path == config.path || data.filter( item => item.path && item.path.startsWith( route.path ) ).length == 1 ) { item.classList.add('is-expanded'); item.classList.add('menu__item--active'); link.classList.add('menu__link--active'); container.closest('.menu__item')?.classList.add('is-expanded'); container.closest('.menu__item')?.classList.add('has-active'); } } link.addEventListener( 'click', () => { const hasPath = route.path && route.path != '#'; const isExpanded = item.classList.contains('is-expanded'); const hasChildren = item.classList.contains('has-children'); document.querySelectorAll('.has-active').forEach(el => el.classList.remove('has-active')); document.querySelectorAll('.is-expanded').forEach(el => el.classList.remove('is-expanded')); document.querySelectorAll('.menu__item').forEach(el => el.classList.remove('menu__item--active')); document.querySelectorAll('.menu__link--active').forEach(el => el.classList.remove('menu__link--active')); if( hasPath ) { link.classList.add("menu__link--active"); } if( isExpanded && hasChildren ) return; item.classList.add("is-expanded"); item.classList.add("menu__item--active"); container.closest('.menu__item')?.classList.add('is-expanded'); container.closest('.menu__item')?.classList.add('has-active'); }); if( route.color ) generator.generate( 'helpers/colorize', route.color, null, link, link.elements.icon ); else link.classList.add( 'behavior-default' ); } if( subRoutes.length > 0 ) { item.classList.add('has-children'); const subMenu = document.createElement('div'); subMenu.classList.add('menu__sub'); item.appendChild( subMenu ); await generator.generate( 'layouts/menu', { name : route.name, path : config.path, }, data, subMenu ); } } });