megamenu_sdc-1.0.x-dev/components/menu_hamburger/menu_hamburger.js
components/menu_hamburger/menu_hamburger.js
(function(Drupal, once, tabbable) {
// Ensure Drupal.megamenu_sdc exists as an object with required methods
Drupal.megamenu_sdc = Drupal.megamenu_sdc || {
areAnySubNavsOpen: function() {
return false;
},
closeAllSubNav: function() {
// Default implementation
},
isDesktopNav: function() {
return document.body.classList.contains('menu-pane-desktop'); // Adjust this value based on your breakpoint
}
};
/**
* This file is largely lifted from Olivero.
*/
/**
* Checks if navWrapper contains "is-active" class.
*
* @param {Element} navWrapper
* Header navigation.
*
* @return {boolean}
* True if navWrapper contains "is-active" class, false if not.
*/
function isNavOpen(navWrapper) {
return navWrapper.classList.contains('is-active');
}
/**
* Opens or closes the header navigation.
*
* @param {object} props
* Navigation props.
* @param {boolean} state
* State which to transition the header navigation menu into.
*/
function toggleNav(props, state) {
const value = !!state;
props.navButton.setAttribute('aria-expanded', value);
props.body.classList.toggle('is-overlay-active', value);
// props.body.classList.toggle('is-fixed', value);
props.navWrapper.classList.toggle('is-active', value);
}
/**
* Initialize the header navigation.
*
* @param {object} props
* Navigation props.
*/
function init(props) {
props.navButton.setAttribute('aria-controls', props.navWrapperId);
props.navButton.setAttribute('aria-expanded', 'false');
props.navButton.addEventListener('click', () => {
toggleNav(props, !isNavOpen(props.navWrapper));
});
// Close any open sub-navigation first, then close the header navigation.
document.addEventListener('keyup', (e) => {
if (e.key === 'Escape') {
if (props.megamenu_sdc.areAnySubNavsOpen()) {
props.megamenu_sdc.closeAllSubNav();
} else {
toggleNav(props, false);
}
}
});
props.overlay.addEventListener('click', () => {
toggleNav(props, false);
});
props.overlay.addEventListener('touchstart', () => {
toggleNav(props, false);
});
// Focus trap. This is added to the header element because the navButton
// element is not a child element of the navWrapper element, and the keydown
// event would not fire if focus is on the navButton element.
props.header.addEventListener('keydown', (e) => {
if (e.key === 'Tab' && isNavOpen(props.navWrapper)) {
const tabbableNavElements = tabbable.tabbable(props.navWrapper);
tabbableNavElements.unshift(props.navButton);
const firstTabbableEl = tabbableNavElements[0];
const lastTabbableEl =
tabbableNavElements[tabbableNavElements.length - 1];
if (e.shiftKey) {
if (
document.activeElement === firstTabbableEl &&
!props.megamenu_sdc.isDesktopNav()
) {
lastTabbableEl.focus();
e.preventDefault();
}
} else if (
document.activeElement === lastTabbableEl &&
!props.megamenu_sdc.isDesktopNav()
) {
firstTabbableEl.focus();
e.preventDefault();
}
}
});
// Remove overlays when browser is resized and desktop nav appears.
window.addEventListener('resize', () => {
if (props.megamenu_sdc.isDesktopNav()) {
toggleNav(props, false);
props.body.classList.remove('is-overlay-active');
props.body.classList.remove('is-fixed');
}
// Ensure that all sub-navigation menus close when the browser is resized.
Drupal.megamenu_sdc.closeAllSubNav();
});
// If hyperlink links to an anchor in the current page, close the
// mobile menu after the click.
props.navWrapper.addEventListener('click', (e) => {
if (
e.target.matches(
`[href*="${window.location.pathname}#"], [href*="${window.location.pathname}#"] *, [href^="#"], [href^="#"] *`,
)
) {
toggleNav(props, false);
}
});
}
Drupal.behaviors.menuHamburger = {
attach(context) {
const headerId = 'header';
const header = once('navigation', `#${headerId}`, context).shift();
const navWrapperId = 'header-nav';
if (header) {
const navWrapper = header.querySelector(`#${navWrapperId}`);
// Get the megamenu_sdc object, with a fallback if it doesn't exist
const { megamenu_sdc } = Drupal;
const navButton = context.querySelector(
'[data-drupal-selector="mobile-nav-button"]',
);
const body = document.body;
const overlay = context.querySelector(
'[data-drupal-selector="header-nav-overlay"]',
);
init({
megamenu_sdc,
header,
navWrapperId,
navWrapper,
navButton,
body,
overlay,
});
}
},
};
})(Drupal, once, tabbable);
