UNPKG

@mantine/hooks

Version:

A collection of 50+ hooks for state and UI management

171 lines (167 loc) 5.3 kB
'use client'; 'use strict'; var React = require('react'); var useReducedMotion = require('../use-reduced-motion/use-reduced-motion.cjs'); var useWindowEvent = require('../use-window-event/use-window-event.cjs'); function useScrollIntoView({ duration = 1250, axis = "y", onScrollFinish, easing = easeInOutQuad, offset = 0, cancelable = true, isList = false } = {}) { const frameID = React.useRef(0); const startTime = React.useRef(0); const shouldStop = React.useRef(false); const scrollableRef = React.useRef(null); const targetRef = React.useRef(null); const reducedMotion = useReducedMotion.useReducedMotion(); const cancel = () => { if (frameID.current) { cancelAnimationFrame(frameID.current); } }; const scrollIntoView = React.useCallback( ({ alignment = "start" } = {}) => { shouldStop.current = false; if (frameID.current) { cancel(); } const start = getScrollStart({ parent: scrollableRef.current, axis }) ?? 0; const change = getRelativePosition({ parent: scrollableRef.current, target: targetRef.current, axis, alignment, offset, isList }) - (scrollableRef.current ? 0 : start); function animateScroll() { if (startTime.current === 0) { startTime.current = performance.now(); } const now = performance.now(); const elapsed = now - startTime.current; const t = reducedMotion || duration === 0 ? 1 : elapsed / duration; const distance = start + change * easing(t); setScrollParam({ parent: scrollableRef.current, axis, distance }); if (!shouldStop.current && t < 1) { frameID.current = requestAnimationFrame(animateScroll); } else { typeof onScrollFinish === "function" && onScrollFinish(); startTime.current = 0; frameID.current = 0; cancel(); } } animateScroll(); }, [axis, duration, easing, isList, offset, onScrollFinish, reducedMotion] ); const handleStop = () => { if (cancelable) { shouldStop.current = true; } }; useWindowEvent.useWindowEvent("wheel", handleStop, { passive: true }); useWindowEvent.useWindowEvent("touchmove", handleStop, { passive: true }); React.useEffect(() => cancel, []); return { scrollableRef, targetRef, scrollIntoView, cancel }; } function easeInOutQuad(t) { return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; } function getRelativePosition({ axis, target, parent, alignment, offset, isList }) { if (!target || !parent && typeof document === "undefined") { return 0; } const isCustomParent = !!parent; const parentElement = parent || document.body; const parentPosition = parentElement.getBoundingClientRect(); const targetPosition = target.getBoundingClientRect(); const getDiff = (property) => targetPosition[property] - parentPosition[property]; if (axis === "y") { const diff = getDiff("top"); if (diff === 0) { return 0; } if (alignment === "start") { const distance = diff - offset; const shouldScroll = distance <= targetPosition.height * (isList ? 0 : 1) || !isList; return shouldScroll ? distance : 0; } const parentHeight = isCustomParent ? parentPosition.height : window.innerHeight; if (alignment === "end") { const distance = diff + offset - parentHeight + targetPosition.height; const shouldScroll = distance >= -targetPosition.height * (isList ? 0 : 1) || !isList; return shouldScroll ? distance : 0; } if (alignment === "center") { return diff - parentHeight / 2 + targetPosition.height / 2; } return 0; } if (axis === "x") { const diff = getDiff("left"); if (diff === 0) { return 0; } if (alignment === "start") { const distance = diff - offset; const shouldScroll = distance <= targetPosition.width || !isList; return shouldScroll ? distance : 0; } const parentWidth = isCustomParent ? parentPosition.width : window.innerWidth; if (alignment === "end") { const distance = diff + offset - parentWidth + targetPosition.width; const shouldScroll = distance >= -targetPosition.width || !isList; return shouldScroll ? distance : 0; } if (alignment === "center") { return diff - parentWidth / 2 + targetPosition.width / 2; } return 0; } return 0; } function getScrollStart({ axis, parent }) { if (!parent && typeof document === "undefined") { return 0; } const method = axis === "y" ? "scrollTop" : "scrollLeft"; if (parent) { return parent[method]; } const { body, documentElement } = document; return body[method] + documentElement[method]; } function setScrollParam({ axis, parent, distance }) { if (!parent && typeof document === "undefined") { return; } const method = axis === "y" ? "scrollTop" : "scrollLeft"; if (parent) { parent[method] = distance; } else { const { body, documentElement } = document; body[method] = distance; documentElement[method] = distance; } } exports.useScrollIntoView = useScrollIntoView; //# sourceMappingURL=use-scroll-into-view.cjs.map