/** * Navigation functionality * Provides mobile sidebar, active link highlighting, and navigation utilities */ export interface NavigationOptions { navSelector?: string; linkSelector?: string; activeClass?: string; openClass?: string; closeOnLinkClick?: boolean; closeOnBackdropClick?: boolean; } /** * Initialize navigation functionality * @param options - Configuration options */ export function initNavigation(options: NavigationOptions = {}): void { const defaults: NavigationOptions = { navSelector: '[data-nav]', linkSelector: 'a:not(.dropdown-trigger)', activeClass: 'active', openClass: 'nav-open', closeOnLinkClick: true, closeOnBackdropClick: true, }; const config = { ...defaults, ...options }; const navs = document.querySelectorAll(config.navSelector!); navs.forEach((nav) => { if (nav.hasAttribute('data-nav-initialized')) return; nav.setAttribute('data-nav-initialized', 'true'); // Close on backdrop click if (config.closeOnBackdropClick) { document.addEventListener('click', (e) => { if (nav.classList.contains(config.openClass!)) { if (e.target === nav || (e.target as HTMLElement).matches('.nav-backdrop')) { closeNavigation(nav as HTMLElement, config.openClass!); } } }); } // Handle link clicks if (config.closeOnLinkClick) { const links = nav.querySelectorAll(config.linkSelector!); links.forEach((link) => { link.addEventListener('click', () => { closeNavigation(nav as HTMLElement, config.openClass!); }); }); } // Highlight active page highlightActivePage(nav as HTMLElement, config); }); } /** * Open navigation */ function openNavigation(nav: HTMLElement, openClass: string): void { nav.classList.add(openClass); nav.setAttribute('data-state', 'open'); } /** * Close navigation */ function closeNavigation(nav: HTMLElement, openClass: string): void { nav.classList.remove(openClass); nav.setAttribute('data-state', 'closed'); } /** * Toggle navigation open/close state */ function toggleNavigation(nav: HTMLElement, openClass: string): void { if (nav.classList.contains(openClass)) { closeNavigation(nav, openClass); } else { openNavigation(nav, openClass); } } /** * Highlight active page link */ function highlightActivePage(nav: HTMLElement, config: NavigationOptions): void { const currentPage = window.location.pathname.split('/').pop() || 'index.html'; const links = nav.querySelectorAll('a:not(.dropdown-trigger)'); links.forEach((link) => { const href = link.getAttribute('href'); if (href === currentPage || href === `./${currentPage}`) { link.classList.add(config.activeClass!); } else { link.classList.remove(config.activeClass!); } }); } /** * Create navigation API */ export function createNavigation() { return { /** * Initialize navigation systems */ init: (options?: NavigationOptions) => { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { initNavigation(options); }); } else { initNavigation(options); } }, /** * Open a specific navigation */ open: (selector: string) => { const nav = document.querySelector(selector) as HTMLElement; if (nav) { openNavigation(nav, 'nav-open'); } }, /** * Close a specific navigation */ close: (selector: string) => { const nav = document.querySelector(selector) as HTMLElement; if (nav) { closeNavigation(nav, 'nav-open'); } }, /** * Toggle a specific navigation */ toggle: (selector: string) => { const nav = document.querySelector(selector) as HTMLElement; if (nav) { toggleNavigation(nav, 'nav-open'); } }, /** * Refresh active page highlighting */ refreshActive: (navSelector?: string) => { const navs = document.querySelectorAll(navSelector || '[data-nav]'); navs.forEach((nav) => { highlightActivePage(nav as HTMLElement, { activeClass: 'active' }); }); }, }; }