All files / src/components/Reorder Item.tsx

92.86% Statements 39/42
50% Branches 12/24
85.71% Functions 6/7
91.18% Lines 31/34

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 87 88 89 90 91 92 93 94 95 96 97 98 99 10031x 31x 31x               31x   31x   31x 31x 31x 31x                                   24x 12x     31x   7x 6x 6x 6x 6x 6x       6x       6x 6x         12x 12x     6x   6x   6x   6x 1x     6x                             1x                 31x  
import { invariant } from "hey-listen"
import * as React from "react"
import {
    ReactHTML,
    FunctionComponent,
    useContext,
    useEffect,
    useRef,
    forwardRef,
} from "react"
import { ReorderContext } from "../../context/ReorderContext"
import { Box } from "../../projection/geometry/types"
import { motion } from "../../render/dom/motion"
import { HTMLMotionProps } from "../../render/html/types"
import { useConstant } from "../../utils/use-constant"
import { useMotionValue } from "../../value/use-motion-value"
import { useTransform } from "../../value/use-transform"
import { isMotionValue } from "../../value/utils/is-motion-value"
 
export interface Props<V> {
    /**
     * A HTML element to render this component as. Defaults to `"li"`.
     *
     * @public
     */
    as?: keyof ReactHTML
 
    /**
     * The value in the list that this component represents.
     *
     * @public
     */
    value: V
}
 
function useDefaultMotionValue(value: any, EdefaultValue: number = 0) {
    return isMotionValue(value) ? value : useMotionValue(defaultValue)
}
 
export function ReorderItem<V>(
    {
        children,
        style,
        value,
        as = "li",
        onDrag,
        ...props
    }: Props<V> & HTMLMotionProps<any> & React.PropsWithChildren<{}>,
    externalRef?: React.Ref<any>
) {
    const Component = useConstant(() => motion(as)) as FunctionComponent<
        HTMLMotionProps<any> & { ref?: React.Ref<any> }
    >
 
    const context = useContext(ReorderContext)
    const point = {
        x: useDefaultMotionValue(style?.x),
        y: useDefaultMotionValue(style?.y),
    }
 
    const zIndex = useTransform([point.x, point.y], ([latestX, latestY]) =>
        latestX || latestY ? 1 : "unset"
    )
 
    const layout = useRef<Box | null>(null)
 
    invariant(Boolean(context), "Reorder.Item must be a child of Reorder.Group")
 
    const { axis, registerItem, updateOrder } = context!
 
    useEffect(() => {
        registerItem(value, layout.current!)
    }, [context])
 
    return (
        <Component
            drag={axis}
            {...props}
            dragSnapToOrigin
            style={{ ...style, x: point.x, y: point.y, zIndex }}
            layout
            onDrag={(event, gesturePoint) => {
                const { velocity } = gesturePoint
                velocity[axis] &&
                    updateOrder(value, point[axis].get(), velocity[axis])
 
                onDrag?.(event, gesturePoint)
            }}
            onLayoutMeasure={(measured) => {
                layout.current = measured
            }}
            ref={externalRef}
        >
            {children}
        </Component>
    )
}
 
export const Item = forwardRef(ReorderItem)