UNPKG

@base-ui-components/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.

188 lines (187 loc) 8.34 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useScrollAreaRoot = useScrollAreaRoot; var React = _interopRequireWildcard(require("react")); var _useEventCallback = require("../../utils/useEventCallback"); var _useEnhancedEffect = require("../../utils/useEnhancedEffect"); var _mergeReactProps = require("../../utils/mergeReactProps"); var _useBaseUiId = require("../../utils/useBaseUiId"); var _constants = require("../constants"); var _getOffset = require("../utils/getOffset"); var _ScrollAreaRootCssVars = require("./ScrollAreaRootCssVars"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } function useScrollAreaRoot(params) { const { dir: dirParam } = params; const [hovering, setHovering] = React.useState(false); const [scrolling, setScrolling] = React.useState(false); const [cornerSize, setCornerSize] = React.useState({ width: 0, height: 0 }); const [thumbSize, setThumbSize] = React.useState({ width: 0, height: 0 }); const [touchModality, setTouchModality] = React.useState(false); const rootId = (0, _useBaseUiId.useBaseUiId)(); const viewportRef = React.useRef(null); const scrollbarYRef = React.useRef(null); const scrollbarXRef = React.useRef(null); const thumbYRef = React.useRef(null); const thumbXRef = React.useRef(null); const cornerRef = React.useRef(null); const thumbDraggingRef = React.useRef(false); const startYRef = React.useRef(0); const startXRef = React.useRef(0); const startScrollTopRef = React.useRef(0); const startScrollLeftRef = React.useRef(0); const currentOrientationRef = React.useRef('vertical'); const timeoutRef = React.useRef(-1); const [hiddenState, setHiddenState] = React.useState({ scrollbarYHidden: false, scrollbarXHidden: false, cornerHidden: false }); const [autoDir, setAutoDir] = React.useState(dirParam); const dir = dirParam ?? autoDir; (0, _useEnhancedEffect.useEnhancedEffect)(() => { if (dirParam === undefined && viewportRef.current) { setAutoDir(getComputedStyle(viewportRef.current).direction); } }, [dirParam]); React.useEffect(() => { return () => { window.clearTimeout(timeoutRef.current); }; }, []); const handleScroll = (0, _useEventCallback.useEventCallback)(() => { setScrolling(true); window.clearTimeout(timeoutRef.current); timeoutRef.current = window.setTimeout(() => { setScrolling(false); }, _constants.SCROLL_TIMEOUT); }); const handlePointerDown = (0, _useEventCallback.useEventCallback)(event => { thumbDraggingRef.current = true; startYRef.current = event.clientY; startXRef.current = event.clientX; currentOrientationRef.current = event.currentTarget.getAttribute('data-orientation'); if (viewportRef.current) { startScrollTopRef.current = viewportRef.current.scrollTop; startScrollLeftRef.current = viewportRef.current.scrollLeft; } if (thumbYRef.current && currentOrientationRef.current === 'vertical') { thumbYRef.current.setPointerCapture(event.pointerId); } if (thumbXRef.current && currentOrientationRef.current === 'horizontal') { thumbXRef.current.setPointerCapture(event.pointerId); } }); const handlePointerMove = (0, _useEventCallback.useEventCallback)(event => { if (!thumbDraggingRef.current) { return; } const deltaY = event.clientY - startYRef.current; const deltaX = event.clientX - startXRef.current; if (viewportRef.current) { const scrollableContentHeight = viewportRef.current.scrollHeight; const viewportHeight = viewportRef.current.clientHeight; const scrollableContentWidth = viewportRef.current.scrollWidth; const viewportWidth = viewportRef.current.clientWidth; if (thumbYRef.current && scrollbarYRef.current && currentOrientationRef.current === 'vertical') { const scrollbarYOffset = (0, _getOffset.getOffset)(scrollbarYRef.current, 'padding', 'y'); const thumbYOffset = (0, _getOffset.getOffset)(thumbYRef.current, 'margin', 'y'); const thumbHeight = thumbYRef.current.offsetHeight; const maxThumbOffsetY = scrollbarYRef.current.offsetHeight - thumbHeight - scrollbarYOffset - thumbYOffset; const scrollRatioY = deltaY / maxThumbOffsetY; viewportRef.current.scrollTop = startScrollTopRef.current + scrollRatioY * (scrollableContentHeight - viewportHeight); event.preventDefault(); setScrolling(true); window.clearTimeout(timeoutRef.current); timeoutRef.current = window.setTimeout(() => { setScrolling(false); }, _constants.SCROLL_TIMEOUT); } if (thumbXRef.current && scrollbarXRef.current && currentOrientationRef.current === 'horizontal') { const scrollbarXOffset = (0, _getOffset.getOffset)(scrollbarXRef.current, 'padding', 'x'); const thumbXOffset = (0, _getOffset.getOffset)(thumbXRef.current, 'margin', 'x'); const thumbWidth = thumbXRef.current.offsetWidth; const maxThumbOffsetX = scrollbarXRef.current.offsetWidth - thumbWidth - scrollbarXOffset - thumbXOffset; const scrollRatioX = deltaX / maxThumbOffsetX; viewportRef.current.scrollLeft = startScrollLeftRef.current + scrollRatioX * (scrollableContentWidth - viewportWidth); event.preventDefault(); setScrolling(true); window.clearTimeout(timeoutRef.current); timeoutRef.current = window.setTimeout(() => { setScrolling(false); }, _constants.SCROLL_TIMEOUT); } } }); const handlePointerUp = (0, _useEventCallback.useEventCallback)(event => { thumbDraggingRef.current = false; if (thumbYRef.current && currentOrientationRef.current === 'vertical') { thumbYRef.current.releasePointerCapture(event.pointerId); } if (thumbXRef.current && currentOrientationRef.current === 'horizontal') { thumbXRef.current.releasePointerCapture(event.pointerId); } }); const handlePointerEnterOrMove = (0, _useEventCallback.useEventCallback)(({ pointerType }) => { const isTouch = pointerType === 'touch'; setTouchModality(isTouch); if (!isTouch) { setHovering(true); } }); const getRootProps = React.useCallback((externalProps = {}) => (0, _mergeReactProps.mergeReactProps)(externalProps, { dir, onPointerEnter: handlePointerEnterOrMove, onPointerMove: handlePointerEnterOrMove, onPointerDown({ pointerType }) { setTouchModality(pointerType === 'touch'); }, onPointerLeave() { setHovering(false); }, style: { position: 'relative', [_ScrollAreaRootCssVars.ScrollAreaRootCssVars.scrollAreaCornerHeight]: `${cornerSize.height}px`, [_ScrollAreaRootCssVars.ScrollAreaRootCssVars.scrollAreaCornerWidth]: `${cornerSize.width}px` } }), [cornerSize, dir, handlePointerEnterOrMove]); return React.useMemo(() => ({ getRootProps, handlePointerDown, handlePointerMove, handlePointerUp, handleScroll, cornerSize, setCornerSize, thumbSize, setThumbSize, touchModality, cornerRef, scrolling, setScrolling, hovering, setHovering, viewportRef, scrollbarYRef, scrollbarXRef, thumbYRef, thumbXRef, rootId, hiddenState, setHiddenState }), [getRootProps, handlePointerDown, handlePointerMove, handlePointerUp, handleScroll, cornerSize, thumbSize, touchModality, cornerRef, scrolling, hovering, setHovering, viewportRef, scrollbarYRef, scrollbarXRef, thumbYRef, thumbXRef, rootId, hiddenState]); }