import * as React from 'react'; import { styleSingleton } from 'react-style-singleton'; import { fullWidthClassName, zeroRightClassName, noScrollbarsClassName, removedBarSizeVariable } from './constants'; import { getGapWidth } from './utils'; const Style = styleSingleton(); export const lockAttribute = 'data-scroll-locked'; // important tip - once we measure scrollBar width and remove them // we could not repeat this operation // thus we are using style-singleton - only the first "yet correct" style will be applied. const getStyles = ({ left, top, right, gap }, allowRelative, gapMode = 'margin', important) => ` .${noScrollbarsClassName} { overflow: hidden ${important}; padding-right: ${gap}px ${important}; } body[${lockAttribute}] { overflow: hidden ${important}; overscroll-behavior: contain; ${[ allowRelative && `position: relative ${important};`, gapMode === 'margin' && ` padding-left: ${left}px; padding-top: ${top}px; padding-right: ${right}px; margin-left:0; margin-top:0; margin-right: ${gap}px ${important}; `, gapMode === 'padding' && `padding-right: ${gap}px ${important};`, ] .filter(Boolean) .join('')} } .${zeroRightClassName} { right: ${gap}px ${important}; } .${fullWidthClassName} { margin-right: ${gap}px ${important}; } .${zeroRightClassName} .${zeroRightClassName} { right: 0 ${important}; } .${fullWidthClassName} .${fullWidthClassName} { margin-right: 0 ${important}; } body[${lockAttribute}] { ${removedBarSizeVariable}: ${gap}px; } `; const getCurrentUseCounter = () => { const counter = parseInt(document.body.getAttribute(lockAttribute) || '0', 10); return isFinite(counter) ? counter : 0; }; export const useLockAttribute = () => { React.useEffect(() => { document.body.setAttribute(lockAttribute, (getCurrentUseCounter() + 1).toString()); return () => { const newCounter = getCurrentUseCounter() - 1; if (newCounter <= 0) { document.body.removeAttribute(lockAttribute); } else { document.body.setAttribute(lockAttribute, newCounter.toString()); } }; }, []); }; /** * Removes page scrollbar and blocks page scroll when mounted */ export const RemoveScrollBar = ({ noRelative, noImportant, gapMode = 'margin' }) => { useLockAttribute(); /* gap will be measured on every component mount however it will be used only by the "first" invocation due to singleton nature of