drupal/core/themes/olivero/js/scripts.es6.js

169 lines
5.0 KiB
JavaScript

/* eslint-disable no-inner-declarations */
((Drupal) => {
/**
* Olivero helper functions.
*
* @namespace
*/
Drupal.olivero = {};
function isDesktopNav() {
const navButtons = document.querySelector('.mobile-buttons');
return (
window.getComputedStyle(navButtons).getPropertyValue('display') === 'none'
);
}
Drupal.olivero.isDesktopNav = isDesktopNav;
const stickyHeaderToggleButton = document.querySelector(
'.sticky-header-toggle',
);
const siteHeaderFixable = document.querySelector('.site-header__fixable');
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.olivero.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()) {
if (pinnedState === true) {
siteHeaderFixable.classList.add('is-expanded');
} else {
siteHeaderFixable.classList.remove('is-expanded');
}
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.olivero.stickyHeaderState',
);
if (!stickyHeaderState) return null;
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 null;
}
return item.value;
}
// Only enable scroll effects 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('.fixable');
function toggleDesktopNavVisibility(entries) {
if (!isDesktopNav()) return;
entries.forEach((entry) => {
// FF doesn't seem to support entry.isIntersecting properly,
// so we check the intersectionRatio.
if (entry.intersectionRatio < 1) {
fixableElements.forEach((el) => el.classList.add('js-fixed'));
} else {
fixableElements.forEach((el) => el.classList.remove('js-fixed'));
}
});
}
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`;
}
function monitorNavPosition() {
const primaryNav = document.querySelector('.site-header');
const options = {
rootMargin: getRootMargin(),
threshold: [0.999, 1],
};
const observer = new IntersectionObserver(
toggleDesktopNavVisibility,
options,
);
observer.observe(primaryNav);
}
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.
document
.querySelector('#site-header__inner')
.addEventListener('focusin', () => {
if (isDesktopNav() && !stickyHeaderIsEnabled()) {
const header = document.querySelector('#header');
const headerNav = header.querySelector('#header-nav');
const headerMargin = header.clientHeight - headerNav.clientHeight;
if (window.scrollY > headerMargin) {
window.scrollTo(0, headerMargin);
}
}
});
monitorNavPosition();
setStickyHeaderStorage(getStickyHeaderStorage());
toggleStickyHeaderState(getStickyHeaderStorage());
}
})(Drupal);