// Gentelella 2026 v4 — shell render (pure) // String-only renderers. No DOM, no window/document access. // Imported by: // 1. The Vite plugin (vite.config.js) to inject shell HTML at build/dev time. // 2. src/v4/shell.js as a runtime fallback for pages that bypass the plugin. // NAV items are either flat — { key, href, text, icon, badge? } — // or a parent with `children: [{ key, href, text, badge? }]` for a submenu. // The parent is `key`-less; its children carry their own keys for the // `data-page` highlight match. The parent stays expanded if any child matches. export const NAV = [ { label: 'General', items: [ { text: 'Dashboards', icon: 'dashboard', children: [ { key: 'dashboard', href: 'index.html', text: 'Operations' }, { key: 'dashboard-2', href: 'index2.html', text: 'Analytics' }, { key: 'dashboard-3', href: 'index3.html', text: 'Sales' }, { key: 'dashboard-4', href: 'index4.html', text: 'System health' } ] }, { text: 'Forms', icon: 'forms', badge: { text: 'Hot', cls: 'badge-red' }, children: [ { key: 'forms', href: 'form.html', text: 'General' }, { key: 'form-advanced', href: 'form_advanced.html', text: 'Advanced controls' }, { key: 'form-buttons', href: 'form_buttons.html', text: 'Buttons' }, { key: 'form-upload', href: 'form_upload.html', text: 'Upload' }, { key: 'form-validation', href: 'form_validation.html', text: 'Validation' }, { key: 'form-wizards', href: 'form_wizards.html', text: 'Wizard' } ] }, { text: 'Tables', icon: 'tables', children: [ { key: 'tables', href: 'tables.html', text: 'Static' }, { key: 'tables-dynamic', href: 'tables_dynamic.html', text: 'Dynamic' } ] }, { text: 'Charts', icon: 'charts', badge: { text: 'New', cls: 'badge-teal' }, children: [ { key: 'charts', href: 'chartjs.html', text: 'Chart cards' }, { key: 'echarts', href: 'echarts.html', text: 'ECharts gallery' }, { key: 'other-charts', href: 'other_charts.html', text: 'SVG charts' } ] }, { key: 'calendar', href: 'calendar.html', text: 'Calendar', icon: 'calendar' }, { key: 'map', href: 'map.html', text: 'Map', icon: 'map' } ] }, { label: 'Apps', items: [ { key: 'chat', href: 'chat.html', text: 'Chat', icon: 'chat', badge: { text: '3', cls: 'badge-teal' } }, { key: 'inbox', href: 'inbox.html', text: 'Inbox', icon: 'mail' }, { key: 'kanban', href: 'kanban.html', text: 'Kanban', icon: 'kanban' }, { key: 'files', href: 'file_manager.html', text: 'Files', icon: 'files' }, { key: 'notifications', href: 'notifications.html', text: 'Notifications', icon: 'bell' } ] }, { label: 'E-commerce', items: [ { key: 'storefront', href: 'e_commerce.html', text: 'Storefront', icon: 'shop' }, { key: 'product', href: 'product_detail.html', text: 'Product', icon: 'tag' }, { text: 'Orders', icon: 'cart', children: [ { key: 'orders', href: 'orders.html', text: 'All orders' }, { key: 'order-detail', href: 'order_detail.html', text: 'Order detail' } ] }, { key: 'invoice', href: 'invoice.html', text: 'Invoice', icon: 'receipt' }, { key: 'pricing', href: 'pricing_tables.html', text: 'Pricing', icon: 'price' } ] }, { label: 'Projects', items: [ { key: 'projects', href: 'projects.html', text: 'All projects', icon: 'projects' }, { key: 'project-detail', href: 'project_detail.html', text: 'Project detail', icon: 'pages' } ] }, { label: 'UI library', items: [ { key: 'ui', href: 'general_elements.html', text: 'Elements', icon: 'ui' }, { key: 'widgets', href: 'widgets.html', text: 'Widgets', icon: 'pages', badge: { text: '5', cls: 'badge-blue' } }, { key: 'playground', href: 'playground.html', text: 'Playground', icon: 'code', badge: { text: 'New', cls: 'badge-teal' } }, { key: 'theme', href: 'theme.html', text: 'Theme', icon: 'paint', badge: { text: 'New', cls: 'badge-teal' } }, { key: 'typography', href: 'typography.html', text: 'Typography', icon: 'type' }, { key: 'icons', href: 'icons.html', text: 'Icons', icon: 'icons' }, { key: 'media', href: 'media_gallery.html', text: 'Media', icon: 'media' } ] }, { label: 'Admin', items: [ { key: 'users', href: 'contacts.html', text: 'Contacts', icon: 'users' }, { key: 'user_management', href: 'user_management.html', text: 'User management', icon: 'profile' }, { key: 'profile', href: 'profile.html', text: 'Your profile', icon: 'profile' }, { key: 'settings', href: 'settings.html', text: 'Settings', icon: 'settings' }, { key: 'faq', href: 'faq.html', text: 'Help center', icon: 'help' } ] }, { label: 'Layouts', items: [ { key: 'fixed-sidebar', href: 'fixed_sidebar.html', text: 'Fixed sidebar', icon: 'layout' }, { key: 'fixed-footer', href: 'fixed_footer.html', text: 'Fixed footer', icon: 'layout' }, { key: 'level2', href: 'level2.html', text: 'Nested page', icon: 'pages' }, { key: 'plain', href: 'plain_page.html', text: 'Blank', icon: 'pages' } ] } ]; export const ICONS = { dashboard: '', forms: '', tables: '', charts: '', calendar: '', ui: '', pages: '', media: '', users: '', profile: '', settings: '', chat: '', bell: '', kanban: '', files: '', shop: '', tag: '', cart: '', help: '', mail: '', map: '', receipt: '', price: '', projects: '', type: '', icons: '', layout: '', code: '', paint: '' }; const CHEVRON = ''; function renderNavItem(item, activeKey) { if (item.children) { const childActive = item.children.some((c) => c.key === activeKey); const sub = item.children.map((c) => { const a = c.key === activeKey; return `${c.text}${c.badge ? `${c.badge.text}` : ''}`; }).join(''); const cls = ['nav-tree']; if (childActive) {cls.push('open', 'has-active');} return `
`; } const a = item.key === activeKey; return ` ${ICONS[item.icon] || ''} ${item.text} ${item.badge ? `${item.badge.text}` : ''} `; } export function renderSidebar(activeKey) { const groups = NAV.map((group) => ` `).join(''); return ` `; } export function renderTopbar(breadcrumb) { const crumbs = (breadcrumb || ['Home']).map((c, i, arr) => { const isLast = i === arr.length - 1; return `${i > 0 ? '' : ''}${c}`; }).join(''); return `
`; } export function renderFooter() { return ` `; } export function renderShell({ activeKey = '', breadcrumb = ['Home'] } = {}) { return { sidebar: renderSidebar(activeKey), topbar: renderTopbar(breadcrumb), footer: renderFooter() }; } export function parseShellAttrs(attrs) { const shell = /data-shell\s*=\s*["']([^"']*)["']/.exec(attrs); if (!shell || shell[1] !== 'admin') {return null;} const page = /data-page\s*=\s*["']([^"']*)["']/.exec(attrs); const bc = /data-breadcrumb\s*=\s*["']([^"']*)["']/.exec(attrs); return { activeKey: page ? page[1] : '', breadcrumb: bc ? bc[1].split('>').map((s) => s.trim()).filter(Boolean) : ['Home'] }; }