128 lines
4.2 KiB
JavaScript
128 lines
4.2 KiB
JavaScript
/**
|
|
* Mobile Menu - Hamburger Navigation
|
|
* Handles mobile sidebar drawer with smooth animations
|
|
*/
|
|
|
|
export function initMobileMenu() {
|
|
// Only initialize on mobile/tablet
|
|
if (window.innerWidth >= 1024) return;
|
|
|
|
// Create hamburger button
|
|
const hamburger = createHamburgerButton();
|
|
|
|
// Place hamburger inside header/topbar actions
|
|
const headerActions = document.querySelector('.header-actions');
|
|
if (headerActions) {
|
|
headerActions.appendChild(hamburger);
|
|
} else {
|
|
// Fallback: add to body if header not found
|
|
document.body.appendChild(hamburger);
|
|
}
|
|
|
|
// Get sidebar
|
|
const sidebar = document.querySelector('.sidebar');
|
|
if (!sidebar) return;
|
|
// Accessibility roles
|
|
sidebar.setAttribute('role', 'navigation');
|
|
sidebar.setAttribute('aria-label', 'Menu principal');
|
|
|
|
// Toggle menu function
|
|
const toggleMenu = () => {
|
|
const isOpen = hamburger.classList.contains('open');
|
|
|
|
hamburger.classList.toggle('open');
|
|
sidebar.classList.toggle('open');
|
|
|
|
// Accessibility
|
|
hamburger.setAttribute('aria-expanded', String(!isOpen));
|
|
sidebar.setAttribute('aria-hidden', String(isOpen));
|
|
};
|
|
|
|
// Event listeners
|
|
hamburger.addEventListener('click', toggleMenu);
|
|
// Close menu when clicking sidebar item
|
|
document.querySelectorAll('.sidebar-item').forEach(item => {
|
|
// Make items focusable for keyboard navigation
|
|
if (!item.hasAttribute('tabindex')) item.setAttribute('tabindex', '0');
|
|
item.addEventListener('click', () => {
|
|
if (window.innerWidth < 1024 && hamburger.classList.contains('open')) {
|
|
// Slide left then close
|
|
sidebar.classList.add('closing-left');
|
|
setTimeout(() => {
|
|
sidebar.classList.remove('closing-left');
|
|
toggleMenu();
|
|
}, 250);
|
|
}
|
|
});
|
|
// Keyboard activation closes menu
|
|
item.addEventListener('keydown', (e) => {
|
|
if ((e.key === 'Enter' || e.key === ' ') && window.innerWidth < 1024 && hamburger.classList.contains('open')) {
|
|
e.preventDefault();
|
|
sidebar.classList.add('closing-left');
|
|
setTimeout(() => {
|
|
sidebar.classList.remove('closing-left');
|
|
toggleMenu();
|
|
}, 250);
|
|
}
|
|
});
|
|
});
|
|
|
|
// Handle resize - close menu and cleanup on desktop
|
|
let resizeTimer;
|
|
window.addEventListener('resize', () => {
|
|
clearTimeout(resizeTimer);
|
|
resizeTimer = setTimeout(() => {
|
|
if (window.innerWidth >= 1024) {
|
|
// Desktop - cleanup mobile menu
|
|
if (hamburger.classList.contains('open')) {
|
|
toggleMenu();
|
|
}
|
|
hamburger.style.display = 'none';
|
|
} else {
|
|
// Mobile - show menu button
|
|
hamburger.style.display = 'flex';
|
|
}
|
|
}, 250);
|
|
});
|
|
|
|
// Keyboard navigation and focus management
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Escape' && hamburger.classList.contains('open')) {
|
|
toggleMenu();
|
|
hamburger.focus();
|
|
}
|
|
});
|
|
|
|
// Manage focus when opening
|
|
hamburger.addEventListener('click', () => {
|
|
if (hamburger.classList.contains('open')) {
|
|
const firstItem = sidebar.querySelector('.sidebar-item');
|
|
if (firstItem) firstItem.focus?.();
|
|
}
|
|
});
|
|
}
|
|
|
|
function createHamburgerButton() {
|
|
const button = document.createElement('button');
|
|
button.className = 'hamburger';
|
|
button.setAttribute('aria-label', 'Menu de navegação');
|
|
button.setAttribute('aria-expanded', 'false');
|
|
button.innerHTML = `
|
|
<div class="hamburger-icon">
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
</div>
|
|
`;
|
|
return button;
|
|
}
|
|
|
|
// Auto-initialize if loaded as module
|
|
if (typeof window !== 'undefined') {
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', initMobileMenu);
|
|
} else {
|
|
initMobileMenu();
|
|
}
|
|
}
|