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