228 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						|
 * @file
 | 
						|
 * Controls the visibility of desktop navigation.
 | 
						|
 *
 | 
						|
 * Shows and hides the desktop navigation based on scroll position and controls
 | 
						|
 * the functionality of the button that shows/hides the navigation.
 | 
						|
 */
 | 
						|
 | 
						|
/* eslint-disable no-inner-declarations */
 | 
						|
((Drupal) => {
 | 
						|
  /**
 | 
						|
   * Olivero helper functions.
 | 
						|
   *
 | 
						|
   * @namespace
 | 
						|
   */
 | 
						|
  Drupal.olivero = {};
 | 
						|
 | 
						|
  /**
 | 
						|
   * Checks if the mobile navigation button is visible.
 | 
						|
   *
 | 
						|
   * @return {boolean}
 | 
						|
   *   True if navButtons is hidden, false if not.
 | 
						|
   */
 | 
						|
  function isDesktopNav() {
 | 
						|
    const navButtons = document.querySelector(
 | 
						|
      '[data-drupal-selector="mobile-buttons"]',
 | 
						|
    );
 | 
						|
    return navButtons
 | 
						|
      ? window.getComputedStyle(navButtons).getPropertyValue('display') ===
 | 
						|
          'none'
 | 
						|
      : false;
 | 
						|
  }
 | 
						|
 | 
						|
  Drupal.olivero.isDesktopNav = isDesktopNav;
 | 
						|
 | 
						|
  const stickyHeaderToggleButton = document.querySelector(
 | 
						|
    '[data-drupal-selector="sticky-header-toggle"]',
 | 
						|
  );
 | 
						|
  const siteHeaderFixable = document.querySelector(
 | 
						|
    '[data-drupal-selector="site-header-fixable"]',
 | 
						|
  );
 | 
						|
 | 
						|
  /**
 | 
						|
   * Checks if the sticky header is enabled.
 | 
						|
   *
 | 
						|
   * @return {boolean}
 | 
						|
   *   True if sticky header is enabled, false if not.
 | 
						|
   */
 | 
						|
  function stickyHeaderIsEnabled() {
 | 
						|
    return stickyHeaderToggleButton.getAttribute('aria-checked') === 'true';
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Save the current sticky header expanded state to localStorage, and set
 | 
						|
   * it to expire after two weeks.
 | 
						|
   *
 | 
						|
   * @param {boolean} expandedState
 | 
						|
   *   Current state of the sticky header button.
 | 
						|
   */
 | 
						|
  function setStickyHeaderStorage(expandedState) {
 | 
						|
    if (!expandedState) {
 | 
						|
      localStorage.removeItem('Drupal.olivero.stickyHeaderState');
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const now = new Date();
 | 
						|
 | 
						|
    const item = {
 | 
						|
      value: expandedState,
 | 
						|
      expiry: now.getTime() + 20160000, // 2 weeks from now.
 | 
						|
    };
 | 
						|
    localStorage.setItem(
 | 
						|
      'Drupal.olivero.stickyHeaderState',
 | 
						|
      JSON.stringify(item),
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Update the expiration date if the sticky header expanded state is set.
 | 
						|
   *
 | 
						|
   * @param {boolean} expandedState
 | 
						|
   *   Current state of the sticky header button.
 | 
						|
   */
 | 
						|
  function updateStickyHeaderStorage(expandedState) {
 | 
						|
    const stickyHeaderState = localStorage.getItem(
 | 
						|
      'Drupal.olivero.stickyHeaderState',
 | 
						|
    );
 | 
						|
 | 
						|
    if (stickyHeaderState !== null) {
 | 
						|
      setStickyHeaderStorage(expandedState);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Toggle the state of the sticky header between always pinned and
 | 
						|
   * only pinned when scrolled to the top of the viewport.
 | 
						|
   *
 | 
						|
   * @param {boolean} pinnedState
 | 
						|
   *   State to change the sticky header to.
 | 
						|
   */
 | 
						|
  function toggleStickyHeaderState(pinnedState) {
 | 
						|
    if (isDesktopNav()) {
 | 
						|
      siteHeaderFixable.classList.toggle('is-expanded', pinnedState);
 | 
						|
      stickyHeaderToggleButton.setAttribute('aria-checked', pinnedState);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Return the sticky header's stored state from localStorage.
 | 
						|
   *
 | 
						|
   * @return {boolean}
 | 
						|
   *   Stored state of the sticky header.
 | 
						|
   */
 | 
						|
  function getStickyHeaderStorage() {
 | 
						|
    const stickyHeaderState = localStorage.getItem(
 | 
						|
      'Drupal.olivero.stickyHeaderState',
 | 
						|
    );
 | 
						|
 | 
						|
    if (!stickyHeaderState) return false;
 | 
						|
 | 
						|
    const item = JSON.parse(stickyHeaderState);
 | 
						|
    const now = new Date();
 | 
						|
 | 
						|
    // Compare the expiry time of the item with the current time.
 | 
						|
    if (now.getTime() > item.expiry) {
 | 
						|
      // If the item is expired, delete the item from storage and return null.
 | 
						|
      localStorage.removeItem('Drupal.olivero.stickyHeaderState');
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    return item.value;
 | 
						|
  }
 | 
						|
 | 
						|
  const fixableElements = document.querySelectorAll(
 | 
						|
    '[data-drupal-selector="site-header-fixable"], [data-drupal-selector="social-bar-inner"]',
 | 
						|
  );
 | 
						|
 | 
						|
  function toggleDesktopNavVisibility(entries) {
 | 
						|
    if (!isDesktopNav()) return;
 | 
						|
 | 
						|
    entries.forEach((entry) => {
 | 
						|
      // Firefox doesn't seem to support entry.isIntersecting properly,
 | 
						|
      // so we check the intersectionRatio.
 | 
						|
      fixableElements.forEach((el) =>
 | 
						|
        el.classList.toggle('is-fixed', entry.intersectionRatio < 1),
 | 
						|
      );
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Gets the root margin by checking for various toolbar classes.
 | 
						|
   *
 | 
						|
   * @return {string}
 | 
						|
   *   Root margin for the Intersection Observer options object.
 | 
						|
   */
 | 
						|
  function getRootMargin() {
 | 
						|
    let rootMarginTop = 72;
 | 
						|
    const { body } = document;
 | 
						|
 | 
						|
    if (body.classList.contains('toolbar-fixed')) {
 | 
						|
      rootMarginTop -= 39;
 | 
						|
    }
 | 
						|
 | 
						|
    if (
 | 
						|
      body.classList.contains('toolbar-horizontal') &&
 | 
						|
      body.classList.contains('toolbar-tray-open')
 | 
						|
    ) {
 | 
						|
      rootMarginTop -= 40;
 | 
						|
    }
 | 
						|
 | 
						|
    return `${rootMarginTop}px 0px 0px 0px`;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Monitor the navigation position.
 | 
						|
   */
 | 
						|
  function monitorNavPosition() {
 | 
						|
    const primaryNav = document.querySelector(
 | 
						|
      '[data-drupal-selector="site-header"]',
 | 
						|
    );
 | 
						|
    const options = {
 | 
						|
      rootMargin: getRootMargin(),
 | 
						|
      threshold: [0.999, 1],
 | 
						|
    };
 | 
						|
 | 
						|
    const observer = new IntersectionObserver(
 | 
						|
      toggleDesktopNavVisibility,
 | 
						|
      options,
 | 
						|
    );
 | 
						|
 | 
						|
    if (primaryNav) {
 | 
						|
      observer.observe(primaryNav);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (stickyHeaderToggleButton) {
 | 
						|
    stickyHeaderToggleButton.addEventListener('click', () => {
 | 
						|
      const pinnedState = !stickyHeaderIsEnabled();
 | 
						|
      toggleStickyHeaderState(pinnedState);
 | 
						|
      setStickyHeaderStorage(pinnedState);
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  // If header is pinned open and a header element gains focus, scroll to the
 | 
						|
  // top of the page to ensure that the header elements can be seen.
 | 
						|
  const siteHeaderInner = document.querySelector(
 | 
						|
    '[data-drupal-selector="site-header-inner"]',
 | 
						|
  );
 | 
						|
  if (siteHeaderInner) {
 | 
						|
    siteHeaderInner.addEventListener('focusin', () => {
 | 
						|
      if (isDesktopNav() && !stickyHeaderIsEnabled()) {
 | 
						|
        const header = document.querySelector(
 | 
						|
          '[data-drupal-selector="site-header"]',
 | 
						|
        );
 | 
						|
        const headerNav = header.querySelector(
 | 
						|
          '[data-drupal-selector="header-nav"]',
 | 
						|
        );
 | 
						|
        const headerMargin = header.clientHeight - headerNav.clientHeight;
 | 
						|
        if (window.scrollY > headerMargin) {
 | 
						|
          window.scrollTo(0, headerMargin);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  monitorNavPosition();
 | 
						|
  updateStickyHeaderStorage(getStickyHeaderStorage());
 | 
						|
  toggleStickyHeaderState(getStickyHeaderStorage());
 | 
						|
})(Drupal);
 |