All files / src/value/scroll use-element-scroll.ts

31.82% Statements 7/22
0% Branches 0/6
0% Functions 0/5
35% Lines 7/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86  30x 30x         30x 30x 30x   30x                                                                   30x                                                                                
import { RefObject } from "react"
import { useConstant } from "../../utils/use-constant"
import {
    createScrollMotionValues,
    ScrollMotionValues,
    createScrollUpdater,
} from "./utils"
import { addDomEvent } from "../../events/use-dom-event"
import { useIsomorphicLayoutEffect } from "../../utils/use-isomorphic-effect"
import { invariant } from "hey-listen"
 
const getElementScrollOffsets = (element: HTMLElement) => () => {
    return {
        xOffset: element.scrollLeft,
        yOffset: element.scrollTop,
        xMaxOffset: element.scrollWidth - element.offsetWidth,
        yMaxOffset: element.scrollHeight - element.offsetHeight,
    }
}
 
/**
 * Returns MotionValues that update when the provided element scrolls:
 *
 * - `scrollX` — Horizontal scroll distance in pixels.
 * - `scrollY` — Vertical scroll distance in pixels.
 * - `scrollXProgress` — Horizontal scroll progress between `0` and `1`.
 * - `scrollYProgress` — Vertical scroll progress between `0` and `1`.
 *
 * This element must be set to `overflow: scroll` on either or both axes to report scroll offset.
 *
 * ```jsx
 * export const MyComponent = () => {
 *   const ref = useRef()
 *   const { scrollYProgress } = useElementScroll(ref)
 *
 *   return (
 *     <div ref={ref}>
 *       <motion.div style={{ scaleX: scrollYProgress }} />
 *     </div>
 *   )
 * }
 * ```
 *
 * @public
 */
export function useElementScroll(
    ref: RefObject<HTMLElement>
): ScrollMotionValues {
    const values = useConstant(createScrollMotionValues)
 
    useIsomorphicLayoutEffect(() => {
        const element = ref.current
 
        invariant(
            !!element,
            "ref provided to useScroll must be passed into a HTML element."
        )
        if (!element) return
 
        const updateScrollValues = createScrollUpdater(
            values,
            getElementScrollOffsets(element)
        )
 
        const scrollListener = addDomEvent(
            element,
            "scroll",
            updateScrollValues,
            { passive: true }
        )
 
        const resizeListener = addDomEvent(
            element,
            "resize",
            updateScrollValues
        )
 
        return () => {
            scrollListener && scrollListener()
            resizeListener && resizeListener()
        }
    }, [])
 
    return values
}