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