|
class NavigationHandler extends UIHandler {
|
constructor() {
|
super();
|
|
this.initializeHandlers({
|
menuToggle: {
|
forEach: true,
|
click: (e) => this.toggleMenu(e)
|
},
|
submenuToggles: {
|
forEach: true,
|
handler: (toggle) => {
|
toggle._boundHandler = this.toggleSubmenu.bind(this);
|
toggle.addEventListener('click', toggle._boundHandler);
|
},
|
click: (e) => this.toggleSubmenu(e)
|
}
|
});
|
|
this.bindElements();
|
this.bindEvents();
|
}
|
|
bindElements() {
|
this.elements = {
|
mobileNav: document.querySelectorAll('nav.mobile, nav:has(.has-submenu)'),
|
menuToggle: document.querySelectorAll('button.toggle.main'),
|
submenus: document.querySelectorAll('.has-submenu'),
|
submenuToggles: document.querySelectorAll('button.toggle:not(.main)')
|
};
|
|
}
|
|
bindEvents() {
|
super.bindEvents();
|
this.bindComponentEvents();
|
}
|
|
toggleMenu(event) {
|
event?.preventDefault();
|
|
const { mobileNav, menuToggle } = this.elements;
|
const isExpanded = !this.isComponentActive('menu');
|
const firstLink = event.target.closest('nav').querySelector('a');
|
|
this.setComponentState('menu', isExpanded, {
|
element: event.target.closest('nav'),
|
toggle: event.target.closest('.toggle'),
|
focusElement: isExpanded ? firstLink : menuToggle,
|
cleanup: () => {
|
// Close any open submenus when closing menu
|
if (!isExpanded) {
|
this.elements.submenus?.forEach(submenu => {
|
if (this.isComponentActive(submenu.id || 'submenu')) {
|
this.closeSubmenu(submenu);
|
}
|
});
|
}
|
}
|
});
|
}
|
|
toggleSubmenu(event) {
|
event?.preventDefault();
|
const toggle = event.currentTarget;
|
const submenuParent = toggle.closest('.has-submenu');
|
const submenuId = submenuParent.id || 'submenu';
|
const isExpanded = !this.isComponentActive(submenuId);
|
const firstSubmenuLink = submenuParent?.querySelector('.submenu a');
|
|
this.setComponentState(submenuId, isExpanded, {
|
element: submenuParent,
|
toggle: toggle,
|
focusElement: isExpanded ? firstSubmenuLink : toggle
|
});
|
}
|
|
closeMenu(mobileNav) {
|
let menuToggle = mobileNav.querySelector('.toggle.main');
|
// Close main menu
|
this.setComponentState('menu', false, {
|
element: mobileNav,
|
toggle: menuToggle,
|
focusElement: menuToggle
|
});
|
let submenus = mobileNav.querySelectorAll('.has-submenu');
|
// Close any open submenus
|
submenus?.forEach(submenu => {
|
const toggle = submenu.querySelector('button.toggle');
|
const submenuId = submenu.id || 'submenu';
|
if (this.isComponentActive(submenuId)) {
|
this.setComponentState(submenuId, false, {
|
element: submenu,
|
toggle: toggle
|
});
|
}
|
});
|
}
|
|
closeSubmenu(submenu) {
|
const toggle = submenu.querySelector('button.toggle');
|
const submenuId = submenu.id || 'submenu';
|
|
this.setComponentState(submenuId, false, {
|
element: submenu,
|
toggle: toggle,
|
focusElement: toggle
|
});
|
}
|
|
handleOutsideClick(event) {
|
if (!event.target.closest('nav')) {
|
this.closeOpenItems();
|
}
|
}
|
|
handleEscapeKey(event) {
|
if (event.key !== 'Escape') return;
|
this.closeOpenItems();
|
|
event.preventDefault();
|
}
|
|
closeOpenItems(){
|
const { mobileNav, submenus } = this.elements;
|
const openSubmenu = Array.from(submenus || [])
|
.find(submenu => submenu.classList.contains('open'));
|
|
const openMenu = Array.from(mobileNav || [])
|
.find(menu => menu.classList.contains('open'));
|
|
if (openSubmenu) {
|
this.closeSubmenu(openSubmenu);
|
}
|
if (openMenu) {
|
this.closeMenu(openMenu);
|
}
|
}
|
|
// Add cleanup method
|
cleanup() {
|
super.cleanup();
|
this.cleanupComponentEvents();
|
}
|
}
|
|
|
// Initialize on DOM load
|
document.addEventListener('DOMContentLoaded', () => {
|
const navigation = new NavigationHandler();
|
});
|