motion 13.5.0 → 14.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4381,36 +4381,29 @@
4381
4381
  return state;
4382
4382
  }
4383
4383
  function resolveVariantFromProps(props, definition, custom, visualElement) {
4384
+ const resolveFunction = (def) => typeof def === "function"
4385
+ ? def(custom !== undefined ? custom : props.custom, ...getValueState(visualElement))
4386
+ : def;
4384
4387
  /**
4385
- * If the variant definition is a function, resolve.
4386
- */
4387
- if (typeof definition === "function") {
4388
- const [current, velocity] = getValueState(visualElement);
4389
- definition = definition(custom !== undefined ? custom : props.custom, current, velocity);
4390
- }
4391
- /**
4392
- * If the variant definition is a variant label, or
4393
- * the function returned a variant label, resolve.
4388
+ * Resolve a function, then a variant label (which the function may have
4389
+ * returned), then a function again as the label may point to one. The
4390
+ * final function can only return a target object.
4394
4391
  */
4392
+ definition = resolveFunction(definition);
4395
4393
  if (typeof definition === "string") {
4396
4394
  definition = props.variants && props.variants[definition];
4397
4395
  }
4398
- /**
4399
- * At this point we've resolved both functions and variant labels,
4400
- * but the resolved variant label might itself have been a function.
4401
- * If so, resolve. This can only have returned a valid target object.
4402
- */
4403
- if (typeof definition === "function") {
4404
- const [current, velocity] = getValueState(visualElement);
4405
- definition = definition(custom !== undefined ? custom : props.custom, current, velocity);
4406
- }
4407
- return definition;
4396
+ return resolveFunction(definition);
4408
4397
  }
4409
4398
 
4410
4399
  function resolveVariant(visualElement, definition, custom) {
4411
- const props = visualElement.getProps();
4412
- return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, visualElement);
4400
+ return resolveVariantFromProps(visualElement.getProps(), definition, custom, visualElement);
4413
4401
  }
4402
+ /**
4403
+ * Exit variants resolve with AnimatePresence's custom, every other type
4404
+ * with the element's own.
4405
+ */
4406
+ const getTypeCustom = (visualElement, type) => type === "exit" ? visualElement.presenceContext?.custom : undefined;
4414
4407
 
4415
4408
  const positionalKeys = new Set([
4416
4409
  "width",
@@ -4493,7 +4486,7 @@
4493
4486
  * had been set to true in the meantime.
4494
4487
  */
4495
4488
  function shouldBlockAnimation({ protectedKeys, needsAnimating }, key) {
4496
- const shouldBlock = protectedKeys.hasOwnProperty(key) && needsAnimating[key] !== true;
4489
+ const shouldBlock = key in protectedKeys && !needsAnimating[key];
4497
4490
  needsAnimating[key] = false;
4498
4491
  return shouldBlock;
4499
4492
  }
@@ -4508,9 +4501,7 @@
4508
4501
  if (transitionOverride)
4509
4502
  transition = transitionOverride;
4510
4503
  const animations = [];
4511
- const animationTypeState = type &&
4512
- visualElement.animationState &&
4513
- visualElement.animationState.getState()[type];
4504
+ const animationTypeState = type && visualElement.animationState?.getState()[type];
4514
4505
  const path = transition?.path;
4515
4506
  if (path) {
4516
4507
  // path mutates `target` to claim x/y; loop below skips them.
@@ -4584,75 +4575,46 @@
4584
4575
  }
4585
4576
 
4586
4577
  function animateVariant(visualElement, variant, options = {}) {
4587
- const resolved = resolveVariant(visualElement, variant, options.type === "exit"
4588
- ? visualElement.presenceContext?.custom
4589
- : undefined);
4590
- let { transition = visualElement.getDefaultTransition() || {} } = resolved || {};
4591
- if (options.transitionOverride) {
4592
- transition = options.transitionOverride;
4593
- }
4594
- /**
4595
- * If we have a variant, create a callback that runs it as an animation.
4596
- * Otherwise, we resolve a Promise immediately for a composable no-op.
4597
- */
4598
- const getAnimation = resolved
4599
- ? () => Promise.all(animateTarget(visualElement, resolved, options))
4600
- : () => Promise.resolve();
4601
- /**
4602
- * If we have children, create a callback that runs all their animations.
4603
- * Otherwise, we resolve a Promise immediately for a composable no-op.
4604
- */
4605
- const getChildAnimations = visualElement.variantChildren && visualElement.variantChildren.size
4606
- ? (forwardDelay = 0) => {
4607
- const { delayChildren = 0, staggerChildren, staggerDirection, } = transition;
4608
- return animateChildren(visualElement, variant, forwardDelay, delayChildren, staggerChildren, staggerDirection, options);
4609
- }
4610
- : () => Promise.resolve();
4578
+ const resolved = resolveVariant(visualElement, variant, getTypeCustom(visualElement, options.type));
4579
+ const transition = options.transitionOverride ||
4580
+ (resolved?.transition ?? (visualElement.getDefaultTransition() || {}));
4581
+ const getAnimation = () => Promise.all(resolved ? animateTarget(visualElement, resolved, options) : []);
4582
+ const getChildAnimations = (delay = 0) => {
4583
+ const { variantChildren } = visualElement;
4584
+ const { delayChildren = 0, staggerChildren, staggerDirection, } = transition;
4585
+ const animations = [];
4586
+ variantChildren?.forEach((child) => {
4587
+ child.notify("AnimationStart", variant);
4588
+ animations.push(animateVariant(child, variant, {
4589
+ ...options,
4590
+ delay: delay +
4591
+ (typeof delayChildren === "function"
4592
+ ? 0
4593
+ : delayChildren) +
4594
+ calcChildStagger(variantChildren, child, delayChildren, staggerChildren, staggerDirection),
4595
+ }).then(() => child.notify("AnimationComplete", variant)));
4596
+ });
4597
+ return Promise.all(animations);
4598
+ };
4611
4599
  /**
4612
- * If the transition explicitly defines a "when" option, we need to resolve either
4613
- * this animation or all children animations before playing the other.
4600
+ * when: "beforeChildren" | "afterChildren" runs this element's animation
4601
+ * and its children's in sequence.
4614
4602
  */
4615
4603
  const { when } = transition;
4616
- if (when) {
4617
- const [first, last] = when === "beforeChildren"
4618
- ? [getAnimation, getChildAnimations]
4619
- : [getChildAnimations, getAnimation];
4620
- return first().then(() => last());
4621
- }
4622
- else {
4623
- return Promise.all([getAnimation(), getChildAnimations(options.delay)]);
4624
- }
4625
- }
4626
- function animateChildren(visualElement, variant, delay = 0, delayChildren = 0, staggerChildren = 0, staggerDirection = 1, options) {
4627
- const animations = [];
4628
- for (const child of visualElement.variantChildren) {
4629
- child.notify("AnimationStart", variant);
4630
- animations.push(animateVariant(child, variant, {
4631
- ...options,
4632
- delay: delay +
4633
- (typeof delayChildren === "function" ? 0 : delayChildren) +
4634
- calcChildStagger(visualElement.variantChildren, child, delayChildren, staggerChildren, staggerDirection),
4635
- }).then(() => child.notify("AnimationComplete", variant)));
4636
- }
4637
- return Promise.all(animations);
4604
+ return when
4605
+ ? when === "beforeChildren"
4606
+ ? getAnimation().then(() => getChildAnimations())
4607
+ : getChildAnimations().then(getAnimation)
4608
+ : Promise.all([getAnimation(), getChildAnimations(options.delay)]);
4638
4609
  }
4639
4610
 
4640
4611
  function animateVisualElement(visualElement, definition, options = {}) {
4641
4612
  visualElement.notify("AnimationStart", definition);
4642
- let animation;
4643
- if (Array.isArray(definition)) {
4644
- const animations = definition.map((variant) => animateVariant(visualElement, variant, options));
4645
- animation = Promise.all(animations);
4646
- }
4647
- else if (typeof definition === "string") {
4648
- animation = animateVariant(visualElement, definition, options);
4649
- }
4650
- else {
4651
- const resolvedDefinition = typeof definition === "function"
4652
- ? resolveVariant(visualElement, definition, options.custom)
4653
- : definition;
4654
- animation = Promise.all(animateTarget(visualElement, resolvedDefinition, options));
4655
- }
4613
+ const animation = Array.isArray(definition)
4614
+ ? Promise.all(definition.map((variant) => animateVariant(visualElement, variant, options)))
4615
+ : typeof definition === "string"
4616
+ ? animateVariant(visualElement, definition, options)
4617
+ : Promise.all(animateTarget(visualElement, resolveVariant(visualElement, definition, options.custom), options));
4656
4618
  return animation.then(() => {
4657
4619
  visualElement.notify("AnimationComplete", definition);
4658
4620
  });
@@ -5178,7 +5140,12 @@
5178
5140
  const onRemove = () => {
5179
5141
  cancelOnChange();
5180
5142
  render && !computed && this.cancel(render);
5181
- this.values.delete(name);
5143
+ /**
5144
+ * A later set() of the same name has already removed this
5145
+ * entry, so leave the one that replaced it.
5146
+ */
5147
+ this.values.get(name)?.onRemove === onRemove &&
5148
+ this.values.delete(name);
5182
5149
  };
5183
5150
  this.values.set(name, {
5184
5151
  value,
@@ -5657,24 +5624,35 @@
5657
5624
  else if (key.startsWith("attr")) {
5658
5625
  return addAttrValue(element, state, key, value, convertAttrKey(key));
5659
5626
  }
5660
- const handler = isCSSVar(key) || key in element.style ? addStyleValue : addAttrValue;
5627
+ /**
5628
+ * Transforms and origins aren't CSS properties (bar `x`/`y` in some
5629
+ * browsers), so check them by name rather than `key in element.style`.
5630
+ */
5631
+ const handler = transformProps.has(key) ||
5632
+ originProps.has(key) ||
5633
+ isCSSVar(key) ||
5634
+ key in element.style
5635
+ ? addStyleValue
5636
+ : addAttrValue;
5661
5637
  return handler(element, state, key, value);
5662
5638
  };
5663
5639
  /**
5664
5640
  * Reads the current value of `key` from an SVG element as the origin of
5665
5641
  * an animation, as the SVG VisualElement does: transforms start from
5666
5642
  * their defaults, CSS variables and the few CSS-only properties come from
5667
- * computed style
5668
- * and everything else is read from the attribute, dash-cased
5669
- * (`strokeWidth` -> `stroke-width`) or, failing that, as written
5670
- * (`baseFrequency`).
5643
+ * computed style, falling back to the attribute when that's empty (e.g.
5644
+ * the element isn't rendered), and everything else is read from the
5645
+ * attribute, dash-cased (`strokeWidth` -> `stroke-width`) or, failing
5646
+ * that, as written (`baseFrequency`).
5671
5647
  */
5672
5648
  const readSVGValue = (element, key) => {
5673
5649
  if (transformProps.has(key)) {
5674
5650
  return numberValueTypes[key]?.default || 0;
5675
5651
  }
5676
5652
  if (isCSSVar(key) || cssStyleProperties.includes(key)) {
5677
- return readStyleValue(element, key);
5653
+ return (readStyleValue(element, key) ||
5654
+ element.getAttribute(camelToDash(key)) ||
5655
+ 0);
5678
5656
  }
5679
5657
  key = convertAttrKey(key);
5680
5658
  return (element.getAttribute(camelToDash(key)) ??
@@ -8259,8 +8237,20 @@
8259
8237
  if (this.projection && !this.projection.instance) {
8260
8238
  this.projection.mount(instance);
8261
8239
  }
8262
- if (this.parent && this.isVariantNode && !this.isControllingVariants) {
8263
- this.removeFromVariantTree = this.parent.addVariantChild(this);
8240
+ /**
8241
+ * Join the closest variant node above, so its variant changes
8242
+ * propagate here, unless inherit={false} cuts the chain.
8243
+ */
8244
+ if (this.isVariantNode && !this.isControllingVariants) {
8245
+ let node = this;
8246
+ do
8247
+ node = node.props.inherit !== false && node.parent;
8248
+ while (node && !node.isVariantNode);
8249
+ if (node) {
8250
+ const { variantChildren } = node;
8251
+ variantChildren.add(this);
8252
+ this.removeFromVariantTree = () => variantChildren.delete(this);
8253
+ }
8264
8254
  }
8265
8255
  this.values.forEach((value, key) => this.bindToMotionValue(key, value));
8266
8256
  /**
@@ -8370,9 +8360,11 @@
8370
8360
  }
8371
8361
  sortNodePosition(other) {
8372
8362
  /**
8373
- * If these nodes aren't even of the same type we can't compare their depth.
8363
+ * If either node isn't mounted, or they aren't even of the same type,
8364
+ * we can't compare their document order.
8374
8365
  */
8375
8366
  if (!this.current ||
8367
+ !other.current ||
8376
8368
  !this.sortInstanceNodePosition ||
8377
8369
  this.type !== other.type) {
8378
8370
  return 0;
@@ -8478,24 +8470,6 @@
8478
8470
  getTransformPagePoint() {
8479
8471
  return this.props.transformPagePoint;
8480
8472
  }
8481
- getClosestVariantNode() {
8482
- return this.isVariantNode
8483
- ? this
8484
- : this.parent
8485
- ? this.parent.getClosestVariantNode()
8486
- : undefined;
8487
- }
8488
- /**
8489
- * Add a child visual element to our set of children.
8490
- */
8491
- addVariantChild(child) {
8492
- const closestVariantNode = this.getClosestVariantNode();
8493
- if (closestVariantNode) {
8494
- closestVariantNode.variantChildren &&
8495
- closestVariantNode.variantChildren.add(child);
8496
- return () => closestVariantNode.variantChildren.delete(child);
8497
- }
8498
- }
8499
8473
  /**
8500
8474
  * Add a motion value and bind it to this visual element.
8501
8475
  */
@@ -8590,41 +8564,6 @@
8590
8564
  setBaseTarget(key, value) {
8591
8565
  this.baseTarget[key] = value;
8592
8566
  }
8593
- /**
8594
- * Find the base target for a value thats been removed from all animation
8595
- * props.
8596
- */
8597
- getBaseTarget(key) {
8598
- const { initial } = this.props;
8599
- let valueFromInitial;
8600
- if (typeof initial === "string" || typeof initial === "object") {
8601
- const variant = resolveVariantFromProps(this.props, initial, this.presenceContext?.custom);
8602
- if (variant) {
8603
- valueFromInitial = variant[key];
8604
- }
8605
- }
8606
- /**
8607
- * If this value still exists in the current initial variant, read that.
8608
- */
8609
- if (initial && valueFromInitial !== undefined) {
8610
- return valueFromInitial;
8611
- }
8612
- /**
8613
- * Alternatively, if this VisualElement config has defined a getBaseTarget
8614
- * so we can read the value from an alternative source, try that.
8615
- */
8616
- const target = this.getBaseTargetFromProps(this.props, key);
8617
- if (target !== undefined && !isMotionValue(target))
8618
- return target;
8619
- /**
8620
- * If the value was initially defined on initial, but it doesn't any more,
8621
- * return undefined. Otherwise return the value as initially read from the DOM.
8622
- */
8623
- return this.initialValues[key] !== undefined &&
8624
- valueFromInitial === undefined
8625
- ? undefined
8626
- : this.baseTarget[key];
8627
- }
8628
8567
  on(eventName, callback) {
8629
8568
  if (!this.events[eventName]) {
8630
8569
  this.events[eventName] = new SubscriptionManager();
@@ -8929,34 +8868,6 @@
8929
8868
  }
8930
8869
  }
8931
8870
 
8932
- const numVariantProps = variantProps.length;
8933
- /**
8934
- * Get variant context from a visual element's parent chain.
8935
- * Uses `any` type for visualElement to avoid circular dependencies.
8936
- */
8937
- function getVariantContext(visualElement) {
8938
- if (!visualElement)
8939
- return undefined;
8940
- if (!visualElement.isControllingVariants) {
8941
- const context = visualElement.parent
8942
- ? getVariantContext(visualElement.parent) || {}
8943
- : {};
8944
- if (visualElement.props.initial !== undefined) {
8945
- context.initial = visualElement.props.initial;
8946
- }
8947
- return context;
8948
- }
8949
- const context = {};
8950
- for (let i = 0; i < numVariantProps; i++) {
8951
- const name = variantProps[i];
8952
- const prop = visualElement.props[name];
8953
- if (isVariantLabel(prop) || prop === false) {
8954
- context[name] = prop;
8955
- }
8956
- }
8957
- return context;
8958
- }
8959
-
8960
8871
  function shallowCompare(next, prev) {
8961
8872
  if (!Array.isArray(prev))
8962
8873
  return false;
@@ -8971,332 +8882,211 @@
8971
8882
  }
8972
8883
 
8973
8884
  const reversePriorityOrder = [...variantPriorityOrder].reverse();
8974
- const numAnimationTypes = variantPriorityOrder.length;
8975
- function createAnimateFunction(visualElement) {
8976
- return (animations) => {
8977
- return Promise.all(animations.map(({ animation, options }) => animateVisualElement(visualElement, animation, options)));
8978
- };
8979
- }
8885
+ /**
8886
+ * Diffs the element's animation props (one layer per AnimationType, highest
8887
+ * priority wins each value) against the previous call and starts animations
8888
+ * for what changed.
8889
+ *
8890
+ * Uses `any` type for visualElement to avoid circular dependencies. It reads
8891
+ * props, parent, presenceContext, variantChildren, enteringChildren,
8892
+ * manuallyAnimateOnMount, blockInitialAnimation, getValue(),
8893
+ * getBaseTargetFromProps(), baseTarget and initialValues.
8894
+ */
8980
8895
  function createAnimationState(visualElement) {
8981
- let animate = createAnimateFunction(visualElement);
8896
+ let animate = (animations) => Promise.all(animations.map(({ animation, options }) => animateVisualElement(visualElement, animation, options)));
8982
8897
  let state = createState();
8983
8898
  let isInitialRender = true;
8984
8899
  /**
8985
- * Track whether the animation state has been reset (e.g. via StrictMode
8986
- * double-invocation or Suspense unmount/remount). On the first
8987
- * animateChanges() call after a reset we need to behave like the initial
8988
- * render for variant-inheritance checks, even though isInitialRender is
8989
- * already false.
8900
+ * After a reset (StrictMode, Suspense, AnimatePresence re-entry) the next
8901
+ * animateChanges() mounts again, but initial={false} no longer applies.
8990
8902
  */
8991
8903
  let wasReset = false;
8992
- /**
8993
- * This function will be used to reduce the animation definitions for
8994
- * each active animation type into an object of resolved values for it.
8995
- */
8996
- const buildResolvedTypeValues = (type) => (acc, definition) => {
8997
- const resolved = resolveVariant(visualElement, definition, type === "exit"
8998
- ? visualElement.presenceContext?.custom
8999
- : undefined);
9000
- if (resolved) {
9001
- const { transition, transitionEnd, ...target } = resolved;
9002
- acc = { ...acc, ...target, ...transitionEnd };
9003
- }
9004
- return acc;
9005
- };
9006
- /**
9007
- * This just allows us to inject mocked animation functions
9008
- * @internal
9009
- */
9010
- function setAnimateFunction(makeAnimator) {
9011
- animate = makeAnimator(visualElement);
9012
- }
9013
- /**
9014
- * When we receive new props, we need to:
9015
- * 1. Create a list of protected keys for each type. This is a directory of
9016
- * value keys that are currently being "handled" by types of a higher priority
9017
- * so that whenever an animation is played of a given type, these values are
9018
- * protected from being animated.
9019
- * 2. Determine if an animation type needs animating.
9020
- * 3. Determine if any values have been removed from a type and figure out
9021
- * what to animate those to.
9022
- */
9023
8904
  function animateChanges(changedActiveType) {
9024
- const { props } = visualElement;
9025
- const context = getVariantContext(visualElement.parent) || {};
9026
- /**
9027
- * A list of animations that we'll build into as we iterate through the animation
9028
- * types. This will get executed at the end of the function.
9029
- */
8905
+ const { props, parent, manuallyAnimateOnMount } = visualElement;
8906
+ const isMounting = isInitialRender || wasReset;
9030
8907
  const animations = [];
9031
8908
  /**
9032
- * Keep track of which values have been removed. Then, as we hit lower priority
9033
- * animation types, we can check if they contain removed values and animate to that.
8909
+ * Values removed from a type. A lower-priority type that defines
8910
+ * them animates them back, otherwise they fall back to a base target.
9034
8911
  */
9035
8912
  const removedKeys = new Set();
9036
8913
  /**
9037
- * A dictionary of all encountered keys. This is an object to let us build into and
9038
- * copy it without iteration. Each time we hit an animation type we set its protected
9039
- * keys - the keys its not allowed to animate - to the latest version of this object.
8914
+ * Values claimed by active, higher-priority types.
9040
8915
  */
9041
8916
  let encounteredKeys = {};
9042
8917
  /**
9043
- * If a variant has been removed at a given index, and this component is controlling
9044
- * variant animations, we want to ensure lower-priority variants are forced to animate.
8918
+ * Once a type has been deactivated, lower-priority variant labels
8919
+ * re-animate so variant children animate back too.
9045
8920
  */
9046
8921
  let removedVariantIndex = Infinity;
9047
8922
  /**
9048
- * Iterate through all animation types in reverse priority order. For each, we want to
9049
- * detect which values it's handling and whether or not they've changed (and therefore
9050
- * need to be animated). If any values have been removed, we want to detect those in
9051
- * lower priority props and flag for animation.
8923
+ * Variant labels are inherited from the closest variant-controlling
8924
+ * ancestor, unless an element on the way has inherit={false}.
9052
8925
  */
9053
- for (let i = 0; i < numAnimationTypes; i++) {
9054
- const type = reversePriorityOrder[i];
8926
+ let source = visualElement;
8927
+ do
8928
+ source = source.props.inherit !== false && source.parent;
8929
+ while (source && !source.isControllingVariants);
8930
+ reversePriorityOrder.forEach((type, i) => {
9055
8931
  const typeState = state[type];
9056
- const prop = props[type] !== undefined
9057
- ? props[type]
9058
- : context[type];
8932
+ const ownProp = props[type];
8933
+ const inheritedProp = source && source.props[type];
8934
+ const prop = ownProp !== undefined
8935
+ ? ownProp
8936
+ : isVariantLabel(inheritedProp) || inheritedProp === false
8937
+ ? inheritedProp
8938
+ : undefined;
9059
8939
  const propIsVariant = isVariantLabel(prop);
9060
- /**
9061
- * If this type has *just* changed isActive status, set activeDelta
9062
- * to that status. Otherwise set to null.
9063
- */
9064
8940
  const activeDelta = type === changedActiveType ? typeState.isActive : null;
9065
8941
  if (activeDelta === false)
9066
8942
  removedVariantIndex = i;
9067
8943
  /**
9068
- * If this prop is an inherited variant, rather than been set directly on the
9069
- * component itself, we want to make sure we allow the parent to trigger animations.
9070
- *
9071
- * TODO: Can probably change this to a !isControllingVariants check
9072
- */
9073
- let isInherited = prop === context[type] &&
9074
- prop !== props[type] &&
9075
- propIsVariant;
9076
- if (isInherited &&
9077
- (isInitialRender || wasReset) &&
9078
- visualElement.manuallyAnimateOnMount) {
9079
- isInherited = false;
9080
- }
9081
- /**
9082
- * Set all encountered keys so far as the protected keys for this type. This will
9083
- * be any key that has been animated or otherwise handled by active, higher-priority types.
8944
+ * Inherited labels are animated by the parent through
8945
+ * variantChildren, unless this element mounted after its parent.
9084
8946
  */
8947
+ const isInherited = propIsVariant &&
8948
+ ownProp === undefined &&
8949
+ !(isMounting && manuallyAnimateOnMount);
9085
8950
  typeState.protectedKeys = { ...encounteredKeys };
9086
- // Check if we can skip analysing this prop early
9087
- if (
9088
- // If it isn't active and hasn't *just* been set as inactive
9089
- (!typeState.isActive && activeDelta === null) ||
9090
- // If we didn't and don't have any defined prop for this animation type
8951
+ if ((!typeState.isActive && activeDelta === null) ||
9091
8952
  (!prop && !typeState.prevProp) ||
9092
- // Or if the prop doesn't define an animation
9093
8953
  isAnimationControls(prop) ||
9094
8954
  typeof prop === "boolean") {
9095
- continue;
8955
+ return;
9096
8956
  }
9097
8957
  /**
9098
- * If exit is already active and wasn't just activated, skip
9099
- * re-processing to prevent interrupting running exit animations.
9100
- * Re-resolving exit with a changed custom value can start new
9101
- * value animations that stop the originals, leaving the exit
9102
- * animation promise unresolved and the component stuck in the DOM.
8958
+ * Don't re-resolve a running exit: a changed custom would start
8959
+ * new value animations that stop the originals, leaving the exit
8960
+ * promise unresolved and the element stuck in the DOM.
9103
8961
  */
9104
8962
  if (type === "exit" && typeState.isActive && activeDelta !== true) {
9105
- if (typeState.prevResolvedValues) {
9106
- encounteredKeys = {
9107
- ...encounteredKeys,
9108
- ...typeState.prevResolvedValues,
9109
- };
9110
- }
9111
- continue;
8963
+ encounteredKeys = {
8964
+ ...encounteredKeys,
8965
+ ...typeState.prevResolvedValues,
8966
+ };
8967
+ return;
9112
8968
  }
9113
- /**
9114
- * As we go look through the values defined on this type, if we detect
9115
- * a changed value or a value that was removed in a higher priority, we set
9116
- * this to true and add this prop to the animation list.
9117
- */
9118
8969
  const variantDidChange = checkVariantsDidChange(typeState.prevProp, prop);
9119
8970
  let shouldAnimateType = variantDidChange ||
9120
- // If we're making this variant active, we want to always make it active
9121
- (type === changedActiveType &&
9122
- typeState.isActive &&
9123
- !isInherited &&
9124
- propIsVariant) ||
9125
- // If we removed a higher-priority variant (i is in reverse order)
9126
- (i > removedVariantIndex && propIsVariant);
8971
+ (propIsVariant &&
8972
+ ((activeDelta && !isInherited) || i > removedVariantIndex));
9127
8973
  let handledRemovedValues = false;
9128
- /**
9129
- * As animations can be set as variant lists, variants or target objects, we
9130
- * coerce everything to an array if it isn't one already
9131
- */
9132
8974
  const definitionList = Array.isArray(prop) ? prop : [prop];
9133
- /**
9134
- * Build an object of all the resolved values. We'll use this in the subsequent
9135
- * animateChanges calls to determine whether a value has changed.
9136
- */
9137
- let resolvedValues = definitionList.reduce(buildResolvedTypeValues(type), {});
9138
- if (activeDelta === false)
9139
- resolvedValues = {};
9140
- /**
9141
- * Now we need to loop through all the keys in the prev prop and this prop,
9142
- * and decide:
9143
- * 1. If the value has changed, and needs animating
9144
- * 2. If it has been removed, and needs adding to the removedKeys set
9145
- * 3. If it has been removed in a higher priority type and needs animating
9146
- * 4. If it hasn't been removed in a higher priority but hasn't changed, and
9147
- * needs adding to the type's protectedKeys list.
9148
- */
9149
- const { prevResolvedValues = {} } = typeState;
9150
- const allKeys = {
9151
- ...prevResolvedValues,
9152
- ...resolvedValues,
9153
- };
9154
- const markToAnimate = (key) => {
9155
- shouldAnimateType = true;
9156
- if (removedKeys.has(key)) {
9157
- handledRemovedValues = true;
9158
- removedKeys.delete(key);
8975
+ let resolvedValues = {};
8976
+ if (activeDelta !== false) {
8977
+ for (const definition of definitionList) {
8978
+ const { transition, transitionEnd, ...target } = resolveVariant(visualElement, definition, getTypeCustom(visualElement, type)) || {};
8979
+ resolvedValues = {
8980
+ ...resolvedValues,
8981
+ ...target,
8982
+ ...transitionEnd,
8983
+ };
9159
8984
  }
9160
- typeState.needsAnimating[key] = true;
9161
- const motionValue = visualElement.getValue(key);
9162
- if (motionValue)
9163
- motionValue.liveStyle = false;
9164
- };
9165
- for (const key in allKeys) {
8985
+ }
8986
+ const { prevResolvedValues } = typeState;
8987
+ for (const key in { ...prevResolvedValues, ...resolvedValues }) {
8988
+ if (key in encounteredKeys)
8989
+ continue;
9166
8990
  const next = resolvedValues[key];
9167
8991
  const prev = prevResolvedValues[key];
9168
- // If we've already handled this we can just skip ahead
9169
- if (encounteredKeys.hasOwnProperty(key))
9170
- continue;
9171
8992
  /**
9172
- * If the value has changed, we probably want to animate it.
8993
+ * Keyframes compare by value, but replay whenever the
8994
+ * variant label changed.
9173
8995
  */
9174
- let valueHasChanged = false;
9175
- if (isKeyframesTarget(next) && isKeyframesTarget(prev)) {
9176
- valueHasChanged =
9177
- !shallowCompare(next, prev) || variantDidChange;
9178
- }
9179
- else {
9180
- valueHasChanged = next !== prev;
9181
- }
9182
- if (valueHasChanged) {
9183
- if (next !== undefined && next !== null) {
9184
- // If next is defined and doesn't equal prev, it needs animating
9185
- markToAnimate(key);
9186
- }
9187
- else {
9188
- // If it's undefined, it's been removed.
9189
- removedKeys.add(key);
9190
- }
8996
+ const valueHasChanged = Array.isArray(next) && Array.isArray(prev)
8997
+ ? !shallowCompare(next, prev) || variantDidChange
8998
+ : next !== prev;
8999
+ if (valueHasChanged &&
9000
+ (next === undefined || next === null)) {
9001
+ removedKeys.add(key);
9191
9002
  }
9192
- else if (next !== undefined && removedKeys.has(key)) {
9193
- /**
9194
- * If next hasn't changed and it isn't undefined, we want to check if it's
9195
- * been removed by a higher priority
9196
- */
9197
- markToAnimate(key);
9003
+ else if (valueHasChanged ||
9004
+ (next !== undefined && removedKeys.has(key))) {
9005
+ shouldAnimateType = true;
9006
+ if (removedKeys.delete(key))
9007
+ handledRemovedValues = true;
9008
+ typeState.needsAnimating[key] = true;
9009
+ const motionValue = visualElement.getValue(key);
9010
+ if (motionValue)
9011
+ motionValue.liveStyle = false;
9198
9012
  }
9199
9013
  else {
9200
- /**
9201
- * If it hasn't changed, we add it to the list of protected values
9202
- * to ensure it doesn't get animated.
9203
- */
9204
9014
  typeState.protectedKeys[key] = true;
9205
9015
  }
9206
9016
  }
9207
- /**
9208
- * Update the typeState so next time animateChanges is called we can compare the
9209
- * latest prop and resolvedValues to these.
9210
- */
9211
9017
  typeState.prevProp = prop;
9212
9018
  typeState.prevResolvedValues = resolvedValues;
9213
9019
  if (typeState.isActive) {
9214
9020
  encounteredKeys = { ...encounteredKeys, ...resolvedValues };
9215
9021
  }
9216
- if ((isInitialRender || wasReset) &&
9217
- visualElement.blockInitialAnimation) {
9218
- shouldAnimateType = false;
9219
- }
9220
- /**
9221
- * If this is an inherited prop we want to skip this animation
9222
- * unless the inherited variants haven't changed on this render.
9223
- */
9224
- const willAnimateViaParent = isInherited && variantDidChange;
9225
- const needsAnimating = !willAnimateViaParent || handledRemovedValues;
9226
- if (shouldAnimateType && needsAnimating) {
9227
- animations.push(...definitionList.map((animation) => {
9022
+ if (shouldAnimateType &&
9023
+ (!(isInherited && variantDidChange) || handledRemovedValues)) {
9024
+ for (const animation of definitionList) {
9228
9025
  const options = { type };
9229
9026
  /**
9230
- * If we're performing the initial animation, but we're not
9231
- * rendering at the same time as the variant-controlling parent,
9232
- * we want to use the parent's transition to calculate the stagger.
9027
+ * Elements that mount into an already-mounted parent
9028
+ * animate themselves, staggered by the parent variant's
9029
+ * delayChildren.
9233
9030
  */
9234
9031
  if (typeof animation === "string" &&
9235
- (isInitialRender || wasReset) &&
9236
- !willAnimateViaParent &&
9237
- visualElement.manuallyAnimateOnMount &&
9238
- visualElement.parent) {
9239
- const { parent } = visualElement;
9240
- const parentVariant = resolveVariant(parent, animation);
9241
- if (parent.enteringChildren && parentVariant) {
9242
- const { delayChildren } = parentVariant.transition || {};
9243
- options.delay = calcChildStagger(parent.enteringChildren, visualElement, delayChildren);
9244
- }
9032
+ isMounting &&
9033
+ manuallyAnimateOnMount &&
9034
+ parent?.enteringChildren) {
9035
+ const delayChildren = resolveVariant(parent, animation)
9036
+ ?.transition?.delayChildren;
9037
+ options.delay = calcChildStagger(parent.enteringChildren, visualElement, delayChildren);
9245
9038
  }
9246
- return {
9247
- animation: animation,
9248
- options,
9249
- };
9250
- }));
9039
+ animations.push({ animation, options });
9040
+ }
9251
9041
  }
9252
- }
9042
+ });
9253
9043
  /**
9254
- * If there are some removed value that haven't been dealt with,
9255
- * we need to create a new animation that falls back either to the value
9256
- * defined in the style prop, or the last read value.
9044
+ * Removed values that no lower-priority type defines animate back to
9045
+ * initial, then style, then the value as first read. If initial no
9046
+ * longer defines a value it once did, it stays where it is.
9257
9047
  */
9258
9048
  if (removedKeys.size) {
9049
+ const { initial } = props;
9259
9050
  const fallbackAnimation = {};
9260
- /**
9261
- * If the initial prop contains a transition we can use that, otherwise
9262
- * allow the animation function to use the visual element's default.
9263
- */
9264
- if (typeof props.initial !== "boolean") {
9265
- const initialTransition = resolveVariant(visualElement, Array.isArray(props.initial)
9266
- ? props.initial[0]
9267
- : props.initial);
9268
- if (initialTransition && initialTransition.transition) {
9269
- fallbackAnimation.transition = initialTransition.transition;
9270
- }
9051
+ const resolvedInitial = typeof initial !== "boolean" &&
9052
+ resolveVariant(visualElement, Array.isArray(initial) ? initial[0] : initial, visualElement.presenceContext?.custom);
9053
+ if (resolvedInitial && resolvedInitial.transition) {
9054
+ fallbackAnimation.transition = resolvedInitial.transition;
9271
9055
  }
9272
9056
  removedKeys.forEach((key) => {
9273
- const fallbackTarget = visualElement.getBaseTarget(key);
9274
9057
  const motionValue = visualElement.getValue(key);
9275
9058
  if (motionValue)
9276
9059
  motionValue.liveStyle = true;
9277
- // @ts-expect-error - @mattgperry to figure if we should do something here
9278
- fallbackAnimation[key] = fallbackTarget ?? null;
9060
+ const fromInitial = resolvedInitial && !Array.isArray(initial)
9061
+ ? resolvedInitial[key]
9062
+ : undefined;
9063
+ const fromProps = visualElement.getBaseTargetFromProps(props, key);
9064
+ fallbackAnimation[key] =
9065
+ (fromInitial !== undefined
9066
+ ? fromInitial
9067
+ : fromProps !== undefined && !isMotionValue(fromProps)
9068
+ ? fromProps
9069
+ : visualElement.initialValues[key] === undefined
9070
+ ? visualElement.baseTarget[key]
9071
+ : undefined) ?? null;
9279
9072
  });
9280
9073
  animations.push({ animation: fallbackAnimation });
9281
9074
  }
9282
- let shouldAnimate = Boolean(animations.length);
9283
- if (isInitialRender &&
9284
- (props.initial === false || props.initial === props.animate) &&
9285
- !visualElement.manuallyAnimateOnMount) {
9286
- shouldAnimate = false;
9287
- }
9288
- isInitialRender = false;
9289
- wasReset = false;
9290
- return shouldAnimate ? animate(animations) : Promise.resolve();
9075
+ const blockAnimation = (isMounting && visualElement.blockInitialAnimation) ||
9076
+ (isInitialRender &&
9077
+ !manuallyAnimateOnMount &&
9078
+ (props.initial === false || props.initial === props.animate));
9079
+ isInitialRender = wasReset = false;
9080
+ return !blockAnimation && animations.length
9081
+ ? animate(animations)
9082
+ : Promise.resolve();
9291
9083
  }
9292
9084
  /**
9293
9085
  * Change whether a certain animation type is active.
9294
9086
  */
9295
9087
  function setActive(type, isActive) {
9296
- // If the active state hasn't changed, we can safely do nothing here
9297
9088
  if (state[type].isActive === isActive)
9298
9089
  return Promise.resolve();
9299
- // Propagate active change to children
9300
9090
  visualElement.variantChildren?.forEach((child) => child.animationState?.setActive(type, isActive));
9301
9091
  state[type].isActive = isActive;
9302
9092
  const animations = animateChanges(type);
@@ -9308,7 +9098,13 @@
9308
9098
  return {
9309
9099
  animateChanges,
9310
9100
  setActive,
9311
- setAnimateFunction,
9101
+ /**
9102
+ * Allows tests to inject a mocked animate function.
9103
+ * @internal
9104
+ */
9105
+ setAnimateFunction: (makeAnimator) => {
9106
+ animate = makeAnimator(visualElement);
9107
+ },
9312
9108
  getState: () => state,
9313
9109
  reset: () => {
9314
9110
  state = createState();
@@ -9325,24 +9121,45 @@
9325
9121
  }
9326
9122
  return false;
9327
9123
  }
9328
- function createTypeState(isActive = false) {
9329
- return {
9330
- isActive,
9331
- protectedKeys: {},
9332
- needsAnimating: {},
9333
- prevResolvedValues: {},
9334
- };
9335
- }
9336
9124
  function createState() {
9337
- return {
9338
- animate: createTypeState(true),
9339
- whileInView: createTypeState(),
9340
- whileHover: createTypeState(),
9341
- whileTap: createTypeState(),
9342
- whileDrag: createTypeState(),
9343
- whileFocus: createTypeState(),
9344
- exit: createTypeState(),
9345
- };
9125
+ const state = {};
9126
+ for (const type of variantPriorityOrder) {
9127
+ state[type] = {
9128
+ isActive: type === "animate",
9129
+ protectedKeys: {},
9130
+ needsAnimating: {},
9131
+ prevResolvedValues: {},
9132
+ };
9133
+ }
9134
+ return state;
9135
+ }
9136
+
9137
+ const numVariantProps = variantProps.length;
9138
+ /**
9139
+ * Get variant context from a visual element's parent chain.
9140
+ * Uses `any` type for visualElement to avoid circular dependencies.
9141
+ */
9142
+ function getVariantContext(visualElement) {
9143
+ if (!visualElement)
9144
+ return undefined;
9145
+ if (!visualElement.isControllingVariants) {
9146
+ const context = visualElement.parent
9147
+ ? getVariantContext(visualElement.parent) || {}
9148
+ : {};
9149
+ if (visualElement.props.initial !== undefined) {
9150
+ context.initial = visualElement.props.initial;
9151
+ }
9152
+ return context;
9153
+ }
9154
+ const context = {};
9155
+ for (let i = 0; i < numVariantProps; i++) {
9156
+ const name = variantProps[i];
9157
+ const prop = visualElement.props[name];
9158
+ if (isVariantLabel(prop) || prop === false) {
9159
+ context[name] = prop;
9160
+ }
9161
+ }
9162
+ return context;
9346
9163
  }
9347
9164
 
9348
9165
  /**
@@ -13471,6 +13288,7 @@
13471
13288
  exports.numberValueTypes = numberValueTypes;
13472
13289
  exports.optimizedAppearDataAttribute = optimizedAppearDataAttribute;
13473
13290
  exports.optimizedAppearDataId = optimizedAppearDataId;
13291
+ exports.originProps = originProps;
13474
13292
  exports.parseAnimateLayoutArgs = parseAnimateLayoutArgs;
13475
13293
  exports.parseCSSVariable = parseCSSVariable;
13476
13294
  exports.parseValueFromTransform = parseValueFromTransform;