framer-motion 7.6.4 → 7.6.6

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 (39) hide show
  1. package/dist/cjs/index.js +789 -850
  2. package/dist/es/animation/use-animated-state.mjs +29 -17
  3. package/dist/es/gestures/drag/VisualElementDragControls.mjs +14 -10
  4. package/dist/es/gestures/use-focus-gesture.mjs +1 -1
  5. package/dist/es/gestures/use-tap-gesture.mjs +1 -1
  6. package/dist/es/index.mjs +1 -1
  7. package/dist/es/motion/features/viewport/use-viewport.mjs +2 -2
  8. package/dist/es/motion/utils/use-visual-element.mjs +3 -3
  9. package/dist/es/projection/node/create-projection-node.mjs +74 -60
  10. package/dist/es/render/VisualElement.mjs +480 -0
  11. package/dist/es/render/dom/DOMVisualElement.mjs +49 -0
  12. package/dist/es/render/dom/create-visual-element.mjs +4 -4
  13. package/dist/es/render/dom/utils/css-variables-conversion.mjs +2 -2
  14. package/dist/es/render/dom/utils/unit-conversion.mjs +4 -4
  15. package/dist/es/render/html/HTMLVisualElement.mjs +41 -0
  16. package/dist/es/render/svg/{visual-element.mjs → SVGVisualElement.mjs} +21 -15
  17. package/dist/es/render/utils/animation.mjs +4 -4
  18. package/dist/es/render/utils/motion-values.mjs +1 -1
  19. package/dist/es/render/utils/resolve-dynamic-variants.mjs +2 -2
  20. package/dist/es/render/utils/setters.mjs +2 -1
  21. package/dist/es/value/index.mjs +1 -1
  22. package/dist/framer-motion.dev.js +789 -850
  23. package/dist/framer-motion.js +1 -1
  24. package/dist/index.d.ts +2101 -1931
  25. package/dist/projection.dev.js +1053 -1136
  26. package/dist/size-rollup-dom-animation-m.js +1 -1
  27. package/dist/size-rollup-dom-animation.js +1 -1
  28. package/dist/size-rollup-dom-max-assets.js +1 -1
  29. package/dist/size-rollup-dom-max.js +1 -1
  30. package/dist/size-rollup-m.js +1 -1
  31. package/dist/size-rollup-motion.js +1 -1
  32. package/dist/size-webpack-dom-animation.js +1 -1
  33. package/dist/size-webpack-dom-max.js +1 -1
  34. package/dist/size-webpack-m.js +1 -1
  35. package/dist/three-entry.d.ts +1956 -1745
  36. package/package.json +8 -8
  37. package/dist/es/render/html/visual-element.mjs +0 -109
  38. package/dist/es/render/index.mjs +0 -515
  39. package/dist/es/render/utils/lifecycles.mjs +0 -43
@@ -1242,7 +1242,7 @@
1242
1242
  * This will be replaced by the build step with the latest version number.
1243
1243
  * When MotionValues are provided to motion components, warn if versions are mixed.
1244
1244
  */
1245
- this.version = "7.6.4";
1245
+ this.version = "7.6.6";
1246
1246
  /**
1247
1247
  * Duration, in milliseconds, since last updating frame.
1248
1248
  *
@@ -2570,7 +2570,7 @@
2570
2570
  const animationTarget = 1000;
2571
2571
  function createProjectionNode({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) {
2572
2572
  return class ProjectionNode {
2573
- constructor(id, latestValues = {}, parent = defaultParent === null || defaultParent === void 0 ? void 0 : defaultParent()) {
2573
+ constructor(elementId, latestValues = {}, parent = defaultParent === null || defaultParent === void 0 ? void 0 : defaultParent()) {
2574
2574
  /**
2575
2575
  * A Set containing all this component's children. This is used to iterate
2576
2576
  * through the children.
@@ -2623,7 +2623,7 @@
2623
2623
  /**
2624
2624
  * An object representing the calculated contextual/accumulated/tree scale.
2625
2625
  * This will be used to scale calculcated projection transforms, as these are
2626
- * calculated in screen-space but need to be scaled for elements to actually
2626
+ * calculated in screen-space but need to be scaled for elements to layoutly
2627
2627
  * make it to their calculated destinations.
2628
2628
  *
2629
2629
  * TODO: Lazy-init
@@ -2653,13 +2653,13 @@
2653
2653
  */
2654
2654
  // TODO Only running on root node
2655
2655
  this.sharedNodes = new Map();
2656
- this.id = id;
2656
+ this.elementId = elementId;
2657
2657
  this.latestValues = latestValues;
2658
2658
  this.root = parent ? parent.root || parent : this;
2659
2659
  this.path = parent ? [...parent.path, parent] : [];
2660
2660
  this.parent = parent;
2661
2661
  this.depth = parent ? parent.depth + 1 : 0;
2662
- id && this.root.registerPotentialNode(id, this);
2662
+ elementId && this.root.registerPotentialNode(elementId, this);
2663
2663
  for (let i = 0; i < this.path.length; i++) {
2664
2664
  this.path[i].shouldResetTransform = true;
2665
2665
  }
@@ -2693,12 +2693,12 @@
2693
2693
  instance instanceof SVGElement && instance.tagName !== "svg";
2694
2694
  this.instance = instance;
2695
2695
  const { layoutId, layout, visualElement } = this.options;
2696
- if (visualElement && !visualElement.getInstance()) {
2696
+ if (visualElement && !visualElement.current) {
2697
2697
  visualElement.mount(instance);
2698
2698
  }
2699
2699
  this.root.nodes.add(this);
2700
2700
  (_a = this.parent) === null || _a === void 0 ? void 0 : _a.children.add(this);
2701
- this.id && this.root.potentialNodes.delete(this.id);
2701
+ this.elementId && this.root.potentialNodes.delete(this.elementId);
2702
2702
  if (isLayoutDirty && (layout || layoutId)) {
2703
2703
  this.isLayoutDirty = true;
2704
2704
  }
@@ -2914,14 +2914,7 @@
2914
2914
  updateSnapshot() {
2915
2915
  if (this.snapshot || !this.instance)
2916
2916
  return;
2917
- const measured = this.measure();
2918
- const layout = this.removeTransform(this.removeElementScroll(measured));
2919
- roundBox(layout);
2920
- this.snapshot = {
2921
- measured,
2922
- layout,
2923
- latestValues: {},
2924
- };
2917
+ this.snapshot = this.measure();
2925
2918
  }
2926
2919
  updateLayout() {
2927
2920
  var _a;
@@ -2946,18 +2939,13 @@
2946
2939
  node.updateScroll();
2947
2940
  }
2948
2941
  }
2949
- const measured = this.measure();
2950
- roundBox(measured);
2951
2942
  const prevLayout = this.layout;
2952
- this.layout = {
2953
- measured,
2954
- actual: this.removeElementScroll(measured),
2955
- };
2943
+ this.layout = this.measure(false);
2956
2944
  this.layoutCorrected = createBox();
2957
2945
  this.isLayoutDirty = false;
2958
2946
  this.projectionDelta = undefined;
2959
- this.notifyListeners("measure", this.layout.actual);
2960
- (_a = this.options.visualElement) === null || _a === void 0 ? void 0 : _a.notifyLayoutMeasure(this.layout.actual, prevLayout === null || prevLayout === void 0 ? void 0 : prevLayout.actual);
2947
+ this.notifyListeners("measure", this.layout.layoutBox);
2948
+ (_a = this.options.visualElement) === null || _a === void 0 ? void 0 : _a.notify("LayoutMeasure", this.layout.layoutBox, prevLayout === null || prevLayout === void 0 ? void 0 : prevLayout.layoutBox);
2961
2949
  }
2962
2950
  updateScroll() {
2963
2951
  if (this.options.layoutScroll && this.instance) {
@@ -2983,7 +2971,25 @@
2983
2971
  this.scheduleRender();
2984
2972
  }
2985
2973
  }
2986
- measure() {
2974
+ measure(removeTransform = true) {
2975
+ const pageBox = this.measurePageBox();
2976
+ let layoutBox = this.removeElementScroll(pageBox);
2977
+ /**
2978
+ * Measurements taken during the pre-render stage
2979
+ * still have transforms applied so we remove them
2980
+ * via calculation.
2981
+ */
2982
+ if (removeTransform) {
2983
+ layoutBox = this.removeTransform(layoutBox);
2984
+ }
2985
+ roundBox(layoutBox);
2986
+ return {
2987
+ measuredBox: pageBox,
2988
+ layoutBox,
2989
+ latestValues: {},
2990
+ };
2991
+ }
2992
+ measurePageBox() {
2987
2993
  const { visualElement } = this.options;
2988
2994
  if (!visualElement)
2989
2995
  return createBox();
@@ -3064,9 +3070,9 @@
3064
3070
  continue;
3065
3071
  hasScale(node.latestValues) && node.updateSnapshot();
3066
3072
  const sourceBox = createBox();
3067
- const nodeBox = node.measure();
3073
+ const nodeBox = node.measurePageBox();
3068
3074
  copyBoxInto(sourceBox, nodeBox);
3069
- removeBoxTransforms(boxWithoutTransform, node.latestValues, (_a = node.snapshot) === null || _a === void 0 ? void 0 : _a.layout, sourceBox);
3075
+ removeBoxTransforms(boxWithoutTransform, node.latestValues, (_a = node.snapshot) === null || _a === void 0 ? void 0 : _a.layoutBox, sourceBox);
3070
3076
  }
3071
3077
  if (hasTransform(this.latestValues)) {
3072
3078
  removeBoxTransforms(boxWithoutTransform, this.latestValues);
@@ -3115,13 +3121,17 @@
3115
3121
  // TODO If this is unsuccessful this currently happens every frame
3116
3122
  if (!this.targetDelta && !this.relativeTarget) {
3117
3123
  // TODO: This is a semi-repetition of further down this function, make DRY
3118
- this.relativeParent = this.getClosestProjectingParent();
3119
- if (this.relativeParent && this.relativeParent.layout) {
3124
+ const relativeParent = this.getClosestProjectingParent();
3125
+ if (relativeParent && relativeParent.layout) {
3126
+ this.relativeParent = relativeParent;
3120
3127
  this.relativeTarget = createBox();
3121
3128
  this.relativeTargetOrigin = createBox();
3122
- calcRelativePosition(this.relativeTargetOrigin, this.layout.actual, this.relativeParent.layout.actual);
3129
+ calcRelativePosition(this.relativeTargetOrigin, this.layout.layoutBox, relativeParent.layout.layoutBox);
3123
3130
  copyBoxInto(this.relativeTarget, this.relativeTargetOrigin);
3124
3131
  }
3132
+ else {
3133
+ this.relativeParent = this.relativeTarget = undefined;
3134
+ }
3125
3135
  }
3126
3136
  /**
3127
3137
  * If we have no relative target or no target delta our target isn't valid
@@ -3150,10 +3160,10 @@
3150
3160
  else if (this.targetDelta) {
3151
3161
  if (Boolean(this.resumingFrom)) {
3152
3162
  // TODO: This is creating a new object every frame
3153
- this.target = this.applyTransform(this.layout.actual);
3163
+ this.target = this.applyTransform(this.layout.layoutBox);
3154
3164
  }
3155
3165
  else {
3156
- copyBoxInto(this.target, this.layout.actual);
3166
+ copyBoxInto(this.target, this.layout.layoutBox);
3157
3167
  }
3158
3168
  applyBoxDelta(this.target, this.targetDelta);
3159
3169
  }
@@ -3161,24 +3171,28 @@
3161
3171
  /**
3162
3172
  * If no target, use own layout as target
3163
3173
  */
3164
- copyBoxInto(this.target, this.layout.actual);
3174
+ copyBoxInto(this.target, this.layout.layoutBox);
3165
3175
  }
3166
3176
  /**
3167
3177
  * If we've been told to attempt to resolve a relative target, do so.
3168
3178
  */
3169
3179
  if (this.attemptToResolveRelativeTarget) {
3170
3180
  this.attemptToResolveRelativeTarget = false;
3171
- this.relativeParent = this.getClosestProjectingParent();
3172
- if (this.relativeParent &&
3173
- Boolean(this.relativeParent.resumingFrom) ===
3181
+ const relativeParent = this.getClosestProjectingParent();
3182
+ if (relativeParent &&
3183
+ Boolean(relativeParent.resumingFrom) ===
3174
3184
  Boolean(this.resumingFrom) &&
3175
- !this.relativeParent.options.layoutScroll &&
3176
- this.relativeParent.target) {
3185
+ !relativeParent.options.layoutScroll &&
3186
+ relativeParent.target) {
3187
+ this.relativeParent = relativeParent;
3177
3188
  this.relativeTarget = createBox();
3178
3189
  this.relativeTargetOrigin = createBox();
3179
- calcRelativePosition(this.relativeTargetOrigin, this.target, this.relativeParent.target);
3190
+ calcRelativePosition(this.relativeTargetOrigin, this.target, relativeParent.target);
3180
3191
  copyBoxInto(this.relativeTarget, this.relativeTargetOrigin);
3181
3192
  }
3193
+ else {
3194
+ this.relativeParent = this.relativeTarget = undefined;
3195
+ }
3182
3196
  }
3183
3197
  }
3184
3198
  getClosestProjectingParent() {
@@ -3214,7 +3228,7 @@
3214
3228
  * Reset the corrected box with the latest values from box, as we're then going
3215
3229
  * to perform mutative operations on it.
3216
3230
  */
3217
- copyBoxInto(this.layoutCorrected, this.layout.actual);
3231
+ copyBoxInto(this.layoutCorrected, this.layout.layoutBox);
3218
3232
  /**
3219
3233
  * Apply all the parent deltas to this box to produce the corrected box. This
3220
3234
  * is the layout box, as it will appear on screen as a result of the transforms of its parents.
@@ -3291,7 +3305,7 @@
3291
3305
  this.relativeTargetOrigin &&
3292
3306
  this.layout &&
3293
3307
  ((_a = this.relativeParent) === null || _a === void 0 ? void 0 : _a.layout)) {
3294
- calcRelativePosition(relativeLayout, this.layout.actual, this.relativeParent.layout.actual);
3308
+ calcRelativePosition(relativeLayout, this.layout.layoutBox, this.relativeParent.layout.layoutBox);
3295
3309
  mixBox(this.relativeTarget, this.relativeTargetOrigin, relativeLayout, progress);
3296
3310
  }
3297
3311
  if (isSharedLayoutAnimation) {
@@ -3375,12 +3389,12 @@
3375
3389
  if (this !== lead &&
3376
3390
  this.layout &&
3377
3391
  layout &&
3378
- shouldAnimatePositionOnly(this.options.animationType, this.layout.actual, layout.actual)) {
3392
+ shouldAnimatePositionOnly(this.options.animationType, this.layout.layoutBox, layout.layoutBox)) {
3379
3393
  target = this.target || createBox();
3380
- const xLength = calcLength(this.layout.actual.x);
3394
+ const xLength = calcLength(this.layout.layoutBox.x);
3381
3395
  target.x.min = lead.target.x.min;
3382
3396
  target.x.max = target.x.min + xLength;
3383
- const yLength = calcLength(this.layout.actual.y);
3397
+ const yLength = calcLength(this.layout.layoutBox.y);
3384
3398
  target.y.min = lead.target.y.min;
3385
3399
  target.y.max = target.y.min + yLength;
3386
3400
  }
@@ -3394,7 +3408,7 @@
3394
3408
  /**
3395
3409
  * Update the delta between the corrected box and the final target box, after
3396
3410
  * user-set transforms are applied to it. This will be used by the renderer to
3397
- * create a transform style that will reproject the element from its actual layout
3411
+ * create a transform style that will reproject the element from its layout layout
3398
3412
  * into the desired bounding box.
3399
3413
  */
3400
3414
  calcBoxDelta(this.projectionDeltaWithTransform, this.layoutCorrected, targetWithTransforms, latestValues);
@@ -3477,7 +3491,7 @@
3477
3491
  return;
3478
3492
  // Force a render of this element to apply the transform with all rotations
3479
3493
  // set to 0.
3480
- visualElement === null || visualElement === void 0 ? void 0 : visualElement.syncRender();
3494
+ visualElement === null || visualElement === void 0 ? void 0 : visualElement.render();
3481
3495
  // Put back all the values we reset
3482
3496
  for (const key in resetValues) {
3483
3497
  visualElement.setStaticValue(key, resetValues[key]);
@@ -3550,7 +3564,7 @@
3550
3564
  }
3551
3565
  else {
3552
3566
  /**
3553
- * Or we're not animating at all, set the lead component to its actual
3567
+ * Or we're not animating at all, set the lead component to its layout
3554
3568
  * opacity and other components to hidden.
3555
3569
  */
3556
3570
  styles.opacity =
@@ -3614,53 +3628,53 @@
3614
3628
  node.layout &&
3615
3629
  snapshot &&
3616
3630
  node.hasListeners("didUpdate")) {
3617
- const { actual: layout, measured: measuredLayout } = node.layout;
3631
+ const { layoutBox: layout, measuredBox: measuredLayout } = node.layout;
3618
3632
  const { animationType } = node.options;
3619
3633
  // TODO Maybe we want to also resize the layout snapshot so we don't trigger
3620
3634
  // animations for instance if layout="size" and an element has only changed position
3621
3635
  if (animationType === "size") {
3622
3636
  eachAxis((axis) => {
3623
3637
  const axisSnapshot = snapshot.isShared
3624
- ? snapshot.measured[axis]
3625
- : snapshot.layout[axis];
3638
+ ? snapshot.measuredBox[axis]
3639
+ : snapshot.layoutBox[axis];
3626
3640
  const length = calcLength(axisSnapshot);
3627
3641
  axisSnapshot.min = layout[axis].min;
3628
3642
  axisSnapshot.max = axisSnapshot.min + length;
3629
3643
  });
3630
3644
  }
3631
- else if (shouldAnimatePositionOnly(animationType, snapshot.layout, layout)) {
3645
+ else if (shouldAnimatePositionOnly(animationType, snapshot.layoutBox, layout)) {
3632
3646
  eachAxis((axis) => {
3633
3647
  const axisSnapshot = snapshot.isShared
3634
- ? snapshot.measured[axis]
3635
- : snapshot.layout[axis];
3648
+ ? snapshot.measuredBox[axis]
3649
+ : snapshot.layoutBox[axis];
3636
3650
  const length = calcLength(layout[axis]);
3637
3651
  axisSnapshot.max = axisSnapshot.min + length;
3638
3652
  });
3639
3653
  }
3640
3654
  const layoutDelta = createDelta();
3641
- calcBoxDelta(layoutDelta, layout, snapshot.layout);
3655
+ calcBoxDelta(layoutDelta, layout, snapshot.layoutBox);
3642
3656
  const visualDelta = createDelta();
3643
3657
  if (snapshot.isShared) {
3644
- calcBoxDelta(visualDelta, node.applyTransform(measuredLayout, true), snapshot.measured);
3658
+ calcBoxDelta(visualDelta, node.applyTransform(measuredLayout, true), snapshot.measuredBox);
3645
3659
  }
3646
3660
  else {
3647
- calcBoxDelta(visualDelta, layout, snapshot.layout);
3661
+ calcBoxDelta(visualDelta, layout, snapshot.layoutBox);
3648
3662
  }
3649
3663
  const hasLayoutChanged = !isDeltaZero(layoutDelta);
3650
3664
  let hasRelativeTargetChanged = false;
3651
3665
  if (!node.resumeFrom) {
3652
- node.relativeParent = node.getClosestProjectingParent();
3666
+ const relativeParent = node.getClosestProjectingParent();
3653
3667
  /**
3654
3668
  * If the relativeParent is itself resuming from a different element then
3655
3669
  * the relative snapshot is not relavent
3656
3670
  */
3657
- if (node.relativeParent && !node.relativeParent.resumeFrom) {
3658
- const { snapshot: parentSnapshot, layout: parentLayout } = node.relativeParent;
3671
+ if (relativeParent && !relativeParent.resumeFrom) {
3672
+ const { snapshot: parentSnapshot, layout: parentLayout } = relativeParent;
3659
3673
  if (parentSnapshot && parentLayout) {
3660
3674
  const relativeSnapshot = createBox();
3661
- calcRelativePosition(relativeSnapshot, snapshot.layout, parentSnapshot.layout);
3675
+ calcRelativePosition(relativeSnapshot, snapshot.layoutBox, parentSnapshot.layoutBox);
3662
3676
  const relativeLayout = createBox();
3663
- calcRelativePosition(relativeLayout, layout, parentLayout.actual);
3677
+ calcRelativePosition(relativeLayout, layout, parentLayout.layoutBox);
3664
3678
  if (!boxEquals(relativeSnapshot, relativeLayout)) {
3665
3679
  hasRelativeTargetChanged = true;
3666
3680
  }
@@ -3695,7 +3709,7 @@
3695
3709
  function resetTransformStyle(node) {
3696
3710
  const { visualElement } = node.options;
3697
3711
  if (visualElement === null || visualElement === void 0 ? void 0 : visualElement.getProps().onBeforeLayoutMeasure) {
3698
- visualElement.notifyBeforeLayoutMeasure();
3712
+ visualElement.notify("BeforeLayoutMeasure");
3699
3713
  }
3700
3714
  node.resetTransform();
3701
3715
  }
@@ -3976,7 +3990,7 @@
3976
3990
  * @internal
3977
3991
  */
3978
3992
  function resolveCSSVariables(visualElement, { ...target }, transitionEnd) {
3979
- const element = visualElement.getInstance();
3993
+ const element = visualElement.current;
3980
3994
  if (!(element instanceof Element))
3981
3995
  return { target, transitionEnd };
3982
3996
  // If `transitionEnd` isn't `undefined`, clone it. We could clone `target` and `transitionEnd`
@@ -3985,7 +3999,7 @@
3985
3999
  transitionEnd = { ...transitionEnd };
3986
4000
  }
3987
4001
  // Go through existing `MotionValue`s and ensure any existing CSS variables are resolved
3988
- visualElement.forEachValue((value) => {
4002
+ visualElement.values.forEach((value) => {
3989
4003
  const current = value.get();
3990
4004
  if (!isCSSVariable$1(current))
3991
4005
  return;
@@ -4066,33 +4080,148 @@
4066
4080
  },
4067
4081
  };
4068
4082
 
4069
- const isBrowser = typeof document !== "undefined";
4083
+ /**
4084
+ * Returns true if the provided key is a CSS variable
4085
+ */
4086
+ function isCSSVariable(key) {
4087
+ return key.startsWith("--");
4088
+ }
4070
4089
 
4071
- // Does this device prefer reduced motion? Returns `null` server-side.
4072
- const prefersReducedMotion = { current: null };
4073
- const hasReducedMotionListener = { current: false };
4090
+ /**
4091
+ * Provided a value and a ValueType, returns the value as that value type.
4092
+ */
4093
+ const getValueAsType = (value, type) => {
4094
+ return type && typeof value === "number"
4095
+ ? type.transform(value)
4096
+ : value;
4097
+ };
4074
4098
 
4075
- function initPrefersReducedMotion() {
4076
- hasReducedMotionListener.current = true;
4077
- if (!isBrowser)
4078
- return;
4079
- if (window.matchMedia) {
4080
- const motionMediaQuery = window.matchMedia("(prefers-reduced-motion)");
4081
- const setReducedMotionPreferences = () => (prefersReducedMotion.current = motionMediaQuery.matches);
4082
- motionMediaQuery.addListener(setReducedMotionPreferences);
4083
- setReducedMotionPreferences();
4099
+ function buildHTMLStyles(state, latestValues, options, transformTemplate) {
4100
+ const { style, vars, transform, transformKeys, transformOrigin } = state;
4101
+ transformKeys.length = 0;
4102
+ // Track whether we encounter any transform or transformOrigin values.
4103
+ let hasTransform = false;
4104
+ let hasTransformOrigin = false;
4105
+ // Does the calculated transform essentially equal "none"?
4106
+ let transformIsNone = true;
4107
+ /**
4108
+ * Loop over all our latest animated values and decide whether to handle them
4109
+ * as a style or CSS variable.
4110
+ *
4111
+ * Transforms and transform origins are kept seperately for further processing.
4112
+ */
4113
+ for (const key in latestValues) {
4114
+ const value = latestValues[key];
4115
+ /**
4116
+ * If this is a CSS variable we don't do any further processing.
4117
+ */
4118
+ if (isCSSVariable(key)) {
4119
+ vars[key] = value;
4120
+ continue;
4121
+ }
4122
+ // Convert the value to its default value type, ie 0 -> "0px"
4123
+ const valueType = numberValueTypes[key];
4124
+ const valueAsType = getValueAsType(value, valueType);
4125
+ if (transformProps.has(key)) {
4126
+ // If this is a transform, flag to enable further transform processing
4127
+ hasTransform = true;
4128
+ transform[key] = valueAsType;
4129
+ transformKeys.push(key);
4130
+ // If we already know we have a non-default transform, early return
4131
+ if (!transformIsNone)
4132
+ continue;
4133
+ // Otherwise check to see if this is a default transform
4134
+ if (value !== (valueType.default || 0))
4135
+ transformIsNone = false;
4136
+ }
4137
+ else if (key.startsWith("origin")) {
4138
+ // If this is a transform origin, flag and enable further transform-origin processing
4139
+ hasTransformOrigin = true;
4140
+ transformOrigin[key] = valueAsType;
4141
+ }
4142
+ else {
4143
+ style[key] = valueAsType;
4144
+ }
4084
4145
  }
4085
- else {
4086
- prefersReducedMotion.current = false;
4146
+ if (!latestValues.transform) {
4147
+ if (hasTransform || transformTemplate) {
4148
+ style.transform = buildTransform(state, options, transformIsNone, transformTemplate);
4149
+ }
4150
+ else if (style.transform) {
4151
+ /**
4152
+ * If we have previously created a transform but currently don't have any,
4153
+ * reset transform style to none.
4154
+ */
4155
+ style.transform = "none";
4156
+ }
4157
+ }
4158
+ /**
4159
+ * Build a transformOrigin style. Uses the same defaults as the browser for
4160
+ * undefined origins.
4161
+ */
4162
+ if (hasTransformOrigin) {
4163
+ const { originX = "50%", originY = "50%", originZ = 0, } = transformOrigin;
4164
+ style.transformOrigin = `${originX} ${originY} ${originZ}`;
4087
4165
  }
4088
4166
  }
4089
4167
 
4090
- function isWillChangeMotionValue(value) {
4091
- return Boolean(isMotionValue(value) && value.add);
4168
+ function isForcedMotionValue(key, { layout, layoutId }) {
4169
+ return (transformProps.has(key) ||
4170
+ key.startsWith("origin") ||
4171
+ ((layout || layoutId !== undefined) &&
4172
+ (!!scaleCorrectors[key] || key === "opacity")));
4092
4173
  }
4093
4174
 
4094
- function isAnimationControls(v) {
4095
- return typeof v === "object" && typeof v.start === "function";
4175
+ function scrapeMotionValuesFromProps(props) {
4176
+ const { style } = props;
4177
+ const newValues = {};
4178
+ for (const key in style) {
4179
+ if (isMotionValue(style[key]) || isForcedMotionValue(key, props)) {
4180
+ newValues[key] = style[key];
4181
+ }
4182
+ }
4183
+ return newValues;
4184
+ }
4185
+
4186
+ function renderHTML(element, { style, vars }, styleProp, projection) {
4187
+ Object.assign(element.style, style, projection && projection.getProjectionStyles(styleProp));
4188
+ // Loop over any CSS variables and assign those.
4189
+ for (const key in vars) {
4190
+ element.style.setProperty(key, vars[key]);
4191
+ }
4192
+ }
4193
+
4194
+ /**
4195
+ * Bounding boxes tend to be defined as top, left, right, bottom. For various operations
4196
+ * it's easier to consider each axis individually. This function returns a bounding box
4197
+ * as a map of single-axis min/max values.
4198
+ */
4199
+ function convertBoundingBoxToBox({ top, left, right, bottom, }) {
4200
+ return {
4201
+ x: { min: left, max: right },
4202
+ y: { min: top, max: bottom },
4203
+ };
4204
+ }
4205
+ /**
4206
+ * Applies a TransformPoint function to a bounding box. TransformPoint is usually a function
4207
+ * provided by Framer to allow measured points to be corrected for device scaling. This is used
4208
+ * when measuring DOM elements and DOM event points.
4209
+ */
4210
+ function transformBoxPoints(point, transformPoint) {
4211
+ if (!transformPoint)
4212
+ return point;
4213
+ const topLeft = transformPoint({ x: point.left, y: point.top });
4214
+ const bottomRight = transformPoint({ x: point.right, y: point.bottom });
4215
+ return {
4216
+ top: topLeft.y,
4217
+ left: topLeft.x,
4218
+ bottom: bottomRight.y,
4219
+ right: bottomRight.x,
4220
+ };
4221
+ }
4222
+
4223
+ function measureViewportBox(instance, transformPoint) {
4224
+ return convertBoundingBoxToBox(transformBoxPoints(instance.getBoundingClientRect(), transformPoint));
4096
4225
  }
4097
4226
 
4098
4227
  /**
@@ -4204,7 +4333,8 @@
4204
4333
  if (origin[key] === undefined) {
4205
4334
  origin[key] = value;
4206
4335
  }
4207
- visualElement.setBaseTarget(key, value);
4336
+ if (value !== null)
4337
+ visualElement.setBaseTarget(key, value);
4208
4338
  }
4209
4339
  }
4210
4340
  function getOriginFromTransition(key, transition) {
@@ -4226,141 +4356,345 @@
4226
4356
  return origin;
4227
4357
  }
4228
4358
 
4229
- /**
4230
- * Decides if the supplied variable is variant label
4231
- */
4232
- function isVariantLabel(v) {
4233
- return typeof v === "string" || Array.isArray(v);
4234
- }
4235
-
4236
- var AnimationType;
4237
- (function (AnimationType) {
4238
- AnimationType["Animate"] = "animate";
4239
- AnimationType["Hover"] = "whileHover";
4240
- AnimationType["Tap"] = "whileTap";
4241
- AnimationType["Drag"] = "whileDrag";
4242
- AnimationType["Focus"] = "whileFocus";
4243
- AnimationType["InView"] = "whileInView";
4244
- AnimationType["Exit"] = "exit";
4245
- })(AnimationType || (AnimationType = {}));
4246
-
4247
- const variantPriorityOrder = [
4248
- AnimationType.Animate,
4249
- AnimationType.InView,
4250
- AnimationType.Focus,
4251
- AnimationType.Hover,
4252
- AnimationType.Tap,
4253
- AnimationType.Drag,
4254
- AnimationType.Exit,
4255
- ];
4256
-
4257
- const names = [
4258
- "LayoutMeasure",
4259
- "BeforeLayoutMeasure",
4260
- "LayoutUpdate",
4261
- "ViewportBoxUpdate",
4262
- "Update",
4263
- "Render",
4264
- "AnimationComplete",
4265
- "LayoutAnimationComplete",
4266
- "AnimationStart",
4267
- "LayoutAnimationStart",
4268
- "SetAxisTarget",
4269
- "Unmount",
4270
- ];
4271
- function createLifecycles() {
4272
- const managers = names.map(() => new SubscriptionManager());
4273
- const propSubscriptions = {};
4274
- const lifecycles = {
4275
- clearAllListeners: () => managers.forEach((manager) => manager.clear()),
4276
- updatePropListeners: (props) => {
4277
- names.forEach((name) => {
4278
- var _a;
4279
- const on = "on" + name;
4280
- const propListener = props[on];
4281
- // Unsubscribe existing subscription
4282
- (_a = propSubscriptions[name]) === null || _a === void 0 ? void 0 : _a.call(propSubscriptions);
4283
- // Add new subscription
4284
- if (propListener) {
4285
- propSubscriptions[name] = lifecycles[on](propListener);
4286
- }
4287
- });
4288
- },
4289
- };
4290
- managers.forEach((manager, i) => {
4291
- lifecycles["on" + names[i]] = (handler) => manager.add(handler);
4292
- lifecycles["notify" + names[i]] = (...args) => manager.notify(...args);
4293
- });
4294
- return lifecycles;
4295
- }
4296
-
4297
- const warned = new Set();
4298
- function warnOnce(condition, message, element) {
4299
- if (condition || warned.has(message))
4300
- return;
4301
- console.warn(message);
4302
- if (element)
4303
- console.warn(element);
4304
- warned.add(message);
4305
- }
4359
+ const isBrowser = typeof document !== "undefined";
4306
4360
 
4307
- function updateMotionValuesFromProps(element, next, prev) {
4308
- const { willChange } = next;
4309
- for (const key in next) {
4310
- const nextValue = next[key];
4311
- const prevValue = prev[key];
4312
- if (isMotionValue(nextValue)) {
4313
- /**
4314
- * If this is a motion value found in props or style, we want to add it
4315
- * to our visual element's motion value map.
4316
- */
4317
- element.addValue(key, nextValue);
4318
- if (isWillChangeMotionValue(willChange)) {
4319
- willChange.add(key);
4320
- }
4321
- /**
4322
- * Check the version of the incoming motion value with this version
4323
- * and warn against mismatches.
4324
- */
4325
- {
4326
- warnOnce(nextValue.version === "7.6.4", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.6.4 may not work as expected.`);
4327
- }
4328
- }
4329
- else if (isMotionValue(prevValue)) {
4330
- /**
4331
- * If we're swapping from a motion value to a static value,
4332
- * create a new motion value from that
4333
- */
4334
- element.addValue(key, motionValue(nextValue));
4335
- if (isWillChangeMotionValue(willChange)) {
4336
- willChange.remove(key);
4337
- }
4338
- }
4339
- else if (prevValue !== nextValue) {
4340
- /**
4341
- * If this is a flat value that has changed, update the motion value
4342
- * or create one if it doesn't exist. We only want to do this if we're
4343
- * not handling the value with our animation state.
4344
- */
4345
- if (element.hasValue(key)) {
4346
- const existingValue = element.getValue(key);
4347
- // TODO: Only update values that aren't being animated or even looked at
4348
- !existingValue.hasAnimated && existingValue.set(nextValue);
4361
+ const positionalKeys = new Set([
4362
+ "width",
4363
+ "height",
4364
+ "top",
4365
+ "left",
4366
+ "right",
4367
+ "bottom",
4368
+ "x",
4369
+ "y",
4370
+ ]);
4371
+ const isPositionalKey = (key) => positionalKeys.has(key);
4372
+ const hasPositionalKey = (target) => {
4373
+ return Object.keys(target).some(isPositionalKey);
4374
+ };
4375
+ const setAndResetVelocity = (value, to) => {
4376
+ // Looks odd but setting it twice doesn't render, it'll just
4377
+ // set both prev and current to the latest value
4378
+ value.set(to, false);
4379
+ value.set(to);
4380
+ };
4381
+ const isNumOrPxType = (v) => v === number || v === px;
4382
+ var BoundingBoxDimension;
4383
+ (function (BoundingBoxDimension) {
4384
+ BoundingBoxDimension["width"] = "width";
4385
+ BoundingBoxDimension["height"] = "height";
4386
+ BoundingBoxDimension["left"] = "left";
4387
+ BoundingBoxDimension["right"] = "right";
4388
+ BoundingBoxDimension["top"] = "top";
4389
+ BoundingBoxDimension["bottom"] = "bottom";
4390
+ })(BoundingBoxDimension || (BoundingBoxDimension = {}));
4391
+ const getPosFromMatrix = (matrix, pos) => parseFloat(matrix.split(", ")[pos]);
4392
+ const getTranslateFromMatrix = (pos2, pos3) => (_bbox, { transform }) => {
4393
+ if (transform === "none" || !transform)
4394
+ return 0;
4395
+ const matrix3d = transform.match(/^matrix3d\((.+)\)$/);
4396
+ if (matrix3d) {
4397
+ return getPosFromMatrix(matrix3d[1], pos3);
4398
+ }
4399
+ else {
4400
+ const matrix = transform.match(/^matrix\((.+)\)$/);
4401
+ if (matrix) {
4402
+ return getPosFromMatrix(matrix[1], pos2);
4403
+ }
4404
+ else {
4405
+ return 0;
4406
+ }
4407
+ }
4408
+ };
4409
+ const transformKeys = new Set(["x", "y", "z"]);
4410
+ const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key));
4411
+ function removeNonTranslationalTransform(visualElement) {
4412
+ const removedTransforms = [];
4413
+ nonTranslationalTransformKeys.forEach((key) => {
4414
+ const value = visualElement.getValue(key);
4415
+ if (value !== undefined) {
4416
+ removedTransforms.push([key, value.get()]);
4417
+ value.set(key.startsWith("scale") ? 1 : 0);
4418
+ }
4419
+ });
4420
+ // Apply changes to element before measurement
4421
+ if (removedTransforms.length)
4422
+ visualElement.render();
4423
+ return removedTransforms;
4424
+ }
4425
+ const positionalValues = {
4426
+ // Dimensions
4427
+ width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight),
4428
+ height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom),
4429
+ top: (_bbox, { top }) => parseFloat(top),
4430
+ left: (_bbox, { left }) => parseFloat(left),
4431
+ bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min),
4432
+ right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min),
4433
+ // Transform
4434
+ x: getTranslateFromMatrix(4, 13),
4435
+ y: getTranslateFromMatrix(5, 14),
4436
+ };
4437
+ const convertChangedValueTypes = (target, visualElement, changedKeys) => {
4438
+ const originBbox = visualElement.measureViewportBox();
4439
+ const element = visualElement.current;
4440
+ const elementComputedStyle = getComputedStyle(element);
4441
+ const { display } = elementComputedStyle;
4442
+ const origin = {};
4443
+ // If the element is currently set to display: "none", make it visible before
4444
+ // measuring the target bounding box
4445
+ if (display === "none") {
4446
+ visualElement.setStaticValue("display", target.display || "block");
4447
+ }
4448
+ /**
4449
+ * Record origins before we render and update styles
4450
+ */
4451
+ changedKeys.forEach((key) => {
4452
+ origin[key] = positionalValues[key](originBbox, elementComputedStyle);
4453
+ });
4454
+ // Apply the latest values (as set in checkAndConvertChangedValueTypes)
4455
+ visualElement.render();
4456
+ const targetBbox = visualElement.measureViewportBox();
4457
+ changedKeys.forEach((key) => {
4458
+ // Restore styles to their **calculated computed style**, not their actual
4459
+ // originally set style. This allows us to animate between equivalent pixel units.
4460
+ const value = visualElement.getValue(key);
4461
+ setAndResetVelocity(value, origin[key]);
4462
+ target[key] = positionalValues[key](targetBbox, elementComputedStyle);
4463
+ });
4464
+ return target;
4465
+ };
4466
+ const checkAndConvertChangedValueTypes = (visualElement, target, origin = {}, transitionEnd = {}) => {
4467
+ target = { ...target };
4468
+ transitionEnd = { ...transitionEnd };
4469
+ const targetPositionalKeys = Object.keys(target).filter(isPositionalKey);
4470
+ // We want to remove any transform values that could affect the element's bounding box before
4471
+ // it's measured. We'll reapply these later.
4472
+ let removedTransformValues = [];
4473
+ let hasAttemptedToRemoveTransformValues = false;
4474
+ const changedValueTypeKeys = [];
4475
+ targetPositionalKeys.forEach((key) => {
4476
+ const value = visualElement.getValue(key);
4477
+ if (!visualElement.hasValue(key))
4478
+ return;
4479
+ let from = origin[key];
4480
+ let fromType = findDimensionValueType(from);
4481
+ const to = target[key];
4482
+ let toType;
4483
+ // TODO: The current implementation of this basically throws an error
4484
+ // if you try and do value conversion via keyframes. There's probably
4485
+ // a way of doing this but the performance implications would need greater scrutiny,
4486
+ // as it'd be doing multiple resize-remeasure operations.
4487
+ if (isKeyframesTarget(to)) {
4488
+ const numKeyframes = to.length;
4489
+ const fromIndex = to[0] === null ? 1 : 0;
4490
+ from = to[fromIndex];
4491
+ fromType = findDimensionValueType(from);
4492
+ for (let i = fromIndex; i < numKeyframes; i++) {
4493
+ if (!toType) {
4494
+ toType = findDimensionValueType(to[i]);
4495
+ invariant(toType === fromType ||
4496
+ (isNumOrPxType(fromType) && isNumOrPxType(toType)), "Keyframes must be of the same dimension as the current value");
4497
+ }
4498
+ else {
4499
+ invariant(findDimensionValueType(to[i]) === toType, "All keyframes must be of the same type");
4500
+ }
4501
+ }
4502
+ }
4503
+ else {
4504
+ toType = findDimensionValueType(to);
4505
+ }
4506
+ if (fromType !== toType) {
4507
+ // If they're both just number or px, convert them both to numbers rather than
4508
+ // relying on resize/remeasure to convert (which is wasteful in this situation)
4509
+ if (isNumOrPxType(fromType) && isNumOrPxType(toType)) {
4510
+ const current = value.get();
4511
+ if (typeof current === "string") {
4512
+ value.set(parseFloat(current));
4513
+ }
4514
+ if (typeof to === "string") {
4515
+ target[key] = parseFloat(to);
4516
+ }
4517
+ else if (Array.isArray(to) && toType === px) {
4518
+ target[key] = to.map(parseFloat);
4519
+ }
4520
+ }
4521
+ else if ((fromType === null || fromType === void 0 ? void 0 : fromType.transform) &&
4522
+ (toType === null || toType === void 0 ? void 0 : toType.transform) &&
4523
+ (from === 0 || to === 0)) {
4524
+ // If one or the other value is 0, it's safe to coerce it to the
4525
+ // type of the other without measurement
4526
+ if (from === 0) {
4527
+ value.set(toType.transform(from));
4528
+ }
4529
+ else {
4530
+ target[key] = fromType.transform(to);
4531
+ }
4349
4532
  }
4350
4533
  else {
4351
- const latestValue = element.getStaticValue(key);
4352
- element.addValue(key, motionValue(latestValue !== undefined ? latestValue : nextValue));
4534
+ // If we're going to do value conversion via DOM measurements, we first
4535
+ // need to remove non-positional transform values that could affect the bbox measurements.
4536
+ if (!hasAttemptedToRemoveTransformValues) {
4537
+ removedTransformValues =
4538
+ removeNonTranslationalTransform(visualElement);
4539
+ hasAttemptedToRemoveTransformValues = true;
4540
+ }
4541
+ changedValueTypeKeys.push(key);
4542
+ transitionEnd[key] =
4543
+ transitionEnd[key] !== undefined
4544
+ ? transitionEnd[key]
4545
+ : target[key];
4546
+ setAndResetVelocity(value, to);
4353
4547
  }
4354
4548
  }
4549
+ });
4550
+ if (changedValueTypeKeys.length) {
4551
+ const scrollY = changedValueTypeKeys.indexOf("height") >= 0
4552
+ ? window.pageYOffset
4553
+ : null;
4554
+ const convertedTarget = convertChangedValueTypes(target, visualElement, changedValueTypeKeys);
4555
+ // If we removed transform values, reapply them before the next render
4556
+ if (removedTransformValues.length) {
4557
+ removedTransformValues.forEach(([key, value]) => {
4558
+ visualElement.getValue(key).set(value);
4559
+ });
4560
+ }
4561
+ // Reapply original values
4562
+ visualElement.render();
4563
+ // Restore scroll position
4564
+ if (isBrowser && scrollY !== null) {
4565
+ window.scrollTo({ top: scrollY });
4566
+ }
4567
+ return { target: convertedTarget, transitionEnd };
4355
4568
  }
4356
- // Handle removed values
4357
- for (const key in prev) {
4358
- if (next[key] === undefined)
4359
- element.removeValue(key);
4569
+ else {
4570
+ return { target, transitionEnd };
4360
4571
  }
4361
- return next;
4572
+ };
4573
+ /**
4574
+ * Convert value types for x/y/width/height/top/left/bottom/right
4575
+ *
4576
+ * Allows animation between `'auto'` -> `'100%'` or `0` -> `'calc(50% - 10vw)'`
4577
+ *
4578
+ * @internal
4579
+ */
4580
+ function unitConversion(visualElement, target, origin, transitionEnd) {
4581
+ return hasPositionalKey(target)
4582
+ ? checkAndConvertChangedValueTypes(visualElement, target, origin, transitionEnd)
4583
+ : { target, transitionEnd };
4584
+ }
4585
+
4586
+ /**
4587
+ * Parse a DOM variant to make it animatable. This involves resolving CSS variables
4588
+ * and ensuring animations like "20%" => "calc(50vw)" are performed in pixels.
4589
+ */
4590
+ const parseDomVariant = (visualElement, target, origin, transitionEnd) => {
4591
+ const resolved = resolveCSSVariables(visualElement, target, transitionEnd);
4592
+ target = resolved.target;
4593
+ transitionEnd = resolved.transitionEnd;
4594
+ return unitConversion(visualElement, target, origin, transitionEnd);
4595
+ };
4596
+
4597
+ const createDefinition = (propNames) => ({
4598
+ isEnabled: (props) => propNames.some((name) => !!props[name]),
4599
+ });
4600
+ const featureDefinitions = {
4601
+ measureLayout: createDefinition(["layout", "layoutId", "drag"]),
4602
+ animation: createDefinition([
4603
+ "animate",
4604
+ "exit",
4605
+ "variants",
4606
+ "whileHover",
4607
+ "whileTap",
4608
+ "whileFocus",
4609
+ "whileDrag",
4610
+ "whileInView",
4611
+ ]),
4612
+ exit: createDefinition(["exit"]),
4613
+ drag: createDefinition(["drag", "dragControls"]),
4614
+ focus: createDefinition(["whileFocus"]),
4615
+ hover: createDefinition(["whileHover", "onHoverStart", "onHoverEnd"]),
4616
+ tap: createDefinition(["whileTap", "onTap", "onTapStart", "onTapCancel"]),
4617
+ pan: createDefinition([
4618
+ "onPan",
4619
+ "onPanStart",
4620
+ "onPanSessionStart",
4621
+ "onPanEnd",
4622
+ ]),
4623
+ inView: createDefinition([
4624
+ "whileInView",
4625
+ "onViewportEnter",
4626
+ "onViewportLeave",
4627
+ ]),
4628
+ };
4629
+
4630
+ function isRefObject(ref) {
4631
+ return (typeof ref === "object" &&
4632
+ Object.prototype.hasOwnProperty.call(ref, "current"));
4633
+ }
4634
+
4635
+ /**
4636
+ * Browser-safe usage of process
4637
+ */
4638
+ const defaultEnvironment = "production";
4639
+ const env = typeof process === "undefined" || process.env === undefined
4640
+ ? defaultEnvironment
4641
+ : "development" ;
4642
+
4643
+ // Does this device prefer reduced motion? Returns `null` server-side.
4644
+ const prefersReducedMotion = { current: null };
4645
+ const hasReducedMotionListener = { current: false };
4646
+
4647
+ function initPrefersReducedMotion() {
4648
+ hasReducedMotionListener.current = true;
4649
+ if (!isBrowser)
4650
+ return;
4651
+ if (window.matchMedia) {
4652
+ const motionMediaQuery = window.matchMedia("(prefers-reduced-motion)");
4653
+ const setReducedMotionPreferences = () => (prefersReducedMotion.current = motionMediaQuery.matches);
4654
+ motionMediaQuery.addListener(setReducedMotionPreferences);
4655
+ setReducedMotionPreferences();
4656
+ }
4657
+ else {
4658
+ prefersReducedMotion.current = false;
4659
+ }
4660
+ }
4661
+
4662
+ function isWillChangeMotionValue(value) {
4663
+ return Boolean(isMotionValue(value) && value.add);
4362
4664
  }
4363
4665
 
4666
+ function isAnimationControls(v) {
4667
+ return typeof v === "object" && typeof v.start === "function";
4668
+ }
4669
+
4670
+ /**
4671
+ * Decides if the supplied variable is variant label
4672
+ */
4673
+ function isVariantLabel(v) {
4674
+ return typeof v === "string" || Array.isArray(v);
4675
+ }
4676
+
4677
+ var AnimationType;
4678
+ (function (AnimationType) {
4679
+ AnimationType["Animate"] = "animate";
4680
+ AnimationType["Hover"] = "whileHover";
4681
+ AnimationType["Tap"] = "whileTap";
4682
+ AnimationType["Drag"] = "whileDrag";
4683
+ AnimationType["Focus"] = "whileFocus";
4684
+ AnimationType["InView"] = "whileInView";
4685
+ AnimationType["Exit"] = "exit";
4686
+ })(AnimationType || (AnimationType = {}));
4687
+
4688
+ const variantPriorityOrder = [
4689
+ AnimationType.Animate,
4690
+ AnimationType.InView,
4691
+ AnimationType.Focus,
4692
+ AnimationType.Hover,
4693
+ AnimationType.Tap,
4694
+ AnimationType.Drag,
4695
+ AnimationType.Exit,
4696
+ ];
4697
+
4364
4698
  const variantProps$1 = [
4365
4699
  "initial",
4366
4700
  "animate",
@@ -4379,989 +4713,552 @@
4379
4713
  return Boolean(isControllingVariants(props) || props.variants);
4380
4714
  }
4381
4715
 
4382
- /**
4383
- * Browser-safe usage of process
4384
- */
4385
- const defaultEnvironment = "production";
4386
- const env = typeof process === "undefined" || process.env === undefined
4387
- ? defaultEnvironment
4388
- : "development" ;
4389
-
4390
- const createDefinition = (propNames) => ({
4391
- isEnabled: (props) => propNames.some((name) => !!props[name]),
4392
- });
4393
- const featureDefinitions = {
4394
- measureLayout: createDefinition(["layout", "layoutId", "drag"]),
4395
- animation: createDefinition([
4396
- "animate",
4397
- "exit",
4398
- "variants",
4399
- "whileHover",
4400
- "whileTap",
4401
- "whileFocus",
4402
- "whileDrag",
4403
- "whileInView",
4404
- ]),
4405
- exit: createDefinition(["exit"]),
4406
- drag: createDefinition(["drag", "dragControls"]),
4407
- focus: createDefinition(["whileFocus"]),
4408
- hover: createDefinition(["whileHover", "onHoverStart", "onHoverEnd"]),
4409
- tap: createDefinition(["whileTap", "onTap", "onTapStart", "onTapCancel"]),
4410
- pan: createDefinition([
4411
- "onPan",
4412
- "onPanStart",
4413
- "onPanSessionStart",
4414
- "onPanEnd",
4415
- ]),
4416
- inView: createDefinition([
4417
- "whileInView",
4418
- "onViewportEnter",
4419
- "onViewportLeave",
4420
- ]),
4421
- };
4422
-
4423
- function isRefObject(ref) {
4424
- return (typeof ref === "object" &&
4425
- Object.prototype.hasOwnProperty.call(ref, "current"));
4716
+ const warned = new Set();
4717
+ function warnOnce(condition, message, element) {
4718
+ if (condition || warned.has(message))
4719
+ return;
4720
+ console.warn(message);
4721
+ if (element)
4722
+ console.warn(element);
4723
+ warned.add(message);
4426
4724
  }
4427
4725
 
4428
- const featureNames = Object.keys(featureDefinitions);
4429
- const numFeatures = featureNames.length;
4430
- const visualElement = ({ treeType = "", build, getBaseTarget, makeTargetAnimatable, measureViewportBox, render: renderInstance, readValueFromInstance, removeValueFromRenderState, sortNodePosition, scrapeMotionValuesFromProps, }) => ({ parent, props, presenceId, blockInitialAnimation, visualState, reducedMotionConfig, }, options = {}) => {
4431
- let isMounted = false;
4432
- const { latestValues, renderState } = visualState;
4433
- /**
4434
- * The instance of the render-specific node that will be hydrated by the
4435
- * exposed React ref. So for example, this visual element can host a
4436
- * HTMLElement, plain object, or Three.js object. The functions provided
4437
- * in VisualElementConfig allow us to interface with this instance.
4438
- */
4439
- let instance;
4440
- /**
4441
- * Manages the subscriptions for a visual element's lifecycle, for instance
4442
- * onRender
4443
- */
4444
- const lifecycles = createLifecycles();
4445
- /**
4446
- * A map of all motion values attached to this visual element. Motion
4447
- * values are source of truth for any given animated value. A motion
4448
- * value might be provided externally by the component via props.
4449
- */
4450
- const values = new Map();
4451
- /**
4452
- * A map of every subscription that binds the provided or generated
4453
- * motion values onChange listeners to this visual element.
4454
- */
4455
- const valueSubscriptions = new Map();
4456
- /**
4457
- * A reference to the previously-provided motion values as returned
4458
- * from scrapeMotionValuesFromProps. We use the keys in here to determine
4459
- * if any motion values need to be removed after props are updated.
4460
- */
4461
- let prevMotionValues = {};
4462
- /**
4463
- * When values are removed from all animation props we need to search
4464
- * for a fallback value to animate to. These values are tracked in baseTarget.
4465
- */
4466
- const baseTarget = {
4467
- ...latestValues,
4468
- };
4469
- /**
4470
- * Create an object of the values we initially animated from (if initial prop present).
4471
- */
4472
- const initialValues = props.initial ? { ...latestValues } : {};
4473
- // Internal methods ========================
4474
- /**
4475
- * On mount, this will be hydrated with a callback to disconnect
4476
- * this visual element from its parent on unmount.
4477
- */
4478
- let removeFromVariantTree;
4479
- /**
4480
- * Render the element with the latest styles outside of the React
4481
- * render lifecycle
4482
- */
4483
- function render() {
4484
- if (!instance || !isMounted)
4485
- return;
4486
- triggerBuild();
4487
- renderInstance(instance, renderState, props.style, element.projection);
4488
- }
4489
- function triggerBuild() {
4490
- build(element, renderState, latestValues, options, props);
4491
- }
4492
- function update() {
4493
- lifecycles.notifyUpdate(latestValues);
4494
- }
4495
- /**
4496
- *
4497
- */
4498
- function bindToMotionValue(key, value) {
4499
- const removeOnChange = value.onChange((latestValue) => {
4500
- latestValues[key] = latestValue;
4501
- props.onUpdate && sync.update(update, false, true);
4502
- });
4503
- const removeOnRenderRequest = value.onRenderRequest(element.scheduleRender);
4504
- valueSubscriptions.set(key, () => {
4505
- removeOnChange();
4506
- removeOnRenderRequest();
4507
- });
4508
- }
4509
- /**
4510
- * Any motion values that are provided to the element when created
4511
- * aren't yet bound to the element, as this would technically be impure.
4512
- * However, we iterate through the motion values and set them to the
4513
- * initial values for this component.
4514
- *
4515
- * TODO: This is impure and we should look at changing this to run on mount.
4516
- * Doing so will break some tests but this isn't neccessarily a breaking change,
4517
- * more a reflection of the test.
4518
- */
4519
- const { willChange, ...initialMotionValues } = scrapeMotionValuesFromProps(props);
4520
- for (const key in initialMotionValues) {
4521
- const value = initialMotionValues[key];
4522
- if (latestValues[key] !== undefined && isMotionValue(value)) {
4523
- value.set(latestValues[key], false);
4726
+ function updateMotionValuesFromProps(element, next, prev) {
4727
+ const { willChange } = next;
4728
+ for (const key in next) {
4729
+ const nextValue = next[key];
4730
+ const prevValue = prev[key];
4731
+ if (isMotionValue(nextValue)) {
4732
+ /**
4733
+ * If this is a motion value found in props or style, we want to add it
4734
+ * to our visual element's motion value map.
4735
+ */
4736
+ element.addValue(key, nextValue);
4524
4737
  if (isWillChangeMotionValue(willChange)) {
4525
4738
  willChange.add(key);
4526
4739
  }
4527
- }
4528
- }
4529
- /**
4530
- * Update external values with initial values
4531
- */
4532
- if (props.values) {
4533
- for (const key in props.values) {
4534
- const value = props.values[key];
4535
- if (latestValues[key] !== undefined && isMotionValue(value)) {
4536
- value.set(latestValues[key]);
4740
+ /**
4741
+ * Check the version of the incoming motion value with this version
4742
+ * and warn against mismatches.
4743
+ */
4744
+ {
4745
+ warnOnce(nextValue.version === "7.6.6", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.6.6 may not work as expected.`);
4537
4746
  }
4538
4747
  }
4539
- }
4540
- /**
4541
- * Determine what role this visual element should take in the variant tree.
4542
- */
4543
- const isControllingVariants$1 = isControllingVariants(props);
4544
- const isVariantNode$1 = isVariantNode(props);
4545
- const element = {
4546
- treeType,
4547
- /**
4548
- * This is a mirror of the internal instance prop, which keeps
4549
- * VisualElement type-compatible with React's RefObject.
4550
- */
4551
- current: null,
4552
- /**
4553
- * The depth of this visual element within the visual element tree.
4554
- */
4555
- depth: parent ? parent.depth + 1 : 0,
4556
- parent,
4557
- children: new Set(),
4558
- /**
4559
- *
4560
- */
4561
- presenceId,
4562
- shouldReduceMotion: null,
4563
- /**
4564
- * If this component is part of the variant tree, it should track
4565
- * any children that are also part of the tree. This is essentially
4566
- * a shadow tree to simplify logic around how to stagger over children.
4567
- */
4568
- variantChildren: isVariantNode$1 ? new Set() : undefined,
4569
- /**
4570
- * Whether this instance is visible. This can be changed imperatively
4571
- * by the projection tree, is analogous to CSS's visibility in that
4572
- * hidden elements should take up layout, and needs enacting by the configured
4573
- * render function.
4574
- */
4575
- isVisible: undefined,
4576
- /**
4577
- * Normally, if a component is controlled by a parent's variants, it can
4578
- * rely on that ancestor to trigger animations further down the tree.
4579
- * However, if a component is created after its parent is mounted, the parent
4580
- * won't trigger that mount animation so the child needs to.
4581
- *
4582
- * TODO: This might be better replaced with a method isParentMounted
4583
- */
4584
- manuallyAnimateOnMount: Boolean(parent === null || parent === void 0 ? void 0 : parent.isMounted()),
4585
- /**
4586
- * This can be set by AnimatePresence to force components that mount
4587
- * at the same time as it to mount as if they have initial={false} set.
4588
- */
4589
- blockInitialAnimation,
4590
- /**
4591
- * Determine whether this component has mounted yet. This is mostly used
4592
- * by variant children to determine whether they need to trigger their
4593
- * own animations on mount.
4594
- */
4595
- isMounted: () => Boolean(instance),
4596
- mount(newInstance) {
4597
- isMounted = true;
4598
- instance = element.current = newInstance;
4599
- if (element.projection) {
4600
- element.projection.mount(newInstance);
4601
- }
4602
- if (isVariantNode$1 && parent && !isControllingVariants$1) {
4603
- removeFromVariantTree = parent === null || parent === void 0 ? void 0 : parent.addVariantChild(element);
4604
- }
4605
- values.forEach((value, key) => bindToMotionValue(key, value));
4606
- if (!hasReducedMotionListener.current) {
4607
- initPrefersReducedMotion();
4608
- }
4609
- element.shouldReduceMotion =
4610
- reducedMotionConfig === "never"
4611
- ? false
4612
- : reducedMotionConfig === "always"
4613
- ? true
4614
- : prefersReducedMotion.current;
4615
- parent === null || parent === void 0 ? void 0 : parent.children.add(element);
4616
- element.setProps(props);
4617
- },
4618
- /**
4619
- *
4620
- */
4621
- unmount() {
4622
- var _a;
4623
- (_a = element.projection) === null || _a === void 0 ? void 0 : _a.unmount();
4624
- cancelSync.update(update);
4625
- cancelSync.render(render);
4626
- valueSubscriptions.forEach((remove) => remove());
4627
- removeFromVariantTree === null || removeFromVariantTree === void 0 ? void 0 : removeFromVariantTree();
4628
- parent === null || parent === void 0 ? void 0 : parent.children.delete(element);
4629
- lifecycles.clearAllListeners();
4630
- instance = undefined;
4631
- isMounted = false;
4632
- },
4633
- loadFeatures(renderedProps, isStrict, preloadedFeatures, projectionId, ProjectionNodeConstructor, initialLayoutGroupConfig) {
4634
- const features = [];
4748
+ else if (isMotionValue(prevValue)) {
4635
4749
  /**
4636
- * If we're in development mode, check to make sure we're not rendering a motion component
4637
- * as a child of LazyMotion, as this will break the file-size benefits of using it.
4750
+ * If we're swapping from a motion value to a static value,
4751
+ * create a new motion value from that
4638
4752
  */
4639
- if (env !== "production" && preloadedFeatures && isStrict) {
4640
- invariant(false, "You have rendered a `motion` component within a `LazyMotion` component. This will break tree shaking. Import and render a `m` component instead.");
4641
- }
4642
- for (let i = 0; i < numFeatures; i++) {
4643
- const name = featureNames[i];
4644
- const { isEnabled, Component } = featureDefinitions[name];
4645
- /**
4646
- * It might be possible in the future to use this moment to
4647
- * dynamically request functionality. In initial tests this
4648
- * was producing a lot of duplication amongst bundles.
4649
- */
4650
- if (isEnabled(renderedProps) && Component) {
4651
- features.push(react.createElement(Component, {
4652
- key: name,
4653
- ...renderedProps,
4654
- visualElement: element,
4655
- }));
4656
- }
4657
- }
4658
- if (!element.projection && ProjectionNodeConstructor) {
4659
- element.projection = new ProjectionNodeConstructor(projectionId, element.getLatestValues(), parent && parent.projection);
4660
- const { layoutId, layout, drag, dragConstraints, layoutScroll, } = renderedProps;
4661
- element.projection.setOptions({
4662
- layoutId,
4663
- layout,
4664
- alwaysMeasureLayout: Boolean(drag) ||
4665
- (dragConstraints && isRefObject(dragConstraints)),
4666
- visualElement: element,
4667
- scheduleRender: () => element.scheduleRender(),
4668
- /**
4669
- * TODO: Update options in an effect. This could be tricky as it'll be too late
4670
- * to update by the time layout animations run.
4671
- * We also need to fix this safeToRemove by linking it up to the one returned by usePresence,
4672
- * ensuring it gets called if there's no potential layout animations.
4673
- *
4674
- */
4675
- animationType: typeof layout === "string" ? layout : "both",
4676
- initialPromotionConfig: initialLayoutGroupConfig,
4677
- layoutScroll,
4678
- });
4679
- }
4680
- return features;
4681
- },
4682
- /**
4683
- * Add a child visual element to our set of children.
4684
- */
4685
- addVariantChild(child) {
4686
- var _a;
4687
- const closestVariantNode = element.getClosestVariantNode();
4688
- if (closestVariantNode) {
4689
- (_a = closestVariantNode.variantChildren) === null || _a === void 0 ? void 0 : _a.add(child);
4690
- return () => closestVariantNode.variantChildren.delete(child);
4753
+ element.addValue(key, motionValue(nextValue));
4754
+ if (isWillChangeMotionValue(willChange)) {
4755
+ willChange.remove(key);
4691
4756
  }
4692
- },
4693
- sortNodePosition(other) {
4757
+ }
4758
+ else if (prevValue !== nextValue) {
4694
4759
  /**
4695
- * If these nodes aren't even of the same type we can't compare their depth.
4760
+ * If this is a flat value that has changed, update the motion value
4761
+ * or create one if it doesn't exist. We only want to do this if we're
4762
+ * not handling the value with our animation state.
4696
4763
  */
4697
- if (!sortNodePosition || treeType !== other.treeType)
4698
- return 0;
4699
- return sortNodePosition(element.getInstance(), other.getInstance());
4700
- },
4701
- /**
4702
- * Returns the closest variant node in the tree starting from
4703
- * this visual element.
4704
- */
4705
- getClosestVariantNode: () => isVariantNode$1 ? element : parent === null || parent === void 0 ? void 0 : parent.getClosestVariantNode(),
4706
- /**
4707
- * Expose the latest layoutId prop.
4708
- */
4709
- getLayoutId: () => props.layoutId,
4710
- /**
4711
- * Returns the current instance.
4712
- */
4713
- getInstance: () => instance,
4714
- /**
4715
- * Get/set the latest static values.
4716
- */
4717
- getStaticValue: (key) => latestValues[key],
4718
- setStaticValue: (key, value) => (latestValues[key] = value),
4719
- /**
4720
- * Returns the latest motion value state. Currently only used to take
4721
- * a snapshot of the visual element - perhaps this can return the whole
4722
- * visual state
4723
- */
4724
- getLatestValues: () => latestValues,
4725
- /**
4726
- * Set the visiblity of the visual element. If it's changed, schedule
4727
- * a render to reflect these changes.
4728
- */
4729
- setVisibility(visibility) {
4730
- if (element.isVisible === visibility)
4731
- return;
4732
- element.isVisible = visibility;
4733
- element.scheduleRender();
4734
- },
4735
- /**
4736
- * Make a target animatable by Popmotion. For instance, if we're
4737
- * trying to animate width from 100px to 100vw we need to measure 100vw
4738
- * in pixels to determine what we really need to animate to. This is also
4739
- * pluggable to support Framer's custom value types like Color,
4740
- * and CSS variables.
4741
- */
4742
- makeTargetAnimatable(target, canMutate = true) {
4743
- return makeTargetAnimatable(element, target, props, canMutate);
4744
- },
4745
- /**
4746
- * Measure the current viewport box with or without transforms.
4747
- * Only measures axis-aligned boxes, rotate and skew must be manually
4748
- * removed with a re-render to work.
4749
- */
4750
- measureViewportBox() {
4751
- return measureViewportBox(instance, props);
4752
- },
4753
- // Motion values ========================
4754
- /**
4755
- * Add a motion value and bind it to this visual element.
4756
- */
4757
- addValue(key, value) {
4758
- // Remove existing value if it exists
4759
- if (element.hasValue(key))
4760
- element.removeValue(key);
4761
- values.set(key, value);
4762
- latestValues[key] = value.get();
4763
- bindToMotionValue(key, value);
4764
- },
4765
- /**
4766
- * Remove a motion value and unbind any active subscriptions.
4767
- */
4768
- removeValue(key) {
4769
- var _a;
4770
- values.delete(key);
4771
- (_a = valueSubscriptions.get(key)) === null || _a === void 0 ? void 0 : _a();
4772
- valueSubscriptions.delete(key);
4773
- delete latestValues[key];
4774
- removeValueFromRenderState(key, renderState);
4775
- },
4776
- /**
4777
- * Check whether we have a motion value for this key
4778
- */
4779
- hasValue: (key) => values.has(key),
4780
- /**
4781
- * Get a motion value for this key. If called with a default
4782
- * value, we'll create one if none exists.
4783
- */
4784
- getValue(key, defaultValue) {
4785
- if (props.values && props.values[key]) {
4786
- return props.values[key];
4764
+ if (element.hasValue(key)) {
4765
+ const existingValue = element.getValue(key);
4766
+ // TODO: Only update values that aren't being animated or even looked at
4767
+ !existingValue.hasAnimated && existingValue.set(nextValue);
4787
4768
  }
4788
- let value = values.get(key);
4789
- if (value === undefined && defaultValue !== undefined) {
4790
- value = motionValue(defaultValue);
4791
- element.addValue(key, value);
4769
+ else {
4770
+ const latestValue = element.getStaticValue(key);
4771
+ element.addValue(key, motionValue(latestValue !== undefined ? latestValue : nextValue));
4792
4772
  }
4793
- return value;
4794
- },
4795
- /**
4796
- * Iterate over our motion values.
4797
- */
4798
- forEachValue: (callback) => values.forEach(callback),
4799
- /**
4800
- * If we're trying to animate to a previously unencountered value,
4801
- * we need to check for it in our state and as a last resort read it
4802
- * directly from the instance (which might have performance implications).
4803
- */
4804
- readValue: (key) => latestValues[key] !== undefined
4805
- ? latestValues[key]
4806
- : readValueFromInstance(instance, key, options),
4807
- /**
4808
- * Set the base target to later animate back to. This is currently
4809
- * only hydrated on creation and when we first read a value.
4810
- */
4811
- setBaseTarget(key, value) {
4812
- baseTarget[key] = value;
4813
- },
4773
+ }
4774
+ }
4775
+ // Handle removed values
4776
+ for (const key in prev) {
4777
+ if (next[key] === undefined)
4778
+ element.removeValue(key);
4779
+ }
4780
+ return next;
4781
+ }
4782
+
4783
+ const featureNames = Object.keys(featureDefinitions);
4784
+ const numFeatures = featureNames.length;
4785
+ const propEventHandlers = [
4786
+ "AnimationStart",
4787
+ "AnimationComplete",
4788
+ "Update",
4789
+ "Unmount",
4790
+ "BeforeLayoutMeasure",
4791
+ "LayoutMeasure",
4792
+ "LayoutAnimationStart",
4793
+ "LayoutAnimationComplete",
4794
+ ];
4795
+ /**
4796
+ * A VisualElement is an imperative abstraction around UI elements such as
4797
+ * HTMLElement, SVGElement, Three.Object3D etc.
4798
+ */
4799
+ class VisualElement {
4800
+ constructor({ parent, props, reducedMotionConfig, visualState, }, options = {}) {
4814
4801
  /**
4815
- * Find the base target for a value thats been removed from all animation
4816
- * props.
4802
+ * A reference to the current underlying Instance, e.g. a HTMLElement
4803
+ * or Three.Mesh etc.
4817
4804
  */
4818
- getBaseTarget(key) {
4819
- var _a;
4820
- const { initial } = props;
4821
- const valueFromInitial = typeof initial === "string" || typeof initial === "object"
4822
- ? (_a = resolveVariantFromProps(props, initial)) === null || _a === void 0 ? void 0 : _a[key]
4823
- : undefined;
4824
- /**
4825
- * If this value still exists in the current initial variant, read that.
4826
- */
4827
- if (initial && valueFromInitial !== undefined) {
4828
- return valueFromInitial;
4829
- }
4830
- /**
4831
- * Alternatively, if this VisualElement config has defined a getBaseTarget
4832
- * so we can read the value from an alternative source, try that.
4833
- */
4834
- if (getBaseTarget) {
4835
- const target = getBaseTarget(props, key);
4836
- if (target !== undefined && !isMotionValue(target))
4837
- return target;
4838
- }
4839
- /**
4840
- * If the value was initially defined on initial, but it doesn't any more,
4841
- * return undefined. Otherwise return the value as initially read from the DOM.
4842
- */
4843
- return initialValues[key] !== undefined &&
4844
- valueFromInitial === undefined
4845
- ? undefined
4846
- : baseTarget[key];
4847
- },
4848
- // Lifecyles ========================
4849
- ...lifecycles,
4805
+ this.current = null;
4850
4806
  /**
4851
- * Build the renderer state based on the latest visual state.
4807
+ * A set containing references to this VisualElement's children.
4852
4808
  */
4853
- build() {
4854
- triggerBuild();
4855
- return renderState;
4856
- },
4809
+ this.children = new Set();
4857
4810
  /**
4858
- * Schedule a render on the next animation frame.
4811
+ * Determine what role this visual element should take in the variant tree.
4859
4812
  */
4860
- scheduleRender() {
4861
- sync.render(render, false, true);
4862
- },
4813
+ this.isVariantNode = false;
4814
+ this.isControllingVariants = false;
4863
4815
  /**
4864
- * Synchronously fire render. It's prefered that we batch renders but
4865
- * in many circumstances, like layout measurement, we need to run this
4866
- * synchronously. However in those instances other measures should be taken
4867
- * to batch reads/writes.
4816
+ * Decides whether this VisualElement should animate in reduced motion
4817
+ * mode.
4818
+ *
4819
+ * TODO: This is currently set on every individual VisualElement but feels
4820
+ * like it could be set globally.
4868
4821
  */
4869
- syncRender: render,
4822
+ this.shouldReduceMotion = null;
4870
4823
  /**
4871
- * Update the provided props. Ensure any newly-added motion values are
4872
- * added to our map, old ones removed, and listeners updated.
4824
+ * A map of all motion values attached to this visual element. Motion
4825
+ * values are source of truth for any given animated value. A motion
4826
+ * value might be provided externally by the component via props.
4873
4827
  */
4874
- setProps(newProps) {
4875
- if (newProps.transformTemplate || props.transformTemplate) {
4876
- element.scheduleRender();
4877
- }
4878
- props = newProps;
4879
- lifecycles.updatePropListeners(newProps);
4880
- prevMotionValues = updateMotionValuesFromProps(element, scrapeMotionValuesFromProps(props), prevMotionValues);
4881
- },
4882
- getProps: () => props,
4883
- // Variants ==============================
4828
+ this.values = new Map();
4884
4829
  /**
4885
- * Returns the variant definition with a given name.
4830
+ * Tracks whether this VisualElement's React component is currently present
4831
+ * within the defined React tree.
4886
4832
  */
4887
- getVariant: (name) => { var _a; return (_a = props.variants) === null || _a === void 0 ? void 0 : _a[name]; },
4833
+ this.isPresent = true;
4888
4834
  /**
4889
- * Returns the defined default transition on this component.
4835
+ * A map of every subscription that binds the provided or generated
4836
+ * motion values onChange listeners to this visual element.
4890
4837
  */
4891
- getDefaultTransition: () => props.transition,
4892
- getTransformPagePoint: () => {
4893
- return props.transformPagePoint;
4894
- },
4838
+ this.valueSubscriptions = new Map();
4895
4839
  /**
4896
- * Used by child variant nodes to get the closest ancestor variant props.
4840
+ * A reference to the previously-provided motion values as returned
4841
+ * from scrapeMotionValuesFromProps. We use the keys in here to determine
4842
+ * if any motion values need to be removed after props are updated.
4897
4843
  */
4898
- getVariantContext(startAtParent = false) {
4899
- if (startAtParent)
4900
- return parent === null || parent === void 0 ? void 0 : parent.getVariantContext();
4901
- if (!isControllingVariants$1) {
4902
- const context = (parent === null || parent === void 0 ? void 0 : parent.getVariantContext()) || {};
4903
- if (props.initial !== undefined) {
4904
- context.initial = props.initial;
4905
- }
4906
- return context;
4907
- }
4908
- const context = {};
4909
- for (let i = 0; i < numVariantProps; i++) {
4910
- const name = variantProps[i];
4911
- const prop = props[name];
4912
- if (isVariantLabel(prop) || prop === false) {
4913
- context[name] = prop;
4844
+ this.prevMotionValues = {};
4845
+ /**
4846
+ * An object containing a SubscriptionManager for each active event.
4847
+ */
4848
+ this.events = {};
4849
+ /**
4850
+ * An object containing an unsubscribe function for each prop event subscription.
4851
+ * For example, every "Update" event can have multiple subscribers via
4852
+ * VisualElement.on(), but only one of those can be defined via the onUpdate prop.
4853
+ */
4854
+ this.propEventSubscriptions = {};
4855
+ this.notifyUpdate = () => this.notify("Update", this.latestValues);
4856
+ this.render = () => {
4857
+ if (!this.current)
4858
+ return;
4859
+ this.triggerBuild();
4860
+ this.renderInstance(this.current, this.renderState, this.props.style, this.projection);
4861
+ };
4862
+ this.scheduleRender = () => sync.render(this.render, false, true);
4863
+ const { latestValues, renderState } = visualState;
4864
+ this.latestValues = latestValues;
4865
+ this.baseTarget = { ...latestValues };
4866
+ this.initialValues = props.initial ? { ...latestValues } : {};
4867
+ this.renderState = renderState;
4868
+ this.parent = parent;
4869
+ this.props = props;
4870
+ this.depth = parent ? parent.depth + 1 : 0;
4871
+ this.reducedMotionConfig = reducedMotionConfig;
4872
+ this.options = options;
4873
+ this.isControllingVariants = isControllingVariants(props);
4874
+ this.isVariantNode = isVariantNode(props);
4875
+ if (this.isVariantNode) {
4876
+ this.variantChildren = new Set();
4877
+ }
4878
+ this.manuallyAnimateOnMount = Boolean(parent && parent.current);
4879
+ /**
4880
+ * Any motion values that are provided to the element when created
4881
+ * aren't yet bound to the element, as this would technically be impure.
4882
+ * However, we iterate through the motion values and set them to the
4883
+ * initial values for this component.
4884
+ *
4885
+ * TODO: This is impure and we should look at changing this to run on mount.
4886
+ * Doing so will break some tests but this isn't neccessarily a breaking change,
4887
+ * more a reflection of the test.
4888
+ */
4889
+ const { willChange, ...initialMotionValues } = this.scrapeMotionValuesFromProps(props);
4890
+ for (const key in initialMotionValues) {
4891
+ const value = initialMotionValues[key];
4892
+ if (latestValues[key] !== undefined && isMotionValue(value)) {
4893
+ value.set(latestValues[key], false);
4894
+ if (isWillChangeMotionValue(willChange)) {
4895
+ willChange.add(key);
4914
4896
  }
4915
4897
  }
4916
- return context;
4917
- },
4918
- };
4919
- return element;
4920
- };
4921
- const variantProps = ["initial", ...variantPriorityOrder];
4922
- const numVariantProps = variantProps.length;
4923
-
4924
- /**
4925
- * Returns true if the provided key is a CSS variable
4926
- */
4927
- function isCSSVariable(key) {
4928
- return key.startsWith("--");
4929
- }
4930
-
4931
- /**
4932
- * Provided a value and a ValueType, returns the value as that value type.
4933
- */
4934
- const getValueAsType = (value, type) => {
4935
- return type && typeof value === "number"
4936
- ? type.transform(value)
4937
- : value;
4938
- };
4939
-
4940
- function buildHTMLStyles(state, latestValues, options, transformTemplate) {
4941
- const { style, vars, transform, transformKeys, transformOrigin } = state;
4942
- transformKeys.length = 0;
4943
- // Track whether we encounter any transform or transformOrigin values.
4944
- let hasTransform = false;
4945
- let hasTransformOrigin = false;
4946
- // Does the calculated transform essentially equal "none"?
4947
- let transformIsNone = true;
4948
- /**
4949
- * Loop over all our latest animated values and decide whether to handle them
4950
- * as a style or CSS variable.
4951
- *
4952
- * Transforms and transform origins are kept seperately for further processing.
4953
- */
4954
- for (const key in latestValues) {
4955
- const value = latestValues[key];
4898
+ }
4956
4899
  /**
4957
- * If this is a CSS variable we don't do any further processing.
4900
+ * Update external values with initial values
4958
4901
  */
4959
- if (isCSSVariable(key)) {
4960
- vars[key] = value;
4961
- continue;
4962
- }
4963
- // Convert the value to its default value type, ie 0 -> "0px"
4964
- const valueType = numberValueTypes[key];
4965
- const valueAsType = getValueAsType(value, valueType);
4966
- if (transformProps.has(key)) {
4967
- // If this is a transform, flag to enable further transform processing
4968
- hasTransform = true;
4969
- transform[key] = valueAsType;
4970
- transformKeys.push(key);
4971
- // If we already know we have a non-default transform, early return
4972
- if (!transformIsNone)
4973
- continue;
4974
- // Otherwise check to see if this is a default transform
4975
- if (value !== (valueType.default || 0))
4976
- transformIsNone = false;
4977
- }
4978
- else if (key.startsWith("origin")) {
4979
- // If this is a transform origin, flag and enable further transform-origin processing
4980
- hasTransformOrigin = true;
4981
- transformOrigin[key] = valueAsType;
4982
- }
4983
- else {
4984
- style[key] = valueAsType;
4985
- }
4986
- }
4987
- if (!latestValues.transform) {
4988
- if (hasTransform || transformTemplate) {
4989
- style.transform = buildTransform(state, options, transformIsNone, transformTemplate);
4990
- }
4991
- else if (style.transform) {
4992
- /**
4993
- * If we have previously created a transform but currently don't have any,
4994
- * reset transform style to none.
4995
- */
4996
- style.transform = "none";
4902
+ if (props.values) {
4903
+ for (const key in props.values) {
4904
+ const value = props.values[key];
4905
+ if (latestValues[key] !== undefined && isMotionValue(value)) {
4906
+ value.set(latestValues[key]);
4907
+ }
4908
+ }
4997
4909
  }
4998
4910
  }
4999
4911
  /**
5000
- * Build a transformOrigin style. Uses the same defaults as the browser for
5001
- * undefined origins.
4912
+ * This method takes React props and returns found MotionValues. For example, HTML
4913
+ * MotionValues will be found within the style prop, whereas for Three.js within attribute arrays.
4914
+ *
4915
+ * This isn't an abstract method as it needs calling in the constructor, but it is
4916
+ * intended to be one.
5002
4917
  */
5003
- if (hasTransformOrigin) {
5004
- const { originX = "50%", originY = "50%", originZ = 0, } = transformOrigin;
5005
- style.transformOrigin = `${originX} ${originY} ${originZ}`;
4918
+ scrapeMotionValuesFromProps(_props) {
4919
+ return {};
5006
4920
  }
5007
- }
5008
-
5009
- const positionalKeys = new Set([
5010
- "width",
5011
- "height",
5012
- "top",
5013
- "left",
5014
- "right",
5015
- "bottom",
5016
- "x",
5017
- "y",
5018
- ]);
5019
- const isPositionalKey = (key) => positionalKeys.has(key);
5020
- const hasPositionalKey = (target) => {
5021
- return Object.keys(target).some(isPositionalKey);
5022
- };
5023
- const setAndResetVelocity = (value, to) => {
5024
- // Looks odd but setting it twice doesn't render, it'll just
5025
- // set both prev and current to the latest value
5026
- value.set(to, false);
5027
- value.set(to);
5028
- };
5029
- const isNumOrPxType = (v) => v === number || v === px;
5030
- var BoundingBoxDimension;
5031
- (function (BoundingBoxDimension) {
5032
- BoundingBoxDimension["width"] = "width";
5033
- BoundingBoxDimension["height"] = "height";
5034
- BoundingBoxDimension["left"] = "left";
5035
- BoundingBoxDimension["right"] = "right";
5036
- BoundingBoxDimension["top"] = "top";
5037
- BoundingBoxDimension["bottom"] = "bottom";
5038
- })(BoundingBoxDimension || (BoundingBoxDimension = {}));
5039
- const getPosFromMatrix = (matrix, pos) => parseFloat(matrix.split(", ")[pos]);
5040
- const getTranslateFromMatrix = (pos2, pos3) => (_bbox, { transform }) => {
5041
- if (transform === "none" || !transform)
5042
- return 0;
5043
- const matrix3d = transform.match(/^matrix3d\((.+)\)$/);
5044
- if (matrix3d) {
5045
- return getPosFromMatrix(matrix3d[1], pos3);
4921
+ mount(instance) {
4922
+ var _a;
4923
+ this.current = instance;
4924
+ if (this.projection) {
4925
+ this.projection.mount(instance);
4926
+ }
4927
+ if (this.parent && this.isVariantNode && !this.isControllingVariants) {
4928
+ this.removeFromVariantTree = (_a = this.parent) === null || _a === void 0 ? void 0 : _a.addVariantChild(this);
4929
+ }
4930
+ this.values.forEach((value, key) => this.bindToMotionValue(key, value));
4931
+ if (!hasReducedMotionListener.current) {
4932
+ initPrefersReducedMotion();
4933
+ }
4934
+ this.shouldReduceMotion =
4935
+ this.reducedMotionConfig === "never"
4936
+ ? false
4937
+ : this.reducedMotionConfig === "always"
4938
+ ? true
4939
+ : prefersReducedMotion.current;
4940
+ if (this.parent)
4941
+ this.parent.children.add(this);
4942
+ this.setProps(this.props);
4943
+ }
4944
+ unmount() {
4945
+ var _a, _b, _c;
4946
+ (_a = this.projection) === null || _a === void 0 ? void 0 : _a.unmount();
4947
+ cancelSync.update(this.notifyUpdate);
4948
+ cancelSync.render(this.render);
4949
+ this.valueSubscriptions.forEach((remove) => remove());
4950
+ (_b = this.removeFromVariantTree) === null || _b === void 0 ? void 0 : _b.call(this);
4951
+ (_c = this.parent) === null || _c === void 0 ? void 0 : _c.children.delete(this);
4952
+ for (const key in this.events) {
4953
+ this.events[key].clear();
4954
+ }
4955
+ this.current = null;
4956
+ }
4957
+ bindToMotionValue(key, value) {
4958
+ const removeOnChange = value.onChange((latestValue) => {
4959
+ this.latestValues[key] = latestValue;
4960
+ this.props.onUpdate &&
4961
+ sync.update(this.notifyUpdate, false, true);
4962
+ });
4963
+ const removeOnRenderRequest = value.onRenderRequest(this.scheduleRender);
4964
+ this.valueSubscriptions.set(key, () => {
4965
+ removeOnChange();
4966
+ removeOnRenderRequest();
4967
+ });
5046
4968
  }
5047
- else {
5048
- const matrix = transform.match(/^matrix\((.+)\)$/);
5049
- if (matrix) {
5050
- return getPosFromMatrix(matrix[1], pos2);
5051
- }
5052
- else {
4969
+ sortNodePosition(other) {
4970
+ /**
4971
+ * If these nodes aren't even of the same type we can't compare their depth.
4972
+ */
4973
+ if (!this.current ||
4974
+ !this.sortInstanceNodePosition ||
4975
+ this.type !== other.type)
5053
4976
  return 0;
5054
- }
4977
+ return this.sortInstanceNodePosition(this.current, other.current);
5055
4978
  }
5056
- };
5057
- const transformKeys = new Set(["x", "y", "z"]);
5058
- const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key));
5059
- function removeNonTranslationalTransform(visualElement) {
5060
- const removedTransforms = [];
5061
- nonTranslationalTransformKeys.forEach((key) => {
5062
- const value = visualElement.getValue(key);
5063
- if (value !== undefined) {
5064
- removedTransforms.push([key, value.get()]);
5065
- value.set(key.startsWith("scale") ? 1 : 0);
4979
+ loadFeatures(renderedProps, isStrict, preloadedFeatures, projectionId, ProjectionNodeConstructor, initialLayoutGroupConfig) {
4980
+ const features = [];
4981
+ /**
4982
+ * If we're in development mode, check to make sure we're not rendering a motion component
4983
+ * as a child of LazyMotion, as this will break the file-size benefits of using it.
4984
+ */
4985
+ if (env !== "production" && preloadedFeatures && isStrict) {
4986
+ invariant(false, "You have rendered a `motion` component within a `LazyMotion` component. This will break tree shaking. Import and render a `m` component instead.");
5066
4987
  }
5067
- });
5068
- // Apply changes to element before measurement
5069
- if (removedTransforms.length)
5070
- visualElement.syncRender();
5071
- return removedTransforms;
5072
- }
5073
- const positionalValues = {
5074
- // Dimensions
5075
- width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight),
5076
- height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom),
5077
- top: (_bbox, { top }) => parseFloat(top),
5078
- left: (_bbox, { left }) => parseFloat(left),
5079
- bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min),
5080
- right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min),
5081
- // Transform
5082
- x: getTranslateFromMatrix(4, 13),
5083
- y: getTranslateFromMatrix(5, 14),
5084
- };
5085
- const convertChangedValueTypes = (target, visualElement, changedKeys) => {
5086
- const originBbox = visualElement.measureViewportBox();
5087
- const element = visualElement.getInstance();
5088
- const elementComputedStyle = getComputedStyle(element);
5089
- const { display } = elementComputedStyle;
5090
- const origin = {};
5091
- // If the element is currently set to display: "none", make it visible before
5092
- // measuring the target bounding box
5093
- if (display === "none") {
5094
- visualElement.setStaticValue("display", target.display || "block");
5095
- }
5096
- /**
5097
- * Record origins before we render and update styles
5098
- */
5099
- changedKeys.forEach((key) => {
5100
- origin[key] = positionalValues[key](originBbox, elementComputedStyle);
5101
- });
5102
- // Apply the latest values (as set in checkAndConvertChangedValueTypes)
5103
- visualElement.syncRender();
5104
- const targetBbox = visualElement.measureViewportBox();
5105
- changedKeys.forEach((key) => {
5106
- // Restore styles to their **calculated computed style**, not their actual
5107
- // originally set style. This allows us to animate between equivalent pixel units.
5108
- const value = visualElement.getValue(key);
5109
- setAndResetVelocity(value, origin[key]);
5110
- target[key] = positionalValues[key](targetBbox, elementComputedStyle);
5111
- });
5112
- return target;
5113
- };
5114
- const checkAndConvertChangedValueTypes = (visualElement, target, origin = {}, transitionEnd = {}) => {
5115
- target = { ...target };
5116
- transitionEnd = { ...transitionEnd };
5117
- const targetPositionalKeys = Object.keys(target).filter(isPositionalKey);
5118
- // We want to remove any transform values that could affect the element's bounding box before
5119
- // it's measured. We'll reapply these later.
5120
- let removedTransformValues = [];
5121
- let hasAttemptedToRemoveTransformValues = false;
5122
- const changedValueTypeKeys = [];
5123
- targetPositionalKeys.forEach((key) => {
5124
- const value = visualElement.getValue(key);
5125
- if (!visualElement.hasValue(key))
5126
- return;
5127
- let from = origin[key];
5128
- let fromType = findDimensionValueType(from);
5129
- const to = target[key];
5130
- let toType;
5131
- // TODO: The current implementation of this basically throws an error
5132
- // if you try and do value conversion via keyframes. There's probably
5133
- // a way of doing this but the performance implications would need greater scrutiny,
5134
- // as it'd be doing multiple resize-remeasure operations.
5135
- if (isKeyframesTarget(to)) {
5136
- const numKeyframes = to.length;
5137
- const fromIndex = to[0] === null ? 1 : 0;
5138
- from = to[fromIndex];
5139
- fromType = findDimensionValueType(from);
5140
- for (let i = fromIndex; i < numKeyframes; i++) {
5141
- if (!toType) {
5142
- toType = findDimensionValueType(to[i]);
5143
- invariant(toType === fromType ||
5144
- (isNumOrPxType(fromType) && isNumOrPxType(toType)), "Keyframes must be of the same dimension as the current value");
5145
- }
5146
- else {
5147
- invariant(findDimensionValueType(to[i]) === toType, "All keyframes must be of the same type");
5148
- }
4988
+ for (let i = 0; i < numFeatures; i++) {
4989
+ const name = featureNames[i];
4990
+ const { isEnabled, Component } = featureDefinitions[name];
4991
+ /**
4992
+ * It might be possible in the future to use this moment to
4993
+ * dynamically request functionality. In initial tests this
4994
+ * was producing a lot of duplication amongst bundles.
4995
+ */
4996
+ if (isEnabled(renderedProps) && Component) {
4997
+ features.push(react.createElement(Component, {
4998
+ key: name,
4999
+ ...renderedProps,
5000
+ visualElement: this,
5001
+ }));
5149
5002
  }
5150
5003
  }
5151
- else {
5152
- toType = findDimensionValueType(to);
5004
+ if (!this.projection && ProjectionNodeConstructor) {
5005
+ this.projection = new ProjectionNodeConstructor(projectionId, this.latestValues, this.parent && this.parent.projection);
5006
+ const { layoutId, layout, drag, dragConstraints, layoutScroll } = renderedProps;
5007
+ this.projection.setOptions({
5008
+ layoutId,
5009
+ layout,
5010
+ alwaysMeasureLayout: Boolean(drag) ||
5011
+ (dragConstraints && isRefObject(dragConstraints)),
5012
+ visualElement: this,
5013
+ scheduleRender: () => this.scheduleRender(),
5014
+ /**
5015
+ * TODO: Update options in an effect. This could be tricky as it'll be too late
5016
+ * to update by the time layout animations run.
5017
+ * We also need to fix this safeToRemove by linking it up to the one returned by usePresence,
5018
+ * ensuring it gets called if there's no potential layout animations.
5019
+ *
5020
+ */
5021
+ animationType: typeof layout === "string" ? layout : "both",
5022
+ initialPromotionConfig: initialLayoutGroupConfig,
5023
+ layoutScroll,
5024
+ });
5025
+ }
5026
+ return features;
5027
+ }
5028
+ triggerBuild() {
5029
+ this.build(this.renderState, this.latestValues, this.options, this.props);
5030
+ }
5031
+ /**
5032
+ * Measure the current viewport box with or without transforms.
5033
+ * Only measures axis-aligned boxes, rotate and skew must be manually
5034
+ * removed with a re-render to work.
5035
+ */
5036
+ measureViewportBox() {
5037
+ return this.current
5038
+ ? this.measureInstanceViewportBox(this.current, this.props)
5039
+ : createBox();
5040
+ }
5041
+ getStaticValue(key) {
5042
+ return this.latestValues[key];
5043
+ }
5044
+ setStaticValue(key, value) {
5045
+ this.latestValues[key] = value;
5046
+ }
5047
+ /**
5048
+ * Make a target animatable by Popmotion. For instance, if we're
5049
+ * trying to animate width from 100px to 100vw we need to measure 100vw
5050
+ * in pixels to determine what we really need to animate to. This is also
5051
+ * pluggable to support Framer's custom value types like Color,
5052
+ * and CSS variables.
5053
+ */
5054
+ makeTargetAnimatable(target, canMutate = true) {
5055
+ return this.makeTargetAnimatableFromInstance(target, this.props, canMutate);
5056
+ }
5057
+ /**
5058
+ * Update the provided props. Ensure any newly-added motion values are
5059
+ * added to our map, old ones removed, and listeners updated.
5060
+ */
5061
+ setProps(props) {
5062
+ if (props.transformTemplate || this.props.transformTemplate) {
5063
+ this.scheduleRender();
5153
5064
  }
5154
- if (fromType !== toType) {
5155
- // If they're both just number or px, convert them both to numbers rather than
5156
- // relying on resize/remeasure to convert (which is wasteful in this situation)
5157
- if (isNumOrPxType(fromType) && isNumOrPxType(toType)) {
5158
- const current = value.get();
5159
- if (typeof current === "string") {
5160
- value.set(parseFloat(current));
5161
- }
5162
- if (typeof to === "string") {
5163
- target[key] = parseFloat(to);
5164
- }
5165
- else if (Array.isArray(to) && toType === px) {
5166
- target[key] = to.map(parseFloat);
5167
- }
5065
+ this.props = props;
5066
+ /**
5067
+ * Update prop event handlers ie onAnimationStart, onAnimationComplete
5068
+ */
5069
+ for (let i = 0; i < propEventHandlers.length; i++) {
5070
+ const key = propEventHandlers[i];
5071
+ if (this.propEventSubscriptions[key]) {
5072
+ this.propEventSubscriptions[key]();
5073
+ delete this.propEventSubscriptions[key];
5168
5074
  }
5169
- else if ((fromType === null || fromType === void 0 ? void 0 : fromType.transform) &&
5170
- (toType === null || toType === void 0 ? void 0 : toType.transform) &&
5171
- (from === 0 || to === 0)) {
5172
- // If one or the other value is 0, it's safe to coerce it to the
5173
- // type of the other without measurement
5174
- if (from === 0) {
5175
- value.set(toType.transform(from));
5176
- }
5177
- else {
5178
- target[key] = fromType.transform(to);
5179
- }
5075
+ const listener = props["on" + key];
5076
+ if (listener) {
5077
+ this.propEventSubscriptions[key] = this.on(key, listener);
5180
5078
  }
5181
- else {
5182
- // If we're going to do value conversion via DOM measurements, we first
5183
- // need to remove non-positional transform values that could affect the bbox measurements.
5184
- if (!hasAttemptedToRemoveTransformValues) {
5185
- removedTransformValues =
5186
- removeNonTranslationalTransform(visualElement);
5187
- hasAttemptedToRemoveTransformValues = true;
5188
- }
5189
- changedValueTypeKeys.push(key);
5190
- transitionEnd[key] =
5191
- transitionEnd[key] !== undefined
5192
- ? transitionEnd[key]
5193
- : target[key];
5194
- setAndResetVelocity(value, to);
5079
+ }
5080
+ this.prevMotionValues = updateMotionValuesFromProps(this, this.scrapeMotionValuesFromProps(props), this.prevMotionValues);
5081
+ }
5082
+ getProps() {
5083
+ return this.props;
5084
+ }
5085
+ /**
5086
+ * Returns the variant definition with a given name.
5087
+ */
5088
+ getVariant(name) {
5089
+ var _a;
5090
+ return (_a = this.props.variants) === null || _a === void 0 ? void 0 : _a[name];
5091
+ }
5092
+ /**
5093
+ * Returns the defined default transition on this component.
5094
+ */
5095
+ getDefaultTransition() {
5096
+ return this.props.transition;
5097
+ }
5098
+ getTransformPagePoint() {
5099
+ return this.props.transformPagePoint;
5100
+ }
5101
+ getClosestVariantNode() {
5102
+ var _a;
5103
+ return this.isVariantNode ? this : (_a = this.parent) === null || _a === void 0 ? void 0 : _a.getClosestVariantNode();
5104
+ }
5105
+ getVariantContext(startAtParent = false) {
5106
+ var _a, _b;
5107
+ if (startAtParent)
5108
+ return (_a = this.parent) === null || _a === void 0 ? void 0 : _a.getVariantContext();
5109
+ if (!this.isControllingVariants) {
5110
+ const context = ((_b = this.parent) === null || _b === void 0 ? void 0 : _b.getVariantContext()) || {};
5111
+ if (this.props.initial !== undefined) {
5112
+ context.initial = this.props.initial;
5195
5113
  }
5114
+ return context;
5196
5115
  }
5197
- });
5198
- if (changedValueTypeKeys.length) {
5199
- const scrollY = changedValueTypeKeys.indexOf("height") >= 0
5200
- ? window.pageYOffset
5201
- : null;
5202
- const convertedTarget = convertChangedValueTypes(target, visualElement, changedValueTypeKeys);
5203
- // If we removed transform values, reapply them before the next render
5204
- if (removedTransformValues.length) {
5205
- removedTransformValues.forEach(([key, value]) => {
5206
- visualElement.getValue(key).set(value);
5207
- });
5116
+ const context = {};
5117
+ for (let i = 0; i < numVariantProps; i++) {
5118
+ const name = variantProps[i];
5119
+ const prop = this.props[name];
5120
+ if (isVariantLabel(prop) || prop === false) {
5121
+ context[name] = prop;
5122
+ }
5208
5123
  }
5209
- // Reapply original values
5210
- visualElement.syncRender();
5211
- // Restore scroll position
5212
- if (isBrowser && scrollY !== null) {
5213
- window.scrollTo({ top: scrollY });
5124
+ return context;
5125
+ }
5126
+ /**
5127
+ * Add a child visual element to our set of children.
5128
+ */
5129
+ addVariantChild(child) {
5130
+ var _a;
5131
+ const closestVariantNode = this.getClosestVariantNode();
5132
+ if (closestVariantNode) {
5133
+ (_a = closestVariantNode.variantChildren) === null || _a === void 0 ? void 0 : _a.add(child);
5134
+ return () => closestVariantNode.variantChildren.delete(child);
5214
5135
  }
5215
- return { target: convertedTarget, transitionEnd };
5216
5136
  }
5217
- else {
5218
- return { target, transitionEnd };
5137
+ /**
5138
+ * Add a motion value and bind it to this visual element.
5139
+ */
5140
+ addValue(key, value) {
5141
+ // Remove existing value if it exists
5142
+ if (this.hasValue(key))
5143
+ this.removeValue(key);
5144
+ this.values.set(key, value);
5145
+ this.latestValues[key] = value.get();
5146
+ this.bindToMotionValue(key, value);
5219
5147
  }
5220
- };
5221
- /**
5222
- * Convert value types for x/y/width/height/top/left/bottom/right
5223
- *
5224
- * Allows animation between `'auto'` -> `'100%'` or `0` -> `'calc(50% - 10vw)'`
5225
- *
5226
- * @internal
5227
- */
5228
- function unitConversion(visualElement, target, origin, transitionEnd) {
5229
- return hasPositionalKey(target)
5230
- ? checkAndConvertChangedValueTypes(visualElement, target, origin, transitionEnd)
5231
- : { target, transitionEnd };
5232
- }
5233
-
5234
- /**
5235
- * Parse a DOM variant to make it animatable. This involves resolving CSS variables
5236
- * and ensuring animations like "20%" => "calc(50vw)" are performed in pixels.
5237
- */
5238
- const parseDomVariant = (visualElement, target, origin, transitionEnd) => {
5239
- const resolved = resolveCSSVariables(visualElement, target, transitionEnd);
5240
- target = resolved.target;
5241
- transitionEnd = resolved.transitionEnd;
5242
- return unitConversion(visualElement, target, origin, transitionEnd);
5243
- };
5244
-
5245
- function isForcedMotionValue(key, { layout, layoutId }) {
5246
- return (transformProps.has(key) ||
5247
- key.startsWith("origin") ||
5248
- ((layout || layoutId !== undefined) &&
5249
- (!!scaleCorrectors[key] || key === "opacity")));
5250
- }
5251
-
5252
- function scrapeMotionValuesFromProps(props) {
5253
- const { style } = props;
5254
- const newValues = {};
5255
- for (const key in style) {
5256
- if (isMotionValue(style[key]) || isForcedMotionValue(key, props)) {
5257
- newValues[key] = style[key];
5148
+ /**
5149
+ * Remove a motion value and unbind any active subscriptions.
5150
+ */
5151
+ removeValue(key) {
5152
+ var _a;
5153
+ this.values.delete(key);
5154
+ (_a = this.valueSubscriptions.get(key)) === null || _a === void 0 ? void 0 : _a();
5155
+ this.valueSubscriptions.delete(key);
5156
+ delete this.latestValues[key];
5157
+ this.removeValueFromRenderState(key, this.renderState);
5158
+ }
5159
+ /**
5160
+ * Check whether we have a motion value for this key
5161
+ */
5162
+ hasValue(key) {
5163
+ return this.values.has(key);
5164
+ }
5165
+ /**
5166
+ * Get a motion value for this key. If called with a default
5167
+ * value, we'll create one if none exists.
5168
+ */
5169
+ getValue(key, defaultValue) {
5170
+ if (this.props.values && this.props.values[key]) {
5171
+ return this.props.values[key];
5172
+ }
5173
+ let value = this.values.get(key);
5174
+ if (value === undefined && defaultValue !== undefined) {
5175
+ value = motionValue(defaultValue);
5176
+ this.addValue(key, value);
5258
5177
  }
5178
+ return value;
5259
5179
  }
5260
- return newValues;
5261
- }
5262
-
5263
- function renderHTML(element, { style, vars }, styleProp, projection) {
5264
- Object.assign(element.style, style, projection && projection.getProjectionStyles(styleProp));
5265
- // Loop over any CSS variables and assign those.
5266
- for (const key in vars) {
5267
- element.style.setProperty(key, vars[key]);
5180
+ /**
5181
+ * If we're trying to animate to a previously unencountered value,
5182
+ * we need to check for it in our state and as a last resort read it
5183
+ * directly from the instance (which might have performance implications).
5184
+ */
5185
+ readValue(key) {
5186
+ return this.latestValues[key] !== undefined || !this.current
5187
+ ? this.latestValues[key]
5188
+ : this.readValueFromInstance(this.current, key, this.options);
5268
5189
  }
5269
- }
5270
-
5271
- /**
5272
- * Bounding boxes tend to be defined as top, left, right, bottom. For various operations
5273
- * it's easier to consider each axis individually. This function returns a bounding box
5274
- * as a map of single-axis min/max values.
5275
- */
5276
- function convertBoundingBoxToBox({ top, left, right, bottom, }) {
5277
- return {
5278
- x: { min: left, max: right },
5279
- y: { min: top, max: bottom },
5280
- };
5281
- }
5282
- /**
5283
- * Applies a TransformPoint function to a bounding box. TransformPoint is usually a function
5284
- * provided by Framer to allow measured points to be corrected for device scaling. This is used
5285
- * when measuring DOM elements and DOM event points.
5286
- */
5287
- function transformBoxPoints(point, transformPoint) {
5288
- if (!transformPoint)
5289
- return point;
5290
- const topLeft = transformPoint({ x: point.left, y: point.top });
5291
- const bottomRight = transformPoint({ x: point.right, y: point.bottom });
5292
- return {
5293
- top: topLeft.y,
5294
- left: topLeft.x,
5295
- bottom: bottomRight.y,
5296
- right: bottomRight.x,
5297
- };
5298
- }
5299
-
5300
- function measureViewportBox(instance, transformPoint) {
5301
- return convertBoundingBoxToBox(transformBoxPoints(instance.getBoundingClientRect(), transformPoint));
5302
- }
5303
-
5304
- function getComputedStyle$1(element) {
5305
- return window.getComputedStyle(element);
5306
- }
5307
- const htmlConfig = {
5308
- treeType: "dom",
5309
- readValueFromInstance(domElement, key) {
5310
- if (transformProps.has(key)) {
5311
- const defaultType = getDefaultValueType(key);
5312
- return defaultType ? defaultType.default || 0 : 0;
5190
+ /**
5191
+ * Set the base target to later animate back to. This is currently
5192
+ * only hydrated on creation and when we first read a value.
5193
+ */
5194
+ setBaseTarget(key, value) {
5195
+ this.baseTarget[key] = value;
5196
+ }
5197
+ /**
5198
+ * Find the base target for a value thats been removed from all animation
5199
+ * props.
5200
+ */
5201
+ getBaseTarget(key) {
5202
+ var _a;
5203
+ const { initial } = this.props;
5204
+ const valueFromInitial = typeof initial === "string" || typeof initial === "object"
5205
+ ? (_a = resolveVariantFromProps(this.props, initial)) === null || _a === void 0 ? void 0 : _a[key]
5206
+ : undefined;
5207
+ /**
5208
+ * If this value still exists in the current initial variant, read that.
5209
+ */
5210
+ if (initial && valueFromInitial !== undefined) {
5211
+ return valueFromInitial;
5313
5212
  }
5314
- else {
5315
- const computedStyle = getComputedStyle$1(domElement);
5316
- const value = (isCSSVariable(key)
5317
- ? computedStyle.getPropertyValue(key)
5318
- : computedStyle[key]) || 0;
5319
- return typeof value === "string" ? value.trim() : value;
5213
+ /**
5214
+ * Alternatively, if this VisualElement config has defined a getBaseTarget
5215
+ * so we can read the value from an alternative source, try that.
5216
+ */
5217
+ const target = this.getBaseTargetFromProps(this.props, key);
5218
+ if (target !== undefined && !isMotionValue(target))
5219
+ return target;
5220
+ /**
5221
+ * If the value was initially defined on initial, but it doesn't any more,
5222
+ * return undefined. Otherwise return the value as initially read from the DOM.
5223
+ */
5224
+ return this.initialValues[key] !== undefined &&
5225
+ valueFromInitial === undefined
5226
+ ? undefined
5227
+ : this.baseTarget[key];
5228
+ }
5229
+ on(eventName, callback) {
5230
+ if (!this.events[eventName]) {
5231
+ this.events[eventName] = new SubscriptionManager();
5320
5232
  }
5321
- },
5322
- sortNodePosition(a, b) {
5233
+ return this.events[eventName].add(callback);
5234
+ }
5235
+ notify(eventName, ...args) {
5236
+ var _a;
5237
+ (_a = this.events[eventName]) === null || _a === void 0 ? void 0 : _a.notify(...args);
5238
+ }
5239
+ }
5240
+ const variantProps = ["initial", ...variantPriorityOrder];
5241
+ const numVariantProps = variantProps.length;
5242
+
5243
+ class DOMVisualElement extends VisualElement {
5244
+ sortInstanceNodePosition(a, b) {
5323
5245
  /**
5324
5246
  * compareDocumentPosition returns a bitmask, by using the bitwise &
5325
5247
  * we're returning true if 2 in that bitmask is set to true. 2 is set
5326
5248
  * to true if b preceeds a.
5327
5249
  */
5328
5250
  return a.compareDocumentPosition(b) & 2 ? 1 : -1;
5329
- },
5330
- getBaseTarget(props, key) {
5251
+ }
5252
+ getBaseTargetFromProps(props, key) {
5331
5253
  var _a;
5332
5254
  return (_a = props.style) === null || _a === void 0 ? void 0 : _a[key];
5333
- },
5334
- measureViewportBox(element, { transformPagePoint }) {
5335
- return measureViewportBox(element, transformPagePoint);
5336
- },
5337
- /**
5338
- * Reset the transform on the current Element. This is called as part
5339
- * of a batched process across the entire layout tree. To remove this write
5340
- * cycle it'd be interesting to see if it's possible to "undo" all the current
5341
- * layout transforms up the tree in the same way this.getBoundingBoxWithoutTransforms
5342
- * works
5343
- */
5344
- resetTransform(element, domElement, props) {
5345
- const { transformTemplate } = props;
5346
- domElement.style.transform = transformTemplate
5347
- ? transformTemplate({}, "")
5348
- : "none";
5349
- // Ensure that whatever happens next, we restore our transform on the next frame
5350
- element.scheduleRender();
5351
- },
5352
- restoreTransform(instance, mutableState) {
5353
- instance.style.transform = mutableState.style.transform;
5354
- },
5255
+ }
5355
5256
  removeValueFromRenderState(key, { vars, style }) {
5356
5257
  delete vars[key];
5357
5258
  delete style[key];
5358
- },
5359
- /**
5360
- * Ensure that HTML and Framer-specific value types like `px`->`%` and `Color`
5361
- * can be animated by Motion.
5362
- */
5363
- makeTargetAnimatable(element, { transition, transitionEnd, ...target }, { transformValues }, isMounted = true) {
5364
- let origin = getOrigin(target, transition || {}, element);
5259
+ }
5260
+ makeTargetAnimatableFromInstance({ transition, transitionEnd, ...target }, { transformValues }, isMounted) {
5261
+ let origin = getOrigin(target, transition || {}, this);
5365
5262
  /**
5366
5263
  * If Framer has provided a function to convert `Color` etc value types, convert them
5367
5264
  */
@@ -5374,8 +5271,8 @@
5374
5271
  origin = transformValues(origin);
5375
5272
  }
5376
5273
  if (isMounted) {
5377
- checkTargetForNewValues(element, target, origin);
5378
- const parsed = parseDomVariant(element, target, origin, transitionEnd);
5274
+ checkTargetForNewValues(this, target, origin);
5275
+ const parsed = parseDomVariant(this, target, origin, transitionEnd);
5379
5276
  transitionEnd = parsed.transitionEnd;
5380
5277
  target = parsed.target;
5381
5278
  }
@@ -5384,28 +5281,48 @@
5384
5281
  transitionEnd,
5385
5282
  ...target,
5386
5283
  };
5387
- },
5388
- scrapeMotionValuesFromProps,
5389
- build(element, renderState, latestValues, options, props) {
5390
- if (element.isVisible !== undefined) {
5391
- renderState.style.visibility = element.isVisible
5392
- ? "visible"
5393
- : "hidden";
5284
+ }
5285
+ }
5286
+
5287
+ function getComputedStyle$1(element) {
5288
+ return window.getComputedStyle(element);
5289
+ }
5290
+ class HTMLVisualElement extends DOMVisualElement {
5291
+ readValueFromInstance(instance, key) {
5292
+ if (transformProps.has(key)) {
5293
+ const defaultType = getDefaultValueType(key);
5294
+ return defaultType ? defaultType.default || 0 : 0;
5295
+ }
5296
+ else {
5297
+ const computedStyle = getComputedStyle$1(instance);
5298
+ const value = (isCSSVariable(key)
5299
+ ? computedStyle.getPropertyValue(key)
5300
+ : computedStyle[key]) || 0;
5301
+ return typeof value === "string" ? value.trim() : value;
5394
5302
  }
5303
+ }
5304
+ measureInstanceViewportBox(instance, { transformPagePoint }) {
5305
+ return measureViewportBox(instance, transformPagePoint);
5306
+ }
5307
+ build(renderState, latestValues, options, props) {
5395
5308
  buildHTMLStyles(renderState, latestValues, options, props.transformTemplate);
5396
- },
5397
- render: renderHTML,
5398
- };
5399
- const htmlVisualElement = visualElement(htmlConfig);
5309
+ }
5310
+ scrapeMotionValuesFromProps(props) {
5311
+ return scrapeMotionValuesFromProps(props);
5312
+ }
5313
+ renderInstance(instance, renderState, styleProp, projection) {
5314
+ renderHTML(instance, renderState, styleProp, projection);
5315
+ }
5316
+ }
5400
5317
 
5401
5318
  exports.HTMLProjectionNode = HTMLProjectionNode;
5319
+ exports.HTMLVisualElement = HTMLVisualElement;
5402
5320
  exports.addScaleCorrector = addScaleCorrector;
5403
5321
  exports.animate = animate$1;
5404
5322
  exports.buildTransform = buildTransform;
5405
5323
  exports.calcBoxDelta = calcBoxDelta;
5406
5324
  exports.correctBorderRadius = correctBorderRadius;
5407
5325
  exports.correctBoxShadow = correctBoxShadow;
5408
- exports.htmlVisualElement = htmlVisualElement;
5409
5326
  exports.mix = mix;
5410
5327
  exports.nodeGroup = nodeGroup;
5411
5328
  exports.sync = sync;