UNPKG

@mantine/hooks

Version:

A collection of 50+ hooks for state and UI management

132 lines (131 loc) 4.39 kB
"use client"; import { useCallback, useEffect, useRef, useState } from "react"; //#region packages/@mantine/hooks/src/use-scroller/use-scroller.ts function useScroller(options = {}) { const { scrollAmount = 200, draggable = true, onScrollStateChange } = options; const containerRef = useRef(null); const [canScrollStart, setCanScrollStart] = useState(false); const [canScrollEnd, setCanScrollEnd] = useState(false); const [isDragging, setIsDragging] = useState(false); const isDraggingRef = useRef(false); const hasDraggedRef = useRef(false); const startX = useRef(0); const scrollLeftStart = useRef(0); const onScrollStateChangeRef = useRef(onScrollStateChange); onScrollStateChangeRef.current = onScrollStateChange; const updateScrollState = useCallback(() => { const container = containerRef.current; if (container) { const { scrollLeft, scrollWidth, clientWidth } = container; const isRtl = getComputedStyle(container).direction === "rtl"; let newCanScrollStart; let newCanScrollEnd; if (isRtl) { newCanScrollStart = scrollLeft < 0; newCanScrollEnd = scrollLeft > -(scrollWidth - clientWidth); } else { newCanScrollStart = scrollLeft > 0; newCanScrollEnd = scrollLeft < scrollWidth - clientWidth - 1; } setCanScrollStart(newCanScrollStart); setCanScrollEnd(newCanScrollEnd); onScrollStateChangeRef.current?.({ canScrollStart: newCanScrollStart, canScrollEnd: newCanScrollEnd }); } }, []); useEffect(() => { updateScrollState(); const container = containerRef.current; if (container) { container.addEventListener("scroll", updateScrollState); const resizeObserver = new ResizeObserver(updateScrollState); resizeObserver.observe(container); return () => { container.removeEventListener("scroll", updateScrollState); resizeObserver.disconnect(); }; } }, [updateScrollState]); const scroll = useCallback((direction) => { const container = containerRef.current; if (container) { const isRtl = getComputedStyle(container).direction === "rtl"; const amount = scrollAmount; const scrollBy = direction === "end" ? amount : -amount; const adjustedScrollBy = isRtl ? -scrollBy : scrollBy; container.scrollBy({ left: adjustedScrollBy, behavior: "smooth" }); } }, [scrollAmount]); const scrollStart = useCallback(() => scroll("start"), [scroll]); const scrollEnd = useCallback(() => scroll("end"), [scroll]); const handleMouseDown = useCallback((event) => { if (!draggable) return; const container = containerRef.current; if (container) { isDraggingRef.current = true; hasDraggedRef.current = false; setIsDragging(true); startX.current = event.pageX - container.offsetLeft; scrollLeftStart.current = container.scrollLeft; container.style.cursor = "grabbing"; container.style.userSelect = "none"; } }, [draggable]); const handleMouseMove = useCallback((event) => { if (!isDraggingRef.current) return; event.preventDefault(); const container = containerRef.current; if (container) { const walk = event.pageX - container.offsetLeft - startX.current; if (Math.abs(walk) > 5) hasDraggedRef.current = true; container.scrollLeft = scrollLeftStart.current - walk; } }, []); const handleMouseUp = useCallback(() => { const wasDragged = hasDraggedRef.current; isDraggingRef.current = false; hasDraggedRef.current = false; setIsDragging(false); const container = containerRef.current; if (container) { container.style.cursor = ""; container.style.userSelect = ""; if (wasDragged) { const suppressClick = (event) => { event.stopPropagation(); event.preventDefault(); container.removeEventListener("click", suppressClick, true); }; container.addEventListener("click", suppressClick, true); } } }, []); const handleMouseLeave = useCallback(() => { if (isDraggingRef.current) handleMouseUp(); }, [handleMouseUp]); return { ref: useCallback((node) => { containerRef.current = node; if (node) updateScrollState(); }, [updateScrollState]), canScrollStart, canScrollEnd, scrollStart, scrollEnd, isDragging, dragHandlers: { onMouseDown: handleMouseDown, onMouseMove: handleMouseMove, onMouseUp: handleMouseUp, onMouseLeave: handleMouseLeave } }; } //#endregion export { useScroller }; //# sourceMappingURL=use-scroller.mjs.map