@exadel/esl
Version:
Exadel Smart Library (ESL) is the lightweight custom elements library that provide a set of super-flexible components
75 lines (74 loc) • 3.21 kB
JavaScript
import { getListScrollParents, getScrollParent } from './parent';
const $html = document.documentElement;
/** Checks if element is blocked from scrolling */
export function isScrollLocked(target) {
return target.hasAttribute('esl-scroll-lock');
}
/** Checks vertical scroll based on content height */
export function hasVerticalScroll(target = $html) {
return target.scrollHeight > target.clientHeight;
}
/** Checks horizontal scroll based on content height */
export function hasHorizontalScroll(target = $html) {
return target.scrollWidth > target.clientWidth;
}
// Scroll lock mutex holder
const initiatorMap = new WeakMap();
/** Occupy inner mutex of scroll lock */
const requestLock = (target, initiator) => {
if (!initiator)
return;
const initiatorList = initiatorMap.get(target) || [];
const index = initiatorList.indexOf(initiator);
if (index === -1)
initiatorList.push(initiator);
initiatorMap.set(target, initiatorList);
};
/** Free inner mutex of the scroll lock */
const requestUnlock = (target, initiator) => {
const initiatorList = initiatorMap.get(target) || [];
const index = initiatorList.indexOf(initiator);
if (index >= 0)
initiatorList.splice(index, 1);
if (!initiatorList.length || !initiator)
initiatorMap.delete(target);
return !initiatorMap.has(target);
};
/**
* Disables a scroll on the element.
* @param target - scrollable element which will be blocked from scrolling
* @param options - additional options to lock scroll
* */
export function lockScroll(target = $html, options = {}) {
requestLock(target, options.initiator);
const scrollable = target === $html ? target : getScrollParent(target);
const hasVScroll = hasVerticalScroll(scrollable);
if (scrollable === $html) {
const scrollWidth = window.innerWidth - document.documentElement.clientWidth;
$html.style.setProperty('--s-lock-offset', `${scrollWidth}px`);
}
scrollable.toggleAttribute('esl-scroll-lock-passive', !hasVScroll);
scrollable.setAttribute('esl-scroll-lock', options.strategy || '');
if (options.recursive && scrollable.parentElement)
lockScroll(scrollable.parentElement, options);
}
/**
* Enables a scroll on the target element in case it was requested with given initiator.
* @param target - scrollable element
* @param options - additional options to unlock scroll
*/
export function unlockScroll(target = $html, options = {}) {
if (!requestUnlock(target, options.initiator))
return;
const scrollable = target === $html ? target : getScrollParent(target);
scrollable.removeAttribute('esl-scroll-lock-passive');
scrollable.removeAttribute('esl-scroll-lock');
if (options.recursive && scrollable.parentElement)
unlockScroll(scrollable.parentElement, options);
}
export function isOffsetChanged(offsets) {
return offsets.some((element) => element.element.scrollTop !== element.top || element.element.scrollLeft !== element.left);
}
export function getParentScrollOffsets($el, $topContainer) {
return getListScrollParents($el, $topContainer).map((el) => ({ element: el, top: el.scrollTop, left: el.scrollLeft }));
}