UNPKG

framer-motion

Version:

An open source and production-ready motion library for React on the web

121 lines (107 loc) • 3.58 kB
import * as React from "react" import { createDomVisualElement } from "./create-dom-visual-element" import { useRender } from "./use-render" import { HTMLMotionComponents, SVGMotionComponents } from "./types" import { MotionComponentConfig, MotionProps, createMotionComponent, } from "../../motion" import { Drag } from "../../motion/features/drag" import { Gestures } from "../../motion/features/gestures" import { Exit } from "../../motion/features/exit" import { Animation } from "../../motion/features/animation" import { AnimateLayout } from "../../motion/features/layout/Animate" import { MeasureLayout } from "../../motion/features/layout/Measure" import { MotionFeature } from "../../motion/features/types" /** * 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. */ export type CustomDomComponent<Props> = React.ForwardRefExoticComponent< React.PropsWithoutRef<Props & MotionProps> & React.RefAttributes<SVGElement | HTMLElement> > type MotionComponents = HTMLMotionComponents & SVGMotionComponents const allMotionFeatures = [ MeasureLayout, Animation, Drag, Gestures, Exit, AnimateLayout, ] const domBaseConfig = { createVisualElement: createDomVisualElement, useRender, } /** * 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.custom(Component) * ``` * * @public */ export function createMotionProxy(defaultFeatures: MotionFeature[]) { type CustomMotionComponent = { custom: typeof custom } type Motion = MotionComponents & CustomMotionComponent const config: MotionComponentConfig<HTMLElement | SVGElement> = { ...domBaseConfig, defaultFeatures, } function custom<Props>( Component: string | React.ComponentType<Props> ): CustomDomComponent<Props> { return createMotionComponent(Component, config) } const componentCache = new Map<string, any>() function get(target: CustomMotionComponent, key: string) { if (key === "custom") return target.custom if (!componentCache.has(key)) { componentCache.set(key, createMotionComponent(key, config)) } return componentCache.get(key) } return new Proxy({ custom }, { get }) as Motion } /** * HTML & SVG components, optimised for use with gestures and animation. These can be used as * drop-in replacements for any HTML & SVG component, all CSS & SVG properties are supported. * * @public */ export const motion = /*@__PURE__*/ createMotionProxy(allMotionFeatures) /** * Create a DOM `motion` component with the provided string. This is primarily intended * as a full alternative to `motion` for consumers who have to support environments that don't * support `Proxy`. * * ```javascript * import { createDomMotionComponent } from "framer-motion" * * const motion = { * div: createDomMotionComponent('div') * } * ``` * * @public */ export function createDomMotionComponent<T extends keyof MotionComponents>( key: T ) { const config: MotionComponentConfig<HTMLElement | SVGElement> = { ...domBaseConfig, defaultFeatures: allMotionFeatures, } return createMotionComponent(key, config) as MotionComponents[T] }