UNPKG

@mantine/hooks

Version:

A collection of 50+ hooks for state and UI management

138 lines (137 loc) 4.92 kB
"use client"; const require_use_window_event = require("../use-window-event/use-window-event.cjs"); const require_use_reduced_motion = require("../use-reduced-motion/use-reduced-motion.cjs"); let react = require("react"); //#region packages/@mantine/hooks/src/use-scroll-into-view/use-scroll-into-view.ts function useScrollIntoView({ duration = 1250, axis = "y", onScrollFinish, easing = easeInOutQuad, offset = 0, cancelable = true, isList = false } = {}) { const frameID = (0, react.useRef)(0); const startTime = (0, react.useRef)(0); const shouldStop = (0, react.useRef)(false); const scrollableRef = (0, react.useRef)(null); const targetRef = (0, react.useRef)(null); const reducedMotion = require_use_reduced_motion.useReducedMotion(); const cancel = () => { if (frameID.current) cancelAnimationFrame(frameID.current); }; const scrollIntoView = (0, 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 elapsed = performance.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; }; /** * Detection of one of these events stops scroll animation * wheel - mouse wheel / touch pad * touchmove - any touchable device */ require_use_window_event.useWindowEvent("wheel", handleStop, { passive: true }); require_use_window_event.useWindowEvent("touchmove", handleStop, { passive: true }); (0, react.useEffect)(() => cancel, []); return { scrollableRef, targetRef, scrollIntoView, cancel }; } function easeInOutQuad(t) { return t < .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 parentPosition = (parent || document.body).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; return distance <= targetPosition.height * (isList ? 0 : 1) || !isList ? distance : 0; } const parentHeight = isCustomParent ? parentPosition.height : window.innerHeight; if (alignment === "end") { const distance = diff + offset - parentHeight + targetPosition.height; return distance >= -targetPosition.height * (isList ? 0 : 1) || !isList ? 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; return distance <= targetPosition.width || !isList ? distance : 0; } const parentWidth = isCustomParent ? parentPosition.width : window.innerWidth; if (alignment === "end") { const distance = diff + offset - parentWidth + targetPosition.width; return distance >= -targetPosition.width || !isList ? 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; } } //#endregion exports.useScrollIntoView = useScrollIntoView; //# sourceMappingURL=use-scroll-into-view.cjs.map