All files / src/render/dom motion-proxy.ts

90.91% Statements 10/11
83.33% Branches 5/6
100% Functions 3/3
90.91% Lines 10/11

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          33x                                                                         33x     115x   58x             63x               63x   63x                   1211x 41x     1211x        
import * as React from "react"
import { DOMMotionComponents } from "./types"
import { HTMLRenderState } from "../html/types"
import { SVGRenderState } from "../svg/types"
import { MotionProps } from "../../motion/types"
import { createMotionComponent, MotionComponentConfig } from "../../motion"
 
/**
 * I'd rather the return type of `custom` to be implicit but this throws
 * incorrect relative paths in the exported types and API Extractor throws
 * a wobbly.
 *
 * @internal
 */
export type CustomDomComponent<Props> = React.ForwardRefExoticComponent<
    React.PropsWithoutRef<Props & MotionProps> &
        React.RefAttributes<SVGElement | HTMLElement>
>
 
export interface CustomMotionComponentConfig {
    forwardMotionProps?: boolean
}
 
export type CreateConfig = <Instance, RenderState, Props>(
    Component: string | React.ComponentType<Props>,
    config: CustomMotionComponentConfig
) => MotionComponentConfig<Instance, RenderState>
 
/**
 * Convert any React component into a `motion` component. The provided component
 * **must** use `React.forwardRef` to the underlying DOM component you want to animate.
 *
 * ```jsx
 * const Component = React.forwardRef((props, ref) => {
 *   return <div ref={ref} />
 * })
 *
 * const MotionComponent = motion(Component)
 * ```
 *
 * @public
 */
export function createMotionProxy(createConfig: CreateConfig) {
    function custom<Props>(
        Component: string | React.ComponentType<Props>,
        customMotionComponentConfig: CustomMotionComponentConfig = {}
    ): CustomDomComponent<Props> {
        return createMotionComponent<
            Props,
            HTMLElement | SVGElement,
            HTMLRenderState | SVGRenderState
        >(createConfig(Component, customMotionComponentConfig))
    }
 
    Iif (typeof Proxy === "undefined") {
        return custom as typeof custom & DOMMotionComponents
    }
 
    /**
     * A cache of generated `motion` components, e.g `motion.div`, `motion.input` etc.
     * Rather than generating them anew every render.
     */
    const componentCache = new Map<string, any>()
 
    return new Proxy(custom, {
        /**
         * Called when `motion` is referenced with a prop: `motion.div`, `motion.input` etc.
         * The prop name is passed through as `key` and we can use that to generate a `motion`
         * DOM component with that name.
         */
        get: (_target, key: string) => {
            /**
             * If this element doesn't exist in the component cache, create it and cache.
             */
            if (!componentCache.has(key)) {
                componentCache.set(key, custom(key))
            }
 
            return componentCache.get(key)!
        },
    }) as typeof custom & DOMMotionComponents
}