framer-motion 12.24.9 → 12.24.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"use-motion-ref.mjs","sources":["../../../../src/motion/utils/use-motion-ref.ts"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { useCallback, useRef } from \"react\"\nimport type { VisualElement } from \"../../render/VisualElement\"\nimport { isRefObject } from \"../../utils/is-ref-object\"\nimport { VisualState } from \"./use-visual-state\"\n\n/**\n * Set a given ref to a given value\n * This utility takes care of different types of refs: callback refs and RefObject(s)\n * Returns a cleanup function if the ref callback returns one (React 19 feature)\n */\nfunction setRef<T>(ref: React.Ref<T>, value: T): void | (() => void) {\n if (typeof ref === \"function\") {\n return ref(value)\n } else if (isRefObject(ref)) {\n ;(ref as any).current = value\n }\n}\n\n/**\n * Creates a ref function that, when called, hydrates the provided\n * external ref and VisualElement.\n */\nexport function useMotionRef<Instance, RenderState>(\n visualState: VisualState<Instance, RenderState>,\n visualElement?: VisualElement<Instance> | null,\n externalRef?: React.Ref<Instance>\n): React.Ref<Instance> {\n // Store the cleanup function from external ref if it returns one\n const externalRefCleanupRef = useRef<(() => void) | null>(null)\n\n return useCallback(\n (instance: Instance) => {\n if (instance) {\n visualState.onMount && visualState.onMount(instance)\n }\n\n if (visualElement) {\n if (instance) {\n visualElement.mount(instance)\n } else {\n visualElement.unmount()\n }\n }\n\n if (externalRef) {\n if (instance) {\n // Mount: call the external ref and store any cleanup function\n const cleanup = setRef(externalRef, instance)\n if (typeof cleanup === \"function\") {\n externalRefCleanupRef.current = cleanup\n }\n } else {\n // Unmount: call stored cleanup function if available, otherwise call ref with null\n if (externalRefCleanupRef.current) {\n externalRefCleanupRef.current()\n externalRefCleanupRef.current = null\n } else {\n // Fallback to React <19 behavior for refs that don't return cleanup\n setRef(externalRef, instance)\n }\n }\n }\n },\n /**\n * Include all dependencies to ensure the callback updates correctly\n */\n [visualElement, visualState, externalRef]\n )\n}\n"],"names":[],"mappings":";;;;AAQA;;;;AAIG;AACH;AACI;AACI;;AACG;AACD;;AAEV;AAEA;;;AAGG;;;AAOC;AAEA;;;;;;AAQgB;;;;;;;;;;AAUA;AACI;;;;;AAIJ;;AAEI;;;;AAGA;;;;;AAKhB;;AAEG;AACH;AAER;;"}
1
+ {"version":3,"file":"use-motion-ref.mjs","sources":["../../../../src/motion/utils/use-motion-ref.ts"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { useCallback, useInsertionEffect, useRef } from \"react\"\nimport type { VisualElement } from \"../../render/VisualElement\"\nimport { VisualState } from \"./use-visual-state\"\n\n/**\n * Creates a ref function that, when called, hydrates the provided\n * external ref and VisualElement.\n */\nexport function useMotionRef<Instance, RenderState>(\n visualState: VisualState<Instance, RenderState>,\n visualElement?: VisualElement<Instance> | null,\n externalRef?: React.Ref<Instance>\n): React.Ref<Instance> {\n /**\n * Store externalRef in a ref to avoid including it in the useCallback\n * dependency array. Including externalRef in dependencies causes issues\n * with libraries like Radix UI that create new callback refs on each render\n * when using asChild - this would cause the callback to be recreated,\n * triggering element remounts and breaking AnimatePresence exit animations.\n */\n const externalRefContainer = useRef(externalRef)\n useInsertionEffect(() => {\n externalRefContainer.current = externalRef\n })\n\n // Store cleanup function returned by callback refs (React 19 feature)\n const refCleanup = useRef<(() => void) | null>(null)\n\n return useCallback(\n (instance: Instance) => {\n if (instance) {\n visualState.onMount?.(instance)\n }\n\n if (visualElement) {\n instance ? visualElement.mount(instance) : visualElement.unmount()\n }\n\n const ref = externalRefContainer.current\n if (typeof ref === \"function\") {\n if (instance) {\n const cleanup = ref(instance)\n if (typeof cleanup === \"function\") {\n refCleanup.current = cleanup\n }\n } else if (refCleanup.current) {\n refCleanup.current()\n refCleanup.current = null\n } else {\n ref(instance)\n }\n } else if (ref) {\n ;(ref as React.MutableRefObject<Instance>).current = instance\n }\n },\n [visualElement]\n )\n}\n"],"names":[],"mappings":";;;AAOA;;;AAGG;;AAMC;;;;;;AAMG;AACH;;AAEI;AACJ;;AAGA;AAEA;;AAGY;;;AAIA;;AAGJ;AACA;;AAEQ;AACA;AACI;;;AAED;;AAEH;;;;;;;AAKF;;AAEV;AAGR;;"}
@@ -2427,6 +2427,12 @@
2427
2427
  super();
2428
2428
  this.finishedTime = null;
2429
2429
  this.isStopped = false;
2430
+ /**
2431
+ * Tracks a manually-set start time that takes precedence over WAAPI's
2432
+ * dynamic startTime. This is cleared when play() or time setter is called,
2433
+ * allowing WAAPI to take over timing.
2434
+ */
2435
+ this.manualStartTime = null;
2430
2436
  if (!options)
2431
2437
  return;
2432
2438
  const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, onComplete, } = options;
@@ -2462,6 +2468,7 @@
2462
2468
  play() {
2463
2469
  if (this.isStopped)
2464
2470
  return;
2471
+ this.manualStartTime = null;
2465
2472
  this.animation.play();
2466
2473
  if (this.state === "finished") {
2467
2474
  this.updateFinished();
@@ -2525,6 +2532,7 @@
2525
2532
  return millisecondsToSeconds(Number(this.animation.currentTime) || 0);
2526
2533
  }
2527
2534
  set time(newTime) {
2535
+ this.manualStartTime = null;
2528
2536
  this.finishedTime = null;
2529
2537
  this.animation.currentTime = secondsToMilliseconds(newTime);
2530
2538
  }
@@ -2547,10 +2555,10 @@
2547
2555
  : this.animation.playState;
2548
2556
  }
2549
2557
  get startTime() {
2550
- return Number(this.animation.startTime);
2558
+ return this.manualStartTime ?? Number(this.animation.startTime);
2551
2559
  }
2552
2560
  set startTime(newStartTime) {
2553
- this.animation.startTime = newStartTime;
2561
+ this.manualStartTime = this.animation.startTime = newStartTime;
2554
2562
  }
2555
2563
  /**
2556
2564
  * Attaches a timeline to the animation, for instance the `ScrollTimeline`.
@@ -2612,7 +2620,7 @@
2612
2620
  */
2613
2621
  replaceTransitionType(options);
2614
2622
  super(options);
2615
- if (options.startTime) {
2623
+ if (options.startTime !== undefined) {
2616
2624
  this.startTime = options.startTime;
2617
2625
  }
2618
2626
  this.options = options;
@@ -2637,8 +2645,14 @@
2637
2645
  ...options,
2638
2646
  autoplay: false,
2639
2647
  });
2640
- const sampleTime = secondsToMilliseconds(this.finishedTime ?? this.time);
2641
- motionValue.setWithVelocity(sampleAnimation.sample(sampleTime - sampleDelta).value, sampleAnimation.sample(sampleTime).value, sampleDelta);
2648
+ /**
2649
+ * Use wall-clock elapsed time for sampling.
2650
+ * Under CPU load, WAAPI's currentTime may not reflect actual
2651
+ * elapsed time, causing incorrect sampling and visual jumps.
2652
+ */
2653
+ const sampleTime = Math.max(sampleDelta, time.now() - this.startTime);
2654
+ const delta = clamp(0, sampleDelta, sampleTime - sampleDelta);
2655
+ motionValue.setWithVelocity(sampleAnimation.sample(Math.max(0, sampleTime - delta)).value, sampleAnimation.sample(sampleTime).value, delta);
2642
2656
  sampleAnimation.stop();
2643
2657
  }
2644
2658
  }
@@ -5137,7 +5151,7 @@
5137
5151
  * Set a given ref to a given value
5138
5152
  * This utility takes care of different types of refs: callback refs and RefObject(s)
5139
5153
  */
5140
- function setRef$1(ref, value) {
5154
+ function setRef(ref, value) {
5141
5155
  if (typeof ref === "function") {
5142
5156
  return ref(value);
5143
5157
  }
@@ -5153,7 +5167,7 @@
5153
5167
  return (node) => {
5154
5168
  let hasCleanup = false;
5155
5169
  const cleanups = refs.map((ref) => {
5156
- const cleanup = setRef$1(ref, node);
5170
+ const cleanup = setRef(ref, node);
5157
5171
  if (!hasCleanup && typeof cleanup === "function") {
5158
5172
  hasCleanup = true;
5159
5173
  }
@@ -5171,7 +5185,7 @@
5171
5185
  cleanup();
5172
5186
  }
5173
5187
  else {
5174
- setRef$1(refs[i], null);
5188
+ setRef(refs[i], null);
5175
5189
  }
5176
5190
  }
5177
5191
  };
@@ -9748,69 +9762,51 @@
9748
9762
 
9749
9763
  const motionComponentSymbol = Symbol.for("motionComponentSymbol");
9750
9764
 
9751
- function isRefObject(ref) {
9752
- return (ref &&
9753
- typeof ref === "object" &&
9754
- Object.prototype.hasOwnProperty.call(ref, "current"));
9755
- }
9756
-
9757
- /**
9758
- * Set a given ref to a given value
9759
- * This utility takes care of different types of refs: callback refs and RefObject(s)
9760
- * Returns a cleanup function if the ref callback returns one (React 19 feature)
9761
- */
9762
- function setRef(ref, value) {
9763
- if (typeof ref === "function") {
9764
- return ref(value);
9765
- }
9766
- else if (isRefObject(ref)) {
9767
- ref.current = value;
9768
- }
9769
- }
9770
9765
  /**
9771
9766
  * Creates a ref function that, when called, hydrates the provided
9772
9767
  * external ref and VisualElement.
9773
9768
  */
9774
9769
  function useMotionRef(visualState, visualElement, externalRef) {
9775
- // Store the cleanup function from external ref if it returns one
9776
- const externalRefCleanupRef = React$1.useRef(null);
9770
+ /**
9771
+ * Store externalRef in a ref to avoid including it in the useCallback
9772
+ * dependency array. Including externalRef in dependencies causes issues
9773
+ * with libraries like Radix UI that create new callback refs on each render
9774
+ * when using asChild - this would cause the callback to be recreated,
9775
+ * triggering element remounts and breaking AnimatePresence exit animations.
9776
+ */
9777
+ const externalRefContainer = React$1.useRef(externalRef);
9778
+ React$1.useInsertionEffect(() => {
9779
+ externalRefContainer.current = externalRef;
9780
+ });
9781
+ // Store cleanup function returned by callback refs (React 19 feature)
9782
+ const refCleanup = React$1.useRef(null);
9777
9783
  return React$1.useCallback((instance) => {
9778
9784
  if (instance) {
9779
- visualState.onMount && visualState.onMount(instance);
9785
+ visualState.onMount?.(instance);
9780
9786
  }
9781
9787
  if (visualElement) {
9782
- if (instance) {
9783
- visualElement.mount(instance);
9784
- }
9785
- else {
9786
- visualElement.unmount();
9787
- }
9788
+ instance ? visualElement.mount(instance) : visualElement.unmount();
9788
9789
  }
9789
- if (externalRef) {
9790
+ const ref = externalRefContainer.current;
9791
+ if (typeof ref === "function") {
9790
9792
  if (instance) {
9791
- // Mount: call the external ref and store any cleanup function
9792
- const cleanup = setRef(externalRef, instance);
9793
+ const cleanup = ref(instance);
9793
9794
  if (typeof cleanup === "function") {
9794
- externalRefCleanupRef.current = cleanup;
9795
+ refCleanup.current = cleanup;
9795
9796
  }
9796
9797
  }
9798
+ else if (refCleanup.current) {
9799
+ refCleanup.current();
9800
+ refCleanup.current = null;
9801
+ }
9797
9802
  else {
9798
- // Unmount: call stored cleanup function if available, otherwise call ref with null
9799
- if (externalRefCleanupRef.current) {
9800
- externalRefCleanupRef.current();
9801
- externalRefCleanupRef.current = null;
9802
- }
9803
- else {
9804
- // Fallback to React <19 behavior for refs that don't return cleanup
9805
- setRef(externalRef, instance);
9806
- }
9803
+ ref(instance);
9807
9804
  }
9808
9805
  }
9809
- },
9810
- /**
9811
- * Include all dependencies to ensure the callback updates correctly
9812
- */
9813
- [visualElement, visualState, externalRef]);
9806
+ else if (ref) {
9807
+ ref.current = instance;
9808
+ }
9809
+ }, [visualElement]);
9814
9810
  }
9815
9811
 
9816
9812
  /**
@@ -9818,6 +9814,12 @@
9818
9814
  */
9819
9815
  const SwitchLayoutGroupContext = React$1.createContext({});
9820
9816
 
9817
+ function isRefObject(ref) {
9818
+ return (ref &&
9819
+ typeof ref === "object" &&
9820
+ Object.prototype.hasOwnProperty.call(ref, "current"));
9821
+ }
9822
+
9821
9823
  function useVisualElement(Component, visualState, props, createVisualElement, ProjectionNodeConstructor, isSVG) {
9822
9824
  const { visualElement: parent } = React$1.useContext(MotionContext);
9823
9825
  const lazyContext = React$1.useContext(LazyContext);