FEATURE Improving the mobile menu behaviour

This commit is contained in:
Nonimart 2025-09-04 16:11:08 +02:00
parent 2c1dabec6e
commit d1610f4281

View File

@ -1,61 +1,140 @@
export default function menuInit() { // Déclaration pour GSAP si disponible
let main_navigation = declare var gsap: any;
document.querySelector('#primary-menu');
const header = document.querySelector('#primary-header');
const primary_menu =
header.querySelector('#primary-menu');
const burgerMenuToggle = header.querySelector(
'#burger-menu-toggle'
);
const submenuToggles = primary_menu.querySelectorAll(
'.menu-item-submenu-toggle'
);
// #### Open/close burger nav class MobileMenu {
burgerMenuToggle.addEventListener('click', function (e) { 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(); e.preventDefault();
header.classList.toggle('nav-open'); this.toggleMobileMenu();
burgerMenuToggle.toggleAttribute('aria-expanded'); }
gsap.from(primary_menu, {
opacity: 20,
y: '-100vh',
duration: 0.5,
ease: Power4.easeOut,
});
});
// #### Close nav when reaching the end of the menu with tab /**
document.addEventListener( * Gère la fermeture du menu lors du focus en dehors
'focusin', */
(e) => { private handleFocusOut(e: Event): void {
const header = document.querySelector( if (!this.header || !this.isMenuOpen()) return;
'#primary-header'
);
if (
header.classList.contains('nav-open') &&
!header.contains(document.activeElement)
) {
header.classList.remove('nav-open');
burgerMenuToggle.setAttribute(
'aria-expanded',
false
);
burgerMenuToggle.focus(); const target = e.target as Element;
} if (!this.header.contains(target)) {
}, this.closeMobileMenu();
true this.mobileMenuToggle?.focus();
); }
}
submenuToggles.forEach((button) => { /**
button.addEventListener('click', function (e) { * Gère le toggle des sous-menus
let isExpanded = */
button.getAttribute('aria-expanded') === 'true'; private handleSubmenuToggle(e: Event, button: Element): void {
button.setAttribute('aria-expanded', !isExpanded); e.preventDefault();
const isExpanded = button.getAttribute('aria-expanded') === 'true';
button.setAttribute('aria-expanded', String(!isExpanded));
button.parentElement const submenu = button.parentElement?.querySelector('.sub-menu');
.querySelector('.sub-menu') submenu?.classList.toggle('sub-menu-open');
.classList.toggle('sub-menu-open'); }
}); }
});
export default function menuInit(): MobileMenu {
return new MobileMenu();
} }