{"version":3,"file":"use-scroll.mjs","sources":["../../../src/value/use-scroll.ts"],"sourcesContent":["\"use client\"\n\nimport {\n    AnimationPlaybackControls,\n    cancelMicrotask,\n    microtask,\n    motionValue,\n    supportsScrollTimeline,\n    supportsViewTimeline,\n} from \"motion-dom\"\nimport { invariant } from \"motion-utils\"\nimport { RefObject, useCallback, useEffect, useRef } from \"react\"\nimport { scroll } from \"../render/dom/scroll\"\nimport { ScrollInfoOptions } from \"../render/dom/scroll/types\"\nimport { offsetToViewTimelineRange } from \"../render/dom/scroll/utils/offset-to-range\"\nimport { useConstant } from \"../utils/use-constant\"\nimport { useIsomorphicLayoutEffect } from \"../utils/use-isomorphic-effect\"\n\nexport interface UseScrollOptions\n    extends Omit<ScrollInfoOptions, \"container\" | \"target\"> {\n    container?: RefObject<HTMLElement | null>\n    target?: RefObject<HTMLElement | null>\n}\n\nconst createScrollMotionValues = () => ({\n    scrollX: motionValue(0),\n    scrollY: motionValue(0),\n    scrollXProgress: motionValue(0),\n    scrollYProgress: motionValue(0),\n})\n\nconst isRefPending = (ref?: RefObject<HTMLElement | null>) => {\n    if (!ref) return false\n    return !ref.current\n}\n\nfunction makeAccelerateConfig(\n    axis: \"x\" | \"y\",\n    options: Omit<UseScrollOptions, \"container\" | \"target\">,\n    container?: RefObject<HTMLElement | null>,\n    target?: RefObject<HTMLElement | null>\n) {\n    return {\n        // Refs attach child-first; defer so target.current is populated\n        // before scroll() reads it.\n        factory: (animation: AnimationPlaybackControls) => {\n            let cleanup: VoidFunction | undefined\n            const start = () => {\n                // A provided ref may be hydrated by an effect declared after\n                // useScroll (or in a parent). Don't attach to the window\n                // scroll in the meantime — that result gets cached and would\n                // permanently mistrack. Wait until the ref resolves.\n                if (isRefPending(container) || isRefPending(target)) {\n                    microtask.read(start)\n                    return\n                }\n                cleanup = scroll(animation, {\n                    ...options,\n                    axis,\n                    container: container?.current || undefined,\n                    target: target?.current || undefined,\n                })\n            }\n            microtask.read(start)\n            return () => {\n                cancelMicrotask(start)\n                cleanup?.()\n            }\n        },\n        times: [0, 1],\n        keyframes: [0, 1],\n        ease: (v: number) => v,\n        duration: 1,\n    }\n}\n\nfunction canAccelerateScroll(\n    target?: RefObject<HTMLElement | null>,\n    offset?: ScrollInfoOptions[\"offset\"]\n) {\n    if (typeof window === \"undefined\") return false\n    return target\n        ? supportsViewTimeline() && !!offsetToViewTimelineRange(offset)\n        : supportsScrollTimeline()\n}\n\nexport function useScroll({\n    container,\n    target,\n    ...options\n}: UseScrollOptions = {}) {\n    const values = useConstant(createScrollMotionValues)\n\n    if (canAccelerateScroll(target, options.offset)) {\n        values.scrollXProgress.accelerate = makeAccelerateConfig(\n            \"x\",\n            options,\n            container,\n            target\n        )\n        values.scrollYProgress.accelerate = makeAccelerateConfig(\n            \"y\",\n            options,\n            container,\n            target\n        )\n    }\n\n    const scrollAnimation = useRef<VoidFunction | null>(null)\n    const needsStart = useRef(false)\n\n    const start = useCallback(() => {\n        scrollAnimation.current = scroll(\n            (\n                _progress: number,\n                {\n                    x,\n                    y,\n                }: {\n                    x: { current: number; progress: number }\n                    y: { current: number; progress: number }\n                }\n            ) => {\n                values.scrollX.set(x.current)\n                values.scrollXProgress.set(x.progress)\n                values.scrollY.set(y.current)\n                values.scrollYProgress.set(y.progress)\n            },\n            {\n                ...options,\n                container: container?.current || undefined,\n                target: target?.current || undefined,\n            }\n        )\n\n        return () => {\n            scrollAnimation.current?.()\n        }\n    }, [container, target, JSON.stringify(options.offset)])\n\n    useIsomorphicLayoutEffect(() => {\n        needsStart.current = false\n\n        if (isRefPending(container) || isRefPending(target)) {\n            needsStart.current = true\n            return\n        } else {\n            return start()\n        }\n    }, [start])\n\n    useEffect(() => {\n        if (!needsStart.current) return\n\n        // Defer to a microtask so any sibling/parent effect that hydrates the\n        // ref has a chance to run first.\n        let cleanup: VoidFunction | undefined\n        const tryStart = () => {\n            const containerPending = isRefPending(container)\n            const targetPending = isRefPending(target)\n            invariant(\n                !containerPending,\n                \"Container ref is defined but not hydrated\",\n                \"use-scroll-ref\"\n            )\n            invariant(\n                !targetPending,\n                \"Target ref is defined but not hydrated\",\n                \"use-scroll-ref\"\n            )\n            if (!containerPending && !targetPending) cleanup = start()\n        }\n        microtask.read(tryStart)\n\n        return () => {\n            cancelMicrotask(tryStart)\n            cleanup?.()\n        }\n    }, [start])\n\n    return values\n}\n"],"names":[],"mappings":";;;;;;;;;AAwBA;AACI;AACA;AACA;AACA;AACH;AAED;AACI;AAAU;AACV;AACJ;AAEA;;;;AASQ;AACI;;;;;;;AAOQ;;;AAGJ;AACI;;AAEA;AACA;AACH;AACL;AACA;AACA;;;AAGA;;AAEJ;AACA;AACA;AACA;;AAER;AAEA;;AAIuC;AACnC;;;AAGJ;AAEM;AAKF;;AAGI;AAMA;;AAQJ;AACA;AAEA;AACI;;;;;AAeI;AAEI;AACA;AACA;AACH;AAGL;AACI;AACJ;AACJ;;AAGI;;AAGI;;;;;;AAKR;;;;;;AAOI;;AAEI;AACA;;;AAWA;;AACJ;AACA;AAEA;;;AAGA;AACJ;AAEA;AACJ;;"}