/** * admin-ui/js/core/navigation.js * Manages active state and keyboard navigation for flat navigation structure. */ class NavigationManager { constructor(navElement) { if (!navElement) return; this.nav = navElement; this.items = Array.from(this.nav.querySelectorAll('.nav-item')); this.init(); } init() { this.bindEvents(); this.updateActiveState(); } bindEvents() { // Listen for navigation changes window.addEventListener('hashchange', this.updateActiveState.bind(this)); window.addEventListener('app-navigate', this.updateActiveState.bind(this)); // Handle keyboard navigation this.nav.addEventListener('keydown', this.onKeyDown.bind(this)); // Handle manual nav item clicks this.items.forEach(item => { item.addEventListener('click', this.onItemClick.bind(this)); }); } onItemClick(event) { const item = event.currentTarget; const page = item.dataset.page; if (page) { window.location.hash = `#${page}`; this.updateActiveState(); } } updateActiveState() { const currentPage = window.location.hash.substring(1) || 'dashboard'; this.items.forEach(item => { const itemPage = item.dataset.page; const isActive = itemPage === currentPage; item.classList.toggle('active', isActive); // Update aria-current for accessibility if (isActive) { item.setAttribute('aria-current', 'page'); } else { item.removeAttribute('aria-current'); } }); } onKeyDown(event) { const activeElement = document.activeElement; if (!this.nav.contains(activeElement)) return; const visibleItems = this.items.filter(el => el.offsetParent !== null); const currentIndex = visibleItems.indexOf(activeElement); switch (event.key) { case 'ArrowDown': event.preventDefault(); if (currentIndex < visibleItems.length - 1) { visibleItems[currentIndex + 1].focus(); } break; case 'ArrowUp': event.preventDefault(); if (currentIndex > 0) { visibleItems[currentIndex - 1].focus(); } break; case 'Enter': case ' ': event.preventDefault(); activeElement.click(); break; case 'Tab': // Allow default Tab behavior (move to next focusable element) break; } } } export default NavigationManager;