UNPKG

@base-ui/react

Version:

Base UI is a library of headless ('unstyled') React components and low-level hooks. You gain complete control over your app's CSS and accessibility features.

31 lines (28 loc) 1.63 kB
'use client'; import * as React from 'react'; import { ownerDocument } from '@base-ui/utils/owner'; import { useScrollLock } from '@base-ui/utils/useScrollLock'; import { useIsoLayoutEffect } from '@base-ui/utils/useIsoLayoutEffect'; // Touch-opened popups normally avoid scroll locking so users can still swipe outside to dismiss. // This hook re-enables scroll lock only when the popup is effectively full-width. // Treat popups with up to 20px of total horizontal gutter as full-width so common ~10px side // padding still locks scroll, since that leaves too little outside space for a reliable swipe. const VIEWPORT_WIDTH_TOLERANCE_PX = 20; /** * Manages scroll lock for anchored popups. For non-touch opens, scroll lock is applied when * enabled. For touch opens, scroll lock is applied only when the positioner width is effectively * viewport-sized. */ export function useAnchoredPopupScrollLock(enabled, touchOpen, positionerElement, referenceElement) { const [touchOpenShouldLockScroll, setTouchOpenShouldLockScroll] = React.useState(false); useIsoLayoutEffect(() => { if (!enabled || !touchOpen || positionerElement == null) { setTouchOpenShouldLockScroll(false); return; } const viewportWidth = ownerDocument(positionerElement).documentElement.clientWidth; const popupWidth = positionerElement.offsetWidth; setTouchOpenShouldLockScroll(viewportWidth > 0 && popupWidth > 0 && popupWidth >= viewportWidth - VIEWPORT_WIDTH_TOLERANCE_PX); }, [enabled, touchOpen, positionerElement]); useScrollLock(enabled && (!touchOpen || touchOpenShouldLockScroll), referenceElement); }