// web-cad v6 – Interactions (theme, tabs, drawers, mobile right-tab-bar) (function() { 'use strict'; // ===================== THEME TOGGLE ===================== const themeBtn = document.getElementById('theme-toggle'); const html = document.documentElement; if (themeBtn) { themeBtn.addEventListener('click', () => { const cur = html.getAttribute('data-theme'); html.setAttribute('data-theme', cur === 'dark' ? 'light' : 'dark'); }); } // ===================== RIGHTBAR (desktop) TAB SWITCHING ===================== document.querySelectorAll('.rightbar-tab').forEach(tab => { tab.addEventListener('click', () => { const panel = tab.dataset.panel; document.querySelectorAll('.rightbar-tab').forEach(t => { t.classList.remove('active'); t.setAttribute('aria-selected', 'false'); }); document.querySelectorAll('.rightbar-panel').forEach(p => p.classList.remove('active')); tab.classList.add('active'); tab.setAttribute('aria-selected', 'true'); const target = document.querySelector(`[data-panel-content="${panel}"]`); if (target) target.classList.add('active'); // sync mobile drawer if open const dTab = document.querySelector(`[data-drawer-tab="${panel}"]`); if (dTab) dTab.click(); }); }); // ===================== DRAWER TAB SWITCHING (mobile) ===================== document.querySelectorAll('.drawer-tab').forEach(tab => { tab.addEventListener('click', () => { const panel = tab.dataset.drawerTab; document.querySelectorAll('.drawer-tab').forEach(t => t.classList.remove('active')); tab.classList.add('active'); // Re-render drawer body for this panel renderDrawerRight(panel); // sync desktop rightbar const rTab = document.querySelector(`.rightbar-tab[data-panel="${panel}"]`); if (rTab) { document.querySelectorAll('.rightbar-tab').forEach(t => t.classList.remove('active')); rTab.classList.add('active'); document.querySelectorAll('.rightbar-panel').forEach(p => p.classList.remove('active')); const target = document.querySelector(`[data-panel-content="${panel}"]`); if (target) target.classList.add('active'); } }); }); // ===================== RIGHT-TAB-BAR (mobile vertical) ===================== document.querySelectorAll('.right-tab-btn').forEach(btn => { btn.addEventListener('click', () => { const which = btn.dataset.drawer; document.querySelectorAll('.right-tab-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); // activate drawer tab + open drawer const dTab = document.querySelector(`[data-drawer-tab="${which}"]`); if (dTab) dTab.click(); openDrawer('right'); }); }); // ===================== DRAWER OPEN/CLOSE ===================== const scrim = document.getElementById('scrim'); const drawerLeft = document.getElementById('drawer-left'); const drawerRight = document.getElementById('drawer-right'); function openDrawer(which) { const drawerLeftBody = document.getElementById('drawer-left-body'); const drawerRightBody = document.getElementById('drawer-right-body'); if (which === 'left' && drawerLeft) { drawerLeft.classList.add('open'); drawerLeft.setAttribute('aria-hidden', 'false'); // Use children.length instead of innerHTML.trim() because placeholder HTML comments would prevent re-render if (drawerLeftBody && drawerLeftBody.children.length === 0) { renderDrawerLeft(); } } if (which === 'right' && drawerRight) { drawerRight.classList.add('open'); drawerRight.setAttribute('aria-hidden', 'false'); if (drawerRightBody && drawerRightBody.children.length === 0) { const active = document.querySelector('.drawer-tab.active'); renderDrawerRight(active ? active.dataset.drawerTab : 'tool'); } } if (scrim) scrim.classList.add('show'); document.body.classList.add('drawer-open'); } function closeDrawers() { if (drawerLeft) { drawerLeft.classList.remove('open'); drawerLeft.setAttribute('aria-hidden', 'true'); } if (drawerRight) { drawerRight.classList.remove('open'); drawerRight.setAttribute('aria-hidden', 'true'); } if (scrim) scrim.classList.remove('show'); document.body.classList.remove('drawer-open'); } // Hamburger opens left drawer const hamburger = document.getElementById('hamburger-btn'); if (hamburger) { hamburger.addEventListener('click', () => openDrawer('left')); } // Close buttons document.querySelectorAll('[data-close-drawer]').forEach(b => { b.addEventListener('click', closeDrawers); }); // Scrim click closes if (scrim) scrim.addEventListener('click', closeDrawers); // ESC key closes document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeDrawers(); }); // ===================== RENDER DRAWER LEFT (clone leftbar) ===================== function renderDrawerLeft() { const src = document.querySelector('.leftbar'); const dst = document.getElementById('drawer-left-body'); if (!src || !dst) return; // Clone leftbar body (skip header which has toggle) const clone = src.cloneNode(true); // Remove the inner toggle button (drawer has its own close) const innerToggle = clone.querySelector('.leftbar-toggle'); if (innerToggle) innerToggle.remove(); // Remove header (drawer has its own header) const header = clone.querySelector('.leftbar-header'); if (header) header.remove(); // Remove footer (not needed in drawer) const footer = clone.querySelector('.leftbar-footer'); if (footer) footer.remove(); // Mark as drawer version + force display (the original .leftbar is hidden on mobile) clone.classList.add('drawer-content-left'); clone.removeAttribute('id'); clone.style.cssText = 'display: flex; flex-direction: column; flex: 1; min-height: 0; border: none; background: transparent;'; dst.innerHTML = ''; dst.appendChild(clone); // Wire up tool buttons inside drawer dst.querySelectorAll('.tool-btn').forEach(b => { b.addEventListener('click', () => { document.querySelectorAll('.tool-btn').forEach(x => x.classList.remove('active')); b.classList.add('active'); // Close drawer on tool selection (mobile UX) setTimeout(() => closeDrawers(), 200); }); }); } // ===================== RENDER DRAWER RIGHT (clone rightbar panel) ===================== function renderDrawerRight(panel) { const src = document.querySelector(`[data-panel-content="${panel}"]`); const dst = document.getElementById('drawer-right-body'); const titleEl = document.getElementById('drawer-right-title'); if (!src || !dst) return; const labels = { tool: 'Werkzeug', layer: 'Layer', library: 'Bibliothek', ki: 'KI Copilot' }; if (titleEl) titleEl.textContent = labels[panel] || 'Panel'; dst.innerHTML = ''; const clone = src.cloneNode(true); clone.classList.add('active'); clone.style.cssText = 'display: block; width: 100%;'; dst.appendChild(clone); // Re-wire KI chips and library categories inside drawer dst.querySelectorAll('.ki-chip').forEach(chip => { chip.addEventListener('click', () => { const input = dst.querySelector('.ki-input'); if (input) { input.value = chip.textContent.trim(); input.focus(); } }); }); dst.querySelectorAll('.lib-cat').forEach(cat => { cat.addEventListener('click', () => { dst.querySelectorAll('.lib-cat').forEach(c => c.classList.remove('active')); cat.classList.add('active'); }); }); } // ===================== RIBBON TAB SWITCHING ===================== document.querySelectorAll('.ribbon-tab').forEach(tab => { tab.addEventListener('click', () => { document.querySelectorAll('.ribbon-tab').forEach(t => { t.classList.remove('active'); t.setAttribute('aria-selected', 'false'); }); tab.classList.add('active'); tab.setAttribute('aria-selected', 'true'); }); }); // ===================== TOOL SELECTION (desktop) ===================== document.querySelectorAll('.leftbar .tool-btn').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('.leftbar .tool-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); }); }); // ===================== LEFTBAR TOGGLE (desktop) ===================== const leftToggle = document.getElementById('leftbar-toggle'); if (leftToggle) { leftToggle.addEventListener('click', () => { const main = document.querySelector('.main'); if (main) main.classList.toggle('left-collapsed'); }); } // ===================== CANVAS VIEW TABS ===================== document.querySelectorAll('.canvas-view-tab').forEach(tab => { tab.addEventListener('click', () => { document.querySelectorAll('.canvas-view-tab').forEach(t => t.classList.remove('active')); tab.classList.add('active'); }); }); // ===================== STATUS BAR TOGGLES ===================== document.querySelectorAll('.status-item').forEach(item => { item.addEventListener('click', () => item.classList.toggle('active')); }); // ===================== CANVAS TOOLBAR (grid, ortho, snap) ===================== ['btn-grid', 'btn-ortho', 'btn-snap'].forEach(id => { const b = document.getElementById(id); if (b) { b.addEventListener('click', () => { b.classList.toggle('active'); const pressed = b.classList.contains('active'); b.setAttribute('aria-pressed', pressed ? 'true' : 'false'); // Also toggle matching status bar item const map = { 'btn-grid': 'GRID', 'btn-ortho': 'ORTHO', 'btn-snap': 'SNAP' }; const label = map[id]; if (label) { document.querySelectorAll('.status-item').forEach(s => { if (s.textContent.trim().startsWith(label)) s.classList.toggle('active', pressed); }); } }); } }); // ===================== ZOOM ===================== let zoom = 100; const zoomDisplay = document.getElementById('zoom-display'); document.getElementById('btn-zoom-in')?.addEventListener('click', () => { zoom = Math.min(400, zoom + 25); if (zoomDisplay) zoomDisplay.textContent = zoom + '%'; }); document.getElementById('btn-zoom-out')?.addEventListener('click', () => { zoom = Math.max(10, zoom - 25); if (zoomDisplay) zoomDisplay.textContent = zoom + '%'; }); document.getElementById('btn-zoom-fit')?.addEventListener('click', () => { zoom = 100; if (zoomDisplay) zoomDisplay.textContent = zoom + '%'; }); // ===================== LIBRARY CATEGORIES (desktop) ===================== document.querySelectorAll('.rightbar .lib-cat').forEach(cat => { cat.addEventListener('click', () => { document.querySelectorAll('.rightbar .lib-cat').forEach(c => c.classList.remove('active')); cat.classList.add('active'); }); }); // ===================== KI CHIPS (desktop) ===================== document.querySelectorAll('.rightbar .ki-chip').forEach(chip => { chip.addEventListener('click', () => { const input = document.querySelector('.rightbar .ki-input'); if (input) { input.value = chip.textContent.trim(); input.focus(); } }); }); // ===================== KEYBOARD SHORTCUTS ===================== const toolMap = { v: 'select', l: 'line', c: 'circle', m: 'move', p: 'pan', z: 'zoom-win', e: 'erase' }; document.addEventListener('keydown', (e) => { if (e.target.matches('input, textarea, select')) return; if (e.key === 'Escape') return; // handled by drawer const k = e.key.toLowerCase(); if (toolMap[k]) { const btn = document.querySelector(`.tool-btn[data-tool="${toolMap[k]}"]`); if (btn) btn.click(); } }); })(); // ===================== TREE COMPONENT TOGGLE ===================== (function initTrees() { // Delegate click on [data-tree-toggle] rows (folder rows) document.addEventListener('click', (e) => { const toggleBtn = e.target.closest('.tree-toggle'); if (toggleBtn) { const row = toggleBtn.closest('[data-tree-toggle]'); if (!row) return; e.stopPropagation(); const node = row.parentElement; toggleTreeNode(node); return; } // Click anywhere on folder row also toggles (except the count which is passive) const folderRow = e.target.closest('[data-tree-toggle]'); if (folderRow && !e.target.closest('.tree-count')) { const node = folderRow.parentElement; toggleTreeNode(node); } }); // Leaf selection (single-select within siblings) document.addEventListener('click', (e) => { const leaf = e.target.closest('[data-tree-select]'); if (!leaf) return; e.stopPropagation(); // Remove .active from siblings within same parent tree const tree = leaf.closest('.tree'); if (tree) { tree.querySelectorAll('[data-tree-select].active').forEach(el => el.classList.remove('active')); } leaf.classList.add('active'); }); function toggleTreeNode(node) { if (!node || !node.classList.contains('tree-node')) return; const expanded = node.getAttribute('aria-expanded') === 'true'; node.setAttribute('aria-expanded', expanded ? 'false' : 'true'); const children = node.querySelector(':scope > .tree-children'); if (children) { if (expanded) { children.setAttribute('hidden', ''); } else { children.removeAttribute('hidden'); } } } // Drag-and-drop for library leafs (visual only - mockup) document.addEventListener('dragstart', (e) => { const draggable = e.target.closest('.lib-draggable'); if (!draggable) return; draggable.classList.add('dragging'); const name = draggable.querySelector('.tree-name')?.textContent || 'Block'; e.dataTransfer.setData('text/plain', name); e.dataTransfer.effectAllowed = 'copy'; }); document.addEventListener('dragend', (e) => { const draggable = e.target.closest('.lib-draggable'); if (draggable) draggable.classList.remove('dragging'); }); })();