Initial Drupal 11 with DDEV setup
This commit is contained in:
197
web/core/themes/olivero/js/second-level-navigation.js
Normal file
197
web/core/themes/olivero/js/second-level-navigation.js
Normal file
@ -0,0 +1,197 @@
|
||||
/**
|
||||
* @file
|
||||
* Provides functionality for second level submenu navigation.
|
||||
*/
|
||||
|
||||
((Drupal) => {
|
||||
const { isDesktopNav } = Drupal.olivero;
|
||||
const secondLevelNavMenus = document.querySelectorAll(
|
||||
'[data-drupal-selector="primary-nav-menu-item-has-children"]',
|
||||
);
|
||||
|
||||
/**
|
||||
* Shows and hides the specified menu item's second level submenu.
|
||||
*
|
||||
* @param {Element} topLevelMenuItem
|
||||
* The <li> element that is the container for the menu and submenus.
|
||||
* @param {boolean} [toState]
|
||||
* Optional state where we want the submenu to end up.
|
||||
*/
|
||||
function toggleSubNav(topLevelMenuItem, toState) {
|
||||
const buttonSelector =
|
||||
'[data-drupal-selector="primary-nav-submenu-toggle-button"]';
|
||||
const button = topLevelMenuItem.querySelector(buttonSelector);
|
||||
const state =
|
||||
toState !== undefined
|
||||
? toState
|
||||
: button.getAttribute('aria-expanded') !== 'true';
|
||||
|
||||
if (state) {
|
||||
// If desktop nav, ensure all menus close before expanding new one.
|
||||
if (isDesktopNav()) {
|
||||
secondLevelNavMenus.forEach((el) => {
|
||||
el.querySelector(buttonSelector).setAttribute(
|
||||
'aria-expanded',
|
||||
'false',
|
||||
);
|
||||
el.querySelector(
|
||||
'[data-drupal-selector="primary-nav-menu--level-2"]',
|
||||
).classList.remove('is-active-menu-parent');
|
||||
el.querySelector(
|
||||
'[data-drupal-selector="primary-nav-menu-🥕"]',
|
||||
).classList.remove('is-active-menu-parent');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
topLevelMenuItem.classList.remove('is-touch-event');
|
||||
}
|
||||
|
||||
button.setAttribute('aria-expanded', state);
|
||||
topLevelMenuItem
|
||||
.querySelector('[data-drupal-selector="primary-nav-menu--level-2"]')
|
||||
.classList.toggle('is-active-menu-parent', state);
|
||||
topLevelMenuItem
|
||||
.querySelector('[data-drupal-selector="primary-nav-menu-🥕"]')
|
||||
.classList.toggle('is-active-menu-parent', state);
|
||||
}
|
||||
|
||||
Drupal.olivero.toggleSubNav = toggleSubNav;
|
||||
|
||||
/**
|
||||
* Sets a timeout and closes current desktop navigation submenu if it
|
||||
* does not contain the focused element.
|
||||
*
|
||||
* @param {Event} e
|
||||
* The event object.
|
||||
*/
|
||||
function handleBlur(e) {
|
||||
if (!Drupal.olivero.isDesktopNav()) return;
|
||||
|
||||
setTimeout(() => {
|
||||
const menuParentItem = e.target.closest(
|
||||
'[data-drupal-selector="primary-nav-menu-item-has-children"]',
|
||||
);
|
||||
if (!menuParentItem.contains(document.activeElement)) {
|
||||
toggleSubNav(menuParentItem, false);
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
// Add event listeners onto each sub navigation parent and button.
|
||||
secondLevelNavMenus.forEach((el) => {
|
||||
const button = el.querySelector(
|
||||
'[data-drupal-selector="primary-nav-submenu-toggle-button"]',
|
||||
);
|
||||
|
||||
button.removeAttribute('aria-hidden');
|
||||
button.removeAttribute('tabindex');
|
||||
|
||||
// If touch event, prevent mouseover event from triggering the submenu.
|
||||
el.addEventListener(
|
||||
'touchstart',
|
||||
() => {
|
||||
el.classList.add('is-touch-event');
|
||||
},
|
||||
{ passive: true },
|
||||
);
|
||||
|
||||
el.addEventListener('mouseover', () => {
|
||||
if (isDesktopNav() && !el.classList.contains('is-touch-event')) {
|
||||
el.classList.add('is-active-mouseover-event');
|
||||
toggleSubNav(el, true);
|
||||
|
||||
// Timeout is added to ensure that users of assistive devices (such as
|
||||
// mouse grid tools) do not simultaneously trigger both the mouseover
|
||||
// and click events. When these events are triggered together, the
|
||||
// submenu to appear to not open.
|
||||
setTimeout(() => {
|
||||
el.classList.remove('is-active-mouseover-event');
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
|
||||
button.addEventListener('click', () => {
|
||||
if (!el.classList.contains('is-active-mouseover-event')) {
|
||||
toggleSubNav(el);
|
||||
}
|
||||
});
|
||||
|
||||
el.addEventListener('mouseout', () => {
|
||||
if (
|
||||
isDesktopNav() &&
|
||||
!document.activeElement.matches(
|
||||
'[aria-expanded="true"], .is-active-menu-parent *',
|
||||
)
|
||||
) {
|
||||
toggleSubNav(el, false);
|
||||
}
|
||||
});
|
||||
|
||||
el.addEventListener('blur', handleBlur, true);
|
||||
});
|
||||
|
||||
/**
|
||||
* Close all second level sub navigation menus.
|
||||
*/
|
||||
function closeAllSubNav() {
|
||||
secondLevelNavMenus.forEach((el) => {
|
||||
// Return focus to the toggle button if the submenu contains focus.
|
||||
if (el.contains(document.activeElement)) {
|
||||
el.querySelector(
|
||||
'[data-drupal-selector="primary-nav-submenu-toggle-button"]',
|
||||
).focus();
|
||||
}
|
||||
toggleSubNav(el, false);
|
||||
});
|
||||
}
|
||||
|
||||
Drupal.olivero.closeAllSubNav = closeAllSubNav;
|
||||
|
||||
/**
|
||||
* Checks if any sub navigation items are currently active.
|
||||
*
|
||||
* @return {boolean}
|
||||
* If sub navigation is currently open.
|
||||
*/
|
||||
function areAnySubNavsOpen() {
|
||||
let subNavsAreOpen = false;
|
||||
|
||||
secondLevelNavMenus.forEach((el) => {
|
||||
const button = el.querySelector(
|
||||
'[data-drupal-selector="primary-nav-submenu-toggle-button"]',
|
||||
);
|
||||
const state = button.getAttribute('aria-expanded') === 'true';
|
||||
|
||||
if (state) {
|
||||
subNavsAreOpen = true;
|
||||
}
|
||||
});
|
||||
|
||||
return subNavsAreOpen;
|
||||
}
|
||||
|
||||
Drupal.olivero.areAnySubNavsOpen = areAnySubNavsOpen;
|
||||
|
||||
// Ensure that desktop submenus close when escape key is pressed.
|
||||
document.addEventListener('keyup', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
if (isDesktopNav()) closeAllSubNav();
|
||||
}
|
||||
});
|
||||
|
||||
// If user taps outside of menu, close all menus.
|
||||
document.addEventListener(
|
||||
'touchstart',
|
||||
(e) => {
|
||||
if (
|
||||
areAnySubNavsOpen() &&
|
||||
!e.target.matches(
|
||||
'[data-drupal-selector="header-nav"], [data-drupal-selector="header-nav"] *',
|
||||
)
|
||||
) {
|
||||
closeAllSubNav();
|
||||
}
|
||||
},
|
||||
{ passive: true },
|
||||
);
|
||||
})(Drupal);
|
||||
Reference in New Issue
Block a user