motion
Version:
The Motion library for the web
102 lines (99 loc) • 4.73 kB
JavaScript
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 };