carhop__dynamiques-theme__P.../resources/js/header.ts
2025-09-04 16:11:08 +02:00

141 lines
4.0 KiB
TypeScript

// Déclaration pour GSAP si disponible
declare var gsap: any;
class MobileMenu {
private mobileMenu: HTMLElement | null;
private mobileMenuToggle: HTMLElement | null;
private header: HTMLElement | null;
private primaryMenu: HTMLElement | null;
private submenuToggles: NodeListOf<Element>;
constructor() {
this.mobileMenu = document.querySelector('#menu-mobile-brand');
this.header = document.querySelector('#primary-header');
this.primaryMenu = this.header?.querySelector('#primary-menu-nav') || null;
this.mobileMenuToggle = this.mobileMenu?.querySelector('#mobile-menu-toggle') || null;
this.submenuToggles =
this.primaryMenu?.querySelectorAll('.menu-item-submenu-toggle') || ([] as any);
this.init();
}
/**
* Initialise le menu mobile et ses événements
*/
private init(): void {
if (!this.mobileMenu || !this.header || !this.mobileMenuToggle || !this.primaryMenu) {
console.warn('Éléments du menu mobile introuvables');
return;
}
this.bindEvents();
}
/**
* Lie tous les événements du menu mobile
*/
private bindEvents(): void {
// Événement pour le toggle du menu mobile
this.mobileMenuToggle?.addEventListener('click', (e) => this.handleToggleClick(e));
// Événement pour fermer le menu avec Tab
document.addEventListener('focusin', (e) => this.handleFocusOut(e), true);
// Événements pour les sous-menus
this.submenuToggles.forEach((button) => {
button.addEventListener('click', (e) => this.handleSubmenuToggle(e, button));
});
}
/**
* Ouvre le menu mobile
*/
public openMobileMenu(): void {
if (!this.mobileMenu || !this.mobileMenuToggle || !this.header) return;
this.mobileMenu.setAttribute('nav-open', 'true');
this.header.setAttribute('nav-open', 'true');
this.mobileMenuToggle.setAttribute('aria-expanded', 'true');
// // Animation GSAP si disponible
// if (typeof gsap !== 'undefined' && this.primaryMenu) {
// gsap.from(this.primaryMenu, {
// opacity: 0,
// y: '-100vh',
// duration: 0.5,
// ease: 'power4.out',
// });
// }
}
/**
* Ferme le menu mobile
*/
public closeMobileMenu(): void {
if (!this.mobileMenu || !this.mobileMenuToggle || !this.header) return;
this.mobileMenu.setAttribute('nav-open', 'false');
this.header.setAttribute('nav-open', 'false');
this.mobileMenuToggle.setAttribute('aria-expanded', 'false');
}
/**
* Toggle l'état du menu mobile
*/
public toggleMobileMenu(): void {
if (!this.mobileMenu) return;
if (this.mobileMenu.getAttribute('nav-open') === 'true') {
this.closeMobileMenu();
this.mobileMenuToggle.textContent = 'Menu';
} else {
this.openMobileMenu();
this.mobileMenuToggle.textContent = 'Fermer';
}
}
/**
* Vérifie si le menu mobile est ouvert
*/
public isMenuOpen(): boolean {
return this.mobileMenu?.getAttribute('nav-open') === 'true' || false;
}
/**
* Gère le clic sur le bouton toggle
*/
private handleToggleClick(e: Event): void {
e.preventDefault();
this.toggleMobileMenu();
}
/**
* Gère la fermeture du menu lors du focus en dehors
*/
private handleFocusOut(e: Event): void {
if (!this.header || !this.isMenuOpen()) return;
const target = e.target as Element;
if (!this.header.contains(target)) {
this.closeMobileMenu();
this.mobileMenuToggle?.focus();
}
}
/**
* Gère le toggle des sous-menus
*/
private handleSubmenuToggle(e: Event, button: Element): void {
e.preventDefault();
const isExpanded = button.getAttribute('aria-expanded') === 'true';
button.setAttribute('aria-expanded', String(!isExpanded));
const submenu = button.parentElement?.querySelector('.sub-menu');
submenu?.classList.toggle('sub-menu-open');
}
}
export default function menuInit(): MobileMenu {
return new MobileMenu();
}