@melt-ui/svelte
Version:

77 lines (76 loc) • 3 kB
JavaScript
// Modified from @zag-js/remove-scroll v0.10.2 (2023-06-10)
// Source: https://github.com/chakra-ui/zag
// https://github.com/chakra-ui/zag/blob/main/packages/utilities/remove-scroll/src/index.ts
import { noop } from './callbacks.js';
import { isIos } from './platform.js';
const LOCK_CLASSNAME = 'data-melt-scroll-lock';
function assignStyle(el, style) {
if (!el)
return;
const previousStyle = el.style.cssText;
Object.assign(el.style, style);
return () => {
el.style.cssText = previousStyle;
};
}
function setCSSProperty(el, property, value) {
if (!el)
return;
const previousValue = el.style.getPropertyValue(property);
el.style.setProperty(property, value);
return () => {
if (previousValue) {
el.style.setProperty(property, previousValue);
}
else {
el.style.removeProperty(property);
}
};
}
function getPaddingProperty(documentElement) {
// RTL <body> scrollbar
const documentLeft = documentElement.getBoundingClientRect().left;
const scrollbarX = Math.round(documentLeft) + documentElement.scrollLeft;
return scrollbarX ? 'paddingLeft' : 'paddingRight';
}
export function removeScroll(_document) {
const doc = _document ?? document;
const win = doc.defaultView ?? window;
const { documentElement, body } = doc;
const locked = body.hasAttribute(LOCK_CLASSNAME);
if (locked)
return noop;
body.setAttribute(LOCK_CLASSNAME, '');
const scrollbarWidth = win.innerWidth - documentElement.clientWidth;
const setScrollbarWidthProperty = () => setCSSProperty(documentElement, '--scrollbar-width', `${scrollbarWidth}px`);
const paddingProperty = getPaddingProperty(documentElement);
const scrollbarSidePadding = win.getComputedStyle(body)[paddingProperty];
const setStyle = () => assignStyle(body, {
overflow: 'hidden',
[paddingProperty]: `calc(${scrollbarSidePadding} + ${scrollbarWidth}px)`,
});
// Only iOS doesn't respect `overflow: hidden` on document.body
const setIOSStyle = () => {
const { scrollX, scrollY, visualViewport } = win;
// iOS 12 does not support `visuaViewport`.
const offsetLeft = visualViewport?.offsetLeft ?? 0;
const offsetTop = visualViewport?.offsetTop ?? 0;
const restoreStyle = assignStyle(body, {
position: 'fixed',
overflow: 'hidden',
top: `${-(scrollY - Math.floor(offsetTop))}px`,
left: `${-(scrollX - Math.floor(offsetLeft))}px`,
right: '0',
[paddingProperty]: `calc(${scrollbarSidePadding} + ${scrollbarWidth}px)`,
});
return () => {
restoreStyle?.();
win.scrollTo(scrollX, scrollY);
};
};
const cleanups = [setScrollbarWidthProperty(), isIos() ? setIOSStyle() : setStyle()];
return () => {
cleanups.forEach((fn) => fn?.());
body.removeAttribute(LOCK_CLASSNAME);
};
}