index.mjs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. "use client";
  2. import { jsxs, jsx } from 'react/jsx-runtime';
  3. import { warning, invariant } from 'motion-utils';
  4. import { forwardRef, useContext } from 'react';
  5. import { LayoutGroupContext } from '../context/LayoutGroupContext.mjs';
  6. import { LazyContext } from '../context/LazyContext.mjs';
  7. import { MotionConfigContext } from '../context/MotionConfigContext.mjs';
  8. import { MotionContext } from '../context/MotionContext/index.mjs';
  9. import { useCreateMotionContext } from '../context/MotionContext/create.mjs';
  10. import { isBrowser } from '../utils/is-browser.mjs';
  11. import { featureDefinitions } from './features/definitions.mjs';
  12. import { loadFeatures } from './features/load-features.mjs';
  13. import { motionComponentSymbol } from './utils/symbol.mjs';
  14. import { useMotionRef } from './utils/use-motion-ref.mjs';
  15. import { useVisualElement } from './utils/use-visual-element.mjs';
  16. /**
  17. * Create a `motion` component.
  18. *
  19. * This function accepts a Component argument, which can be either a string (ie "div"
  20. * for `motion.div`), or an actual React component.
  21. *
  22. * Alongside this is a config option which provides a way of rendering the provided
  23. * component "offline", or outside the React render cycle.
  24. */
  25. function createRendererMotionComponent({ preloadedFeatures, createVisualElement, useRender, useVisualState, Component, }) {
  26. preloadedFeatures && loadFeatures(preloadedFeatures);
  27. function MotionComponent(props, externalRef) {
  28. /**
  29. * If we need to measure the element we load this functionality in a
  30. * separate class component in order to gain access to getSnapshotBeforeUpdate.
  31. */
  32. let MeasureLayout;
  33. const configAndProps = {
  34. ...useContext(MotionConfigContext),
  35. ...props,
  36. layoutId: useLayoutId(props),
  37. };
  38. const { isStatic } = configAndProps;
  39. const context = useCreateMotionContext(props);
  40. const visualState = useVisualState(props, isStatic);
  41. if (!isStatic && isBrowser) {
  42. useStrictMode(configAndProps, preloadedFeatures);
  43. const layoutProjection = getProjectionFunctionality(configAndProps);
  44. MeasureLayout = layoutProjection.MeasureLayout;
  45. /**
  46. * Create a VisualElement for this component. A VisualElement provides a common
  47. * interface to renderer-specific APIs (ie DOM/Three.js etc) as well as
  48. * providing a way of rendering to these APIs outside of the React render loop
  49. * for more performant animations and interactions
  50. */
  51. context.visualElement = useVisualElement(Component, visualState, configAndProps, createVisualElement, layoutProjection.ProjectionNode);
  52. }
  53. /**
  54. * The mount order and hierarchy is specific to ensure our element ref
  55. * is hydrated by the time features fire their effects.
  56. */
  57. 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)] }));
  58. }
  59. MotionComponent.displayName = `motion.${typeof Component === "string"
  60. ? Component
  61. : `create(${Component.displayName ?? Component.name ?? ""})`}`;
  62. const ForwardRefMotionComponent = forwardRef(MotionComponent);
  63. ForwardRefMotionComponent[motionComponentSymbol] = Component;
  64. return ForwardRefMotionComponent;
  65. }
  66. function useLayoutId({ layoutId }) {
  67. const layoutGroupId = useContext(LayoutGroupContext).id;
  68. return layoutGroupId && layoutId !== undefined
  69. ? layoutGroupId + "-" + layoutId
  70. : layoutId;
  71. }
  72. function useStrictMode(configAndProps, preloadedFeatures) {
  73. const isStrict = useContext(LazyContext).strict;
  74. /**
  75. * If we're in development mode, check to make sure we're not rendering a motion component
  76. * as a child of LazyMotion, as this will break the file-size benefits of using it.
  77. */
  78. if (process.env.NODE_ENV !== "production" &&
  79. preloadedFeatures &&
  80. isStrict) {
  81. const strictMessage = "You have rendered a `motion` component within a `LazyMotion` component. This will break tree shaking. Import and render a `m` component instead.";
  82. configAndProps.ignoreStrict
  83. ? warning(false, strictMessage)
  84. : invariant(false, strictMessage);
  85. }
  86. }
  87. function getProjectionFunctionality(props) {
  88. const { drag, layout } = featureDefinitions;
  89. if (!drag && !layout)
  90. return {};
  91. const combined = { ...drag, ...layout };
  92. return {
  93. MeasureLayout: drag?.isEnabled(props) || layout?.isEnabled(props)
  94. ? combined.MeasureLayout
  95. : undefined,
  96. ProjectionNode: combined.ProjectionNode,
  97. };
  98. }
  99. export { createRendererMotionComponent };