103 lines
5.0 KiB
JavaScript
103 lines
5.0 KiB
JavaScript
"use client";
|
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
import { warning, invariant } from 'motion-utils';
|
|
import { forwardRef, useContext } from 'react';
|
|
import { LayoutGroupContext } from '../context/LayoutGroupContext.mjs';
|
|
import { LazyContext } from '../context/LazyContext.mjs';
|
|
import { MotionConfigContext } from '../context/MotionConfigContext.mjs';
|
|
import { MotionContext } from '../context/MotionContext/index.mjs';
|
|
import { useCreateMotionContext } from '../context/MotionContext/create.mjs';
|
|
import { isBrowser } from '../utils/is-browser.mjs';
|
|
import { featureDefinitions } from './features/definitions.mjs';
|
|
import { loadFeatures } from './features/load-features.mjs';
|
|
import { motionComponentSymbol } from './utils/symbol.mjs';
|
|
import { useMotionRef } from './utils/use-motion-ref.mjs';
|
|
import { useVisualElement } from './utils/use-visual-element.mjs';
|
|
|
|
/**
|
|
* Create a `motion` component.
|
|
*
|
|
* This function accepts a Component argument, which can be either a string (ie "div"
|
|
* for `motion.div`), or an actual React component.
|
|
*
|
|
* Alongside this is a config option which provides a way of rendering the provided
|
|
* component "offline", or outside the React render cycle.
|
|
*/
|
|
function createRendererMotionComponent({ preloadedFeatures, createVisualElement, useRender, useVisualState, Component, }) {
|
|
var _a, _b;
|
|
preloadedFeatures && loadFeatures(preloadedFeatures);
|
|
function MotionComponent(props, externalRef) {
|
|
/**
|
|
* If we need to measure the element we load this functionality in a
|
|
* separate class component in order to gain access to getSnapshotBeforeUpdate.
|
|
*/
|
|
let MeasureLayout;
|
|
const configAndProps = {
|
|
...useContext(MotionConfigContext),
|
|
...props,
|
|
layoutId: useLayoutId(props),
|
|
};
|
|
const { isStatic } = configAndProps;
|
|
const context = useCreateMotionContext(props);
|
|
const visualState = useVisualState(props, isStatic);
|
|
if (!isStatic && isBrowser) {
|
|
useStrictMode(configAndProps, preloadedFeatures);
|
|
const layoutProjection = getProjectionFunctionality(configAndProps);
|
|
MeasureLayout = layoutProjection.MeasureLayout;
|
|
/**
|
|
* Create a VisualElement for this component. A VisualElement provides a common
|
|
* interface to renderer-specific APIs (ie DOM/Three.js etc) as well as
|
|
* providing a way of rendering to these APIs outside of the React render loop
|
|
* for more performant animations and interactions
|
|
*/
|
|
context.visualElement = useVisualElement(Component, visualState, configAndProps, createVisualElement, layoutProjection.ProjectionNode);
|
|
}
|
|
/**
|
|
* The mount order and hierarchy is specific to ensure our element ref
|
|
* is hydrated by the time features fire their effects.
|
|
*/
|
|
return (jsxs(MotionContext.Provider, { value: context, children: [MeasureLayout && context.visualElement ? (jsx(MeasureLayout, { visualElement: context.visualElement, ...configAndProps })) : null, useRender(Component, props, useMotionRef(visualState, context.visualElement, externalRef), visualState, isStatic, context.visualElement)] }));
|
|
}
|
|
MotionComponent.displayName = `motion.${typeof Component === "string"
|
|
? Component
|
|
: `create(${(_b = (_a = Component.displayName) !== null && _a !== void 0 ? _a : Component.name) !== null && _b !== void 0 ? _b : ""})`}`;
|
|
const ForwardRefMotionComponent = forwardRef(MotionComponent);
|
|
ForwardRefMotionComponent[motionComponentSymbol] = Component;
|
|
return ForwardRefMotionComponent;
|
|
}
|
|
function useLayoutId({ layoutId }) {
|
|
const layoutGroupId = useContext(LayoutGroupContext).id;
|
|
return layoutGroupId && layoutId !== undefined
|
|
? layoutGroupId + "-" + layoutId
|
|
: layoutId;
|
|
}
|
|
function useStrictMode(configAndProps, preloadedFeatures) {
|
|
const isStrict = useContext(LazyContext).strict;
|
|
/**
|
|
* If we're in development mode, check to make sure we're not rendering a motion component
|
|
* as a child of LazyMotion, as this will break the file-size benefits of using it.
|
|
*/
|
|
if (process.env.NODE_ENV !== "production" &&
|
|
preloadedFeatures &&
|
|
isStrict) {
|
|
const strictMessage = "You have rendered a `motion` component within a `LazyMotion` component. This will break tree shaking. Import and render a `m` component instead.";
|
|
configAndProps.ignoreStrict
|
|
? warning(false, strictMessage)
|
|
: invariant(false, strictMessage);
|
|
}
|
|
}
|
|
function getProjectionFunctionality(props) {
|
|
const { drag, layout } = featureDefinitions;
|
|
if (!drag && !layout)
|
|
return {};
|
|
const combined = { ...drag, ...layout };
|
|
return {
|
|
MeasureLayout: (drag === null || drag === void 0 ? void 0 : drag.isEnabled(props)) || (layout === null || layout === void 0 ? void 0 : layout.isEnabled(props))
|
|
? combined.MeasureLayout
|
|
: undefined,
|
|
ProjectionNode: combined.ProjectionNode,
|
|
};
|
|
}
|
|
|
|
export { createRendererMotionComponent };
|