UNPKG

@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
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 })); }