motion 13.5.0 → 13.5.1

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.
@@ -3037,13 +3037,16 @@
3037
3037
  /**
3038
3038
  * Attaches a timeline to the animation, for instance the `ScrollTimeline`.
3039
3039
  */
3040
- attachTimeline({ timeline, onAttach, observe, }) {
3040
+ attachTimeline({ timeline, onAttach, observe,
3041
+ // Deprecated rangeStart/rangeEnd from framer-motion 13.0–13.4. Removed in v14.
3042
+ ...range }) {
3041
3043
  if (this.allowFlatten) {
3042
3044
  this.animation.effect?.updateTiming({ easing: "linear" });
3043
3045
  }
3044
3046
  this.animation.onfinish = null;
3045
3047
  if (timeline && supportsScrollTimeline()) {
3046
3048
  this.animation.timeline = timeline;
3049
+ Object.assign(this.animation, range);
3047
3050
  onAttach?.(this.animation);
3048
3051
  return noop;
3049
3052
  }
@@ -4381,36 +4384,29 @@
4381
4384
  return state;
4382
4385
  }
4383
4386
  function resolveVariantFromProps(props, definition, custom, visualElement) {
4387
+ const resolveFunction = (def) => typeof def === "function"
4388
+ ? def(custom !== undefined ? custom : props.custom, ...getValueState(visualElement))
4389
+ : def;
4384
4390
  /**
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.
4391
+ * Resolve a function, then a variant label (which the function may have
4392
+ * returned), then a function again as the label may point to one. The
4393
+ * final function can only return a target object.
4394
4394
  */
4395
+ definition = resolveFunction(definition);
4395
4396
  if (typeof definition === "string") {
4396
4397
  definition = props.variants && props.variants[definition];
4397
4398
  }
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;
4399
+ return resolveFunction(definition);
4408
4400
  }
4409
4401
 
4410
4402
  function resolveVariant(visualElement, definition, custom) {
4411
- const props = visualElement.getProps();
4412
- return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, visualElement);
4403
+ return resolveVariantFromProps(visualElement.getProps(), definition, custom, visualElement);
4413
4404
  }
4405
+ /**
4406
+ * Exit variants resolve with AnimatePresence's custom, every other type
4407
+ * with the element's own.
4408
+ */
4409
+ const getTypeCustom = (visualElement, type) => type === "exit" ? visualElement.presenceContext?.custom : undefined;
4414
4410
 
4415
4411
  const positionalKeys = new Set([
4416
4412
  "width",
@@ -4493,7 +4489,7 @@
4493
4489
  * had been set to true in the meantime.
4494
4490
  */
4495
4491
  function shouldBlockAnimation({ protectedKeys, needsAnimating }, key) {
4496
- const shouldBlock = protectedKeys.hasOwnProperty(key) && needsAnimating[key] !== true;
4492
+ const shouldBlock = key in protectedKeys && !needsAnimating[key];
4497
4493
  needsAnimating[key] = false;
4498
4494
  return shouldBlock;
4499
4495
  }
@@ -4508,9 +4504,7 @@
4508
4504
  if (transitionOverride)
4509
4505
  transition = transitionOverride;
4510
4506
  const animations = [];
4511
- const animationTypeState = type &&
4512
- visualElement.animationState &&
4513
- visualElement.animationState.getState()[type];
4507
+ const animationTypeState = type && visualElement.animationState?.getState()[type];
4514
4508
  const path = transition?.path;
4515
4509
  if (path) {
4516
4510
  // path mutates `target` to claim x/y; loop below skips them.
@@ -4584,75 +4578,46 @@
4584
4578
  }
4585
4579
 
4586
4580
  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();
4581
+ const resolved = resolveVariant(visualElement, variant, getTypeCustom(visualElement, options.type));
4582
+ const transition = options.transitionOverride ||
4583
+ (resolved?.transition ?? (visualElement.getDefaultTransition() || {}));
4584
+ const getAnimation = () => Promise.all(resolved ? animateTarget(visualElement, resolved, options) : []);
4585
+ const getChildAnimations = (delay = 0) => {
4586
+ const { variantChildren } = visualElement;
4587
+ const { delayChildren = 0, staggerChildren, staggerDirection, } = transition;
4588
+ const animations = [];
4589
+ variantChildren?.forEach((child) => {
4590
+ child.notify("AnimationStart", variant);
4591
+ animations.push(animateVariant(child, variant, {
4592
+ ...options,
4593
+ delay: delay +
4594
+ (typeof delayChildren === "function"
4595
+ ? 0
4596
+ : delayChildren) +
4597
+ calcChildStagger(variantChildren, child, delayChildren, staggerChildren, staggerDirection),
4598
+ }).then(() => child.notify("AnimationComplete", variant)));
4599
+ });
4600
+ return Promise.all(animations);
4601
+ };
4611
4602
  /**
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.
4603
+ * when: "beforeChildren" | "afterChildren" runs this element's animation
4604
+ * and its children's in sequence.
4614
4605
  */
4615
4606
  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);
4607
+ return when
4608
+ ? when === "beforeChildren"
4609
+ ? getAnimation().then(() => getChildAnimations())
4610
+ : getChildAnimations().then(getAnimation)
4611
+ : Promise.all([getAnimation(), getChildAnimations(options.delay)]);
4638
4612
  }
4639
4613
 
4640
4614
  function animateVisualElement(visualElement, definition, options = {}) {
4641
4615
  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
- }
4616
+ const animation = Array.isArray(definition)
4617
+ ? Promise.all(definition.map((variant) => animateVariant(visualElement, variant, options)))
4618
+ : typeof definition === "string"
4619
+ ? animateVariant(visualElement, definition, options)
4620
+ : Promise.all(animateTarget(visualElement, resolveVariant(visualElement, definition, options.custom), options));
4656
4621
  return animation.then(() => {
4657
4622
  visualElement.notify("AnimationComplete", definition);
4658
4623
  });
@@ -5178,7 +5143,12 @@
5178
5143
  const onRemove = () => {
5179
5144
  cancelOnChange();
5180
5145
  render && !computed && this.cancel(render);
5181
- this.values.delete(name);
5146
+ /**
5147
+ * A later set() of the same name has already removed this
5148
+ * entry, so leave the one that replaced it.
5149
+ */
5150
+ this.values.get(name)?.onRemove === onRemove &&
5151
+ this.values.delete(name);
5182
5152
  };
5183
5153
  this.values.set(name, {
5184
5154
  value,
@@ -5657,24 +5627,35 @@
5657
5627
  else if (key.startsWith("attr")) {
5658
5628
  return addAttrValue(element, state, key, value, convertAttrKey(key));
5659
5629
  }
5660
- const handler = isCSSVar(key) || key in element.style ? addStyleValue : addAttrValue;
5630
+ /**
5631
+ * Transforms and origins aren't CSS properties (bar `x`/`y` in some
5632
+ * browsers), so check them by name rather than `key in element.style`.
5633
+ */
5634
+ const handler = transformProps.has(key) ||
5635
+ originProps.has(key) ||
5636
+ isCSSVar(key) ||
5637
+ key in element.style
5638
+ ? addStyleValue
5639
+ : addAttrValue;
5661
5640
  return handler(element, state, key, value);
5662
5641
  };
5663
5642
  /**
5664
5643
  * Reads the current value of `key` from an SVG element as the origin of
5665
5644
  * an animation, as the SVG VisualElement does: transforms start from
5666
5645
  * 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`).
5646
+ * computed style, falling back to the attribute when that's empty (e.g.
5647
+ * the element isn't rendered), and everything else is read from the
5648
+ * attribute, dash-cased (`strokeWidth` -> `stroke-width`) or, failing
5649
+ * that, as written (`baseFrequency`).
5671
5650
  */
5672
5651
  const readSVGValue = (element, key) => {
5673
5652
  if (transformProps.has(key)) {
5674
5653
  return numberValueTypes[key]?.default || 0;
5675
5654
  }
5676
5655
  if (isCSSVar(key) || cssStyleProperties.includes(key)) {
5677
- return readStyleValue(element, key);
5656
+ return (readStyleValue(element, key) ||
5657
+ element.getAttribute(camelToDash(key)) ||
5658
+ 0);
5678
5659
  }
5679
5660
  key = convertAttrKey(key);
5680
5661
  return (element.getAttribute(camelToDash(key)) ??
@@ -6377,6 +6358,25 @@
6377
6358
  return typeof a === "function" ? resizeWindow(a) : resizeElement(a, b);
6378
6359
  }
6379
6360
 
6361
+ /**
6362
+ * @deprecated Kept because framer-motion 13.0–13.4 import it from
6363
+ * motion-dom with a ^ range. Removed in v14.
6364
+ */
6365
+ function observeTimeline(update, timeline) {
6366
+ let prevProgress;
6367
+ const onFrame = () => {
6368
+ const { currentTime } = timeline;
6369
+ const percentage = currentTime === null ? 0 : currentTime.value;
6370
+ const progress = percentage / 100;
6371
+ if (prevProgress !== progress) {
6372
+ update(progress);
6373
+ }
6374
+ prevProgress = progress;
6375
+ };
6376
+ frame.preUpdate(onFrame, true);
6377
+ return () => cancelFrame(onFrame);
6378
+ }
6379
+
6380
6380
  const statsBuffer = {
6381
6381
  value: null,
6382
6382
  addProjectionMetrics: null,
@@ -8259,8 +8259,20 @@
8259
8259
  if (this.projection && !this.projection.instance) {
8260
8260
  this.projection.mount(instance);
8261
8261
  }
8262
- if (this.parent && this.isVariantNode && !this.isControllingVariants) {
8263
- this.removeFromVariantTree = this.parent.addVariantChild(this);
8262
+ /**
8263
+ * Join the closest variant node above, so its variant changes
8264
+ * propagate here, unless inherit={false} cuts the chain.
8265
+ */
8266
+ if (this.isVariantNode && !this.isControllingVariants) {
8267
+ let node = this;
8268
+ do
8269
+ node = node.props.inherit !== false && node.parent;
8270
+ while (node && !node.isVariantNode);
8271
+ if (node) {
8272
+ const { variantChildren } = node;
8273
+ variantChildren.add(this);
8274
+ this.removeFromVariantTree = () => variantChildren.delete(this);
8275
+ }
8264
8276
  }
8265
8277
  this.values.forEach((value, key) => this.bindToMotionValue(key, value));
8266
8278
  /**
@@ -8370,9 +8382,11 @@
8370
8382
  }
8371
8383
  sortNodePosition(other) {
8372
8384
  /**
8373
- * If these nodes aren't even of the same type we can't compare their depth.
8385
+ * If either node isn't mounted, or they aren't even of the same type,
8386
+ * we can't compare their document order.
8374
8387
  */
8375
8388
  if (!this.current ||
8389
+ !other.current ||
8376
8390
  !this.sortInstanceNodePosition ||
8377
8391
  this.type !== other.type) {
8378
8392
  return 0;
@@ -8478,24 +8492,6 @@
8478
8492
  getTransformPagePoint() {
8479
8493
  return this.props.transformPagePoint;
8480
8494
  }
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
8495
  /**
8500
8496
  * Add a motion value and bind it to this visual element.
8501
8497
  */
@@ -8590,41 +8586,6 @@
8590
8586
  setBaseTarget(key, value) {
8591
8587
  this.baseTarget[key] = value;
8592
8588
  }
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
8589
  on(eventName, callback) {
8629
8590
  if (!this.events[eventName]) {
8630
8591
  this.events[eventName] = new SubscriptionManager();
@@ -8929,34 +8890,6 @@
8929
8890
  }
8930
8891
  }
8931
8892
 
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
8893
  function shallowCompare(next, prev) {
8961
8894
  if (!Array.isArray(prev))
8962
8895
  return false;
@@ -8971,332 +8904,211 @@
8971
8904
  }
8972
8905
 
8973
8906
  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
- }
8907
+ /**
8908
+ * Diffs the element's animation props (one layer per AnimationType, highest
8909
+ * priority wins each value) against the previous call and starts animations
8910
+ * for what changed.
8911
+ *
8912
+ * Uses `any` type for visualElement to avoid circular dependencies. It reads
8913
+ * props, parent, presenceContext, variantChildren, enteringChildren,
8914
+ * manuallyAnimateOnMount, blockInitialAnimation, getValue(),
8915
+ * getBaseTargetFromProps(), baseTarget and initialValues.
8916
+ */
8980
8917
  function createAnimationState(visualElement) {
8981
- let animate = createAnimateFunction(visualElement);
8918
+ let animate = (animations) => Promise.all(animations.map(({ animation, options }) => animateVisualElement(visualElement, animation, options)));
8982
8919
  let state = createState();
8983
8920
  let isInitialRender = true;
8984
8921
  /**
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.
8922
+ * After a reset (StrictMode, Suspense, AnimatePresence re-entry) the next
8923
+ * animateChanges() mounts again, but initial={false} no longer applies.
8990
8924
  */
8991
8925
  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
8926
  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
- */
8927
+ const { props, parent, manuallyAnimateOnMount } = visualElement;
8928
+ const isMounting = isInitialRender || wasReset;
9030
8929
  const animations = [];
9031
8930
  /**
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.
8931
+ * Values removed from a type. A lower-priority type that defines
8932
+ * them animates them back, otherwise they fall back to a base target.
9034
8933
  */
9035
8934
  const removedKeys = new Set();
9036
8935
  /**
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.
8936
+ * Values claimed by active, higher-priority types.
9040
8937
  */
9041
8938
  let encounteredKeys = {};
9042
8939
  /**
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.
8940
+ * Once a type has been deactivated, lower-priority variant labels
8941
+ * re-animate so variant children animate back too.
9045
8942
  */
9046
8943
  let removedVariantIndex = Infinity;
9047
8944
  /**
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.
8945
+ * Variant labels are inherited from the closest variant-controlling
8946
+ * ancestor, unless an element on the way has inherit={false}.
9052
8947
  */
9053
- for (let i = 0; i < numAnimationTypes; i++) {
9054
- const type = reversePriorityOrder[i];
8948
+ let source = visualElement;
8949
+ do
8950
+ source = source.props.inherit !== false && source.parent;
8951
+ while (source && !source.isControllingVariants);
8952
+ reversePriorityOrder.forEach((type, i) => {
9055
8953
  const typeState = state[type];
9056
- const prop = props[type] !== undefined
9057
- ? props[type]
9058
- : context[type];
8954
+ const ownProp = props[type];
8955
+ const inheritedProp = source && source.props[type];
8956
+ const prop = ownProp !== undefined
8957
+ ? ownProp
8958
+ : isVariantLabel(inheritedProp) || inheritedProp === false
8959
+ ? inheritedProp
8960
+ : undefined;
9059
8961
  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
8962
  const activeDelta = type === changedActiveType ? typeState.isActive : null;
9065
8963
  if (activeDelta === false)
9066
8964
  removedVariantIndex = i;
9067
8965
  /**
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.
8966
+ * Inherited labels are animated by the parent through
8967
+ * variantChildren, unless this element mounted after its parent.
9084
8968
  */
8969
+ const isInherited = propIsVariant &&
8970
+ ownProp === undefined &&
8971
+ !(isMounting && manuallyAnimateOnMount);
9085
8972
  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
8973
+ if ((!typeState.isActive && activeDelta === null) ||
9091
8974
  (!prop && !typeState.prevProp) ||
9092
- // Or if the prop doesn't define an animation
9093
8975
  isAnimationControls(prop) ||
9094
8976
  typeof prop === "boolean") {
9095
- continue;
8977
+ return;
9096
8978
  }
9097
8979
  /**
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.
8980
+ * Don't re-resolve a running exit: a changed custom would start
8981
+ * new value animations that stop the originals, leaving the exit
8982
+ * promise unresolved and the element stuck in the DOM.
9103
8983
  */
9104
8984
  if (type === "exit" && typeState.isActive && activeDelta !== true) {
9105
- if (typeState.prevResolvedValues) {
9106
- encounteredKeys = {
9107
- ...encounteredKeys,
9108
- ...typeState.prevResolvedValues,
9109
- };
9110
- }
9111
- continue;
8985
+ encounteredKeys = {
8986
+ ...encounteredKeys,
8987
+ ...typeState.prevResolvedValues,
8988
+ };
8989
+ return;
9112
8990
  }
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
8991
  const variantDidChange = checkVariantsDidChange(typeState.prevProp, prop);
9119
8992
  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);
8993
+ (propIsVariant &&
8994
+ ((activeDelta && !isInherited) || i > removedVariantIndex));
9127
8995
  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
8996
  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);
8997
+ let resolvedValues = {};
8998
+ if (activeDelta !== false) {
8999
+ for (const definition of definitionList) {
9000
+ const { transition, transitionEnd, ...target } = resolveVariant(visualElement, definition, getTypeCustom(visualElement, type)) || {};
9001
+ resolvedValues = {
9002
+ ...resolvedValues,
9003
+ ...target,
9004
+ ...transitionEnd,
9005
+ };
9159
9006
  }
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) {
9007
+ }
9008
+ const { prevResolvedValues } = typeState;
9009
+ for (const key in { ...prevResolvedValues, ...resolvedValues }) {
9010
+ if (key in encounteredKeys)
9011
+ continue;
9166
9012
  const next = resolvedValues[key];
9167
9013
  const prev = prevResolvedValues[key];
9168
- // If we've already handled this we can just skip ahead
9169
- if (encounteredKeys.hasOwnProperty(key))
9170
- continue;
9171
9014
  /**
9172
- * If the value has changed, we probably want to animate it.
9015
+ * Keyframes compare by value, but replay whenever the
9016
+ * variant label changed.
9173
9017
  */
9174
- let valueHasChanged = false;
9175
- if (isKeyframesTarget(next) && isKeyframesTarget(prev)) {
9176
- valueHasChanged =
9177
- !shallowCompare(next, prev) || variantDidChange;
9018
+ const valueHasChanged = Array.isArray(next) && Array.isArray(prev)
9019
+ ? !shallowCompare(next, prev) || variantDidChange
9020
+ : next !== prev;
9021
+ if (valueHasChanged &&
9022
+ (next === undefined || next === null)) {
9023
+ removedKeys.add(key);
9178
9024
  }
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
- }
9191
- }
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);
9025
+ else if (valueHasChanged ||
9026
+ (next !== undefined && removedKeys.has(key))) {
9027
+ shouldAnimateType = true;
9028
+ if (removedKeys.delete(key))
9029
+ handledRemovedValues = true;
9030
+ typeState.needsAnimating[key] = true;
9031
+ const motionValue = visualElement.getValue(key);
9032
+ if (motionValue)
9033
+ motionValue.liveStyle = false;
9198
9034
  }
9199
9035
  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
9036
  typeState.protectedKeys[key] = true;
9205
9037
  }
9206
9038
  }
9207
- /**
9208
- * Update the typeState so next time animateChanges is called we can compare the
9209
- * latest prop and resolvedValues to these.
9210
- */
9211
9039
  typeState.prevProp = prop;
9212
9040
  typeState.prevResolvedValues = resolvedValues;
9213
9041
  if (typeState.isActive) {
9214
9042
  encounteredKeys = { ...encounteredKeys, ...resolvedValues };
9215
9043
  }
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) => {
9044
+ if (shouldAnimateType &&
9045
+ (!(isInherited && variantDidChange) || handledRemovedValues)) {
9046
+ for (const animation of definitionList) {
9228
9047
  const options = { type };
9229
9048
  /**
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.
9049
+ * Elements that mount into an already-mounted parent
9050
+ * animate themselves, staggered by the parent variant's
9051
+ * delayChildren.
9233
9052
  */
9234
9053
  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
- }
9054
+ isMounting &&
9055
+ manuallyAnimateOnMount &&
9056
+ parent?.enteringChildren) {
9057
+ const delayChildren = resolveVariant(parent, animation)
9058
+ ?.transition?.delayChildren;
9059
+ options.delay = calcChildStagger(parent.enteringChildren, visualElement, delayChildren);
9245
9060
  }
9246
- return {
9247
- animation: animation,
9248
- options,
9249
- };
9250
- }));
9061
+ animations.push({ animation, options });
9062
+ }
9251
9063
  }
9252
- }
9064
+ });
9253
9065
  /**
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.
9066
+ * Removed values that no lower-priority type defines animate back to
9067
+ * initial, then style, then the value as first read. If initial no
9068
+ * longer defines a value it once did, it stays where it is.
9257
9069
  */
9258
9070
  if (removedKeys.size) {
9071
+ const { initial } = props;
9259
9072
  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
- }
9073
+ const resolvedInitial = typeof initial !== "boolean" &&
9074
+ resolveVariant(visualElement, Array.isArray(initial) ? initial[0] : initial, visualElement.presenceContext?.custom);
9075
+ if (resolvedInitial && resolvedInitial.transition) {
9076
+ fallbackAnimation.transition = resolvedInitial.transition;
9271
9077
  }
9272
9078
  removedKeys.forEach((key) => {
9273
- const fallbackTarget = visualElement.getBaseTarget(key);
9274
9079
  const motionValue = visualElement.getValue(key);
9275
9080
  if (motionValue)
9276
9081
  motionValue.liveStyle = true;
9277
- // @ts-expect-error - @mattgperry to figure if we should do something here
9278
- fallbackAnimation[key] = fallbackTarget ?? null;
9082
+ const fromInitial = resolvedInitial && !Array.isArray(initial)
9083
+ ? resolvedInitial[key]
9084
+ : undefined;
9085
+ const fromProps = visualElement.getBaseTargetFromProps(props, key);
9086
+ fallbackAnimation[key] =
9087
+ (fromInitial !== undefined
9088
+ ? fromInitial
9089
+ : fromProps !== undefined && !isMotionValue(fromProps)
9090
+ ? fromProps
9091
+ : visualElement.initialValues[key] === undefined
9092
+ ? visualElement.baseTarget[key]
9093
+ : undefined) ?? null;
9279
9094
  });
9280
9095
  animations.push({ animation: fallbackAnimation });
9281
9096
  }
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();
9097
+ const blockAnimation = (isMounting && visualElement.blockInitialAnimation) ||
9098
+ (isInitialRender &&
9099
+ !manuallyAnimateOnMount &&
9100
+ (props.initial === false || props.initial === props.animate));
9101
+ isInitialRender = wasReset = false;
9102
+ return !blockAnimation && animations.length
9103
+ ? animate(animations)
9104
+ : Promise.resolve();
9291
9105
  }
9292
9106
  /**
9293
9107
  * Change whether a certain animation type is active.
9294
9108
  */
9295
9109
  function setActive(type, isActive) {
9296
- // If the active state hasn't changed, we can safely do nothing here
9297
9110
  if (state[type].isActive === isActive)
9298
9111
  return Promise.resolve();
9299
- // Propagate active change to children
9300
9112
  visualElement.variantChildren?.forEach((child) => child.animationState?.setActive(type, isActive));
9301
9113
  state[type].isActive = isActive;
9302
9114
  const animations = animateChanges(type);
@@ -9308,7 +9120,13 @@
9308
9120
  return {
9309
9121
  animateChanges,
9310
9122
  setActive,
9311
- setAnimateFunction,
9123
+ /**
9124
+ * Allows tests to inject a mocked animate function.
9125
+ * @internal
9126
+ */
9127
+ setAnimateFunction: (makeAnimator) => {
9128
+ animate = makeAnimator(visualElement);
9129
+ },
9312
9130
  getState: () => state,
9313
9131
  reset: () => {
9314
9132
  state = createState();
@@ -9325,24 +9143,45 @@
9325
9143
  }
9326
9144
  return false;
9327
9145
  }
9328
- function createTypeState(isActive = false) {
9329
- return {
9330
- isActive,
9331
- protectedKeys: {},
9332
- needsAnimating: {},
9333
- prevResolvedValues: {},
9334
- };
9335
- }
9336
9146
  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
- };
9147
+ const state = {};
9148
+ for (const type of variantPriorityOrder) {
9149
+ state[type] = {
9150
+ isActive: type === "animate",
9151
+ protectedKeys: {},
9152
+ needsAnimating: {},
9153
+ prevResolvedValues: {},
9154
+ };
9155
+ }
9156
+ return state;
9157
+ }
9158
+
9159
+ const numVariantProps = variantProps.length;
9160
+ /**
9161
+ * Get variant context from a visual element's parent chain.
9162
+ * Uses `any` type for visualElement to avoid circular dependencies.
9163
+ */
9164
+ function getVariantContext(visualElement) {
9165
+ if (!visualElement)
9166
+ return undefined;
9167
+ if (!visualElement.isControllingVariants) {
9168
+ const context = visualElement.parent
9169
+ ? getVariantContext(visualElement.parent) || {}
9170
+ : {};
9171
+ if (visualElement.props.initial !== undefined) {
9172
+ context.initial = visualElement.props.initial;
9173
+ }
9174
+ return context;
9175
+ }
9176
+ const context = {};
9177
+ for (let i = 0; i < numVariantProps; i++) {
9178
+ const name = variantProps[i];
9179
+ const prop = visualElement.props[name];
9180
+ if (isVariantLabel(prop) || prop === false) {
9181
+ context[name] = prop;
9182
+ }
9183
+ }
9184
+ return context;
9346
9185
  }
9347
9186
 
9348
9187
  /**
@@ -13469,8 +13308,10 @@
13469
13308
  exports.noop = noop;
13470
13309
  exports.number = number;
13471
13310
  exports.numberValueTypes = numberValueTypes;
13311
+ exports.observeTimeline = observeTimeline;
13472
13312
  exports.optimizedAppearDataAttribute = optimizedAppearDataAttribute;
13473
13313
  exports.optimizedAppearDataId = optimizedAppearDataId;
13314
+ exports.originProps = originProps;
13474
13315
  exports.parseAnimateLayoutArgs = parseAnimateLayoutArgs;
13475
13316
  exports.parseCSSVariable = parseCSSVariable;
13476
13317
  exports.parseValueFromTransform = parseValueFromTransform;