UNPKG

motion

Version:

The Motion library for the web

102 lines (99 loc) 4.73 kB
import { __rest } from 'tslib'; import * as React from 'react'; import { forwardRef, useRef, useContext, createElement, useMemo } from 'react'; import { useAnimation } from './hooks/use-animation.es.js'; import { useHover } from './hooks/use-hover.es.js'; import { usePress } from './hooks/use-press.es.js'; import { useExit } from './hooks/use-exit.es.js'; import { useViewport } from './hooks/use-viewport.es.js'; import { convertKeyframesToStyles } from './utils/keyframes.es.js'; import { resolvePose } from './utils/poses.es.js'; import { AnimationContext } from './context.es.js'; function createAnimatedComponent(Component) { function Animated(props, _externalRef) { // TODO: Support externalRef if provided const ref = useRef(null); const { options: defaultOptions, style, initial, hover, press, exit, inViewport, viewport, poses, onStart, onComplete, onViewportEnter, onViewportLeave, inherit = true } = props, forwardProps = __rest(props /** * Track throughout the render which poses are considered active and should * be passed to children. */ , ["options", "style", "initial", "hover", "press", "exit", "inViewport", "viewport", "poses", "onStart", "onComplete", "onViewportEnter", "onViewportLeave", "inherit"]); /** * Track throughout the render which poses are considered active and should * be passed to children. */ const isPoseActive = { initial: true, style: true }; /** * Inherit poses from the parent context, */ let inherited = useContext(AnimationContext); if (!inherit) inherited = {}; const resolvedStyle = resolvePose(style, inherited.style, poses); const resolvedInitial = resolvePose(initial, inherited.initial, poses); const initialTarget = Object.assign(Object.assign({}, resolvedStyle), resolvedInitial); const target = Object.assign(Object.assign({}, resolvedInitial), resolvedStyle); const options = Object.assign(Object.assign({}, defaultOptions), resolvedStyle === null || resolvedStyle === void 0 ? void 0 : resolvedStyle.options); /** * If we haven't created a style prop for SSR yet (this is the initial render) * make one. We provide this to React every render as beyond that with manage style * via animations. */ const styleProp = useRef(null); styleProp.current || (styleProp.current = convertKeyframesToStyles(initialTarget)); /** * Attach animation event handlers (gestures/exit/viewport appearance). * This are called in reverse order of which styles should take priority when * active, for example if there's a hover and press gesture active the press * gesture will take precedence. */ const hoverProps = useHover(target, options, props, inherited, isPoseActive); const pressProps = usePress(target, options, props, inherited, isPoseActive); useViewport(ref, target, options, props, inherited, isPoseActive); const onExitComplete = useExit(target, options, props, inherited); /** * Compare our final calculated style target with the one from the previous render * and trigger any necessary animations. */ useAnimation(ref, initialTarget, target, options, onStart, (animation) => { onComplete && onComplete(animation); onExitComplete && onExitComplete(); }); const element = createElement(Component, Object.assign({}, forwardProps, hoverProps, pressProps, { style: styleProp.current, ref, })); /** * Create a variant context to pass forward to child components. */ const context = variantProps.reduce((acc, key) => { acc[key] = undefined; if (props[key]) { if (typeof props[key] === "string" && isPoseActive[key]) { acc[key] = props[key]; } } else if (inherited[key]) { acc[key] = inherited[key]; } return acc; }, {}); /** * Memoize the context so we only trigger a re-render in children if the values * within it change. */ const memoizedContext = useMemo(() => context, Object.values(context)); return (React.createElement(AnimationContext.Provider, { value: memoizedContext }, element)); } return forwardRef(Animated); } const variantProps = [ "initial", "style", "hover", "press", "inViewport", "exit", ]; export { createAnimatedComponent };