You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
6.0 KiB
215 lines
6.0 KiB
/** |
|
* @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) => { |
|
/** |
|
* Olives helper functions. |
|
* |
|
* @namespace |
|
*/ |
|
Drupal.olives = {}; |
|
|
|
/** |
|
* 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.olives.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) { |
|
const now = new Date(); |
|
|
|
const item = { |
|
value: expandedState, |
|
expiry: now.getTime() + 20160000, // 2 weeks from now. |
|
}; |
|
localStorage.setItem( |
|
'Drupal.olives.stickyHeaderState', |
|
JSON.stringify(item), |
|
); |
|
} |
|
|
|
/** |
|
* 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); |
|
setStickyHeaderStorage(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.olives.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.olives.stickyHeaderState'); |
|
return false; |
|
} |
|
return item.value; |
|
} |
|
|
|
// Only enable scroll interactivity if the browser supports Intersection |
|
// Observer. |
|
// @see https://github.com/w3c/IntersectionObserver/blob/master/polyfill/intersection-observer.js#L19-L21 |
|
if ( |
|
'IntersectionObserver' in window && |
|
'IntersectionObserverEntry' in window && |
|
'intersectionRatio' in window.IntersectionObserverEntry.prototype |
|
) { |
|
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', () => { |
|
toggleStickyHeaderState(!stickyHeaderIsEnabled()); |
|
}); |
|
} |
|
|
|
// 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(); |
|
setStickyHeaderStorage(getStickyHeaderStorage()); |
|
toggleStickyHeaderState(getStickyHeaderStorage()); |
|
} |
|
})(Drupal);
|
|
|