((Drupal) => {
/**
* Checks if navWrapper contains "is-active" class.
* @param {object} 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);
if (value) {
props.body.classList.add('js-overlay-active');
props.body.classList.add('js-fixed');
props.navWrapper.classList.add('is-active');
} else {
props.body.classList.remove('js-overlay-active');
props.body.classList.remove('js-fixed');
props.navWrapper.classList.remove('is-active');
}
}
/**
* Init function for 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));
});
// Closes any open sub navigation first, then close header navigation.
document.addEventListener('keyup', (e) => {
if (e.key === 'Escape') {
if (props.olivero.areAnySubNavsOpen()) {
props.olivero.closeAllSubNav();
} else {
toggleNav(props, false);
}
}
});
props.overlay.addEventListener('click', () => {
toggleNav(props, false);
});
props.overlay.addEventListener('touchstart', () => {
toggleNav(props, false);
});
// Focus trap.
props.navWrapper.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey) {
if (
document.activeElement === props.firstFocusableEl &&
!props.olivero.isDesktopNav()
) {
props.navButton.focus();
e.preventDefault();
}
} else if (
document.activeElement === props.lastFocusableEl &&
!props.olivero.isDesktopNav()
) {
props.navButton.focus();
e.preventDefault();
}
}
});
// Remove overlays when browser is resized and desktop nav appears.
// @todo Use core/drupal.debounce library to throttle when we move into theming.
window.addEventListener('resize', () => {
if (props.olivero.isDesktopNav()) {
toggleNav(props, false);
props.body.classList.remove('js-overlay-active');
props.body.classList.remove('js-fixed');
}
});
}
/**
* Initialize the navigation JS.
*/
Drupal.behaviors.oliveroNavigation = {
attach(context, settings) {
const navWrapperId = 'header-nav';
const navWrapper = context.querySelector(
`#${navWrapperId}:not(.${navWrapperId}-processed)`,
);
if (navWrapper) {
navWrapper.classList.add(`${navWrapperId}-processed`);
const { olivero } = Drupal;
const navButton = context.querySelector('.mobile-nav-button');
const body = context.querySelector('body');
const overlay = context.querySelector('.overlay');
const focusableNavElements = navWrapper.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
);
const firstFocusableEl = focusableNavElements[0];
const lastFocusableEl =
focusableNavElements[focusableNavElements.length - 1];
init({
settings,
olivero,
navWrapperId,
navWrapper,
navButton,
body,
overlay,
firstFocusableEl,
lastFocusableEl,
});
}
},
};
})(Drupal);