framer-motion 12.35.2 → 12.37.0

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.
Files changed (49) hide show
  1. package/README.md +12 -15
  2. package/dist/cjs/client.js +1 -1
  3. package/dist/cjs/dom.js +38 -6
  4. package/dist/cjs/dom.js.map +1 -1
  5. package/dist/cjs/{feature-bundle-DqHxNjy5.js → feature-bundle-CBfLWgGA.js} +47 -6
  6. package/dist/cjs/feature-bundle-CBfLWgGA.js.map +1 -0
  7. package/dist/cjs/index.js +41 -8
  8. package/dist/cjs/index.js.map +1 -1
  9. package/dist/cjs/m.js +7 -1
  10. package/dist/cjs/m.js.map +1 -1
  11. package/dist/dom-mini.js +1 -1
  12. package/dist/dom.js +1 -1
  13. package/dist/es/components/AnimatePresence/PopChild.mjs +1 -0
  14. package/dist/es/components/AnimatePresence/PopChild.mjs.map +1 -1
  15. package/dist/es/components/AnimatePresence/index.mjs +1 -1
  16. package/dist/es/components/AnimatePresence/index.mjs.map +1 -1
  17. package/dist/es/gestures/drag/VisualElementDragControls.mjs +2 -1
  18. package/dist/es/gestures/drag/VisualElementDragControls.mjs.map +1 -1
  19. package/dist/es/gestures/pan/PanSession.mjs.map +1 -1
  20. package/dist/es/motion/features/animation/exit.mjs +31 -1
  21. package/dist/es/motion/features/animation/exit.mjs.map +1 -1
  22. package/dist/es/motion/features/viewport/index.mjs +7 -3
  23. package/dist/es/motion/features/viewport/index.mjs.map +1 -1
  24. package/dist/es/render/dom/scroll/utils/offset-to-range.mjs +38 -6
  25. package/dist/es/render/dom/scroll/utils/offset-to-range.mjs.map +1 -1
  26. package/dist/es/render/dom/utils/filter-props.mjs +8 -1
  27. package/dist/es/render/dom/utils/filter-props.mjs.map +1 -1
  28. package/dist/es/value/use-spring.mjs.map +1 -1
  29. package/dist/framer-motion.dev.js +272 -88
  30. package/dist/framer-motion.js +1 -1
  31. package/dist/mini.js +1 -1
  32. package/dist/size-rollup-animate.js +1 -1
  33. package/dist/size-rollup-animate.js.map +1 -1
  34. package/dist/size-rollup-dom-animation-assets.js +1 -1
  35. package/dist/size-rollup-dom-animation-m.js +1 -1
  36. package/dist/size-rollup-dom-animation.js +1 -1
  37. package/dist/size-rollup-dom-max-assets.js +1 -1
  38. package/dist/size-rollup-dom-max.js +1 -1
  39. package/dist/size-rollup-m.js +1 -1
  40. package/dist/size-rollup-m.js.map +1 -1
  41. package/dist/size-rollup-motion.js +1 -1
  42. package/dist/size-rollup-motion.js.map +1 -1
  43. package/dist/size-rollup-scroll.js +1 -1
  44. package/dist/size-rollup-scroll.js.map +1 -1
  45. package/dist/size-rollup-waapi-animate.js +1 -1
  46. package/dist/size-rollup-waapi-animate.js.map +1 -1
  47. package/dist/types/index.d.ts +5 -4
  48. package/package.json +4 -4
  49. package/dist/cjs/feature-bundle-DqHxNjy5.js.map +0 -1
@@ -324,7 +324,11 @@
324
324
  const backIn = /*@__PURE__*/ reverseEasing(backOut);
325
325
  const backInOut = /*@__PURE__*/ mirrorEasing(backIn);
326
326
 
327
- const anticipate = (p) => (p *= 2) < 1 ? 0.5 * backIn(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
327
+ const anticipate = (p) => p >= 1
328
+ ? 1
329
+ : (p *= 2) < 1
330
+ ? 0.5 * backIn(p)
331
+ : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
328
332
 
329
333
  const circIn = (p) => 1 - Math.sin(Math.acos(p));
330
334
  const circOut = reverseEasing(circIn);
@@ -443,8 +447,7 @@
443
447
  const queue = addToCurrentFrame ? thisFrame : nextFrame;
444
448
  if (keepAlive)
445
449
  toKeepAlive.add(callback);
446
- if (!queue.has(callback))
447
- queue.add(callback);
450
+ queue.add(callback);
448
451
  return callback;
449
452
  },
450
453
  /**
@@ -469,7 +472,10 @@
469
472
  return;
470
473
  }
471
474
  isProcessing = true;
472
- [thisFrame, nextFrame] = [nextFrame, thisFrame];
475
+ // Swap this frame and the next to avoid GC
476
+ const prevFrame = thisFrame;
477
+ thisFrame = nextFrame;
478
+ nextFrame = prevFrame;
473
479
  // Execute this frame
474
480
  thisFrame.forEach(triggerCallback);
475
481
  /**
@@ -508,11 +514,12 @@
508
514
  }, {});
509
515
  const { setup, read, resolveKeyframes, preUpdate, update, preRender, render, postRender, } = steps;
510
516
  const processBatch = () => {
511
- const timestamp = MotionGlobalConfig.useManualTiming
517
+ const useManualTiming = MotionGlobalConfig.useManualTiming;
518
+ const timestamp = useManualTiming
512
519
  ? state.timestamp
513
520
  : performance.now();
514
521
  runNextFrame = false;
515
- if (!MotionGlobalConfig.useManualTiming) {
522
+ if (!useManualTiming) {
516
523
  state.delta = useDefaultElapsed
517
524
  ? 1000 / 60
518
525
  : Math.max(Math.min(timestamp - state.timestamp, maxElapsed$1), 1);
@@ -1704,9 +1711,9 @@
1704
1711
  };
1705
1712
  }
1706
1713
 
1707
- const isNotNull$1 = (value) => value !== null;
1708
- function getFinalKeyframe$1(keyframes, { repeat, repeatType = "loop" }, finalKeyframe, speed = 1) {
1709
- const resolvedKeyframes = keyframes.filter(isNotNull$1);
1714
+ const isNotNull = (value) => value !== null;
1715
+ function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe, speed = 1) {
1716
+ const resolvedKeyframes = keyframes.filter(isNotNull);
1710
1717
  const useFirstKeyframe = speed < 0 || (repeat && repeatType !== "loop" && repeat % 2 === 1);
1711
1718
  const index = useFirstKeyframe ? 0 : resolvedKeyframes.length - 1;
1712
1719
  return !index || finalKeyframe === undefined
@@ -1771,6 +1778,14 @@
1771
1778
  * Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed.
1772
1779
  */
1773
1780
  this.playbackSpeed = 1;
1781
+ /**
1782
+ * Reusable state object for the delay phase to avoid
1783
+ * allocating a new object every frame.
1784
+ */
1785
+ this.delayState = {
1786
+ done: false,
1787
+ value: undefined,
1788
+ };
1774
1789
  /**
1775
1790
  * This method is bound to the instance to fix a pattern where
1776
1791
  * animation.stop is returned as a reference from a useEffect.
@@ -1932,9 +1947,14 @@
1932
1947
  * This prevents delay: x, duration: 0 animations from finishing
1933
1948
  * instantly.
1934
1949
  */
1935
- const state = isInDelayPhase
1936
- ? { done: false, value: keyframes[0] }
1937
- : frameGenerator.next(elapsed);
1950
+ let state;
1951
+ if (isInDelayPhase) {
1952
+ this.delayState.value = keyframes[0];
1953
+ state = this.delayState;
1954
+ }
1955
+ else {
1956
+ state = frameGenerator.next(elapsed);
1957
+ }
1938
1958
  if (mixKeyframes && !isInDelayPhase) {
1939
1959
  state.value = mixKeyframes(state.value);
1940
1960
  }
@@ -1949,7 +1969,7 @@
1949
1969
  (this.state === "finished" || (this.state === "running" && done));
1950
1970
  // TODO: The exception for inertia could be cleaner here
1951
1971
  if (isAnimationFinished && type !== inertia) {
1952
- state.value = getFinalKeyframe$1(keyframes, this.options, finalKeyframe, this.speed);
1972
+ state.value = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
1953
1973
  }
1954
1974
  if (onUpdate) {
1955
1975
  onUpdate(state.value);
@@ -2244,8 +2264,18 @@
2244
2264
  }
2245
2265
  const positionalValues = {
2246
2266
  // Dimensions
2247
- width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight),
2248
- height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom),
2267
+ width: ({ x }, { paddingLeft = "0", paddingRight = "0", boxSizing }) => {
2268
+ const width = x.max - x.min;
2269
+ return boxSizing === "border-box"
2270
+ ? width
2271
+ : width - parseFloat(paddingLeft) - parseFloat(paddingRight);
2272
+ },
2273
+ height: ({ y }, { paddingTop = "0", paddingBottom = "0", boxSizing }) => {
2274
+ const height = y.max - y.min;
2275
+ return boxSizing === "border-box"
2276
+ ? height
2277
+ : height - parseFloat(paddingTop) - parseFloat(paddingBottom);
2278
+ },
2249
2279
  top: (_bbox, { top }) => parseFloat(top),
2250
2280
  left: (_bbox, { left }) => parseFloat(left),
2251
2281
  bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min),
@@ -2547,7 +2577,7 @@
2547
2577
  this.animation.onfinish = () => {
2548
2578
  this.finishedTime = this.time;
2549
2579
  if (!pseudoElement) {
2550
- const keyframe = getFinalKeyframe$1(keyframes, this.options, finalKeyframe, this.speed);
2580
+ const keyframe = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
2551
2581
  if (this.updateMotionValue) {
2552
2582
  this.updateMotionValue(keyframe);
2553
2583
  }
@@ -2853,17 +2883,42 @@
2853
2883
  /**
2854
2884
  * A list of values that can be hardware-accelerated.
2855
2885
  */
2856
- const acceleratedValues$1 = new Set([
2886
+ const acceleratedValues = new Set([
2857
2887
  "opacity",
2858
2888
  "clipPath",
2859
2889
  "filter",
2860
2890
  "transform",
2861
- // TODO: Could be re-enabled now we have support for linear() easing
2891
+ // TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved
2892
+ // or until we implement support for linear() easing.
2862
2893
  // "background-color"
2863
2894
  ]);
2895
+
2896
+ const browserColorFunctions = /^(?:oklch|oklab|lab|lch|color|color-mix|light-dark)\(/;
2897
+ function hasBrowserOnlyColors(keyframes) {
2898
+ for (let i = 0; i < keyframes.length; i++) {
2899
+ if (typeof keyframes[i] === "string" &&
2900
+ browserColorFunctions.test(keyframes[i])) {
2901
+ return true;
2902
+ }
2903
+ }
2904
+ return false;
2905
+ }
2906
+
2907
+ const colorProperties = new Set([
2908
+ "color",
2909
+ "backgroundColor",
2910
+ "outlineColor",
2911
+ "fill",
2912
+ "stroke",
2913
+ "borderColor",
2914
+ "borderTopColor",
2915
+ "borderRightColor",
2916
+ "borderBottomColor",
2917
+ "borderLeftColor",
2918
+ ]);
2864
2919
  const supportsWaapi = /*@__PURE__*/ memo(() => Object.hasOwnProperty.call(Element.prototype, "animate"));
2865
2920
  function supportsBrowserAnimation(options) {
2866
- const { motionValue, name, repeatDelay, repeatType, damping, type } = options;
2921
+ const { motionValue, name, repeatDelay, repeatType, damping, type, keyframes, } = options;
2867
2922
  const subject = motionValue?.owner?.current;
2868
2923
  /**
2869
2924
  * We use this check instead of isHTMLElement() because we explicitly
@@ -2877,7 +2932,13 @@
2877
2932
  const { onUpdate, transformTemplate } = motionValue.owner.getProps();
2878
2933
  return (supportsWaapi() &&
2879
2934
  name &&
2880
- acceleratedValues$1.has(name) &&
2935
+ /**
2936
+ * Force WAAPI for color properties with browser-only color formats
2937
+ * (oklch, oklab, lab, lch, etc.) that the JS animation path can't parse.
2938
+ */
2939
+ (acceleratedValues.has(name) ||
2940
+ (colorProperties.has(name) &&
2941
+ hasBrowserOnlyColors(keyframes))) &&
2881
2942
  (name !== "transform" || !transformTemplate) &&
2882
2943
  /**
2883
2944
  * If we're outputting values to onUpdate then we can't use WAAPI as there's
@@ -2937,9 +2998,11 @@
2937
2998
  * If we can't animate this value with the resolved keyframes
2938
2999
  * then we should complete it immediately.
2939
3000
  */
3001
+ let canAnimateValue = true;
2940
3002
  if (!canAnimate(keyframes, name, type, velocity)) {
3003
+ canAnimateValue = false;
2941
3004
  if (MotionGlobalConfig.instantAnimations || !delay) {
2942
- onUpdate?.(getFinalKeyframe$1(keyframes, options, finalKeyframe));
3005
+ onUpdate?.(getFinalKeyframe(keyframes, options, finalKeyframe));
2943
3006
  }
2944
3007
  keyframes[0] = keyframes[keyframes.length - 1];
2945
3008
  makeAnimationInstant(options);
@@ -2974,15 +3037,29 @@
2974
3037
  * Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via
2975
3038
  * WAAPI. Therefore, this animation must be JS to ensure it runs "under" the
2976
3039
  * optimised animation.
3040
+ *
3041
+ * Also skip WAAPI when keyframes aren't animatable, as the resolved
3042
+ * values may not be valid CSS and would trigger browser warnings.
2977
3043
  */
2978
- const useWaapi = !isHandoff && supportsBrowserAnimation(resolvedOptions);
3044
+ const useWaapi = canAnimateValue &&
3045
+ !isHandoff &&
3046
+ supportsBrowserAnimation(resolvedOptions);
2979
3047
  const element = resolvedOptions.motionValue?.owner?.current;
2980
- const animation = useWaapi
2981
- ? new NativeAnimationExtended({
2982
- ...resolvedOptions,
2983
- element,
2984
- })
2985
- : new JSAnimation(resolvedOptions);
3048
+ let animation;
3049
+ if (useWaapi) {
3050
+ try {
3051
+ animation = new NativeAnimationExtended({
3052
+ ...resolvedOptions,
3053
+ element,
3054
+ });
3055
+ }
3056
+ catch {
3057
+ animation = new JSAnimation(resolvedOptions);
3058
+ }
3059
+ }
3060
+ else {
3061
+ animation = new JSAnimation(resolvedOptions);
3062
+ }
2986
3063
  animation.finished.then(() => {
2987
3064
  this.notifyFinished();
2988
3065
  }).catch(noop$1);
@@ -3160,8 +3237,11 @@
3160
3237
  const animationMaps = new WeakMap();
3161
3238
  const animationMapKey = (name, pseudoElement = "") => `${name}:${pseudoElement}`;
3162
3239
  function getAnimationMap(element) {
3163
- const map = animationMaps.get(element) || new Map();
3164
- animationMaps.set(element, map);
3240
+ let map = animationMaps.get(element);
3241
+ if (!map) {
3242
+ map = new Map();
3243
+ animationMaps.set(element, map);
3244
+ }
3165
3245
  return map;
3166
3246
  }
3167
3247
 
@@ -3253,17 +3333,6 @@
3253
3333
  return ease;
3254
3334
  };
3255
3335
 
3256
- const isNotNull = (value) => value !== null;
3257
- function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
3258
- const resolvedKeyframes = keyframes.filter(isNotNull);
3259
- const index = repeat && repeatType !== "loop" && repeat % 2 === 1
3260
- ? 0
3261
- : resolvedKeyframes.length - 1;
3262
- return !index || finalKeyframe === undefined
3263
- ? resolvedKeyframes[index]
3264
- : finalKeyframe;
3265
- }
3266
-
3267
3336
  /**
3268
3337
  * If `transition` has `inherit: true`, shallow-merge it with
3269
3338
  * `parentTransition` (child keys win) and strip the `inherit` key.
@@ -3287,13 +3356,29 @@
3287
3356
  return valueTransition;
3288
3357
  }
3289
3358
 
3359
+ const orchestrationKeys = new Set([
3360
+ "when",
3361
+ "delay",
3362
+ "delayChildren",
3363
+ "staggerChildren",
3364
+ "staggerDirection",
3365
+ "repeat",
3366
+ "repeatType",
3367
+ "repeatDelay",
3368
+ "from",
3369
+ "elapsed",
3370
+ ]);
3290
3371
  /**
3291
3372
  * Decide whether a transition is defined on a given Transition.
3292
3373
  * This filters out orchestration options and returns true
3293
3374
  * if any options are left.
3294
3375
  */
3295
- function isTransitionDefined({ when, delay: _delay, delayChildren, staggerChildren, staggerDirection, repeat, repeatType, repeatDelay, from, elapsed, ...transition }) {
3296
- return !!Object.keys(transition).length;
3376
+ function isTransitionDefined(transition) {
3377
+ for (const key in transition) {
3378
+ if (!orchestrationKeys.has(key))
3379
+ return true;
3380
+ }
3381
+ return false;
3297
3382
  }
3298
3383
 
3299
3384
  const animateMotionValue = (name, value, target, transition = {}, element, isHandoff) => (onComplete) => {
@@ -4425,19 +4510,6 @@
4425
4510
  return true;
4426
4511
  });
4427
4512
 
4428
- /**
4429
- * A list of values that can be hardware-accelerated.
4430
- */
4431
- const acceleratedValues = new Set([
4432
- "opacity",
4433
- "clipPath",
4434
- "filter",
4435
- "transform",
4436
- // TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved
4437
- // or until we implement support for linear() easing.
4438
- // "background-color"
4439
- ]);
4440
-
4441
4513
  function resolveElements(elementOrSelector, scope, selectorCache) {
4442
4514
  if (elementOrSelector == null) {
4443
4515
  return [];
@@ -4583,7 +4655,9 @@
4583
4655
  * that works across iframes
4584
4656
  */
4585
4657
  function isHTMLElement(element) {
4586
- return isObject(element) && "offsetHeight" in element;
4658
+ return (isObject(element) &&
4659
+ "offsetHeight" in element &&
4660
+ !("ownerSVGElement" in element));
4587
4661
  }
4588
4662
 
4589
4663
  const translateAlias$1 = {
@@ -5321,6 +5395,7 @@
5321
5395
  activeAnimation.stop();
5322
5396
  activeAnimation = null;
5323
5397
  }
5398
+ value.animation = undefined;
5324
5399
  };
5325
5400
  const startAnimation = () => {
5326
5401
  const currentValue = asNumber$1(value.get());
@@ -5352,8 +5427,10 @@
5352
5427
  // multiple calls within the same frame (e.g. rapid mouse events)
5353
5428
  const scheduleAnimation = () => {
5354
5429
  startAnimation();
5430
+ value.animation = activeAnimation ?? undefined;
5355
5431
  value["events"].animationStart?.notify();
5356
5432
  activeAnimation?.then(() => {
5433
+ value.animation = undefined;
5357
5434
  value["events"].animationComplete?.notify();
5358
5435
  });
5359
5436
  };
@@ -5363,7 +5440,16 @@
5363
5440
  frame.postRender(scheduleAnimation);
5364
5441
  }, stopAnimation);
5365
5442
  if (isMotionValue(source)) {
5366
- const removeSourceOnChange = source.on("change", (v) => value.set(parseValue(v, unit)));
5443
+ let skipNextAnimation = options.skipInitialAnimation === true;
5444
+ const removeSourceOnChange = source.on("change", (v) => {
5445
+ if (skipNextAnimation) {
5446
+ skipNextAnimation = false;
5447
+ value.jump(parseValue(v, unit), false);
5448
+ }
5449
+ else {
5450
+ value.set(parseValue(v, unit));
5451
+ }
5452
+ });
5367
5453
  const removeValueOnDestroy = value.on("destroy", removeSourceOnChange);
5368
5454
  return () => {
5369
5455
  removeSourceOnChange();
@@ -6643,10 +6729,8 @@
6643
6729
  node.options.layoutScroll &&
6644
6730
  node.scroll &&
6645
6731
  node !== node.root) {
6646
- transformBox(box, {
6647
- x: -node.scroll.offset.x,
6648
- y: -node.scroll.offset.y,
6649
- });
6732
+ translateAxis(box.x, -node.scroll.offset.x);
6733
+ translateAxis(box.y, -node.scroll.offset.y);
6650
6734
  }
6651
6735
  if (delta) {
6652
6736
  // Incoporate each ancestor's scale into a cumulative treeScale for this component
@@ -6673,8 +6757,8 @@
6673
6757
  }
6674
6758
  }
6675
6759
  function translateAxis(axis, distance) {
6676
- axis.min = axis.min + distance;
6677
- axis.max = axis.max + distance;
6760
+ axis.min += distance;
6761
+ axis.max += distance;
6678
6762
  }
6679
6763
  /**
6680
6764
  * Apply a transform to an axis from the latest resolved motion values.
@@ -7841,8 +7925,13 @@
7841
7925
  return transform || "none";
7842
7926
  }
7843
7927
 
7844
- const borders = ["TopLeft", "TopRight", "BottomLeft", "BottomRight"];
7845
- const numBorders = borders.length;
7928
+ const borderLabels = [
7929
+ "borderTopLeftRadius",
7930
+ "borderTopRightRadius",
7931
+ "borderBottomLeftRadius",
7932
+ "borderBottomRightRadius",
7933
+ ];
7934
+ const numBorders = borderLabels.length;
7846
7935
  const asNumber = (value) => typeof value === "string" ? parseFloat(value) : value;
7847
7936
  const isPx = (value) => typeof value === "number" || px.test(value);
7848
7937
  function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnlyMember) {
@@ -7857,7 +7946,7 @@
7857
7946
  * Mix border radius
7858
7947
  */
7859
7948
  for (let i = 0; i < numBorders; i++) {
7860
- const borderLabel = `border${borders[i]}Radius`;
7949
+ const borderLabel = borderLabels[i];
7861
7950
  let followRadius = getRadius(follow, borderLabel);
7862
7951
  let leadRadius = getRadius(lead, borderLabel);
7863
7952
  if (followRadius === undefined && leadRadius === undefined)
@@ -8502,6 +8591,11 @@
8502
8591
  }
8503
8592
  else {
8504
8593
  this.isUpdating = false;
8594
+ /**
8595
+ * Ensure animation-blocked nodes (e.g. during drag)
8596
+ * get measured even when memoized (willUpdate skipped).
8597
+ */
8598
+ this.nodes.forEach(ensureDraggedNodesSnapshotted);
8505
8599
  /**
8506
8600
  * Write
8507
8601
  */
@@ -8600,7 +8694,8 @@
8600
8694
  const prevLayout = this.layout;
8601
8695
  this.layout = this.measure(false);
8602
8696
  this.layoutVersion++;
8603
- this.layoutCorrected = createBox();
8697
+ if (!this.layoutCorrected)
8698
+ this.layoutCorrected = createBox();
8604
8699
  this.isLayoutDirty = false;
8605
8700
  this.projectionDelta = undefined;
8606
8701
  this.notifyListeners("measure", this.layout.layoutBox);
@@ -8711,8 +8806,8 @@
8711
8806
  }
8712
8807
  return boxWithoutScroll;
8713
8808
  }
8714
- applyTransform(box, transformOnly = false) {
8715
- const withTransforms = createBox();
8809
+ applyTransform(box, transformOnly = false, output) {
8810
+ const withTransforms = output || createBox();
8716
8811
  copyBoxInto(withTransforms, box);
8717
8812
  for (let i = 0; i < this.path.length; i++) {
8718
8813
  const node = this.path[i];
@@ -8720,10 +8815,8 @@
8720
8815
  node.options.layoutScroll &&
8721
8816
  node.scroll &&
8722
8817
  node !== node.root) {
8723
- transformBox(withTransforms, {
8724
- x: -node.scroll.offset.x,
8725
- y: -node.scroll.offset.y,
8726
- });
8818
+ translateAxis(withTransforms.x, -node.scroll.offset.x);
8819
+ translateAxis(withTransforms.y, -node.scroll.offset.y);
8727
8820
  }
8728
8821
  if (!hasTransform(node.latestValues))
8729
8822
  continue;
@@ -8866,8 +8959,7 @@
8866
8959
  }
8867
8960
  else if (this.targetDelta) {
8868
8961
  if (Boolean(this.resumingFrom)) {
8869
- // TODO: This is creating a new object every frame
8870
- this.target = this.applyTransform(this.layout.layoutBox);
8962
+ this.applyTransform(this.layout.layoutBox, false, this.target);
8871
8963
  }
8872
8964
  else {
8873
8965
  copyBoxInto(this.target, this.layout.layoutBox);
@@ -9477,6 +9569,12 @@
9477
9569
  axisSnapshot.max = axisSnapshot.min + length;
9478
9570
  });
9479
9571
  }
9572
+ else if (animationType === "x" || animationType === "y") {
9573
+ const snapAxis = animationType === "x" ? "y" : "x";
9574
+ copyAxisInto(isShared
9575
+ ? snapshot.measuredBox[snapAxis]
9576
+ : snapshot.layoutBox[snapAxis], layout[snapAxis]);
9577
+ }
9480
9578
  else if (shouldAnimatePositionOnly(animationType, snapshot.layoutBox, layout)) {
9481
9579
  eachAxis((axis) => {
9482
9580
  const axisSnapshot = isShared
@@ -9592,6 +9690,18 @@
9592
9690
  function clearIsLayoutDirty(node) {
9593
9691
  node.isLayoutDirty = false;
9594
9692
  }
9693
+ /**
9694
+ * When a node is animation-blocked (e.g. during drag) and its component
9695
+ * didn't re-render (memoized), willUpdate() is never called so there's
9696
+ * no snapshot. Use the previous layout as a snapshot and mark dirty so
9697
+ * resetTransform/updateLayout/notifyLayoutUpdate process it normally.
9698
+ */
9699
+ function ensureDraggedNodesSnapshotted(node) {
9700
+ if (node.isAnimationBlocked && node.layout && !node.isLayoutDirty) {
9701
+ node.snapshot = node.layout;
9702
+ node.isLayoutDirty = true;
9703
+ }
9704
+ }
9595
9705
  function resetTransformStyle(node) {
9596
9706
  const { visualElement } = node.options;
9597
9707
  if (visualElement && visualElement.getProps().onBeforeLayoutMeasure) {
@@ -10147,6 +10257,7 @@
10147
10257
  `);
10148
10258
  }
10149
10259
  return () => {
10260
+ ref.current?.removeAttribute("data-motion-pop-id");
10150
10261
  if (parent.contains(style)) {
10151
10262
  parent.removeChild(style);
10152
10263
  }
@@ -10425,8 +10536,8 @@
10425
10536
  if (exitingComponents.current.has(key)) {
10426
10537
  return;
10427
10538
  }
10428
- exitingComponents.current.add(key);
10429
10539
  if (exitComplete.has(key)) {
10540
+ exitingComponents.current.add(key);
10430
10541
  exitComplete.set(key, true);
10431
10542
  }
10432
10543
  else {
@@ -10706,8 +10817,12 @@
10706
10817
  * We attempt to import this package but require won't be defined in esm environments, in that case
10707
10818
  * isPropValid will have to be provided via `MotionContext`. In a 6.0.0 this should probably be removed
10708
10819
  * in favour of explicit injection.
10820
+ *
10821
+ * String concatenation prevents bundlers like webpack (e.g. Storybook)
10822
+ * from statically resolving this optional dependency at build time.
10709
10823
  */
10710
- loadExternalIsValidProp(require("@emotion/is-prop-valid").default);
10824
+ const emotionPkg = "@emotion/is-prop-" + "valid";
10825
+ loadExternalIsValidProp(require(emotionPkg).default);
10711
10826
  }
10712
10827
  catch {
10713
10828
  // We don't need to actually do anything here - the fallback is the existing `isPropValid`.
@@ -10724,6 +10839,8 @@
10724
10839
  */
10725
10840
  if (key === "values" && typeof props.values === "object")
10726
10841
  continue;
10842
+ if (isMotionValue(props[key]))
10843
+ continue;
10727
10844
  if (shouldForward(key) ||
10728
10845
  (forwardMotionProps === true && isValidMotionProp(key)) ||
10729
10846
  (!isDom && !isValidMotionProp(key)) ||
@@ -11436,6 +11553,7 @@
11436
11553
  constructor() {
11437
11554
  super(...arguments);
11438
11555
  this.id = id$1++;
11556
+ this.isExitComplete = false;
11439
11557
  }
11440
11558
  update() {
11441
11559
  if (!this.node.presenceContext)
@@ -11445,9 +11563,38 @@
11445
11563
  if (!this.node.animationState || isPresent === prevIsPresent) {
11446
11564
  return;
11447
11565
  }
11566
+ if (isPresent && prevIsPresent === false) {
11567
+ /**
11568
+ * When re-entering, if the exit animation already completed
11569
+ * (element is at rest), reset to initial values so the enter
11570
+ * animation replays from the correct position.
11571
+ */
11572
+ if (this.isExitComplete) {
11573
+ const { initial, custom } = this.node.getProps();
11574
+ if (typeof initial === "string") {
11575
+ const resolved = resolveVariant(this.node, initial, custom);
11576
+ if (resolved) {
11577
+ const { transition, transitionEnd, ...target } = resolved;
11578
+ for (const key in target) {
11579
+ this.node
11580
+ .getValue(key)
11581
+ ?.jump(target[key]);
11582
+ }
11583
+ }
11584
+ }
11585
+ this.node.animationState.reset();
11586
+ this.node.animationState.animateChanges();
11587
+ }
11588
+ else {
11589
+ this.node.animationState.setActive("exit", false);
11590
+ }
11591
+ this.isExitComplete = false;
11592
+ return;
11593
+ }
11448
11594
  const exitAnimation = this.node.animationState.setActive("exit", !isPresent);
11449
11595
  if (onExitComplete && !isPresent) {
11450
11596
  exitAnimation.then(() => {
11597
+ this.isExitComplete = true;
11451
11598
  onExitComplete(this.id);
11452
11599
  });
11453
11600
  }
@@ -12174,7 +12321,8 @@
12174
12321
  return;
12175
12322
  }
12176
12323
  let transition = (constraints && constraints[axis]) || {};
12177
- if (dragSnapToOrigin)
12324
+ if (dragSnapToOrigin === true ||
12325
+ dragSnapToOrigin === axis)
12178
12326
  transition = { min: 0, max: 0 };
12179
12327
  /**
12180
12328
  * Overdamp the boundary spring if `dragElastic` is disabled. There's still a frame
@@ -12815,7 +12963,7 @@
12815
12963
  this.isInView = false;
12816
12964
  }
12817
12965
  startObserver() {
12818
- this.unmount();
12966
+ this.stopObserver?.();
12819
12967
  const { viewport = {} } = this.node.getProps();
12820
12968
  const { root, margin: rootMargin, amount = "some", once } = viewport;
12821
12969
  const options = {
@@ -12852,7 +13000,7 @@
12852
13000
  const callback = isIntersecting ? onViewportEnter : onViewportLeave;
12853
13001
  callback && callback(entry);
12854
13002
  };
12855
- return observeIntersection(this.node.current, options, onIntersectionUpdate);
13003
+ this.stopObserver = observeIntersection(this.node.current, options, onIntersectionUpdate);
12856
13004
  }
12857
13005
  mount() {
12858
13006
  this.startObserver();
@@ -12866,7 +13014,11 @@
12866
13014
  this.startObserver();
12867
13015
  }
12868
13016
  }
12869
- unmount() { }
13017
+ unmount() {
13018
+ this.stopObserver?.();
13019
+ this.hasEnteredView = false;
13020
+ this.isInView = false;
13021
+ }
12870
13022
  }
12871
13023
  function hasViewportOptionChanged({ viewport = {} }, { viewport: prevViewport = {} } = {}) {
12872
13024
  return (name) => viewport[name] !== prevViewport[name];
@@ -14320,16 +14472,48 @@
14320
14472
  [ScrollOffset.Any, "cover"],
14321
14473
  [ScrollOffset.All, "contain"],
14322
14474
  ];
14323
- function matchesPreset(offset, preset) {
14475
+ const stringToProgress = {
14476
+ start: 0,
14477
+ end: 1,
14478
+ };
14479
+ function parseStringOffset(s) {
14480
+ const parts = s.trim().split(/\s+/);
14481
+ if (parts.length !== 2)
14482
+ return undefined;
14483
+ const a = stringToProgress[parts[0]];
14484
+ const b = stringToProgress[parts[1]];
14485
+ if (a === undefined || b === undefined)
14486
+ return undefined;
14487
+ return [a, b];
14488
+ }
14489
+ function normaliseOffset(offset) {
14324
14490
  if (offset.length !== 2)
14491
+ return undefined;
14492
+ const result = [];
14493
+ for (const item of offset) {
14494
+ if (Array.isArray(item)) {
14495
+ result.push(item);
14496
+ }
14497
+ else if (typeof item === "string") {
14498
+ const parsed = parseStringOffset(item);
14499
+ if (!parsed)
14500
+ return undefined;
14501
+ result.push(parsed);
14502
+ }
14503
+ else {
14504
+ return undefined;
14505
+ }
14506
+ }
14507
+ return result;
14508
+ }
14509
+ function matchesPreset(offset, preset) {
14510
+ const normalised = normaliseOffset(offset);
14511
+ if (!normalised)
14325
14512
  return false;
14326
14513
  for (let i = 0; i < 2; i++) {
14327
- const o = offset[i];
14514
+ const o = normalised[i];
14328
14515
  const p = preset[i];
14329
- if (!Array.isArray(o) ||
14330
- o.length !== 2 ||
14331
- o[0] !== p[0] ||
14332
- o[1] !== p[1])
14516
+ if (o[0] !== p[0] || o[1] !== p[1])
14333
14517
  return false;
14334
14518
  }
14335
14519
  return true;