@betterstore/react 0.2.43 → 0.2.45

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.
package/dist/index.cjs.js CHANGED
@@ -3943,8 +3943,10 @@ const AnimatePresence = ({ children, custom, initial = true, onExitComplete, pre
3943
3943
  };
3944
3944
 
3945
3945
  const stepsOrder = [
3946
+ "setup", // Compute
3946
3947
  "read", // Read
3947
3948
  "resolveKeyframes", // Write/Read/Write/Read
3949
+ "preUpdate", // Compute
3948
3950
  "update", // Compute
3949
3951
  "preRender", // Compute
3950
3952
  "render", // Write
@@ -4043,9 +4045,7 @@ function createRenderStep(runNextFrame, stepName) {
4043
4045
  return step;
4044
4046
  }
4045
4047
 
4046
- const MotionGlobalConfig = {
4047
- useManualTiming: false,
4048
- };
4048
+ const MotionGlobalConfig = {};
4049
4049
 
4050
4050
  const maxElapsed = 40;
4051
4051
  function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
@@ -4061,11 +4061,13 @@ function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
4061
4061
  acc[key] = createRenderStep(flagRunNextFrame, allowKeepAlive ? key : undefined);
4062
4062
  return acc;
4063
4063
  }, {});
4064
- const { read, resolveKeyframes, update, preRender, render, postRender } = steps;
4064
+ const { setup, read, resolveKeyframes, preUpdate, update, preRender, render, postRender, } = steps;
4065
4065
  const processBatch = () => {
4066
- const timestamp = performance.now();
4066
+ const timestamp = MotionGlobalConfig.useManualTiming
4067
+ ? state.timestamp
4068
+ : performance.now();
4067
4069
  runNextFrame = false;
4068
- {
4070
+ if (!MotionGlobalConfig.useManualTiming) {
4069
4071
  state.delta = useDefaultElapsed
4070
4072
  ? 1000 / 60
4071
4073
  : Math.max(Math.min(timestamp - state.timestamp, maxElapsed), 1);
@@ -4073,8 +4075,10 @@ function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
4073
4075
  state.timestamp = timestamp;
4074
4076
  state.isProcessing = true;
4075
4077
  // Unrolled render loop for better per-frame performance
4078
+ setup.process(state);
4076
4079
  read.process(state);
4077
4080
  resolveKeyframes.process(state);
4081
+ preUpdate.process(state);
4078
4082
  update.process(state);
4079
4083
  preRender.process(state);
4080
4084
  render.process(state);
@@ -4689,7 +4693,7 @@ const transformPropOrder = [
4689
4693
  /**
4690
4694
  * A quick lookup for transform props.
4691
4695
  */
4692
- const transformProps = new Set(transformPropOrder);
4696
+ const transformProps = /*@__PURE__*/ (() => new Set(transformPropOrder))();
4693
4697
 
4694
4698
  function isForcedMotionValue(key, { layout, layoutId }) {
4695
4699
  return (transformProps.has(key) ||
@@ -4731,6 +4735,12 @@ const scale = {
4731
4735
  default: 1,
4732
4736
  };
4733
4737
 
4738
+ const int = {
4739
+ ...number,
4740
+ transform: Math.round,
4741
+ };
4742
+
4743
+ /*#__NO_SIDE_EFFECTS__*/
4734
4744
  const createUnitType = (unit) => ({
4735
4745
  test: (v) => typeof v === "string" && v.endsWith(unit) && v.split(" ").length === 1,
4736
4746
  parse: parseFloat,
@@ -4741,13 +4751,40 @@ const percent = /*@__PURE__*/ createUnitType("%");
4741
4751
  const px = /*@__PURE__*/ createUnitType("px");
4742
4752
  const vh = /*@__PURE__*/ createUnitType("vh");
4743
4753
  const vw = /*@__PURE__*/ createUnitType("vw");
4744
- const progressPercentage = {
4754
+ const progressPercentage = /*@__PURE__*/ (() => ({
4745
4755
  ...percent,
4746
4756
  parse: (v) => percent.parse(v) / 100,
4747
4757
  transform: (v) => percent.transform(v * 100),
4758
+ }))();
4759
+
4760
+ const transformValueTypes = {
4761
+ rotate: degrees,
4762
+ rotateX: degrees,
4763
+ rotateY: degrees,
4764
+ rotateZ: degrees,
4765
+ scale,
4766
+ scaleX: scale,
4767
+ scaleY: scale,
4768
+ scaleZ: scale,
4769
+ skew: degrees,
4770
+ skewX: degrees,
4771
+ skewY: degrees,
4772
+ distance: px,
4773
+ translateX: px,
4774
+ translateY: px,
4775
+ translateZ: px,
4776
+ x: px,
4777
+ y: px,
4778
+ z: px,
4779
+ perspective: px,
4780
+ transformPerspective: px,
4781
+ opacity: alpha,
4782
+ originX: progressPercentage,
4783
+ originY: progressPercentage,
4784
+ originZ: px,
4748
4785
  };
4749
4786
 
4750
- const browserNumberValueTypes = {
4787
+ const numberValueTypes = {
4751
4788
  // Border props
4752
4789
  borderWidth: px,
4753
4790
  borderTopWidth: px,
@@ -4783,45 +4820,8 @@ const browserNumberValueTypes = {
4783
4820
  // Misc
4784
4821
  backgroundPositionX: px,
4785
4822
  backgroundPositionY: px,
4786
- };
4787
-
4788
- const transformValueTypes = {
4789
- rotate: degrees,
4790
- rotateX: degrees,
4791
- rotateY: degrees,
4792
- rotateZ: degrees,
4793
- scale,
4794
- scaleX: scale,
4795
- scaleY: scale,
4796
- scaleZ: scale,
4797
- skew: degrees,
4798
- skewX: degrees,
4799
- skewY: degrees,
4800
- distance: px,
4801
- translateX: px,
4802
- translateY: px,
4803
- translateZ: px,
4804
- x: px,
4805
- y: px,
4806
- z: px,
4807
- perspective: px,
4808
- transformPerspective: px,
4809
- opacity: alpha,
4810
- originX: progressPercentage,
4811
- originY: progressPercentage,
4812
- originZ: px,
4813
- };
4814
-
4815
- const int = {
4816
- ...number,
4817
- transform: Math.round,
4818
- };
4819
-
4820
- const numberValueTypes = {
4821
- ...browserNumberValueTypes,
4822
4823
  ...transformValueTypes,
4823
4824
  zIndex: int,
4824
- size: px,
4825
4825
  // SVG
4826
4826
  fillOpacity: alpha,
4827
4827
  strokeOpacity: alpha,
@@ -5088,25 +5088,10 @@ function buildSVGPath(attrs, length, spacing = 1, offset = 0, useDashCase = true
5088
5088
  attrs[keys.array] = `${pathLength} ${pathSpacing}`;
5089
5089
  }
5090
5090
 
5091
- function calcOrigin$1(origin, offset, size) {
5092
- return typeof origin === "string"
5093
- ? origin
5094
- : px.transform(offset + size * origin);
5095
- }
5096
- /**
5097
- * The SVG transform origin defaults are different to CSS and is less intuitive,
5098
- * so we use the measured dimensions of the SVG to reconcile these.
5099
- */
5100
- function calcSVGTransformOrigin(dimensions, originX, originY) {
5101
- const pxOriginX = calcOrigin$1(originX, dimensions.x, dimensions.width);
5102
- const pxOriginY = calcOrigin$1(originY, dimensions.y, dimensions.height);
5103
- return `${pxOriginX} ${pxOriginY}`;
5104
- }
5105
-
5106
5091
  /**
5107
5092
  * Build SVG visual attrbutes, like cx and style.transform
5108
5093
  */
5109
- function buildSVGAttrs(state, { attrX, attrY, attrScale, originX, originY, pathLength, pathSpacing = 1, pathOffset = 0,
5094
+ function buildSVGAttrs(state, { attrX, attrY, attrScale, pathLength, pathSpacing = 1, pathOffset = 0,
5110
5095
  // This is object creation, which we try to avoid per-frame.
5111
5096
  ...latest }, isSVGTag, transformTemplate) {
5112
5097
  buildHTMLStyles(state, latest, transformTemplate);
@@ -5122,20 +5107,26 @@ function buildSVGAttrs(state, { attrX, attrY, attrScale, originX, originY, pathL
5122
5107
  }
5123
5108
  state.attrs = state.style;
5124
5109
  state.style = {};
5125
- const { attrs, style, dimensions } = state;
5110
+ const { attrs, style } = state;
5126
5111
  /**
5127
- * However, we apply transforms as CSS transforms. So if we detect a transform we take it from attrs
5128
- * and copy it into style.
5112
+ * However, we apply transforms as CSS transforms.
5113
+ * So if we detect a transform, transformOrigin we take it from attrs and copy it into style.
5129
5114
  */
5130
5115
  if (attrs.transform) {
5131
- if (dimensions)
5132
- style.transform = attrs.transform;
5116
+ style.transform = attrs.transform;
5133
5117
  delete attrs.transform;
5134
5118
  }
5135
- // Parse transformOrigin
5136
- if (dimensions &&
5137
- (originX !== undefined || originY !== undefined || style.transform)) {
5138
- style.transformOrigin = calcSVGTransformOrigin(dimensions, originX !== undefined ? originX : 0.5, originY !== undefined ? originY : 0.5);
5119
+ if (style.transform || attrs.transformOrigin) {
5120
+ style.transformOrigin = attrs.transformOrigin ?? "50% 50%";
5121
+ delete attrs.transformOrigin;
5122
+ }
5123
+ if (style.transform) {
5124
+ /**
5125
+ * SVG's element transform-origin uses its own median as a reference.
5126
+ * Therefore, transformBox becomes a fill-box
5127
+ */
5128
+ style.transformBox = "fill-box";
5129
+ delete attrs.transformBox;
5139
5130
  }
5140
5131
  // Render attrX/attrY/attrScale as attributes
5141
5132
  if (attrX !== undefined)
@@ -5234,44 +5225,20 @@ function resolveVariantFromProps(props, definition, custom, visualElement) {
5234
5225
  return definition;
5235
5226
  }
5236
5227
 
5237
- const isKeyframesTarget = (v) => {
5238
- return Array.isArray(v);
5239
- };
5240
-
5241
- const isCustomValue = (v) => {
5242
- return Boolean(v && typeof v === "object" && v.mix && v.toValue);
5243
- };
5244
- const resolveFinalValueInKeyframes = (v) => {
5245
- // TODO maybe throw if v.length - 1 is placeholder token?
5246
- return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v;
5247
- };
5248
-
5249
5228
  /**
5250
5229
  * If the provided value is a MotionValue, this returns the actual value, otherwise just the value itself
5251
5230
  *
5252
5231
  * TODO: Remove and move to library
5253
5232
  */
5254
5233
  function resolveMotionValue(value) {
5255
- const unwrappedValue = isMotionValue(value) ? value.get() : value;
5256
- return isCustomValue(unwrappedValue)
5257
- ? unwrappedValue.toValue()
5258
- : unwrappedValue;
5234
+ return isMotionValue(value) ? value.get() : value;
5259
5235
  }
5260
5236
 
5261
- function makeState({ scrapeMotionValuesFromProps, createRenderState, onUpdate, }, props, context, presenceContext) {
5237
+ function makeState({ scrapeMotionValuesFromProps, createRenderState, }, props, context, presenceContext) {
5262
5238
  const state = {
5263
5239
  latestValues: makeLatestValues(props, context, presenceContext, scrapeMotionValuesFromProps),
5264
5240
  renderState: createRenderState(),
5265
5241
  };
5266
- if (onUpdate) {
5267
- /**
5268
- * onMount works without the VisualElement because it could be
5269
- * called before the VisualElement payload has been hydrated.
5270
- * (e.g. if someone is using m components <m.circle />)
5271
- */
5272
- state.onMount = (instance) => onUpdate({ props, current: instance, ...state });
5273
- state.onUpdate = (visualElement) => onUpdate(visualElement);
5274
- }
5275
5242
  return state;
5276
5243
  }
5277
5244
  const makeUseVisualState = (config) => (props, isStatic) => {
@@ -5358,68 +5325,6 @@ const htmlMotionConfig = {
5358
5325
  }),
5359
5326
  };
5360
5327
 
5361
- function updateSVGDimensions(instance, renderState) {
5362
- try {
5363
- renderState.dimensions =
5364
- typeof instance.getBBox === "function"
5365
- ? instance.getBBox()
5366
- : instance.getBoundingClientRect();
5367
- }
5368
- catch (e) {
5369
- // Most likely trying to measure an unrendered element under Firefox
5370
- renderState.dimensions = {
5371
- x: 0,
5372
- y: 0,
5373
- width: 0,
5374
- height: 0,
5375
- };
5376
- }
5377
- }
5378
-
5379
- function renderHTML(element, { style, vars }, styleProp, projection) {
5380
- Object.assign(element.style, style, projection && projection.getProjectionStyles(styleProp));
5381
- // Loop over any CSS variables and assign those.
5382
- for (const key in vars) {
5383
- element.style.setProperty(key, vars[key]);
5384
- }
5385
- }
5386
-
5387
- /**
5388
- * A set of attribute names that are always read/written as camel case.
5389
- */
5390
- const camelCaseAttributes = new Set([
5391
- "baseFrequency",
5392
- "diffuseConstant",
5393
- "kernelMatrix",
5394
- "kernelUnitLength",
5395
- "keySplines",
5396
- "keyTimes",
5397
- "limitingConeAngle",
5398
- "markerHeight",
5399
- "markerWidth",
5400
- "numOctaves",
5401
- "targetX",
5402
- "targetY",
5403
- "surfaceScale",
5404
- "specularConstant",
5405
- "specularExponent",
5406
- "stdDeviation",
5407
- "tableValues",
5408
- "viewBox",
5409
- "gradientTransform",
5410
- "pathLength",
5411
- "startOffset",
5412
- "textLength",
5413
- "lengthAdjust",
5414
- ]);
5415
-
5416
- function renderSVG(element, renderState, _styleProp, projection) {
5417
- renderHTML(element, renderState, undefined, projection);
5418
- for (const key in renderState.attrs) {
5419
- element.setAttribute(!camelCaseAttributes.has(key) ? camelToDash(key) : key, renderState.attrs[key]);
5420
- }
5421
- }
5422
-
5423
5328
  function scrapeMotionValuesFromProps(props, prevProps, visualElement) {
5424
5329
  const newValues = scrapeMotionValuesFromProps$1(props, prevProps, visualElement);
5425
5330
  for (const key in props) {
@@ -5434,49 +5339,10 @@ function scrapeMotionValuesFromProps(props, prevProps, visualElement) {
5434
5339
  return newValues;
5435
5340
  }
5436
5341
 
5437
- const layoutProps = ["x", "y", "width", "height", "cx", "cy", "r"];
5438
5342
  const svgMotionConfig = {
5439
5343
  useVisualState: makeUseVisualState({
5440
5344
  scrapeMotionValuesFromProps: scrapeMotionValuesFromProps,
5441
5345
  createRenderState: createSvgRenderState,
5442
- onUpdate: ({ props, prevProps, current, renderState, latestValues, }) => {
5443
- if (!current)
5444
- return;
5445
- let hasTransform = !!props.drag;
5446
- if (!hasTransform) {
5447
- for (const key in latestValues) {
5448
- if (transformProps.has(key)) {
5449
- hasTransform = true;
5450
- break;
5451
- }
5452
- }
5453
- }
5454
- if (!hasTransform)
5455
- return;
5456
- let needsMeasure = !prevProps;
5457
- if (prevProps) {
5458
- /**
5459
- * Check the layout props for changes, if any are found we need to
5460
- * measure the element again.
5461
- */
5462
- for (let i = 0; i < layoutProps.length; i++) {
5463
- const key = layoutProps[i];
5464
- if (props[key] !==
5465
- prevProps[key]) {
5466
- needsMeasure = true;
5467
- }
5468
- }
5469
- }
5470
- if (!needsMeasure)
5471
- return;
5472
- frame.read(() => {
5473
- updateSVGDimensions(current, renderState);
5474
- frame.render(() => {
5475
- buildSVGAttrs(renderState, latestValues, isSVGTag(current.tagName), props.transformTemplate);
5476
- renderSVG(current, renderState);
5477
- });
5478
- });
5479
- },
5480
5346
  }),
5481
5347
  };
5482
5348
 
@@ -5501,15 +5367,9 @@ function resolveVariant(visualElement, definition, custom) {
5501
5367
  return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, visualElement);
5502
5368
  }
5503
5369
 
5504
- const positionalKeys = new Set([
5505
- "width",
5506
- "height",
5507
- "top",
5508
- "left",
5509
- "right",
5510
- "bottom",
5511
- ...transformPropOrder,
5512
- ]);
5370
+ const isKeyframesTarget = (v) => {
5371
+ return Array.isArray(v);
5372
+ };
5513
5373
 
5514
5374
  let now;
5515
5375
  function clearTime() {
@@ -5620,7 +5480,7 @@ class MotionValue {
5620
5480
  * This will be replaced by the build step with the latest version number.
5621
5481
  * When MotionValues are provided to motion components, warn if versions are mixed.
5622
5482
  */
5623
- this.version = "12.7.4";
5483
+ this.version = "12.9.0";
5624
5484
  /**
5625
5485
  * Tracks whether this value can output a velocity. Currently this is only true
5626
5486
  * if the value is numerical, but we might be able to widen the scope here and support
@@ -5646,12 +5506,12 @@ class MotionValue {
5646
5506
  this.prev = this.current;
5647
5507
  this.setCurrent(v);
5648
5508
  // Update update subscribers
5649
- if (this.current !== this.prev && this.events.change) {
5650
- this.events.change.notify(this.current);
5509
+ if (this.current !== this.prev) {
5510
+ this.events.change?.notify(this.current);
5651
5511
  }
5652
5512
  // Update render subscribers
5653
- if (render && this.events.renderRequest) {
5654
- this.events.renderRequest.notify(this.current);
5513
+ if (render) {
5514
+ this.events.renderRequest?.notify(this.current);
5655
5515
  }
5656
5516
  };
5657
5517
  this.hasAnimated = false;
@@ -5884,6 +5744,7 @@ class MotionValue {
5884
5744
  * @public
5885
5745
  */
5886
5746
  destroy() {
5747
+ this.events.destroy?.notify();
5887
5748
  this.clearListeners();
5888
5749
  this.stop();
5889
5750
  if (this.stopPassiveEffect) {
@@ -5907,6 +5768,10 @@ function setMotionValue(visualElement, key, value) {
5907
5768
  visualElement.addValue(key, motionValue(value));
5908
5769
  }
5909
5770
  }
5771
+ function resolveFinalValueInKeyframes(v) {
5772
+ // TODO maybe throw if v.length - 1 is placeholder token?
5773
+ return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v;
5774
+ }
5910
5775
  function setTarget(visualElement, definition) {
5911
5776
  const resolved = resolveVariant(visualElement, definition);
5912
5777
  let { transitionEnd = {}, transition = {}, ...target } = resolved || {};
@@ -5941,89 +5806,79 @@ function getOptimisedAppearId(visualElement) {
5941
5806
  return visualElement.props[optimizedAppearDataAttribute];
5942
5807
  }
5943
5808
 
5944
- /*
5945
- Bezier function generator
5946
- This has been modified from Gaëtan Renaudeau's BezierEasing
5947
- https://github.com/gre/bezier-easing/blob/master/src/index.js
5948
- https://github.com/gre/bezier-easing/blob/master/LICENSE
5949
-
5950
- I've removed the newtonRaphsonIterate algo because in benchmarking it
5951
- wasn't noticiably faster than binarySubdivision, indeed removing it
5952
- usually improved times, depending on the curve.
5953
- I also removed the lookup table, as for the added bundle size and loop we're
5954
- only cutting ~4 or so subdivision iterations. I bumped the max iterations up
5955
- to 12 to compensate and this still tended to be faster for no perceivable
5956
- loss in accuracy.
5957
- Usage
5958
- const easeOut = cubicBezier(.17,.67,.83,.67);
5959
- const x = easeOut(0.5); // returns 0.627...
5960
- */
5961
- // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
5962
- const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) *
5963
- t;
5964
- const subdivisionPrecision = 0.0000001;
5965
- const subdivisionMaxIterations = 12;
5966
- function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) {
5967
- let currentX;
5968
- let currentT;
5969
- let i = 0;
5970
- do {
5971
- currentT = lowerBound + (upperBound - lowerBound) / 2.0;
5972
- currentX = calcBezier(currentT, mX1, mX2) - x;
5973
- if (currentX > 0.0) {
5974
- upperBound = currentT;
5975
- }
5976
- else {
5977
- lowerBound = currentT;
5978
- }
5979
- } while (Math.abs(currentX) > subdivisionPrecision &&
5980
- ++i < subdivisionMaxIterations);
5981
- return currentT;
5982
- }
5983
- function cubicBezier(mX1, mY1, mX2, mY2) {
5984
- // If this is a linear gradient, return linear easing
5985
- if (mX1 === mY1 && mX2 === mY2)
5986
- return noop;
5987
- const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2);
5988
- // If animation is at start/end, return t without easing
5989
- return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
5809
+ const isNotNull$1 = (value) => value !== null;
5810
+ function getFinalKeyframe$1(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
5811
+ const resolvedKeyframes = keyframes.filter(isNotNull$1);
5812
+ const index = repeat && repeatType !== "loop" && repeat % 2 === 1
5813
+ ? 0
5814
+ : resolvedKeyframes.length - 1;
5815
+ return resolvedKeyframes[index]
5816
+ ;
5990
5817
  }
5991
5818
 
5992
- // Accepts an easing function and returns a new one that outputs mirrored values for
5993
- // the second half of the animation. Turns easeIn into easeInOut.
5994
- const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
5995
-
5996
- // Accepts an easing function and returns a new one that outputs reversed values.
5997
- // Turns easeIn into easeOut.
5998
- const reverseEasing = (easing) => (p) => 1 - easing(1 - p);
5999
-
6000
- const backOut = /*@__PURE__*/ cubicBezier(0.33, 1.53, 0.69, 0.99);
6001
- const backIn = /*@__PURE__*/ reverseEasing(backOut);
6002
- const backInOut = /*@__PURE__*/ mirrorEasing(backIn);
6003
-
6004
- const anticipate = (p) => (p *= 2) < 1 ? 0.5 * backIn(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
6005
-
6006
- const circIn = (p) => 1 - Math.sin(Math.acos(p));
6007
- const circOut = reverseEasing(circIn);
6008
- const circInOut = mirrorEasing(circIn);
6009
-
5819
+ const underDampedSpring = {
5820
+ type: "spring",
5821
+ stiffness: 500,
5822
+ damping: 25,
5823
+ restSpeed: 10,
5824
+ };
5825
+ const criticallyDampedSpring = (target) => ({
5826
+ type: "spring",
5827
+ stiffness: 550,
5828
+ damping: target === 0 ? 2 * Math.sqrt(550) : 30,
5829
+ restSpeed: 10,
5830
+ });
5831
+ const keyframesTransition = {
5832
+ type: "keyframes",
5833
+ duration: 0.8,
5834
+ };
6010
5835
  /**
6011
- * Check if the value is a zero value string like "0px" or "0%"
6012
- */
6013
- const isZeroValueString = (v) => /^0[^.\s]+$/u.test(v);
6014
-
6015
- function isNone(value) {
6016
- if (typeof value === "number") {
6017
- return value === 0;
6018
- }
6019
- else if (value !== null) {
6020
- return value === "none" || value === "0" || isZeroValueString(value);
5836
+ * Default easing curve is a slightly shallower version of
5837
+ * the default browser easing curve.
5838
+ */
5839
+ const ease = {
5840
+ type: "keyframes",
5841
+ ease: [0.25, 0.1, 0.35, 1],
5842
+ duration: 0.3,
5843
+ };
5844
+ const getDefaultTransition = (valueKey, { keyframes }) => {
5845
+ if (keyframes.length > 2) {
5846
+ return keyframesTransition;
6021
5847
  }
6022
- else {
6023
- return true;
5848
+ else if (transformProps.has(valueKey)) {
5849
+ return valueKey.startsWith("scale")
5850
+ ? criticallyDampedSpring(keyframes[1])
5851
+ : underDampedSpring;
6024
5852
  }
5853
+ return ease;
5854
+ };
5855
+
5856
+ /**
5857
+ * Decide whether a transition is defined on a given Transition.
5858
+ * This filters out orchestration options and returns true
5859
+ * if any options are left.
5860
+ */
5861
+ function isTransitionDefined({ when, delay: _delay, delayChildren, staggerChildren, staggerDirection, repeat, repeatType, repeatDelay, from, elapsed, ...transition }) {
5862
+ return !!Object.keys(transition).length;
5863
+ }
5864
+
5865
+ function getValueTransition(transition, key) {
5866
+ return (transition?.[key] ??
5867
+ transition?.["default"] ??
5868
+ transition);
6025
5869
  }
6026
5870
 
5871
+ /**
5872
+ * Converts seconds to milliseconds
5873
+ *
5874
+ * @param seconds - Time in seconds.
5875
+ * @return milliseconds - Converted time in milliseconds.
5876
+ */
5877
+ /*#__NO_SIDE_EFFECTS__*/
5878
+ const secondsToMilliseconds = (seconds) => seconds * 1000;
5879
+ /*#__NO_SIDE_EFFECTS__*/
5880
+ const millisecondsToSeconds = (milliseconds) => milliseconds / 1000;
5881
+
6027
5882
  // If this number is a decimal, make it just five decimal places
6028
5883
  // to avoid exponents
6029
5884
  const sanitize = (v) => Math.round(v * 100000) / 100000;
@@ -6240,848 +6095,129 @@ const complex = {
6240
6095
  getAnimatableNone: getAnimatableNone$1,
6241
6096
  };
6242
6097
 
6243
- /**
6244
- * Properties that should default to 1 or 100%
6245
- */
6246
- const maxDefaults = new Set(["brightness", "contrast", "saturate", "opacity"]);
6247
- function applyDefaultFilter(v) {
6248
- const [name, value] = v.slice(0, -1).split("(");
6249
- if (name === "drop-shadow")
6250
- return v;
6251
- const [number] = value.match(floatRegex) || [];
6252
- if (!number)
6253
- return v;
6254
- const unit = value.replace(number, "");
6255
- let defaultValue = maxDefaults.has(name) ? 1 : 0;
6256
- if (number !== value)
6257
- defaultValue *= 100;
6258
- return name + "(" + defaultValue + unit + ")";
6098
+ // Adapted from https://gist.github.com/mjackson/5311256
6099
+ function hueToRgb(p, q, t) {
6100
+ if (t < 0)
6101
+ t += 1;
6102
+ if (t > 1)
6103
+ t -= 1;
6104
+ if (t < 1 / 6)
6105
+ return p + (q - p) * 6 * t;
6106
+ if (t < 1 / 2)
6107
+ return q;
6108
+ if (t < 2 / 3)
6109
+ return p + (q - p) * (2 / 3 - t) * 6;
6110
+ return p;
6111
+ }
6112
+ function hslaToRgba({ hue, saturation, lightness, alpha }) {
6113
+ hue /= 360;
6114
+ saturation /= 100;
6115
+ lightness /= 100;
6116
+ let red = 0;
6117
+ let green = 0;
6118
+ let blue = 0;
6119
+ if (!saturation) {
6120
+ red = green = blue = lightness;
6121
+ }
6122
+ else {
6123
+ const q = lightness < 0.5
6124
+ ? lightness * (1 + saturation)
6125
+ : lightness + saturation - lightness * saturation;
6126
+ const p = 2 * lightness - q;
6127
+ red = hueToRgb(p, q, hue + 1 / 3);
6128
+ green = hueToRgb(p, q, hue);
6129
+ blue = hueToRgb(p, q, hue - 1 / 3);
6130
+ }
6131
+ return {
6132
+ red: Math.round(red * 255),
6133
+ green: Math.round(green * 255),
6134
+ blue: Math.round(blue * 255),
6135
+ alpha,
6136
+ };
6259
6137
  }
6260
- const functionRegex = /\b([a-z-]*)\(.*?\)/gu;
6261
- const filter = {
6262
- ...complex,
6263
- getAnimatableNone: (v) => {
6264
- const functions = v.match(functionRegex);
6265
- return functions ? functions.map(applyDefaultFilter).join(" ") : v;
6266
- },
6267
- };
6268
6138
 
6269
- /**
6270
- * A map of default value types for common values
6271
- */
6272
- const defaultValueTypes = {
6273
- ...numberValueTypes,
6274
- // Color props
6275
- color,
6276
- backgroundColor: color,
6277
- outlineColor: color,
6278
- fill: color,
6279
- stroke: color,
6280
- // Border props
6281
- borderColor: color,
6282
- borderTopColor: color,
6283
- borderRightColor: color,
6284
- borderBottomColor: color,
6285
- borderLeftColor: color,
6286
- filter,
6287
- WebkitFilter: filter,
6139
+ function mixImmediate(a, b) {
6140
+ return (p) => (p > 0 ? b : a);
6141
+ }
6142
+
6143
+ /*
6144
+ Value in range from progress
6145
+
6146
+ Given a lower limit and an upper limit, we return the value within
6147
+ that range as expressed by progress (usually a number from 0 to 1)
6148
+
6149
+ So progress = 0.5 would change
6150
+
6151
+ from -------- to
6152
+
6153
+ to
6154
+
6155
+ from ---- to
6156
+
6157
+ E.g. from = 10, to = 20, progress = 0.5 => 15
6158
+
6159
+ @param [number]: Lower limit of range
6160
+ @param [number]: Upper limit of range
6161
+ @param [number]: The progress between lower and upper limits expressed 0-1
6162
+ @return [number]: Value as calculated from progress within range (not limited within range)
6163
+ */
6164
+ const mixNumber$1 = (from, to, progress) => {
6165
+ return from + (to - from) * progress;
6288
6166
  };
6289
- /**
6290
- * Gets the default ValueType for the provided value key
6291
- */
6292
- const getDefaultValueType = (key) => defaultValueTypes[key];
6293
6167
 
6294
- function getAnimatableNone(key, value) {
6295
- let defaultValueType = getDefaultValueType(key);
6296
- if (defaultValueType !== filter)
6297
- defaultValueType = complex;
6298
- // If value is not recognised as animatable, ie "none", create an animatable version origin based on the target
6299
- return defaultValueType.getAnimatableNone
6300
- ? defaultValueType.getAnimatableNone(value)
6301
- : undefined;
6168
+ // Linear color space blending
6169
+ // Explained https://www.youtube.com/watch?v=LKnqECcg6Gw
6170
+ // Demonstrated http://codepen.io/osublake/pen/xGVVaN
6171
+ const mixLinearColor = (from, to, v) => {
6172
+ const fromExpo = from * from;
6173
+ const expo = v * (to * to - fromExpo) + fromExpo;
6174
+ return expo < 0 ? 0 : Math.sqrt(expo);
6175
+ };
6176
+ const colorTypes = [hex, rgba, hsla];
6177
+ const getColorType = (v) => colorTypes.find((type) => type.test(v));
6178
+ function asRGBA(color) {
6179
+ const type = getColorType(color);
6180
+ warning(Boolean(type), `'${color}' is not an animatable color. Use the equivalent color code instead.`);
6181
+ if (!Boolean(type))
6182
+ return false;
6183
+ let model = type.parse(color);
6184
+ if (type === hsla) {
6185
+ // TODO Remove this cast - needed since Motion's stricter typing
6186
+ model = hslaToRgba(model);
6187
+ }
6188
+ return model;
6302
6189
  }
6190
+ const mixColor = (from, to) => {
6191
+ const fromRGBA = asRGBA(from);
6192
+ const toRGBA = asRGBA(to);
6193
+ if (!fromRGBA || !toRGBA) {
6194
+ return mixImmediate(from, to);
6195
+ }
6196
+ const blended = { ...fromRGBA };
6197
+ return (v) => {
6198
+ blended.red = mixLinearColor(fromRGBA.red, toRGBA.red, v);
6199
+ blended.green = mixLinearColor(fromRGBA.green, toRGBA.green, v);
6200
+ blended.blue = mixLinearColor(fromRGBA.blue, toRGBA.blue, v);
6201
+ blended.alpha = mixNumber$1(fromRGBA.alpha, toRGBA.alpha, v);
6202
+ return rgba.transform(blended);
6203
+ };
6204
+ };
6303
6205
 
6206
+ const invisibleValues = new Set(["none", "hidden"]);
6304
6207
  /**
6305
- * If we encounter keyframes like "none" or "0" and we also have keyframes like
6306
- * "#fff" or "200px 200px" we want to find a keyframe to serve as a template for
6307
- * the "none" keyframes. In this case "#fff" or "200px 200px" - then these get turned into
6308
- * zero equivalents, i.e. "#fff0" or "0px 0px".
6208
+ * Returns a function that, when provided a progress value between 0 and 1,
6209
+ * will return the "none" or "hidden" string only when the progress is that of
6210
+ * the origin or target.
6309
6211
  */
6310
- const invalidTemplates = new Set(["auto", "none", "0"]);
6311
- function makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name) {
6312
- let i = 0;
6313
- let animatableTemplate = undefined;
6314
- while (i < unresolvedKeyframes.length && !animatableTemplate) {
6315
- const keyframe = unresolvedKeyframes[i];
6316
- if (typeof keyframe === "string" &&
6317
- !invalidTemplates.has(keyframe) &&
6318
- analyseComplexValue(keyframe).values.length) {
6319
- animatableTemplate = unresolvedKeyframes[i];
6320
- }
6321
- i++;
6212
+ function mixVisibility(origin, target) {
6213
+ if (invisibleValues.has(origin)) {
6214
+ return (p) => (p <= 0 ? origin : target);
6322
6215
  }
6323
- if (animatableTemplate && name) {
6324
- for (const noneIndex of noneKeyframeIndexes) {
6325
- unresolvedKeyframes[noneIndex] = getAnimatableNone(name, animatableTemplate);
6326
- }
6216
+ else {
6217
+ return (p) => (p >= 1 ? target : origin);
6327
6218
  }
6328
6219
  }
6329
6220
 
6330
- const radToDeg = (rad) => (rad * 180) / Math.PI;
6331
- const rotate = (v) => {
6332
- const angle = radToDeg(Math.atan2(v[1], v[0]));
6333
- return rebaseAngle(angle);
6334
- };
6335
- const matrix2dParsers = {
6336
- x: 4,
6337
- y: 5,
6338
- translateX: 4,
6339
- translateY: 5,
6340
- scaleX: 0,
6341
- scaleY: 3,
6342
- scale: (v) => (Math.abs(v[0]) + Math.abs(v[3])) / 2,
6343
- rotate,
6344
- rotateZ: rotate,
6345
- skewX: (v) => radToDeg(Math.atan(v[1])),
6346
- skewY: (v) => radToDeg(Math.atan(v[2])),
6347
- skew: (v) => (Math.abs(v[1]) + Math.abs(v[2])) / 2,
6348
- };
6349
- const rebaseAngle = (angle) => {
6350
- angle = angle % 360;
6351
- if (angle < 0)
6352
- angle += 360;
6353
- return angle;
6354
- };
6355
- const rotateZ = rotate;
6356
- const scaleX = (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1]);
6357
- const scaleY = (v) => Math.sqrt(v[4] * v[4] + v[5] * v[5]);
6358
- const matrix3dParsers = {
6359
- x: 12,
6360
- y: 13,
6361
- z: 14,
6362
- translateX: 12,
6363
- translateY: 13,
6364
- translateZ: 14,
6365
- scaleX,
6366
- scaleY,
6367
- scale: (v) => (scaleX(v) + scaleY(v)) / 2,
6368
- rotateX: (v) => rebaseAngle(radToDeg(Math.atan2(v[6], v[5]))),
6369
- rotateY: (v) => rebaseAngle(radToDeg(Math.atan2(-v[2], v[0]))),
6370
- rotateZ,
6371
- rotate: rotateZ,
6372
- skewX: (v) => radToDeg(Math.atan(v[4])),
6373
- skewY: (v) => radToDeg(Math.atan(v[1])),
6374
- skew: (v) => (Math.abs(v[1]) + Math.abs(v[4])) / 2,
6375
- };
6376
- function defaultTransformValue(name) {
6377
- return name.includes("scale") ? 1 : 0;
6378
- }
6379
- function parseValueFromTransform(transform, name) {
6380
- if (!transform || transform === "none") {
6381
- return defaultTransformValue(name);
6382
- }
6383
- const matrix3dMatch = transform.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);
6384
- let parsers;
6385
- let match;
6386
- if (matrix3dMatch) {
6387
- parsers = matrix3dParsers;
6388
- match = matrix3dMatch;
6389
- }
6390
- else {
6391
- const matrix2dMatch = transform.match(/^matrix\(([-\d.e\s,]+)\)$/u);
6392
- parsers = matrix2dParsers;
6393
- match = matrix2dMatch;
6394
- }
6395
- if (!match) {
6396
- return defaultTransformValue(name);
6397
- }
6398
- const valueParser = parsers[name];
6399
- const values = match[1].split(",").map(convertTransformToNumber);
6400
- return typeof valueParser === "function"
6401
- ? valueParser(values)
6402
- : values[valueParser];
6403
- }
6404
- const readTransformValue = (instance, name) => {
6405
- const { transform = "none" } = getComputedStyle(instance);
6406
- return parseValueFromTransform(transform, name);
6407
- };
6408
- function convertTransformToNumber(value) {
6409
- return parseFloat(value.trim());
6410
- }
6411
-
6412
- const isNumOrPxType = (v) => v === number || v === px;
6413
- const transformKeys = new Set(["x", "y", "z"]);
6414
- const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key));
6415
- function removeNonTranslationalTransform(visualElement) {
6416
- const removedTransforms = [];
6417
- nonTranslationalTransformKeys.forEach((key) => {
6418
- const value = visualElement.getValue(key);
6419
- if (value !== undefined) {
6420
- removedTransforms.push([key, value.get()]);
6421
- value.set(key.startsWith("scale") ? 1 : 0);
6422
- }
6423
- });
6424
- return removedTransforms;
6425
- }
6426
- const positionalValues = {
6427
- // Dimensions
6428
- width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight),
6429
- height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom),
6430
- top: (_bbox, { top }) => parseFloat(top),
6431
- left: (_bbox, { left }) => parseFloat(left),
6432
- bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min),
6433
- right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min),
6434
- // Transform
6435
- x: (_bbox, { transform }) => parseValueFromTransform(transform, "x"),
6436
- y: (_bbox, { transform }) => parseValueFromTransform(transform, "y"),
6437
- };
6438
- // Alias translate longform names
6439
- positionalValues.translateX = positionalValues.x;
6440
- positionalValues.translateY = positionalValues.y;
6441
-
6442
- const toResolve = new Set();
6443
- let isScheduled = false;
6444
- let anyNeedsMeasurement = false;
6445
- function measureAllKeyframes() {
6446
- if (anyNeedsMeasurement) {
6447
- const resolversToMeasure = Array.from(toResolve).filter((resolver) => resolver.needsMeasurement);
6448
- const elementsToMeasure = new Set(resolversToMeasure.map((resolver) => resolver.element));
6449
- const transformsToRestore = new Map();
6450
- /**
6451
- * Write pass
6452
- * If we're measuring elements we want to remove bounding box-changing transforms.
6453
- */
6454
- elementsToMeasure.forEach((element) => {
6455
- const removedTransforms = removeNonTranslationalTransform(element);
6456
- if (!removedTransforms.length)
6457
- return;
6458
- transformsToRestore.set(element, removedTransforms);
6459
- element.render();
6460
- });
6461
- // Read
6462
- resolversToMeasure.forEach((resolver) => resolver.measureInitialState());
6463
- // Write
6464
- elementsToMeasure.forEach((element) => {
6465
- element.render();
6466
- const restore = transformsToRestore.get(element);
6467
- if (restore) {
6468
- restore.forEach(([key, value]) => {
6469
- element.getValue(key)?.set(value);
6470
- });
6471
- }
6472
- });
6473
- // Read
6474
- resolversToMeasure.forEach((resolver) => resolver.measureEndState());
6475
- // Write
6476
- resolversToMeasure.forEach((resolver) => {
6477
- if (resolver.suspendedScrollY !== undefined) {
6478
- window.scrollTo(0, resolver.suspendedScrollY);
6479
- }
6480
- });
6481
- }
6482
- anyNeedsMeasurement = false;
6483
- isScheduled = false;
6484
- toResolve.forEach((resolver) => resolver.complete());
6485
- toResolve.clear();
6486
- }
6487
- function readAllKeyframes() {
6488
- toResolve.forEach((resolver) => {
6489
- resolver.readKeyframes();
6490
- if (resolver.needsMeasurement) {
6491
- anyNeedsMeasurement = true;
6492
- }
6493
- });
6494
- }
6495
- function flushKeyframeResolvers() {
6496
- readAllKeyframes();
6497
- measureAllKeyframes();
6498
- }
6499
- class KeyframeResolver {
6500
- constructor(unresolvedKeyframes, onComplete, name, motionValue, element, isAsync = false) {
6501
- /**
6502
- * Track whether this resolver has completed. Once complete, it never
6503
- * needs to attempt keyframe resolution again.
6504
- */
6505
- this.isComplete = false;
6506
- /**
6507
- * Track whether this resolver is async. If it is, it'll be added to the
6508
- * resolver queue and flushed in the next frame. Resolvers that aren't going
6509
- * to trigger read/write thrashing don't need to be async.
6510
- */
6511
- this.isAsync = false;
6512
- /**
6513
- * Track whether this resolver needs to perform a measurement
6514
- * to resolve its keyframes.
6515
- */
6516
- this.needsMeasurement = false;
6517
- /**
6518
- * Track whether this resolver is currently scheduled to resolve
6519
- * to allow it to be cancelled and resumed externally.
6520
- */
6521
- this.isScheduled = false;
6522
- this.unresolvedKeyframes = [...unresolvedKeyframes];
6523
- this.onComplete = onComplete;
6524
- this.name = name;
6525
- this.motionValue = motionValue;
6526
- this.element = element;
6527
- this.isAsync = isAsync;
6528
- }
6529
- scheduleResolve() {
6530
- this.isScheduled = true;
6531
- if (this.isAsync) {
6532
- toResolve.add(this);
6533
- if (!isScheduled) {
6534
- isScheduled = true;
6535
- frame.read(readAllKeyframes);
6536
- frame.resolveKeyframes(measureAllKeyframes);
6537
- }
6538
- }
6539
- else {
6540
- this.readKeyframes();
6541
- this.complete();
6542
- }
6543
- }
6544
- readKeyframes() {
6545
- const { unresolvedKeyframes, name, element, motionValue } = this;
6546
- /**
6547
- * If a keyframe is null, we hydrate it either by reading it from
6548
- * the instance, or propagating from previous keyframes.
6549
- */
6550
- for (let i = 0; i < unresolvedKeyframes.length; i++) {
6551
- if (unresolvedKeyframes[i] === null) {
6552
- /**
6553
- * If the first keyframe is null, we need to find its value by sampling the element
6554
- */
6555
- if (i === 0) {
6556
- const currentValue = motionValue?.get();
6557
- const finalKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
6558
- if (currentValue !== undefined) {
6559
- unresolvedKeyframes[0] = currentValue;
6560
- }
6561
- else if (element && name) {
6562
- const valueAsRead = element.readValue(name, finalKeyframe);
6563
- if (valueAsRead !== undefined && valueAsRead !== null) {
6564
- unresolvedKeyframes[0] = valueAsRead;
6565
- }
6566
- }
6567
- if (unresolvedKeyframes[0] === undefined) {
6568
- unresolvedKeyframes[0] = finalKeyframe;
6569
- }
6570
- if (motionValue && currentValue === undefined) {
6571
- motionValue.set(unresolvedKeyframes[0]);
6572
- }
6573
- }
6574
- else {
6575
- unresolvedKeyframes[i] = unresolvedKeyframes[i - 1];
6576
- }
6577
- }
6578
- }
6579
- }
6580
- setFinalKeyframe() { }
6581
- measureInitialState() { }
6582
- renderEndStyles() { }
6583
- measureEndState() { }
6584
- complete() {
6585
- this.isComplete = true;
6586
- this.onComplete(this.unresolvedKeyframes, this.finalKeyframe);
6587
- toResolve.delete(this);
6588
- }
6589
- cancel() {
6590
- if (!this.isComplete) {
6591
- this.isScheduled = false;
6592
- toResolve.delete(this);
6593
- }
6594
- }
6595
- resume() {
6596
- if (!this.isComplete)
6597
- this.scheduleResolve();
6598
- }
6599
- }
6600
-
6601
- /**
6602
- * Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1"
6603
- */
6604
- const isNumericalString = (v) => /^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(v);
6605
-
6606
- /**
6607
- * Parse Framer's special CSS variable format into a CSS token and a fallback.
6608
- *
6609
- * ```
6610
- * `var(--foo, #fff)` => [`--foo`, '#fff']
6611
- * ```
6612
- *
6613
- * @param current
6614
- */
6615
- const splitCSSVariableRegex =
6616
- // eslint-disable-next-line redos-detector/no-unsafe-regex -- false positive, as it can match a lot of words
6617
- /^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;
6618
- function parseCSSVariable(current) {
6619
- const match = splitCSSVariableRegex.exec(current);
6620
- if (!match)
6621
- return [,];
6622
- const [, token1, token2, fallback] = match;
6623
- return [`--${token1 ?? token2}`, fallback];
6624
- }
6625
- const maxDepth = 4;
6626
- function getVariableValue(current, element, depth = 1) {
6627
- invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`);
6628
- const [token, fallback] = parseCSSVariable(current);
6629
- // No CSS variable detected
6630
- if (!token)
6631
- return;
6632
- // Attempt to read this CSS variable off the element
6633
- const resolved = window.getComputedStyle(element).getPropertyValue(token);
6634
- if (resolved) {
6635
- const trimmed = resolved.trim();
6636
- return isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed;
6637
- }
6638
- return isCSSVariableToken(fallback)
6639
- ? getVariableValue(fallback, element, depth + 1)
6640
- : fallback;
6641
- }
6642
-
6643
- /**
6644
- * Tests a provided value against a ValueType
6645
- */
6646
- const testValueType = (v) => (type) => type.test(v);
6647
-
6648
- /**
6649
- * ValueType for "auto"
6650
- */
6651
- const auto = {
6652
- test: (v) => v === "auto",
6653
- parse: (v) => v,
6654
- };
6655
-
6656
- /**
6657
- * A list of value types commonly used for dimensions
6658
- */
6659
- const dimensionValueTypes = [number, px, percent, degrees, vw, vh, auto];
6660
- /**
6661
- * Tests a dimensional value against the list of dimension ValueTypes
6662
- */
6663
- const findDimensionValueType = (v) => dimensionValueTypes.find(testValueType(v));
6664
-
6665
- class DOMKeyframesResolver extends KeyframeResolver {
6666
- constructor(unresolvedKeyframes, onComplete, name, motionValue, element) {
6667
- super(unresolvedKeyframes, onComplete, name, motionValue, element, true);
6668
- }
6669
- readKeyframes() {
6670
- const { unresolvedKeyframes, element, name } = this;
6671
- if (!element || !element.current)
6672
- return;
6673
- super.readKeyframes();
6674
- /**
6675
- * If any keyframe is a CSS variable, we need to find its value by sampling the element
6676
- */
6677
- for (let i = 0; i < unresolvedKeyframes.length; i++) {
6678
- let keyframe = unresolvedKeyframes[i];
6679
- if (typeof keyframe === "string") {
6680
- keyframe = keyframe.trim();
6681
- if (isCSSVariableToken(keyframe)) {
6682
- const resolved = getVariableValue(keyframe, element.current);
6683
- if (resolved !== undefined) {
6684
- unresolvedKeyframes[i] = resolved;
6685
- }
6686
- if (i === unresolvedKeyframes.length - 1) {
6687
- this.finalKeyframe = keyframe;
6688
- }
6689
- }
6690
- }
6691
- }
6692
- /**
6693
- * Resolve "none" values. We do this potentially twice - once before and once after measuring keyframes.
6694
- * This could be seen as inefficient but it's a trade-off to avoid measurements in more situations, which
6695
- * have a far bigger performance impact.
6696
- */
6697
- this.resolveNoneKeyframes();
6698
- /**
6699
- * Check to see if unit type has changed. If so schedule jobs that will
6700
- * temporarily set styles to the destination keyframes.
6701
- * Skip if we have more than two keyframes or this isn't a positional value.
6702
- * TODO: We can throw if there are multiple keyframes and the value type changes.
6703
- */
6704
- if (!positionalKeys.has(name) || unresolvedKeyframes.length !== 2) {
6705
- return;
6706
- }
6707
- const [origin, target] = unresolvedKeyframes;
6708
- const originType = findDimensionValueType(origin);
6709
- const targetType = findDimensionValueType(target);
6710
- /**
6711
- * Either we don't recognise these value types or we can animate between them.
6712
- */
6713
- if (originType === targetType)
6714
- return;
6715
- /**
6716
- * If both values are numbers or pixels, we can animate between them by
6717
- * converting them to numbers.
6718
- */
6719
- if (isNumOrPxType(originType) && isNumOrPxType(targetType)) {
6720
- for (let i = 0; i < unresolvedKeyframes.length; i++) {
6721
- const value = unresolvedKeyframes[i];
6722
- if (typeof value === "string") {
6723
- unresolvedKeyframes[i] = parseFloat(value);
6724
- }
6725
- }
6726
- }
6727
- else {
6728
- /**
6729
- * Else, the only way to resolve this is by measuring the element.
6730
- */
6731
- this.needsMeasurement = true;
6732
- }
6733
- }
6734
- resolveNoneKeyframes() {
6735
- const { unresolvedKeyframes, name } = this;
6736
- const noneKeyframeIndexes = [];
6737
- for (let i = 0; i < unresolvedKeyframes.length; i++) {
6738
- if (isNone(unresolvedKeyframes[i])) {
6739
- noneKeyframeIndexes.push(i);
6740
- }
6741
- }
6742
- if (noneKeyframeIndexes.length) {
6743
- makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name);
6744
- }
6745
- }
6746
- measureInitialState() {
6747
- const { element, unresolvedKeyframes, name } = this;
6748
- if (!element || !element.current)
6749
- return;
6750
- if (name === "height") {
6751
- this.suspendedScrollY = window.pageYOffset;
6752
- }
6753
- this.measuredOrigin = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
6754
- unresolvedKeyframes[0] = this.measuredOrigin;
6755
- // Set final key frame to measure after next render
6756
- const measureKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
6757
- if (measureKeyframe !== undefined) {
6758
- element.getValue(name, measureKeyframe).jump(measureKeyframe, false);
6759
- }
6760
- }
6761
- measureEndState() {
6762
- const { element, name, unresolvedKeyframes } = this;
6763
- if (!element || !element.current)
6764
- return;
6765
- const value = element.getValue(name);
6766
- value && value.jump(this.measuredOrigin, false);
6767
- const finalKeyframeIndex = unresolvedKeyframes.length - 1;
6768
- const finalKeyframe = unresolvedKeyframes[finalKeyframeIndex];
6769
- unresolvedKeyframes[finalKeyframeIndex] = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
6770
- if (finalKeyframe !== null && this.finalKeyframe === undefined) {
6771
- this.finalKeyframe = finalKeyframe;
6772
- }
6773
- // If we removed transform values, reapply them before the next render
6774
- if (this.removedTransforms?.length) {
6775
- this.removedTransforms.forEach(([unsetTransformName, unsetTransformValue]) => {
6776
- element
6777
- .getValue(unsetTransformName)
6778
- .set(unsetTransformValue);
6779
- });
6780
- }
6781
- this.resolveNoneKeyframes();
6782
- }
6783
- }
6784
-
6785
- /**
6786
- * Check if a value is animatable. Examples:
6787
- *
6788
- * ✅: 100, "100px", "#fff"
6789
- * ❌: "block", "url(2.jpg)"
6790
- * @param value
6791
- *
6792
- * @internal
6793
- */
6794
- const isAnimatable = (value, name) => {
6795
- // If the list of keys tat might be non-animatable grows, replace with Set
6796
- if (name === "zIndex")
6797
- return false;
6798
- // If it's a number or a keyframes array, we can animate it. We might at some point
6799
- // need to do a deep isAnimatable check of keyframes, or let Popmotion handle this,
6800
- // but for now lets leave it like this for performance reasons
6801
- if (typeof value === "number" || Array.isArray(value))
6802
- return true;
6803
- if (typeof value === "string" && // It's animatable if we have a string
6804
- (complex.test(value) || value === "0") && // And it contains numbers and/or colors
6805
- !value.startsWith("url(") // Unless it starts with "url("
6806
- ) {
6807
- return true;
6808
- }
6809
- return false;
6810
- };
6811
-
6812
- function isGenerator(type) {
6813
- return typeof type === "function" && "applyToOptions" in type;
6814
- }
6815
-
6816
- function hasKeyframesChanged(keyframes) {
6817
- const current = keyframes[0];
6818
- if (keyframes.length === 1)
6819
- return true;
6820
- for (let i = 0; i < keyframes.length; i++) {
6821
- if (keyframes[i] !== current)
6822
- return true;
6823
- }
6824
- }
6825
- function canAnimate(keyframes, name, type, velocity) {
6826
- /**
6827
- * Check if we're able to animate between the start and end keyframes,
6828
- * and throw a warning if we're attempting to animate between one that's
6829
- * animatable and another that isn't.
6830
- */
6831
- const originKeyframe = keyframes[0];
6832
- if (originKeyframe === null)
6833
- return false;
6834
- /**
6835
- * These aren't traditionally animatable but we do support them.
6836
- * In future we could look into making this more generic or replacing
6837
- * this function with mix() === mixImmediate
6838
- */
6839
- if (name === "display" || name === "visibility")
6840
- return true;
6841
- const targetKeyframe = keyframes[keyframes.length - 1];
6842
- const isOriginAnimatable = isAnimatable(originKeyframe, name);
6843
- const isTargetAnimatable = isAnimatable(targetKeyframe, name);
6844
- warning(isOriginAnimatable === isTargetAnimatable, `You are trying to animate ${name} from "${originKeyframe}" to "${targetKeyframe}". ${originKeyframe} is not an animatable value - to enable this animation set ${originKeyframe} to a value animatable to ${targetKeyframe} via the \`style\` property.`);
6845
- // Always skip if any of these are true
6846
- if (!isOriginAnimatable || !isTargetAnimatable) {
6847
- return false;
6848
- }
6849
- return (hasKeyframesChanged(keyframes) ||
6850
- ((type === "spring" || isGenerator(type)) && velocity));
6851
- }
6852
-
6853
- const isNotNull = (value) => value !== null;
6854
- function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
6855
- const resolvedKeyframes = keyframes.filter(isNotNull);
6856
- const index = repeat && repeatType !== "loop" && repeat % 2 === 1
6857
- ? 0
6858
- : resolvedKeyframes.length - 1;
6859
- return !index || finalKeyframe === undefined
6860
- ? resolvedKeyframes[index]
6861
- : finalKeyframe;
6862
- }
6863
-
6864
- /**
6865
- * Maximum time allowed between an animation being created and it being
6866
- * resolved for us to use the latter as the start time.
6867
- *
6868
- * This is to ensure that while we prefer to "start" an animation as soon
6869
- * as it's triggered, we also want to avoid a visual jump if there's a big delay
6870
- * between these two moments.
6871
- */
6872
- const MAX_RESOLVE_DELAY = 40;
6873
- class BaseAnimation {
6874
- constructor({ autoplay = true, delay = 0, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", ...options }) {
6875
- // Track whether the animation has been stopped. Stopped animations won't restart.
6876
- this.isStopped = false;
6877
- this.hasAttemptedResolve = false;
6878
- this.createdAt = time.now();
6879
- this.options = {
6880
- autoplay,
6881
- delay,
6882
- type,
6883
- repeat,
6884
- repeatDelay,
6885
- repeatType,
6886
- ...options,
6887
- };
6888
- this.updateFinishedPromise();
6889
- }
6890
- /**
6891
- * This method uses the createdAt and resolvedAt to calculate the
6892
- * animation startTime. *Ideally*, we would use the createdAt time as t=0
6893
- * as the following frame would then be the first frame of the animation in
6894
- * progress, which would feel snappier.
6895
- *
6896
- * However, if there's a delay (main thread work) between the creation of
6897
- * the animation and the first commited frame, we prefer to use resolvedAt
6898
- * to avoid a sudden jump into the animation.
6899
- */
6900
- calcStartTime() {
6901
- if (!this.resolvedAt)
6902
- return this.createdAt;
6903
- return this.resolvedAt - this.createdAt > MAX_RESOLVE_DELAY
6904
- ? this.resolvedAt
6905
- : this.createdAt;
6906
- }
6907
- /**
6908
- * A getter for resolved data. If keyframes are not yet resolved, accessing
6909
- * this.resolved will synchronously flush all pending keyframe resolvers.
6910
- * This is a deoptimisation, but at its worst still batches read/writes.
6911
- */
6912
- get resolved() {
6913
- if (!this._resolved && !this.hasAttemptedResolve) {
6914
- flushKeyframeResolvers();
6915
- }
6916
- return this._resolved;
6917
- }
6918
- /**
6919
- * A method to be called when the keyframes resolver completes. This method
6920
- * will check if its possible to run the animation and, if not, skip it.
6921
- * Otherwise, it will call initPlayback on the implementing class.
6922
- */
6923
- onKeyframesResolved(keyframes, finalKeyframe) {
6924
- this.resolvedAt = time.now();
6925
- this.hasAttemptedResolve = true;
6926
- const { name, type, velocity, delay, onComplete, onUpdate, isGenerator, } = this.options;
6927
- /**
6928
- * If we can't animate this value with the resolved keyframes
6929
- * then we should complete it immediately.
6930
- */
6931
- if (!isGenerator && !canAnimate(keyframes, name, type, velocity)) {
6932
- // Finish immediately
6933
- if (!delay) {
6934
- onUpdate &&
6935
- onUpdate(getFinalKeyframe(keyframes, this.options, finalKeyframe));
6936
- onComplete && onComplete();
6937
- this.resolveFinishedPromise();
6938
- return;
6939
- }
6940
- // Finish after a delay
6941
- else {
6942
- this.options.duration = 0;
6943
- }
6944
- }
6945
- const resolvedAnimation = this.initPlayback(keyframes, finalKeyframe);
6946
- if (resolvedAnimation === false)
6947
- return;
6948
- this._resolved = {
6949
- keyframes,
6950
- finalKeyframe,
6951
- ...resolvedAnimation,
6952
- };
6953
- this.onPostResolved();
6954
- }
6955
- onPostResolved() { }
6956
- /**
6957
- * Allows the returned animation to be awaited or promise-chained. Currently
6958
- * resolves when the animation finishes at all but in a future update could/should
6959
- * reject if its cancels.
6960
- */
6961
- then(resolve, reject) {
6962
- return this.currentFinishedPromise.then(resolve, reject);
6963
- }
6964
- flatten() {
6965
- if (!this.options.allowFlatten)
6966
- return;
6967
- this.options.type = "keyframes";
6968
- this.options.ease = "linear";
6969
- }
6970
- updateFinishedPromise() {
6971
- this.currentFinishedPromise = new Promise((resolve) => {
6972
- this.resolveFinishedPromise = resolve;
6973
- });
6974
- }
6975
- }
6976
-
6977
- /*
6978
- Value in range from progress
6979
-
6980
- Given a lower limit and an upper limit, we return the value within
6981
- that range as expressed by progress (usually a number from 0 to 1)
6982
-
6983
- So progress = 0.5 would change
6984
-
6985
- from -------- to
6986
-
6987
- to
6988
-
6989
- from ---- to
6990
-
6991
- E.g. from = 10, to = 20, progress = 0.5 => 15
6992
-
6993
- @param [number]: Lower limit of range
6994
- @param [number]: Upper limit of range
6995
- @param [number]: The progress between lower and upper limits expressed 0-1
6996
- @return [number]: Value as calculated from progress within range (not limited within range)
6997
- */
6998
- const mixNumber$1 = (from, to, progress) => {
6999
- return from + (to - from) * progress;
7000
- };
7001
-
7002
- // Adapted from https://gist.github.com/mjackson/5311256
7003
- function hueToRgb(p, q, t) {
7004
- if (t < 0)
7005
- t += 1;
7006
- if (t > 1)
7007
- t -= 1;
7008
- if (t < 1 / 6)
7009
- return p + (q - p) * 6 * t;
7010
- if (t < 1 / 2)
7011
- return q;
7012
- if (t < 2 / 3)
7013
- return p + (q - p) * (2 / 3 - t) * 6;
7014
- return p;
7015
- }
7016
- function hslaToRgba({ hue, saturation, lightness, alpha }) {
7017
- hue /= 360;
7018
- saturation /= 100;
7019
- lightness /= 100;
7020
- let red = 0;
7021
- let green = 0;
7022
- let blue = 0;
7023
- if (!saturation) {
7024
- red = green = blue = lightness;
7025
- }
7026
- else {
7027
- const q = lightness < 0.5
7028
- ? lightness * (1 + saturation)
7029
- : lightness + saturation - lightness * saturation;
7030
- const p = 2 * lightness - q;
7031
- red = hueToRgb(p, q, hue + 1 / 3);
7032
- green = hueToRgb(p, q, hue);
7033
- blue = hueToRgb(p, q, hue - 1 / 3);
7034
- }
7035
- return {
7036
- red: Math.round(red * 255),
7037
- green: Math.round(green * 255),
7038
- blue: Math.round(blue * 255),
7039
- alpha,
7040
- };
7041
- }
7042
-
7043
- function mixImmediate(a, b) {
7044
- return (p) => (p > 0 ? b : a);
7045
- }
7046
-
7047
- // Linear color space blending
7048
- // Explained https://www.youtube.com/watch?v=LKnqECcg6Gw
7049
- // Demonstrated http://codepen.io/osublake/pen/xGVVaN
7050
- const mixLinearColor = (from, to, v) => {
7051
- const fromExpo = from * from;
7052
- const expo = v * (to * to - fromExpo) + fromExpo;
7053
- return expo < 0 ? 0 : Math.sqrt(expo);
7054
- };
7055
- const colorTypes = [hex, rgba, hsla];
7056
- const getColorType = (v) => colorTypes.find((type) => type.test(v));
7057
- function asRGBA(color) {
7058
- const type = getColorType(color);
7059
- warning(Boolean(type), `'${color}' is not an animatable color. Use the equivalent color code instead.`);
7060
- if (!Boolean(type))
7061
- return false;
7062
- let model = type.parse(color);
7063
- if (type === hsla) {
7064
- // TODO Remove this cast - needed since Motion's stricter typing
7065
- model = hslaToRgba(model);
7066
- }
7067
- return model;
7068
- }
7069
- const mixColor = (from, to) => {
7070
- const fromRGBA = asRGBA(from);
7071
- const toRGBA = asRGBA(to);
7072
- if (!fromRGBA || !toRGBA) {
7073
- return mixImmediate(from, to);
7074
- }
7075
- const blended = { ...fromRGBA };
7076
- return (v) => {
7077
- blended.red = mixLinearColor(fromRGBA.red, toRGBA.red, v);
7078
- blended.green = mixLinearColor(fromRGBA.green, toRGBA.green, v);
7079
- blended.blue = mixLinearColor(fromRGBA.blue, toRGBA.blue, v);
7080
- blended.alpha = mixNumber$1(fromRGBA.alpha, toRGBA.alpha, v);
7081
- return rgba.transform(blended);
7082
- };
7083
- };
7084
-
7085
6221
  /**
7086
6222
  * Pipe
7087
6223
  * Compose other transformers to run linearily
@@ -7092,21 +6228,6 @@ const mixColor = (from, to) => {
7092
6228
  const combineFunctions = (a, b) => (v) => b(a(v));
7093
6229
  const pipe = (...transformers) => transformers.reduce(combineFunctions);
7094
6230
 
7095
- const invisibleValues = new Set(["none", "hidden"]);
7096
- /**
7097
- * Returns a function that, when provided a progress value between 0 and 1,
7098
- * will return the "none" or "hidden" string only when the progress is that of
7099
- * the origin or target.
7100
- */
7101
- function mixVisibility(origin, target) {
7102
- if (invisibleValues.has(origin)) {
7103
- return (p) => (p <= 0 ? origin : target);
7104
- }
7105
- else {
7106
- return (p) => (p >= 1 ? target : origin);
7107
- }
7108
- }
7109
-
7110
6231
  function mixNumber(a, b) {
7111
6232
  return (p) => mixNumber$1(a, b, p);
7112
6233
  }
@@ -7187,16 +6308,71 @@ const mixComplex = (origin, target) => {
7187
6308
  warning(true, `Complex values '${origin}' and '${target}' too different to mix. Ensure all colors are of the same type, and that each contains the same quantity of number and color values. Falling back to instant transition.`);
7188
6309
  return mixImmediate(origin, target);
7189
6310
  }
7190
- };
6311
+ };
6312
+
6313
+ function mix(from, to, p) {
6314
+ if (typeof from === "number" &&
6315
+ typeof to === "number" &&
6316
+ typeof p === "number") {
6317
+ return mixNumber$1(from, to, p);
6318
+ }
6319
+ const mixer = getMixer(from);
6320
+ return mixer(from, to);
6321
+ }
6322
+
6323
+ const frameloopDriver = (update) => {
6324
+ const passTimestamp = ({ timestamp }) => update(timestamp);
6325
+ return {
6326
+ start: () => frame.update(passTimestamp, true),
6327
+ stop: () => cancelFrame(passTimestamp),
6328
+ /**
6329
+ * If we're processing this frame we can use the
6330
+ * framelocked timestamp to keep things in sync.
6331
+ */
6332
+ now: () => (frameData.isProcessing ? frameData.timestamp : time.now()),
6333
+ };
6334
+ };
6335
+
6336
+ const generateLinearEasing = (easing, duration, // as milliseconds
6337
+ resolution = 10 // as milliseconds
6338
+ ) => {
6339
+ let points = "";
6340
+ const numPoints = Math.max(Math.round(duration / resolution), 2);
6341
+ for (let i = 0; i < numPoints; i++) {
6342
+ points += easing(i / (numPoints - 1)) + ", ";
6343
+ }
6344
+ return `linear(${points.substring(0, points.length - 2)})`;
6345
+ };
6346
+
6347
+ /**
6348
+ * Implement a practical max duration for keyframe generation
6349
+ * to prevent infinite loops
6350
+ */
6351
+ const maxGeneratorDuration = 20000;
6352
+ function calcGeneratorDuration(generator) {
6353
+ let duration = 0;
6354
+ const timeStep = 50;
6355
+ let state = generator.next(duration);
6356
+ while (!state.done && duration < maxGeneratorDuration) {
6357
+ duration += timeStep;
6358
+ state = generator.next(duration);
6359
+ }
6360
+ return duration >= maxGeneratorDuration ? Infinity : duration;
6361
+ }
7191
6362
 
7192
- function mix(from, to, p) {
7193
- if (typeof from === "number" &&
7194
- typeof to === "number" &&
7195
- typeof p === "number") {
7196
- return mixNumber$1(from, to, p);
7197
- }
7198
- const mixer = getMixer(from);
7199
- return mixer(from, to);
6363
+ /**
6364
+ * Create a progress => progress easing function from a generator.
6365
+ */
6366
+ function createGeneratorEasing(options, scale = 100, createGenerator) {
6367
+ const generator = createGenerator({ ...options, keyframes: [0, scale] });
6368
+ const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
6369
+ return {
6370
+ type: "keyframes",
6371
+ ease: (progress) => {
6372
+ return generator.next(duration * progress).value / scale;
6373
+ },
6374
+ duration: millisecondsToSeconds(duration),
6375
+ };
7200
6376
  }
7201
6377
 
7202
6378
  const velocitySampleDuration = 5; // ms
@@ -7231,17 +6407,6 @@ const springDefaults = {
7231
6407
  maxDamping: 1,
7232
6408
  };
7233
6409
 
7234
- /**
7235
- * Converts seconds to milliseconds
7236
- *
7237
- * @param seconds - Time in seconds.
7238
- * @return milliseconds - Converted time in milliseconds.
7239
- */
7240
- /*#__NO_SIDE_EFFECTS__*/
7241
- const secondsToMilliseconds = (seconds) => seconds * 1000;
7242
- /*#__NO_SIDE_EFFECTS__*/
7243
- const millisecondsToSeconds = (milliseconds) => milliseconds / 1000;
7244
-
7245
6410
  const safeMin = 0.001;
7246
6411
  function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, velocity = springDefaults.velocity, mass = springDefaults.mass, }) {
7247
6412
  let envelope;
@@ -7322,81 +6487,6 @@ function calcAngularFreq(undampedFreq, dampingRatio) {
7322
6487
  return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
7323
6488
  }
7324
6489
 
7325
- /**
7326
- * Implement a practical max duration for keyframe generation
7327
- * to prevent infinite loops
7328
- */
7329
- const maxGeneratorDuration = 20000;
7330
- function calcGeneratorDuration(generator) {
7331
- let duration = 0;
7332
- const timeStep = 50;
7333
- let state = generator.next(duration);
7334
- while (!state.done && duration < maxGeneratorDuration) {
7335
- duration += timeStep;
7336
- state = generator.next(duration);
7337
- }
7338
- return duration >= maxGeneratorDuration ? Infinity : duration;
7339
- }
7340
-
7341
- /**
7342
- * Create a progress => progress easing function from a generator.
7343
- */
7344
- function createGeneratorEasing(options, scale = 100, createGenerator) {
7345
- const generator = createGenerator({ ...options, keyframes: [0, scale] });
7346
- const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
7347
- return {
7348
- type: "keyframes",
7349
- ease: (progress) => {
7350
- return generator.next(duration * progress).value / scale;
7351
- },
7352
- duration: millisecondsToSeconds(duration),
7353
- };
7354
- }
7355
-
7356
- /**
7357
- * Add the ability for test suites to manually set support flags
7358
- * to better test more environments.
7359
- */
7360
- const supportsFlags = {};
7361
-
7362
- /*#__NO_SIDE_EFFECTS__*/
7363
- function memo(callback) {
7364
- let result;
7365
- return () => {
7366
- if (result === undefined)
7367
- result = callback();
7368
- return result;
7369
- };
7370
- }
7371
-
7372
- function memoSupports(callback, supportsFlag) {
7373
- const memoized = memo(callback);
7374
- return () => supportsFlags[supportsFlag] ?? memoized();
7375
- }
7376
-
7377
- const supportsLinearEasing = /*@__PURE__*/ memoSupports(() => {
7378
- try {
7379
- document
7380
- .createElement("div")
7381
- .animate({ opacity: 0 }, { easing: "linear(0, 1)" });
7382
- }
7383
- catch (e) {
7384
- return false;
7385
- }
7386
- return true;
7387
- }, "linearEasing");
7388
-
7389
- const generateLinearEasing = (easing, duration, // as milliseconds
7390
- resolution = 10 // as milliseconds
7391
- ) => {
7392
- let points = "";
7393
- const numPoints = Math.max(Math.round(duration / resolution), 2);
7394
- for (let i = 0; i < numPoints; i++) {
7395
- points += easing(i / (numPoints - 1)) + ", ";
7396
- }
7397
- return `linear(${points.substring(0, points.length - 2)})`;
7398
- };
7399
-
7400
6490
  const durationKeys = ["duration", "bounce"];
7401
6491
  const physicsKeys = ["stiffness", "damping", "mass"];
7402
6492
  function isSpringType(options, keys) {
@@ -7523,7 +6613,7 @@ function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce
7523
6613
  next: (t) => {
7524
6614
  const current = resolveSpring(t);
7525
6615
  if (!isResolvedFromDuration) {
7526
- let currentVelocity = 0.0;
6616
+ let currentVelocity = t === 0 ? initialVelocity : 0.0;
7527
6617
  /**
7528
6618
  * We only need to calculate velocity for under-damped springs
7529
6619
  * as over- and critically-damped springs can't overshoot, so
@@ -7557,7 +6647,7 @@ function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce
7557
6647
  }
7558
6648
  spring.applyToOptions = (options) => {
7559
6649
  const generatorOptions = createGeneratorEasing(options, 100, spring);
7560
- options.ease = supportsLinearEasing() ? generatorOptions.ease : "easeOut";
6650
+ options.ease = generatorOptions.ease;
7561
6651
  options.duration = secondsToMilliseconds(generatorOptions.duration);
7562
6652
  options.type = "keyframes";
7563
6653
  return options;
@@ -7646,44 +6736,6 @@ function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, b
7646
6736
  };
7647
6737
  }
7648
6738
 
7649
- const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1);
7650
- const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1);
7651
- const easeInOut = /*@__PURE__*/ cubicBezier(0.42, 0, 0.58, 1);
7652
-
7653
- const isEasingArray = (ease) => {
7654
- return Array.isArray(ease) && typeof ease[0] !== "number";
7655
- };
7656
-
7657
- const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number";
7658
-
7659
- const easingLookup = {
7660
- linear: noop,
7661
- easeIn,
7662
- easeInOut,
7663
- easeOut,
7664
- circIn,
7665
- circInOut,
7666
- circOut,
7667
- backIn,
7668
- backInOut,
7669
- backOut,
7670
- anticipate,
7671
- };
7672
- const easingDefinitionToFunction = (definition) => {
7673
- if (isBezierDefinition(definition)) {
7674
- // If cubic bezier definition, create bezier curve
7675
- invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`);
7676
- const [x1, y1, x2, y2] = definition;
7677
- return cubicBezier(x1, y1, x2, y2);
7678
- }
7679
- else if (typeof definition === "string") {
7680
- // Else lookup from table
7681
- invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`);
7682
- return easingLookup[definition];
7683
- }
7684
- return definition;
7685
- };
7686
-
7687
6739
  /*
7688
6740
  Progress within given range
7689
6741
 
@@ -7704,7 +6756,7 @@ const progress = (from, to, value) => {
7704
6756
 
7705
6757
  function createMixers(output, ease, customMixer) {
7706
6758
  const mixers = [];
7707
- const mixerFactory = customMixer || mix;
6759
+ const mixerFactory = customMixer || MotionGlobalConfig.mix || mix;
7708
6760
  const numMixers = output.length - 1;
7709
6761
  for (let i = 0; i < numMixers; i++) {
7710
6762
  let mixer = mixerFactory(output[i], output[i + 1]);
@@ -7778,17 +6830,124 @@ function fillOffset(offset, remaining) {
7778
6830
  const offsetProgress = progress(0, remaining, i);
7779
6831
  offset.push(mixNumber$1(min, 1, offsetProgress));
7780
6832
  }
7781
- }
7782
-
7783
- function defaultOffset(arr) {
7784
- const offset = [0];
7785
- fillOffset(offset, arr.length - 1);
7786
- return offset;
7787
- }
7788
-
7789
- function convertOffsetToTimes(offset, duration) {
7790
- return offset.map((o) => o * duration);
7791
- }
6833
+ }
6834
+
6835
+ function defaultOffset(arr) {
6836
+ const offset = [0];
6837
+ fillOffset(offset, arr.length - 1);
6838
+ return offset;
6839
+ }
6840
+
6841
+ function convertOffsetToTimes(offset, duration) {
6842
+ return offset.map((o) => o * duration);
6843
+ }
6844
+
6845
+ /*
6846
+ Bezier function generator
6847
+ This has been modified from Gaëtan Renaudeau's BezierEasing
6848
+ https://github.com/gre/bezier-easing/blob/master/src/index.js
6849
+ https://github.com/gre/bezier-easing/blob/master/LICENSE
6850
+
6851
+ I've removed the newtonRaphsonIterate algo because in benchmarking it
6852
+ wasn't noticiably faster than binarySubdivision, indeed removing it
6853
+ usually improved times, depending on the curve.
6854
+ I also removed the lookup table, as for the added bundle size and loop we're
6855
+ only cutting ~4 or so subdivision iterations. I bumped the max iterations up
6856
+ to 12 to compensate and this still tended to be faster for no perceivable
6857
+ loss in accuracy.
6858
+ Usage
6859
+ const easeOut = cubicBezier(.17,.67,.83,.67);
6860
+ const x = easeOut(0.5); // returns 0.627...
6861
+ */
6862
+ // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
6863
+ const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) *
6864
+ t;
6865
+ const subdivisionPrecision = 0.0000001;
6866
+ const subdivisionMaxIterations = 12;
6867
+ function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) {
6868
+ let currentX;
6869
+ let currentT;
6870
+ let i = 0;
6871
+ do {
6872
+ currentT = lowerBound + (upperBound - lowerBound) / 2.0;
6873
+ currentX = calcBezier(currentT, mX1, mX2) - x;
6874
+ if (currentX > 0.0) {
6875
+ upperBound = currentT;
6876
+ }
6877
+ else {
6878
+ lowerBound = currentT;
6879
+ }
6880
+ } while (Math.abs(currentX) > subdivisionPrecision &&
6881
+ ++i < subdivisionMaxIterations);
6882
+ return currentT;
6883
+ }
6884
+ function cubicBezier(mX1, mY1, mX2, mY2) {
6885
+ // If this is a linear gradient, return linear easing
6886
+ if (mX1 === mY1 && mX2 === mY2)
6887
+ return noop;
6888
+ const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2);
6889
+ // If animation is at start/end, return t without easing
6890
+ return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
6891
+ }
6892
+
6893
+ const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1);
6894
+ const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1);
6895
+ const easeInOut = /*@__PURE__*/ cubicBezier(0.42, 0, 0.58, 1);
6896
+
6897
+ const isEasingArray = (ease) => {
6898
+ return Array.isArray(ease) && typeof ease[0] !== "number";
6899
+ };
6900
+
6901
+ // Accepts an easing function and returns a new one that outputs mirrored values for
6902
+ // the second half of the animation. Turns easeIn into easeInOut.
6903
+ const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
6904
+
6905
+ // Accepts an easing function and returns a new one that outputs reversed values.
6906
+ // Turns easeIn into easeOut.
6907
+ const reverseEasing = (easing) => (p) => 1 - easing(1 - p);
6908
+
6909
+ const backOut = /*@__PURE__*/ cubicBezier(0.33, 1.53, 0.69, 0.99);
6910
+ const backIn = /*@__PURE__*/ reverseEasing(backOut);
6911
+ const backInOut = /*@__PURE__*/ mirrorEasing(backIn);
6912
+
6913
+ const anticipate = (p) => (p *= 2) < 1 ? 0.5 * backIn(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
6914
+
6915
+ const circIn = (p) => 1 - Math.sin(Math.acos(p));
6916
+ const circOut = reverseEasing(circIn);
6917
+ const circInOut = mirrorEasing(circIn);
6918
+
6919
+ const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number";
6920
+
6921
+ const easingLookup = {
6922
+ linear: noop,
6923
+ easeIn,
6924
+ easeInOut,
6925
+ easeOut,
6926
+ circIn,
6927
+ circInOut,
6928
+ circOut,
6929
+ backIn,
6930
+ backInOut,
6931
+ backOut,
6932
+ anticipate,
6933
+ };
6934
+ const isValidEasing = (easing) => {
6935
+ return typeof easing === "string";
6936
+ };
6937
+ const easingDefinitionToFunction = (definition) => {
6938
+ if (isBezierDefinition(definition)) {
6939
+ // If cubic bezier definition, create bezier curve
6940
+ invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`);
6941
+ const [x1, y1, x2, y2] = definition;
6942
+ return cubicBezier(x1, y1, x2, y2);
6943
+ }
6944
+ else if (isValidEasing(definition)) {
6945
+ // Else lookup from table
6946
+ invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`);
6947
+ return easingLookup[definition];
6948
+ }
6949
+ return definition;
6950
+ };
7792
6951
 
7793
6952
  function defaultEasing(values, easing) {
7794
6953
  return values.map(() => easing || easeInOut).splice(0, values.length - 1);
@@ -7833,68 +6992,84 @@ function keyframes({ duration = 300, keyframes: keyframeValues, times, ease = "e
7833
6992
  };
7834
6993
  }
7835
6994
 
7836
- const frameloopDriver = (update) => {
7837
- const passTimestamp = ({ timestamp }) => update(timestamp);
7838
- return {
7839
- start: () => frame.update(passTimestamp, true),
7840
- stop: () => cancelFrame(passTimestamp),
7841
- /**
7842
- * If we're processing this frame we can use the
7843
- * framelocked timestamp to keep things in sync.
7844
- */
7845
- now: () => (frameData.isProcessing ? frameData.timestamp : time.now()),
7846
- };
7847
- };
6995
+ const isNotNull = (value) => value !== null;
6996
+ function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe, speed = 1) {
6997
+ const resolvedKeyframes = keyframes.filter(isNotNull);
6998
+ const useFirstKeyframe = speed < 0 || (repeat && repeatType !== "loop" && repeat % 2 === 1);
6999
+ const index = useFirstKeyframe ? 0 : resolvedKeyframes.length - 1;
7000
+ return !index || finalKeyframe === undefined
7001
+ ? resolvedKeyframes[index]
7002
+ : finalKeyframe;
7003
+ }
7848
7004
 
7849
- const generators = {
7005
+ const transitionTypeMap = {
7850
7006
  decay: inertia,
7851
7007
  inertia,
7852
7008
  tween: keyframes,
7853
7009
  keyframes: keyframes,
7854
7010
  spring,
7855
7011
  };
7012
+ function replaceTransitionType(transition) {
7013
+ if (typeof transition.type === "string") {
7014
+ transition.type = transitionTypeMap[transition.type];
7015
+ }
7016
+ }
7017
+
7018
+ class WithPromise {
7019
+ constructor() {
7020
+ this.count = 0;
7021
+ this.updateFinished();
7022
+ }
7023
+ get finished() {
7024
+ return this._finished;
7025
+ }
7026
+ updateFinished() {
7027
+ this.count++;
7028
+ this._finished = new Promise((resolve) => {
7029
+ this.resolve = resolve;
7030
+ });
7031
+ }
7032
+ notifyFinished() {
7033
+ this.resolve();
7034
+ }
7035
+ /**
7036
+ * Allows the animation to be awaited.
7037
+ *
7038
+ * @deprecated Use `finished` instead.
7039
+ */
7040
+ then(onResolve, onReject) {
7041
+ return this.finished.then(onResolve, onReject);
7042
+ }
7043
+ }
7044
+
7856
7045
  const percentToProgress = (percent) => percent / 100;
7857
- /**
7858
- * Animation that runs on the main thread. Designed to be WAAPI-spec in the subset of
7859
- * features we expose publically. Mostly the compatibility is to ensure visual identity
7860
- * between both WAAPI and main thread animations.
7861
- */
7862
- class MainThreadAnimation extends BaseAnimation {
7046
+ class JSAnimation extends WithPromise {
7863
7047
  constructor(options) {
7864
- super(options);
7865
- /**
7866
- * The time at which the animation was paused.
7867
- */
7868
- this.holdTime = null;
7869
- /**
7870
- * The time at which the animation was cancelled.
7871
- */
7872
- this.cancelTime = null;
7048
+ super();
7049
+ this.state = "idle";
7050
+ this.startTime = null;
7051
+ this.isStopped = false;
7873
7052
  /**
7874
7053
  * The current time of the animation.
7875
7054
  */
7876
7055
  this.currentTime = 0;
7877
7056
  /**
7878
- * Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed.
7879
- */
7880
- this.playbackSpeed = 1;
7881
- /**
7882
- * The state of the animation to apply when the animation is resolved. This
7883
- * allows calls to the public API to control the animation before it is resolved,
7884
- * without us having to resolve it first.
7057
+ * The time at which the animation was paused.
7885
7058
  */
7886
- this.pendingPlayState = "running";
7059
+ this.holdTime = null;
7887
7060
  /**
7888
- * The time at which the animation was started.
7061
+ * Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed.
7889
7062
  */
7890
- this.startTime = null;
7891
- this.state = "idle";
7063
+ this.playbackSpeed = 1;
7892
7064
  /**
7893
7065
  * This method is bound to the instance to fix a pattern where
7894
7066
  * animation.stop is returned as a reference from a useEffect.
7895
7067
  */
7896
7068
  this.stop = () => {
7897
- this.resolver.cancel();
7069
+ const { motionValue } = this.options;
7070
+ if (motionValue && motionValue.updatedAt !== time.now()) {
7071
+ this.tick(time.now());
7072
+ }
7898
7073
  this.isStopped = true;
7899
7074
  if (this.state === "idle")
7900
7075
  return;
@@ -7902,49 +7077,35 @@ class MainThreadAnimation extends BaseAnimation {
7902
7077
  const { onStop } = this.options;
7903
7078
  onStop && onStop();
7904
7079
  };
7905
- const { name, motionValue, element, keyframes } = this.options;
7906
- const KeyframeResolver$1 = element?.KeyframeResolver || KeyframeResolver;
7907
- const onResolved = (resolvedKeyframes, finalKeyframe) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe);
7908
- this.resolver = new KeyframeResolver$1(keyframes, onResolved, name, motionValue, element);
7909
- this.resolver.scheduleResolve();
7910
- }
7911
- flatten() {
7912
- super.flatten();
7913
- // If we've already resolved the animation, re-initialise it
7914
- if (this._resolved) {
7915
- Object.assign(this._resolved, this.initPlayback(this._resolved.keyframes));
7916
- }
7917
- }
7918
- initPlayback(keyframes$1) {
7919
- const { type = "keyframes", repeat = 0, repeatDelay = 0, repeatType, velocity = 0, } = this.options;
7920
- const generatorFactory = isGenerator(type)
7921
- ? type
7922
- : generators[type] || keyframes;
7923
- /**
7924
- * If our generator doesn't support mixing numbers, we need to replace keyframes with
7925
- * [0, 100] and then make a function that maps that to the actual keyframes.
7926
- *
7927
- * 100 is chosen instead of 1 as it works nicer with spring animations.
7928
- */
7929
- let mapPercentToKeyframes;
7930
- let mirroredGenerator;
7080
+ this.options = options;
7081
+ this.initAnimation();
7082
+ this.play();
7083
+ if (options.autoplay === false)
7084
+ this.pause();
7085
+ }
7086
+ initAnimation() {
7087
+ const { options } = this;
7088
+ replaceTransitionType(options);
7089
+ const { type = keyframes, repeat = 0, repeatDelay = 0, repeatType, velocity = 0, } = options;
7090
+ let { keyframes: keyframes$1 } = options;
7091
+ const generatorFactory = type || keyframes;
7931
7092
  if (process.env.NODE_ENV !== "production" &&
7932
7093
  generatorFactory !== keyframes) {
7933
7094
  invariant(keyframes$1.length <= 2, `Only two keyframes currently supported with spring and inertia animations. Trying to animate ${keyframes$1}`);
7934
7095
  }
7935
7096
  if (generatorFactory !== keyframes &&
7936
7097
  typeof keyframes$1[0] !== "number") {
7937
- mapPercentToKeyframes = pipe(percentToProgress, mix(keyframes$1[0], keyframes$1[1]));
7098
+ this.mixKeyframes = pipe(percentToProgress, mix(keyframes$1[0], keyframes$1[1]));
7938
7099
  keyframes$1 = [0, 100];
7939
7100
  }
7940
- const generator = generatorFactory({ ...this.options, keyframes: keyframes$1 });
7101
+ const generator = generatorFactory({ ...options, keyframes: keyframes$1 });
7941
7102
  /**
7942
7103
  * If we have a mirror repeat type we need to create a second generator that outputs the
7943
7104
  * mirrored (not reversed) animation and later ping pong between the two generators.
7944
7105
  */
7945
7106
  if (repeatType === "mirror") {
7946
- mirroredGenerator = generatorFactory({
7947
- ...this.options,
7107
+ this.mirroredGenerator = generatorFactory({
7108
+ ...options,
7948
7109
  keyframes: [...keyframes$1].reverse(),
7949
7110
  velocity: -velocity,
7950
7111
  });
@@ -7961,38 +7122,29 @@ class MainThreadAnimation extends BaseAnimation {
7961
7122
  generator.calculatedDuration = calcGeneratorDuration(generator);
7962
7123
  }
7963
7124
  const { calculatedDuration } = generator;
7964
- const resolvedDuration = calculatedDuration + repeatDelay;
7965
- const totalDuration = resolvedDuration * (repeat + 1) - repeatDelay;
7966
- return {
7967
- generator,
7968
- mirroredGenerator,
7969
- mapPercentToKeyframes,
7970
- calculatedDuration,
7971
- resolvedDuration,
7972
- totalDuration,
7973
- };
7125
+ this.calculatedDuration = calculatedDuration;
7126
+ this.resolvedDuration = calculatedDuration + repeatDelay;
7127
+ this.totalDuration = this.resolvedDuration * (repeat + 1) - repeatDelay;
7128
+ this.generator = generator;
7974
7129
  }
7975
- onPostResolved() {
7976
- const { autoplay = true } = this.options;
7977
- this.play();
7978
- if (this.pendingPlayState === "paused" || !autoplay) {
7979
- this.pause();
7130
+ updateTime(timestamp) {
7131
+ const animationTime = Math.round(timestamp - this.startTime) * this.playbackSpeed;
7132
+ // Update currentTime
7133
+ if (this.holdTime !== null) {
7134
+ this.currentTime = this.holdTime;
7980
7135
  }
7981
7136
  else {
7982
- this.state = this.pendingPlayState;
7137
+ // Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 =
7138
+ // 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for
7139
+ // example.
7140
+ this.currentTime = animationTime;
7983
7141
  }
7984
7142
  }
7985
7143
  tick(timestamp, sample = false) {
7986
- const { resolved } = this;
7987
- // If the animations has failed to resolve, return the final keyframe.
7988
- if (!resolved) {
7989
- const { keyframes } = this.options;
7990
- return { done: true, value: keyframes[keyframes.length - 1] };
7991
- }
7992
- const { finalKeyframe, generator, mirroredGenerator, mapPercentToKeyframes, keyframes, calculatedDuration, totalDuration, resolvedDuration, } = resolved;
7144
+ const { generator, totalDuration, mixKeyframes, mirroredGenerator, resolvedDuration, calculatedDuration, } = this;
7993
7145
  if (this.startTime === null)
7994
7146
  return generator.next(0);
7995
- const { delay, repeat, repeatType, repeatDelay, onUpdate } = this.options;
7147
+ const { delay = 0, keyframes, repeat, repeatType, repeatDelay, type, onUpdate, finalKeyframe, } = this.options;
7996
7148
  /**
7997
7149
  * requestAnimationFrame timestamps can come through as lower than
7998
7150
  * the startTime as set by performance.now(). Here we prevent this,
@@ -8005,23 +7157,15 @@ class MainThreadAnimation extends BaseAnimation {
8005
7157
  else if (this.speed < 0) {
8006
7158
  this.startTime = Math.min(timestamp - totalDuration / this.speed, this.startTime);
8007
7159
  }
8008
- // Update currentTime
8009
7160
  if (sample) {
8010
7161
  this.currentTime = timestamp;
8011
7162
  }
8012
- else if (this.holdTime !== null) {
8013
- this.currentTime = this.holdTime;
8014
- }
8015
7163
  else {
8016
- // Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 =
8017
- // 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for
8018
- // example.
8019
- this.currentTime =
8020
- Math.round(timestamp - this.startTime) * this.speed;
7164
+ this.updateTime(timestamp);
8021
7165
  }
8022
7166
  // Rebase on delay
8023
- const timeWithoutDelay = this.currentTime - delay * (this.speed >= 0 ? 1 : -1);
8024
- const isInDelayPhase = this.speed >= 0
7167
+ const timeWithoutDelay = this.currentTime - delay * (this.playbackSpeed >= 0 ? 1 : -1);
7168
+ const isInDelayPhase = this.playbackSpeed >= 0
8025
7169
  ? timeWithoutDelay < 0
8026
7170
  : timeWithoutDelay > totalDuration;
8027
7171
  this.currentTime = Math.max(timeWithoutDelay, 0);
@@ -8082,20 +7226,21 @@ class MainThreadAnimation extends BaseAnimation {
8082
7226
  const state = isInDelayPhase
8083
7227
  ? { done: false, value: keyframes[0] }
8084
7228
  : frameGenerator.next(elapsed);
8085
- if (mapPercentToKeyframes) {
8086
- state.value = mapPercentToKeyframes(state.value);
7229
+ if (mixKeyframes) {
7230
+ state.value = mixKeyframes(state.value);
8087
7231
  }
8088
7232
  let { done } = state;
8089
7233
  if (!isInDelayPhase && calculatedDuration !== null) {
8090
7234
  done =
8091
- this.speed >= 0
7235
+ this.playbackSpeed >= 0
8092
7236
  ? this.currentTime >= totalDuration
8093
7237
  : this.currentTime <= 0;
8094
7238
  }
8095
7239
  const isAnimationFinished = this.holdTime === null &&
8096
7240
  (this.state === "finished" || (this.state === "running" && done));
8097
- if (isAnimationFinished && finalKeyframe !== undefined) {
8098
- state.value = getFinalKeyframe(keyframes, this.options, finalKeyframe);
7241
+ // TODO: The exception for inertia could be cleaner here
7242
+ if (isAnimationFinished && type !== inertia) {
7243
+ state.value = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
8099
7244
  }
8100
7245
  if (onUpdate) {
8101
7246
  onUpdate(state.value);
@@ -8105,9 +7250,16 @@ class MainThreadAnimation extends BaseAnimation {
8105
7250
  }
8106
7251
  return state;
8107
7252
  }
7253
+ /**
7254
+ * Allows the returned animation to be awaited or promise-chained. Currently
7255
+ * resolves when the animation finishes at all but in a future update could/should
7256
+ * reject if its cancels.
7257
+ */
7258
+ then(resolve, reject) {
7259
+ return this.finished.then(resolve, reject);
7260
+ }
8108
7261
  get duration() {
8109
- const { resolved } = this;
8110
- return resolved ? millisecondsToSeconds(resolved.calculatedDuration) : 0;
7262
+ return millisecondsToSeconds(this.calculatedDuration);
8111
7263
  }
8112
7264
  get time() {
8113
7265
  return millisecondsToSeconds(this.currentTime);
@@ -8115,125 +7267,417 @@ class MainThreadAnimation extends BaseAnimation {
8115
7267
  set time(newTime) {
8116
7268
  newTime = secondsToMilliseconds(newTime);
8117
7269
  this.currentTime = newTime;
8118
- if (this.holdTime !== null || this.speed === 0) {
7270
+ if (this.startTime === null ||
7271
+ this.holdTime !== null ||
7272
+ this.playbackSpeed === 0) {
8119
7273
  this.holdTime = newTime;
8120
7274
  }
8121
7275
  else if (this.driver) {
8122
- this.startTime = this.driver.now() - newTime / this.speed;
7276
+ this.startTime = this.driver.now() - newTime / this.playbackSpeed;
7277
+ }
7278
+ }
7279
+ get speed() {
7280
+ return this.playbackSpeed;
7281
+ }
7282
+ set speed(newSpeed) {
7283
+ this.updateTime(time.now());
7284
+ const hasChanged = this.playbackSpeed !== newSpeed;
7285
+ this.playbackSpeed = newSpeed;
7286
+ if (hasChanged) {
7287
+ this.time = millisecondsToSeconds(this.currentTime);
8123
7288
  }
8124
7289
  }
8125
- get speed() {
8126
- return this.playbackSpeed;
7290
+ play() {
7291
+ if (this.isStopped)
7292
+ return;
7293
+ const { driver = frameloopDriver, onPlay, startTime } = this.options;
7294
+ if (!this.driver) {
7295
+ this.driver = driver((timestamp) => this.tick(timestamp));
7296
+ }
7297
+ onPlay && onPlay();
7298
+ const now = this.driver.now();
7299
+ if (this.holdTime !== null) {
7300
+ this.startTime = now - this.holdTime;
7301
+ }
7302
+ else if (this.state === "finished") {
7303
+ this.updateFinished();
7304
+ this.startTime = now;
7305
+ }
7306
+ else if (!this.startTime) {
7307
+ this.startTime = startTime ?? now;
7308
+ }
7309
+ if (this.state === "finished" && this.speed < 0) {
7310
+ this.startTime += this.calculatedDuration;
7311
+ }
7312
+ this.holdTime = null;
7313
+ /**
7314
+ * Set playState to running only after we've used it in
7315
+ * the previous logic.
7316
+ */
7317
+ this.state = "running";
7318
+ this.driver.start();
7319
+ }
7320
+ pause() {
7321
+ this.state = "paused";
7322
+ this.updateTime(time.now());
7323
+ this.holdTime = this.currentTime;
7324
+ }
7325
+ complete() {
7326
+ if (this.state !== "running") {
7327
+ this.play();
7328
+ }
7329
+ this.state = "finished";
7330
+ this.holdTime = null;
7331
+ }
7332
+ finish() {
7333
+ this.teardown();
7334
+ this.state = "finished";
7335
+ const { onComplete } = this.options;
7336
+ onComplete && onComplete();
7337
+ }
7338
+ cancel() {
7339
+ this.holdTime = null;
7340
+ this.startTime = 0;
7341
+ this.tick(0);
7342
+ this.teardown();
7343
+ }
7344
+ teardown() {
7345
+ this.notifyFinished();
7346
+ this.state = "idle";
7347
+ this.stopDriver();
7348
+ this.startTime = this.holdTime = null;
7349
+ }
7350
+ stopDriver() {
7351
+ if (!this.driver)
7352
+ return;
7353
+ this.driver.stop();
7354
+ this.driver = undefined;
7355
+ }
7356
+ sample(sampleTime) {
7357
+ this.startTime = 0;
7358
+ return this.tick(sampleTime, true);
7359
+ }
7360
+ attachTimeline(timeline) {
7361
+ if (this.options.allowFlatten) {
7362
+ this.options.type = "keyframes";
7363
+ this.options.ease = "linear";
7364
+ this.initAnimation();
7365
+ }
7366
+ return timeline.observe(this);
7367
+ }
7368
+ }
7369
+
7370
+ function fillWildcards(keyframes) {
7371
+ for (let i = 1; i < keyframes.length; i++) {
7372
+ keyframes[i] ?? (keyframes[i] = keyframes[i - 1]);
7373
+ }
7374
+ }
7375
+
7376
+ const radToDeg = (rad) => (rad * 180) / Math.PI;
7377
+ const rotate = (v) => {
7378
+ const angle = radToDeg(Math.atan2(v[1], v[0]));
7379
+ return rebaseAngle(angle);
7380
+ };
7381
+ const matrix2dParsers = {
7382
+ x: 4,
7383
+ y: 5,
7384
+ translateX: 4,
7385
+ translateY: 5,
7386
+ scaleX: 0,
7387
+ scaleY: 3,
7388
+ scale: (v) => (Math.abs(v[0]) + Math.abs(v[3])) / 2,
7389
+ rotate,
7390
+ rotateZ: rotate,
7391
+ skewX: (v) => radToDeg(Math.atan(v[1])),
7392
+ skewY: (v) => radToDeg(Math.atan(v[2])),
7393
+ skew: (v) => (Math.abs(v[1]) + Math.abs(v[2])) / 2,
7394
+ };
7395
+ const rebaseAngle = (angle) => {
7396
+ angle = angle % 360;
7397
+ if (angle < 0)
7398
+ angle += 360;
7399
+ return angle;
7400
+ };
7401
+ const rotateZ = rotate;
7402
+ const scaleX = (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1]);
7403
+ const scaleY = (v) => Math.sqrt(v[4] * v[4] + v[5] * v[5]);
7404
+ const matrix3dParsers = {
7405
+ x: 12,
7406
+ y: 13,
7407
+ z: 14,
7408
+ translateX: 12,
7409
+ translateY: 13,
7410
+ translateZ: 14,
7411
+ scaleX,
7412
+ scaleY,
7413
+ scale: (v) => (scaleX(v) + scaleY(v)) / 2,
7414
+ rotateX: (v) => rebaseAngle(radToDeg(Math.atan2(v[6], v[5]))),
7415
+ rotateY: (v) => rebaseAngle(radToDeg(Math.atan2(-v[2], v[0]))),
7416
+ rotateZ,
7417
+ rotate: rotateZ,
7418
+ skewX: (v) => radToDeg(Math.atan(v[4])),
7419
+ skewY: (v) => radToDeg(Math.atan(v[1])),
7420
+ skew: (v) => (Math.abs(v[1]) + Math.abs(v[4])) / 2,
7421
+ };
7422
+ function defaultTransformValue(name) {
7423
+ return name.includes("scale") ? 1 : 0;
7424
+ }
7425
+ function parseValueFromTransform(transform, name) {
7426
+ if (!transform || transform === "none") {
7427
+ return defaultTransformValue(name);
7428
+ }
7429
+ const matrix3dMatch = transform.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);
7430
+ let parsers;
7431
+ let match;
7432
+ if (matrix3dMatch) {
7433
+ parsers = matrix3dParsers;
7434
+ match = matrix3dMatch;
7435
+ }
7436
+ else {
7437
+ const matrix2dMatch = transform.match(/^matrix\(([-\d.e\s,]+)\)$/u);
7438
+ parsers = matrix2dParsers;
7439
+ match = matrix2dMatch;
7440
+ }
7441
+ if (!match) {
7442
+ return defaultTransformValue(name);
8127
7443
  }
8128
- set speed(newSpeed) {
8129
- const hasChanged = this.playbackSpeed !== newSpeed;
8130
- this.playbackSpeed = newSpeed;
8131
- if (hasChanged) {
8132
- this.time = millisecondsToSeconds(this.currentTime);
7444
+ const valueParser = parsers[name];
7445
+ const values = match[1].split(",").map(convertTransformToNumber);
7446
+ return typeof valueParser === "function"
7447
+ ? valueParser(values)
7448
+ : values[valueParser];
7449
+ }
7450
+ const readTransformValue = (instance, name) => {
7451
+ const { transform = "none" } = getComputedStyle(instance);
7452
+ return parseValueFromTransform(transform, name);
7453
+ };
7454
+ function convertTransformToNumber(value) {
7455
+ return parseFloat(value.trim());
7456
+ }
7457
+
7458
+ const isNumOrPxType = (v) => v === number || v === px;
7459
+ const transformKeys = new Set(["x", "y", "z"]);
7460
+ const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key));
7461
+ function removeNonTranslationalTransform(visualElement) {
7462
+ const removedTransforms = [];
7463
+ nonTranslationalTransformKeys.forEach((key) => {
7464
+ const value = visualElement.getValue(key);
7465
+ if (value !== undefined) {
7466
+ removedTransforms.push([key, value.get()]);
7467
+ value.set(key.startsWith("scale") ? 1 : 0);
8133
7468
  }
7469
+ });
7470
+ return removedTransforms;
7471
+ }
7472
+ const positionalValues = {
7473
+ // Dimensions
7474
+ width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight),
7475
+ height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom),
7476
+ top: (_bbox, { top }) => parseFloat(top),
7477
+ left: (_bbox, { left }) => parseFloat(left),
7478
+ bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min),
7479
+ right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min),
7480
+ // Transform
7481
+ x: (_bbox, { transform }) => parseValueFromTransform(transform, "x"),
7482
+ y: (_bbox, { transform }) => parseValueFromTransform(transform, "y"),
7483
+ };
7484
+ // Alias translate longform names
7485
+ positionalValues.translateX = positionalValues.x;
7486
+ positionalValues.translateY = positionalValues.y;
7487
+
7488
+ const toResolve = new Set();
7489
+ let isScheduled = false;
7490
+ let anyNeedsMeasurement = false;
7491
+ let isForced = false;
7492
+ function measureAllKeyframes() {
7493
+ if (anyNeedsMeasurement) {
7494
+ const resolversToMeasure = Array.from(toResolve).filter((resolver) => resolver.needsMeasurement);
7495
+ const elementsToMeasure = new Set(resolversToMeasure.map((resolver) => resolver.element));
7496
+ const transformsToRestore = new Map();
7497
+ /**
7498
+ * Write pass
7499
+ * If we're measuring elements we want to remove bounding box-changing transforms.
7500
+ */
7501
+ elementsToMeasure.forEach((element) => {
7502
+ const removedTransforms = removeNonTranslationalTransform(element);
7503
+ if (!removedTransforms.length)
7504
+ return;
7505
+ transformsToRestore.set(element, removedTransforms);
7506
+ element.render();
7507
+ });
7508
+ // Read
7509
+ resolversToMeasure.forEach((resolver) => resolver.measureInitialState());
7510
+ // Write
7511
+ elementsToMeasure.forEach((element) => {
7512
+ element.render();
7513
+ const restore = transformsToRestore.get(element);
7514
+ if (restore) {
7515
+ restore.forEach(([key, value]) => {
7516
+ element.getValue(key)?.set(value);
7517
+ });
7518
+ }
7519
+ });
7520
+ // Read
7521
+ resolversToMeasure.forEach((resolver) => resolver.measureEndState());
7522
+ // Write
7523
+ resolversToMeasure.forEach((resolver) => {
7524
+ if (resolver.suspendedScrollY !== undefined) {
7525
+ window.scrollTo(0, resolver.suspendedScrollY);
7526
+ }
7527
+ });
8134
7528
  }
8135
- play() {
8136
- if (!this.resolver.isScheduled) {
8137
- this.resolver.resume();
8138
- }
8139
- if (!this._resolved) {
8140
- this.pendingPlayState = "running";
8141
- return;
8142
- }
8143
- if (this.isStopped)
8144
- return;
8145
- const { driver = frameloopDriver, onPlay, startTime } = this.options;
8146
- if (!this.driver) {
8147
- this.driver = driver((timestamp) => this.tick(timestamp));
8148
- }
8149
- onPlay && onPlay();
8150
- const now = this.driver.now();
8151
- if (this.holdTime !== null) {
8152
- this.startTime = now - this.holdTime;
8153
- }
8154
- else if (!this.startTime) {
8155
- this.startTime = startTime ?? this.calcStartTime();
8156
- }
8157
- else if (this.state === "finished") {
8158
- this.startTime = now;
8159
- }
8160
- if (this.state === "finished") {
8161
- this.updateFinishedPromise();
7529
+ anyNeedsMeasurement = false;
7530
+ isScheduled = false;
7531
+ toResolve.forEach((resolver) => resolver.complete(isForced));
7532
+ toResolve.clear();
7533
+ }
7534
+ function readAllKeyframes() {
7535
+ toResolve.forEach((resolver) => {
7536
+ resolver.readKeyframes();
7537
+ if (resolver.needsMeasurement) {
7538
+ anyNeedsMeasurement = true;
8162
7539
  }
8163
- this.cancelTime = this.startTime;
8164
- this.holdTime = null;
7540
+ });
7541
+ }
7542
+ function flushKeyframeResolvers() {
7543
+ isForced = true;
7544
+ readAllKeyframes();
7545
+ measureAllKeyframes();
7546
+ isForced = false;
7547
+ }
7548
+ class KeyframeResolver {
7549
+ constructor(unresolvedKeyframes, onComplete, name, motionValue, element, isAsync = false) {
8165
7550
  /**
8166
- * Set playState to running only after we've used it in
8167
- * the previous logic.
7551
+ * Track whether this resolver has completed. Once complete, it never
7552
+ * needs to attempt keyframe resolution again.
8168
7553
  */
8169
- this.state = "running";
8170
- this.driver.start();
7554
+ this.isComplete = false;
7555
+ /**
7556
+ * Track whether this resolver is async. If it is, it'll be added to the
7557
+ * resolver queue and flushed in the next frame. Resolvers that aren't going
7558
+ * to trigger read/write thrashing don't need to be async.
7559
+ */
7560
+ this.isAsync = false;
7561
+ /**
7562
+ * Track whether this resolver needs to perform a measurement
7563
+ * to resolve its keyframes.
7564
+ */
7565
+ this.needsMeasurement = false;
7566
+ /**
7567
+ * Track whether this resolver is currently scheduled to resolve
7568
+ * to allow it to be cancelled and resumed externally.
7569
+ */
7570
+ this.isScheduled = false;
7571
+ this.unresolvedKeyframes = [...unresolvedKeyframes];
7572
+ this.onComplete = onComplete;
7573
+ this.name = name;
7574
+ this.motionValue = motionValue;
7575
+ this.element = element;
7576
+ this.isAsync = isAsync;
8171
7577
  }
8172
- pause() {
8173
- if (!this._resolved) {
8174
- this.pendingPlayState = "paused";
8175
- return;
7578
+ scheduleResolve() {
7579
+ this.isScheduled = true;
7580
+ if (this.isAsync) {
7581
+ toResolve.add(this);
7582
+ if (!isScheduled) {
7583
+ isScheduled = true;
7584
+ frame.read(readAllKeyframes);
7585
+ frame.resolveKeyframes(measureAllKeyframes);
7586
+ }
7587
+ }
7588
+ else {
7589
+ this.readKeyframes();
7590
+ this.complete();
8176
7591
  }
8177
- this.state = "paused";
8178
- this.holdTime = this.currentTime ?? 0;
8179
7592
  }
8180
- complete() {
8181
- if (this.state !== "running") {
8182
- this.play();
7593
+ readKeyframes() {
7594
+ const { unresolvedKeyframes, name, element, motionValue } = this;
7595
+ // If initial keyframe is null we need to read it from the DOM
7596
+ if (unresolvedKeyframes[0] === null) {
7597
+ const currentValue = motionValue?.get();
7598
+ // TODO: This doesn't work if the final keyframe is a wildcard
7599
+ const finalKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
7600
+ if (currentValue !== undefined) {
7601
+ unresolvedKeyframes[0] = currentValue;
7602
+ }
7603
+ else if (element && name) {
7604
+ const valueAsRead = element.readValue(name, finalKeyframe);
7605
+ if (valueAsRead !== undefined && valueAsRead !== null) {
7606
+ unresolvedKeyframes[0] = valueAsRead;
7607
+ }
7608
+ }
7609
+ if (unresolvedKeyframes[0] === undefined) {
7610
+ unresolvedKeyframes[0] = finalKeyframe;
7611
+ }
7612
+ if (motionValue && currentValue === undefined) {
7613
+ motionValue.set(unresolvedKeyframes[0]);
7614
+ }
8183
7615
  }
8184
- this.pendingPlayState = this.state = "finished";
8185
- this.holdTime = null;
7616
+ fillWildcards(unresolvedKeyframes);
8186
7617
  }
8187
- finish() {
8188
- this.teardown();
8189
- this.state = "finished";
8190
- const { onComplete } = this.options;
8191
- onComplete && onComplete();
7618
+ setFinalKeyframe() { }
7619
+ measureInitialState() { }
7620
+ renderEndStyles() { }
7621
+ measureEndState() { }
7622
+ complete(isForced = false) {
7623
+ this.isComplete = true;
7624
+ this.onComplete(this.unresolvedKeyframes, this.finalKeyframe, isForced);
7625
+ toResolve.delete(this);
8192
7626
  }
8193
7627
  cancel() {
8194
- if (this.cancelTime !== null) {
8195
- this.tick(this.cancelTime);
7628
+ if (!this.isComplete) {
7629
+ this.isScheduled = false;
7630
+ toResolve.delete(this);
8196
7631
  }
8197
- this.teardown();
8198
- this.updateFinishedPromise();
8199
- }
8200
- teardown() {
8201
- this.state = "idle";
8202
- this.stopDriver();
8203
- this.resolveFinishedPromise();
8204
- this.updateFinishedPromise();
8205
- this.startTime = this.cancelTime = null;
8206
- this.resolver.cancel();
8207
7632
  }
8208
- stopDriver() {
8209
- if (!this.driver)
8210
- return;
8211
- this.driver.stop();
8212
- this.driver = undefined;
8213
- }
8214
- sample(time) {
8215
- this.startTime = 0;
8216
- return this.tick(time, true);
8217
- }
8218
- get finished() {
8219
- return this.currentFinishedPromise;
7633
+ resume() {
7634
+ if (!this.isComplete)
7635
+ this.scheduleResolve();
8220
7636
  }
8221
7637
  }
8222
7638
 
7639
+ const isCSSVar = (name) => name.startsWith("--");
7640
+
7641
+ function setStyle(element, name, value) {
7642
+ isCSSVar(name)
7643
+ ? element.style.setProperty(name, value)
7644
+ : (element.style[name] = value);
7645
+ }
7646
+
7647
+ /*#__NO_SIDE_EFFECTS__*/
7648
+ function memo(callback) {
7649
+ let result;
7650
+ return () => {
7651
+ if (result === undefined)
7652
+ result = callback();
7653
+ return result;
7654
+ };
7655
+ }
7656
+
7657
+ const supportsScrollTimeline = /* @__PURE__ */ memo(() => window.ScrollTimeline !== undefined);
7658
+
8223
7659
  /**
8224
- * A list of values that can be hardware-accelerated.
7660
+ * Add the ability for test suites to manually set support flags
7661
+ * to better test more environments.
8225
7662
  */
8226
- const acceleratedValues = new Set([
8227
- "opacity",
8228
- "clipPath",
8229
- "filter",
8230
- "transform",
8231
- // TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved
8232
- // or until we implement support for linear() easing.
8233
- // "background-color"
8234
- ]);
7663
+ const supportsFlags = {};
8235
7664
 
8236
- const supportsWaapi = /*@__PURE__*/ memo(() => Object.hasOwnProperty.call(Element.prototype, "animate"));
7665
+ function memoSupports(callback, supportsFlag) {
7666
+ const memoized = memo(callback);
7667
+ return () => supportsFlags[supportsFlag] ?? memoized();
7668
+ }
7669
+
7670
+ const supportsLinearEasing = /*@__PURE__*/ memoSupports(() => {
7671
+ try {
7672
+ document
7673
+ .createElement("div")
7674
+ .animate({ opacity: 0 }, { easing: "linear(0, 1)" });
7675
+ }
7676
+ catch (e) {
7677
+ return false;
7678
+ }
7679
+ return true;
7680
+ }, "linearEasing");
8237
7681
 
8238
7682
  const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
8239
7683
 
@@ -8253,8 +7697,10 @@ function mapEasingToNativeEasing(easing, duration) {
8253
7697
  if (!easing) {
8254
7698
  return undefined;
8255
7699
  }
8256
- else if (typeof easing === "function" && supportsLinearEasing()) {
8257
- return generateLinearEasing(easing, duration);
7700
+ else if (typeof easing === "function") {
7701
+ return supportsLinearEasing()
7702
+ ? generateLinearEasing(easing, duration)
7703
+ : "ease-out";
8258
7704
  }
8259
7705
  else if (isBezierDefinition(easing)) {
8260
7706
  return cubicBezierAsString(easing);
@@ -8268,7 +7714,7 @@ function mapEasingToNativeEasing(easing, duration) {
8268
7714
  }
8269
7715
  }
8270
7716
 
8271
- function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duration = 300, repeat = 0, repeatType = "loop", ease = "easeInOut", times, } = {}, pseudoElement = undefined) {
7717
+ function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duration = 300, repeat = 0, repeatType = "loop", ease = "easeOut", times, } = {}, pseudoElement = undefined) {
8272
7718
  const keyframeOptions = {
8273
7719
  [valueName]: keyframes,
8274
7720
  };
@@ -8280,473 +7726,517 @@ function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duratio
8280
7726
  */
8281
7727
  if (Array.isArray(easing))
8282
7728
  keyframeOptions.easing = easing;
8283
- const animation = element.animate(keyframeOptions, {
7729
+ const options = {
8284
7730
  delay,
8285
7731
  duration,
8286
7732
  easing: !Array.isArray(easing) ? easing : "linear",
8287
7733
  fill: "both",
8288
7734
  iterations: repeat + 1,
8289
7735
  direction: repeatType === "reverse" ? "alternate" : "normal",
8290
- pseudoElement,
8291
- });
7736
+ };
7737
+ if (pseudoElement)
7738
+ options.pseudoElement = pseudoElement;
7739
+ const animation = element.animate(keyframeOptions, options);
8292
7740
  return animation;
8293
7741
  }
8294
7742
 
8295
- function attachTimeline(animation, timeline) {
8296
- animation.timeline = timeline;
8297
- animation.onfinish = null;
7743
+ function isGenerator(type) {
7744
+ return typeof type === "function" && "applyToOptions" in type;
8298
7745
  }
8299
7746
 
8300
- function isWaapiSupportedEasing(easing) {
8301
- return Boolean((typeof easing === "function" && supportsLinearEasing()) ||
8302
- !easing ||
8303
- (typeof easing === "string" &&
8304
- (easing in supportedWaapiEasing || supportsLinearEasing())) ||
8305
- isBezierDefinition(easing) ||
8306
- (Array.isArray(easing) && easing.every(isWaapiSupportedEasing)));
7747
+ function applyGeneratorOptions({ type, ...options }) {
7748
+ if (isGenerator(type) && supportsLinearEasing()) {
7749
+ return type.applyToOptions(options);
7750
+ }
7751
+ else {
7752
+ options.duration ?? (options.duration = 300);
7753
+ options.ease ?? (options.ease = "easeOut");
7754
+ }
7755
+ return options;
8307
7756
  }
8308
7757
 
8309
7758
  /**
8310
- * 10ms is chosen here as it strikes a balance between smooth
8311
- * results (more than one keyframe per frame at 60fps) and
8312
- * keyframe quantity.
8313
- */
8314
- const sampleDelta = 10; //ms
8315
- /**
8316
- * Implement a practical max duration for keyframe generation
8317
- * to prevent infinite loops
8318
- */
8319
- const maxDuration = 20000;
8320
- /**
8321
- * Check if an animation can run natively via WAAPI or requires pregenerated keyframes.
8322
- * WAAPI doesn't support spring or function easings so we run these as JS animation before
8323
- * handing off.
7759
+ * NativeAnimation implements AnimationPlaybackControls for the browser's Web Animations API.
8324
7760
  */
8325
- function requiresPregeneratedKeyframes(options) {
8326
- return (isGenerator(options.type) ||
8327
- options.type === "spring" ||
8328
- !isWaapiSupportedEasing(options.ease));
8329
- }
8330
- function pregenerateKeyframes(keyframes, options) {
8331
- /**
8332
- * Create a main-thread animation to pregenerate keyframes.
8333
- * We sample this at regular intervals to generate keyframes that we then
8334
- * linearly interpolate between.
8335
- */
8336
- const sampleAnimation = new MainThreadAnimation({
8337
- ...options,
8338
- keyframes,
8339
- repeat: 0,
8340
- delay: 0,
8341
- isGenerator: true,
8342
- });
8343
- let state = { done: false, value: keyframes[0] };
8344
- const pregeneratedKeyframes = [];
8345
- /**
8346
- * Bail after 20 seconds of pre-generated keyframes as it's likely
8347
- * we're heading for an infinite loop.
8348
- */
8349
- let t = 0;
8350
- while (!state.done && t < maxDuration) {
8351
- state = sampleAnimation.sample(t);
8352
- pregeneratedKeyframes.push(state.value);
8353
- t += sampleDelta;
8354
- }
8355
- return {
8356
- times: undefined,
8357
- keyframes: pregeneratedKeyframes,
8358
- duration: t - sampleDelta,
8359
- ease: "linear",
8360
- };
8361
- }
8362
- const unsupportedEasingFunctions = {
8363
- anticipate,
8364
- backInOut,
8365
- circInOut,
8366
- };
8367
- function isUnsupportedEase(key) {
8368
- return key in unsupportedEasingFunctions;
8369
- }
8370
- class AcceleratedAnimation extends BaseAnimation {
7761
+ class NativeAnimation extends WithPromise {
8371
7762
  constructor(options) {
8372
- super(options);
8373
- const { name, motionValue, element, keyframes } = this.options;
8374
- this.resolver = new DOMKeyframesResolver(keyframes, (resolvedKeyframes, finalKeyframe) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe), name, motionValue, element);
8375
- this.resolver.scheduleResolve();
8376
- }
8377
- initPlayback(keyframes, finalKeyframe) {
8378
- let { duration = 300, times, ease, type, motionValue, name, startTime, } = this.options;
7763
+ super();
7764
+ this.finishedTime = null;
7765
+ this.isStopped = false;
7766
+ if (!options)
7767
+ return;
7768
+ const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, onComplete, } = options;
7769
+ this.isPseudoElement = Boolean(pseudoElement);
7770
+ this.allowFlatten = allowFlatten;
7771
+ this.options = options;
7772
+ invariant(typeof options.type !== "string", `animateMini doesn't support "type" as a string. Did you mean to import { spring } from "motion"?`);
7773
+ const transition = applyGeneratorOptions(options);
7774
+ this.animation = startWaapiAnimation(element, name, keyframes, transition, pseudoElement);
7775
+ if (transition.autoplay === false) {
7776
+ this.animation.pause();
7777
+ }
7778
+ this.animation.onfinish = () => {
7779
+ this.finishedTime = this.time;
7780
+ if (!pseudoElement) {
7781
+ const keyframe = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
7782
+ if (this.updateMotionValue) {
7783
+ this.updateMotionValue(keyframe);
7784
+ }
7785
+ else {
7786
+ /**
7787
+ * If we can, we want to commit the final style as set by the user,
7788
+ * rather than the computed keyframe value supplied by the animation.
7789
+ */
7790
+ setStyle(element, name, keyframe);
7791
+ }
7792
+ this.animation.cancel();
7793
+ }
7794
+ onComplete?.();
7795
+ this.notifyFinished();
7796
+ };
8379
7797
  /**
8380
- * If element has since been unmounted, return false to indicate
8381
- * the animation failed to initialised.
7798
+ * TODO: In a breaking change, we should replace this with `.notifyCancel()`
8382
7799
  */
8383
- if (!motionValue.owner || !motionValue.owner.current) {
8384
- return false;
7800
+ this.animation.oncancel = () => this.notifyFinished();
7801
+ }
7802
+ play() {
7803
+ if (this.isStopped)
7804
+ return;
7805
+ this.animation.play();
7806
+ if (this.state === "finished") {
7807
+ this.updateFinished();
8385
7808
  }
8386
- /**
8387
- * If the user has provided an easing function name that isn't supported
8388
- * by WAAPI (like "anticipate"), we need to provide the corressponding
8389
- * function. This will later get converted to a linear() easing function.
8390
- */
8391
- if (typeof ease === "string" &&
8392
- supportsLinearEasing() &&
8393
- isUnsupportedEase(ease)) {
8394
- ease = unsupportedEasingFunctions[ease];
7809
+ }
7810
+ pause() {
7811
+ this.animation.pause();
7812
+ }
7813
+ complete() {
7814
+ this.animation.finish?.();
7815
+ }
7816
+ cancel() {
7817
+ try {
7818
+ this.animation.cancel();
8395
7819
  }
8396
- /**
8397
- * If this animation needs pre-generated keyframes then generate.
8398
- */
8399
- if (requiresPregeneratedKeyframes(this.options)) {
8400
- const { onComplete, onUpdate, motionValue, element, ...options } = this.options;
8401
- const pregeneratedAnimation = pregenerateKeyframes(keyframes, options);
8402
- keyframes = pregeneratedAnimation.keyframes;
8403
- // If this is a very short animation, ensure we have
8404
- // at least two keyframes to animate between as older browsers
8405
- // can't animate between a single keyframe.
8406
- if (keyframes.length === 1) {
8407
- keyframes[1] = keyframes[0];
8408
- }
8409
- duration = pregeneratedAnimation.duration;
8410
- times = pregeneratedAnimation.times;
8411
- ease = pregeneratedAnimation.ease;
8412
- type = "keyframes";
8413
- }
8414
- const animation = startWaapiAnimation(motionValue.owner.current, name, keyframes, { ...this.options, duration, times, ease });
8415
- // Override the browser calculated startTime with one synchronised to other JS
8416
- // and WAAPI animations starting this event loop.
8417
- animation.startTime = startTime ?? this.calcStartTime();
8418
- if (this.pendingTimeline) {
8419
- attachTimeline(animation, this.pendingTimeline);
8420
- this.pendingTimeline = undefined;
7820
+ catch (e) { }
7821
+ }
7822
+ stop() {
7823
+ if (this.isStopped)
7824
+ return;
7825
+ this.isStopped = true;
7826
+ const { state } = this;
7827
+ if (state === "idle" || state === "finished") {
7828
+ return;
7829
+ }
7830
+ if (this.updateMotionValue) {
7831
+ this.updateMotionValue();
8421
7832
  }
8422
7833
  else {
8423
- /**
8424
- * Prefer the `onfinish` prop as it's more widely supported than
8425
- * the `finished` promise.
8426
- *
8427
- * Here, we synchronously set the provided MotionValue to the end
8428
- * keyframe. If we didn't, when the WAAPI animation is finished it would
8429
- * be removed from the element which would then revert to its old styles.
8430
- */
8431
- animation.onfinish = () => {
8432
- const { onComplete } = this.options;
8433
- motionValue.set(getFinalKeyframe(keyframes, this.options, finalKeyframe));
8434
- onComplete && onComplete();
8435
- this.cancel();
8436
- this.resolveFinishedPromise();
8437
- };
7834
+ this.commitStyles();
7835
+ }
7836
+ if (!this.isPseudoElement)
7837
+ this.cancel();
7838
+ }
7839
+ /**
7840
+ * WAAPI doesn't natively have any interruption capabilities.
7841
+ *
7842
+ * In this method, we commit styles back to the DOM before cancelling
7843
+ * the animation.
7844
+ *
7845
+ * This is designed to be overridden by NativeAnimationExtended, which
7846
+ * will create a renderless JS animation and sample it twice to calculate
7847
+ * its current value, "previous" value, and therefore allow
7848
+ * Motion to also correctly calculate velocity for any subsequent animation
7849
+ * while deferring the commit until the next animation frame.
7850
+ */
7851
+ commitStyles() {
7852
+ if (!this.isPseudoElement) {
7853
+ this.animation.commitStyles?.();
8438
7854
  }
8439
- return {
8440
- animation,
8441
- duration,
8442
- times,
8443
- type,
8444
- ease,
8445
- keyframes: keyframes,
8446
- };
8447
7855
  }
8448
7856
  get duration() {
8449
- const { resolved } = this;
8450
- if (!resolved)
8451
- return 0;
8452
- const { duration } = resolved;
8453
- return millisecondsToSeconds(duration);
7857
+ const duration = this.animation.effect?.getComputedTiming?.().duration || 0;
7858
+ return millisecondsToSeconds(Number(duration));
8454
7859
  }
8455
7860
  get time() {
8456
- const { resolved } = this;
8457
- if (!resolved)
8458
- return 0;
8459
- const { animation } = resolved;
8460
- return millisecondsToSeconds(animation.currentTime || 0);
7861
+ return millisecondsToSeconds(Number(this.animation.currentTime) || 0);
8461
7862
  }
8462
7863
  set time(newTime) {
8463
- const { resolved } = this;
8464
- if (!resolved)
8465
- return;
8466
- const { animation } = resolved;
8467
- animation.currentTime = secondsToMilliseconds(newTime);
7864
+ this.finishedTime = null;
7865
+ this.animation.currentTime = secondsToMilliseconds(newTime);
8468
7866
  }
7867
+ /**
7868
+ * The playback speed of the animation.
7869
+ * 1 = normal speed, 2 = double speed, 0.5 = half speed.
7870
+ */
8469
7871
  get speed() {
8470
- const { resolved } = this;
8471
- if (!resolved)
8472
- return 1;
8473
- const { animation } = resolved;
8474
- return animation.playbackRate;
8475
- }
8476
- get finished() {
8477
- return this.resolved.animation.finished;
7872
+ return this.animation.playbackRate;
8478
7873
  }
8479
7874
  set speed(newSpeed) {
8480
- const { resolved } = this;
8481
- if (!resolved)
8482
- return;
8483
- const { animation } = resolved;
8484
- animation.playbackRate = newSpeed;
7875
+ // Allow backwards playback after finishing
7876
+ if (newSpeed < 0)
7877
+ this.finishedTime = null;
7878
+ this.animation.playbackRate = newSpeed;
8485
7879
  }
8486
7880
  get state() {
8487
- const { resolved } = this;
8488
- if (!resolved)
8489
- return "idle";
8490
- const { animation } = resolved;
8491
- return animation.playState;
7881
+ return this.finishedTime !== null
7882
+ ? "finished"
7883
+ : this.animation.playState;
8492
7884
  }
8493
7885
  get startTime() {
8494
- const { resolved } = this;
8495
- if (!resolved)
8496
- return null;
8497
- const { animation } = resolved;
8498
- // Coerce to number as TypeScript incorrectly types this
8499
- // as CSSNumberish
8500
- return animation.startTime;
7886
+ return Number(this.animation.startTime);
7887
+ }
7888
+ set startTime(newStartTime) {
7889
+ this.animation.startTime = newStartTime;
8501
7890
  }
8502
7891
  /**
8503
- * Replace the default DocumentTimeline with another AnimationTimeline.
8504
- * Currently used for scroll animations.
7892
+ * Attaches a timeline to the animation, for instance the `ScrollTimeline`.
8505
7893
  */
8506
- attachTimeline(timeline) {
8507
- if (!this._resolved) {
8508
- this.pendingTimeline = timeline;
7894
+ attachTimeline({ timeline, observe }) {
7895
+ if (this.allowFlatten) {
7896
+ this.animation.effect?.updateTiming({ easing: "linear" });
8509
7897
  }
8510
- else {
8511
- const { resolved } = this;
8512
- if (!resolved)
8513
- return noop;
8514
- const { animation } = resolved;
8515
- attachTimeline(animation, timeline);
7898
+ this.animation.onfinish = null;
7899
+ if (timeline && supportsScrollTimeline()) {
7900
+ this.animation.timeline = timeline;
7901
+ return noop;
8516
7902
  }
8517
- return noop;
8518
- }
8519
- play() {
8520
- if (this.isStopped)
8521
- return;
8522
- const { resolved } = this;
8523
- if (!resolved)
8524
- return;
8525
- const { animation } = resolved;
8526
- if (animation.playState === "finished") {
8527
- this.updateFinishedPromise();
7903
+ else {
7904
+ return observe(this);
8528
7905
  }
8529
- animation.play();
8530
7906
  }
8531
- pause() {
8532
- const { resolved } = this;
8533
- if (!resolved)
8534
- return;
8535
- const { animation } = resolved;
8536
- animation.pause();
7907
+ }
7908
+
7909
+ const unsupportedEasingFunctions = {
7910
+ anticipate,
7911
+ backInOut,
7912
+ circInOut,
7913
+ };
7914
+ function isUnsupportedEase(key) {
7915
+ return key in unsupportedEasingFunctions;
7916
+ }
7917
+ function replaceStringEasing(transition) {
7918
+ if (typeof transition.ease === "string" &&
7919
+ isUnsupportedEase(transition.ease)) {
7920
+ transition.ease = unsupportedEasingFunctions[transition.ease];
8537
7921
  }
8538
- stop() {
8539
- this.resolver.cancel();
8540
- this.isStopped = true;
8541
- if (this.state === "idle")
8542
- return;
8543
- this.resolveFinishedPromise();
8544
- this.updateFinishedPromise();
8545
- const { resolved } = this;
8546
- if (!resolved)
8547
- return;
8548
- const { animation, keyframes, duration, type, ease, times } = resolved;
8549
- if (animation.playState === "idle" ||
8550
- animation.playState === "finished") {
8551
- return;
8552
- }
7922
+ }
7923
+
7924
+ /**
7925
+ * 10ms is chosen here as it strikes a balance between smooth
7926
+ * results (more than one keyframe per frame at 60fps) and
7927
+ * keyframe quantity.
7928
+ */
7929
+ const sampleDelta = 10; //ms
7930
+ class NativeAnimationExtended extends NativeAnimation {
7931
+ constructor(options) {
8553
7932
  /**
8554
- * WAAPI doesn't natively have any interruption capabilities.
7933
+ * The base NativeAnimation function only supports a subset
7934
+ * of Motion easings, and WAAPI also only supports some
7935
+ * easing functions via string/cubic-bezier definitions.
8555
7936
  *
8556
- * Rather than read commited styles back out of the DOM, we can
8557
- * create a renderless JS animation and sample it twice to calculate
8558
- * its current value, "previous" value, and therefore allow
8559
- * Motion to calculate velocity for any subsequent animation.
7937
+ * This function replaces those unsupported easing functions
7938
+ * with a JS easing function. This will later get compiled
7939
+ * to a linear() easing function.
8560
7940
  */
8561
- if (this.time) {
8562
- const { motionValue, onUpdate, onComplete, element, ...options } = this.options;
8563
- const sampleAnimation = new MainThreadAnimation({
8564
- ...options,
8565
- keyframes,
8566
- duration,
8567
- type,
8568
- ease,
8569
- times,
8570
- isGenerator: true,
8571
- });
8572
- const sampleTime = secondsToMilliseconds(this.time);
8573
- motionValue.setWithVelocity(sampleAnimation.sample(sampleTime - sampleDelta).value, sampleAnimation.sample(sampleTime).value, sampleDelta);
7941
+ replaceStringEasing(options);
7942
+ /**
7943
+ * Ensure we replace the transition type with a generator function
7944
+ * before passing to WAAPI.
7945
+ *
7946
+ * TODO: Does this have a better home? It could be shared with
7947
+ * JSAnimation.
7948
+ */
7949
+ replaceTransitionType(options);
7950
+ super(options);
7951
+ if (options.startTime) {
7952
+ this.startTime = options.startTime;
8574
7953
  }
8575
- const { onStop } = this.options;
8576
- onStop && onStop();
8577
- this.cancel();
7954
+ this.options = options;
8578
7955
  }
8579
- complete() {
8580
- const { resolved } = this;
8581
- if (!resolved)
7956
+ /**
7957
+ * WAAPI doesn't natively have any interruption capabilities.
7958
+ *
7959
+ * Rather than read commited styles back out of the DOM, we can
7960
+ * create a renderless JS animation and sample it twice to calculate
7961
+ * its current value, "previous" value, and therefore allow
7962
+ * Motion to calculate velocity for any subsequent animation.
7963
+ */
7964
+ updateMotionValue(value) {
7965
+ const { motionValue, onUpdate, onComplete, element, ...options } = this.options;
7966
+ if (!motionValue)
8582
7967
  return;
8583
- resolved.animation.finish();
8584
- }
8585
- cancel() {
8586
- const { resolved } = this;
8587
- if (!resolved)
7968
+ if (value !== undefined) {
7969
+ motionValue.set(value);
8588
7970
  return;
8589
- resolved.animation.cancel();
8590
- }
8591
- static supports(options) {
8592
- const { motionValue, name, repeatDelay, repeatType, damping, type } = options;
8593
- if (!motionValue ||
8594
- !motionValue.owner ||
8595
- !(motionValue.owner.current instanceof HTMLElement)) {
8596
- return false;
8597
7971
  }
8598
- const { onUpdate, transformTemplate } = motionValue.owner.getProps();
8599
- return (supportsWaapi() &&
8600
- name &&
8601
- acceleratedValues.has(name) &&
8602
- (name !== "transform" || !transformTemplate) &&
8603
- /**
8604
- * If we're outputting values to onUpdate then we can't use WAAPI as there's
8605
- * no way to read the value from WAAPI every frame.
8606
- */
8607
- !onUpdate &&
8608
- !repeatDelay &&
8609
- repeatType !== "mirror" &&
8610
- damping !== 0 &&
8611
- type !== "inertia");
7972
+ const sampleAnimation = new JSAnimation({
7973
+ ...options,
7974
+ autoplay: false,
7975
+ });
7976
+ const sampleTime = secondsToMilliseconds(this.finishedTime ?? this.time);
7977
+ motionValue.setWithVelocity(sampleAnimation.sample(sampleTime - sampleDelta).value, sampleAnimation.sample(sampleTime).value, sampleDelta);
7978
+ sampleAnimation.stop();
8612
7979
  }
8613
7980
  }
8614
7981
 
8615
- const underDampedSpring = {
8616
- type: "spring",
8617
- stiffness: 500,
8618
- damping: 25,
8619
- restSpeed: 10,
8620
- };
8621
- const criticallyDampedSpring = (target) => ({
8622
- type: "spring",
8623
- stiffness: 550,
8624
- damping: target === 0 ? 2 * Math.sqrt(550) : 30,
8625
- restSpeed: 10,
8626
- });
8627
- const keyframesTransition = {
8628
- type: "keyframes",
8629
- duration: 0.8,
8630
- };
8631
7982
  /**
8632
- * Default easing curve is a slightly shallower version of
8633
- * the default browser easing curve.
7983
+ * Check if a value is animatable. Examples:
7984
+ *
7985
+ * ✅: 100, "100px", "#fff"
7986
+ * ❌: "block", "url(2.jpg)"
7987
+ * @param value
7988
+ *
7989
+ * @internal
8634
7990
  */
8635
- const ease = {
8636
- type: "keyframes",
8637
- ease: [0.25, 0.1, 0.35, 1],
8638
- duration: 0.3,
8639
- };
8640
- const getDefaultTransition = (valueKey, { keyframes }) => {
8641
- if (keyframes.length > 2) {
8642
- return keyframesTransition;
8643
- }
8644
- else if (transformProps.has(valueKey)) {
8645
- return valueKey.startsWith("scale")
8646
- ? criticallyDampedSpring(keyframes[1])
8647
- : underDampedSpring;
7991
+ const isAnimatable = (value, name) => {
7992
+ // If the list of keys tat might be non-animatable grows, replace with Set
7993
+ if (name === "zIndex")
7994
+ return false;
7995
+ // If it's a number or a keyframes array, we can animate it. We might at some point
7996
+ // need to do a deep isAnimatable check of keyframes, or let Popmotion handle this,
7997
+ // but for now lets leave it like this for performance reasons
7998
+ if (typeof value === "number" || Array.isArray(value))
7999
+ return true;
8000
+ if (typeof value === "string" && // It's animatable if we have a string
8001
+ (complex.test(value) || value === "0") && // And it contains numbers and/or colors
8002
+ !value.startsWith("url(") // Unless it starts with "url("
8003
+ ) {
8004
+ return true;
8648
8005
  }
8649
- return ease;
8006
+ return false;
8650
8007
  };
8651
8008
 
8009
+ function hasKeyframesChanged(keyframes) {
8010
+ const current = keyframes[0];
8011
+ if (keyframes.length === 1)
8012
+ return true;
8013
+ for (let i = 0; i < keyframes.length; i++) {
8014
+ if (keyframes[i] !== current)
8015
+ return true;
8016
+ }
8017
+ }
8018
+ function canAnimate(keyframes, name, type, velocity) {
8019
+ /**
8020
+ * Check if we're able to animate between the start and end keyframes,
8021
+ * and throw a warning if we're attempting to animate between one that's
8022
+ * animatable and another that isn't.
8023
+ */
8024
+ const originKeyframe = keyframes[0];
8025
+ if (originKeyframe === null)
8026
+ return false;
8027
+ /**
8028
+ * These aren't traditionally animatable but we do support them.
8029
+ * In future we could look into making this more generic or replacing
8030
+ * this function with mix() === mixImmediate
8031
+ */
8032
+ if (name === "display" || name === "visibility")
8033
+ return true;
8034
+ const targetKeyframe = keyframes[keyframes.length - 1];
8035
+ const isOriginAnimatable = isAnimatable(originKeyframe, name);
8036
+ const isTargetAnimatable = isAnimatable(targetKeyframe, name);
8037
+ warning(isOriginAnimatable === isTargetAnimatable, `You are trying to animate ${name} from "${originKeyframe}" to "${targetKeyframe}". ${originKeyframe} is not an animatable value - to enable this animation set ${originKeyframe} to a value animatable to ${targetKeyframe} via the \`style\` property.`);
8038
+ // Always skip if any of these are true
8039
+ if (!isOriginAnimatable || !isTargetAnimatable) {
8040
+ return false;
8041
+ }
8042
+ return (hasKeyframesChanged(keyframes) ||
8043
+ ((type === "spring" || isGenerator(type)) && velocity));
8044
+ }
8045
+
8652
8046
  /**
8653
- * Decide whether a transition is defined on a given Transition.
8654
- * This filters out orchestration options and returns true
8655
- * if any options are left.
8047
+ * A list of values that can be hardware-accelerated.
8656
8048
  */
8657
- function isTransitionDefined({ when, delay: _delay, delayChildren, staggerChildren, staggerDirection, repeat, repeatType, repeatDelay, from, elapsed, ...transition }) {
8658
- return !!Object.keys(transition).length;
8659
- }
8660
-
8661
- function getValueTransition(transition, key) {
8662
- return (transition?.[key] ??
8663
- transition?.["default"] ??
8664
- transition);
8049
+ const acceleratedValues = new Set([
8050
+ "opacity",
8051
+ "clipPath",
8052
+ "filter",
8053
+ "transform",
8054
+ // TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved
8055
+ // or until we implement support for linear() easing.
8056
+ // "background-color"
8057
+ ]);
8058
+ const supportsWaapi = /*@__PURE__*/ memo(() => Object.hasOwnProperty.call(Element.prototype, "animate"));
8059
+ function supportsBrowserAnimation(options) {
8060
+ const { motionValue, name, repeatDelay, repeatType, damping, type } = options;
8061
+ if (!motionValue ||
8062
+ !motionValue.owner ||
8063
+ !(motionValue.owner.current instanceof HTMLElement)) {
8064
+ return false;
8065
+ }
8066
+ const { onUpdate, transformTemplate } = motionValue.owner.getProps();
8067
+ return (supportsWaapi() &&
8068
+ name &&
8069
+ acceleratedValues.has(name) &&
8070
+ (name !== "transform" || !transformTemplate) &&
8071
+ /**
8072
+ * If we're outputting values to onUpdate then we can't use WAAPI as there's
8073
+ * no way to read the value from WAAPI every frame.
8074
+ */
8075
+ !onUpdate &&
8076
+ !repeatDelay &&
8077
+ repeatType !== "mirror" &&
8078
+ damping !== 0 &&
8079
+ type !== "inertia");
8665
8080
  }
8666
8081
 
8667
- const supportsScrollTimeline = /* @__PURE__ */ memo(() => window.ScrollTimeline !== undefined);
8668
-
8669
- class GroupAnimation {
8670
- constructor(animations) {
8671
- // Bound to accomodate common `return animation.stop` pattern
8672
- this.stop = () => this.runAll("stop");
8673
- this.animations = animations.filter(Boolean);
8082
+ /**
8083
+ * Maximum time allowed between an animation being created and it being
8084
+ * resolved for us to use the latter as the start time.
8085
+ *
8086
+ * This is to ensure that while we prefer to "start" an animation as soon
8087
+ * as it's triggered, we also want to avoid a visual jump if there's a big delay
8088
+ * between these two moments.
8089
+ */
8090
+ const MAX_RESOLVE_DELAY = 40;
8091
+ class AsyncMotionValueAnimation extends WithPromise {
8092
+ constructor({ autoplay = true, delay = 0, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", keyframes, name, motionValue, element, ...options }) {
8093
+ super();
8094
+ /**
8095
+ * Bound to support return animation.stop pattern
8096
+ */
8097
+ this.stop = () => {
8098
+ if (this._animation) {
8099
+ this._animation.stop();
8100
+ this.stopTimeline?.();
8101
+ }
8102
+ else {
8103
+ this.keyframeResolver?.cancel();
8104
+ }
8105
+ };
8106
+ this.createdAt = time.now();
8107
+ const optionsWithDefaults = {
8108
+ autoplay,
8109
+ delay,
8110
+ type,
8111
+ repeat,
8112
+ repeatDelay,
8113
+ repeatType,
8114
+ name,
8115
+ motionValue,
8116
+ element,
8117
+ ...options,
8118
+ };
8119
+ const KeyframeResolver$1 = element?.KeyframeResolver || KeyframeResolver;
8120
+ this.keyframeResolver = new KeyframeResolver$1(keyframes, (resolvedKeyframes, finalKeyframe, forced) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe, optionsWithDefaults, !forced), name, motionValue, element);
8121
+ this.keyframeResolver?.scheduleResolve();
8122
+ }
8123
+ onKeyframesResolved(keyframes, finalKeyframe, options, sync) {
8124
+ this.keyframeResolver = undefined;
8125
+ const { name, type, velocity, delay, isHandoff, onUpdate } = options;
8126
+ this.resolvedAt = time.now();
8127
+ /**
8128
+ * If we can't animate this value with the resolved keyframes
8129
+ * then we should complete it immediately.
8130
+ */
8131
+ if (!canAnimate(keyframes, name, type, velocity)) {
8132
+ if (MotionGlobalConfig.instantAnimations || !delay) {
8133
+ onUpdate?.(getFinalKeyframe(keyframes, options, finalKeyframe));
8134
+ }
8135
+ keyframes[0] = keyframes[keyframes.length - 1];
8136
+ options.duration = 0;
8137
+ options.repeat = 0;
8138
+ }
8139
+ /**
8140
+ * Resolve startTime for the animation.
8141
+ *
8142
+ * This method uses the createdAt and resolvedAt to calculate the
8143
+ * animation startTime. *Ideally*, we would use the createdAt time as t=0
8144
+ * as the following frame would then be the first frame of the animation in
8145
+ * progress, which would feel snappier.
8146
+ *
8147
+ * However, if there's a delay (main thread work) between the creation of
8148
+ * the animation and the first commited frame, we prefer to use resolvedAt
8149
+ * to avoid a sudden jump into the animation.
8150
+ */
8151
+ const startTime = sync
8152
+ ? !this.resolvedAt
8153
+ ? this.createdAt
8154
+ : this.resolvedAt - this.createdAt > MAX_RESOLVE_DELAY
8155
+ ? this.resolvedAt
8156
+ : this.createdAt
8157
+ : undefined;
8158
+ const resolvedOptions = {
8159
+ startTime,
8160
+ finalKeyframe,
8161
+ ...options,
8162
+ keyframes,
8163
+ };
8164
+ /**
8165
+ * Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via
8166
+ * WAAPI. Therefore, this animation must be JS to ensure it runs "under" the
8167
+ * optimised animation.
8168
+ */
8169
+ const animation = !isHandoff && supportsBrowserAnimation(resolvedOptions)
8170
+ ? new NativeAnimationExtended({
8171
+ ...resolvedOptions,
8172
+ element: resolvedOptions.motionValue.owner.current,
8173
+ })
8174
+ : new JSAnimation(resolvedOptions);
8175
+ animation.finished.then(() => this.notifyFinished()).catch(noop);
8176
+ if (this.pendingTimeline) {
8177
+ this.stopTimeline = animation.attachTimeline(this.pendingTimeline);
8178
+ this.pendingTimeline = undefined;
8179
+ }
8180
+ this._animation = animation;
8674
8181
  }
8675
8182
  get finished() {
8676
- return Promise.all(this.animations.map((animation) => animation.finished));
8183
+ if (!this._animation) {
8184
+ return this._finished;
8185
+ }
8186
+ else {
8187
+ return this.animation.finished;
8188
+ }
8677
8189
  }
8678
- /**
8679
- * TODO: Filter out cancelled or stopped animations before returning
8680
- */
8681
- getAll(propName) {
8682
- return this.animations[0][propName];
8190
+ then(onResolve, _onReject) {
8191
+ return this.finished.finally(onResolve).then(() => { });
8683
8192
  }
8684
- setAll(propName, newValue) {
8685
- for (let i = 0; i < this.animations.length; i++) {
8686
- this.animations[i][propName] = newValue;
8193
+ get animation() {
8194
+ if (!this._animation) {
8195
+ flushKeyframeResolvers();
8687
8196
  }
8197
+ return this._animation;
8688
8198
  }
8689
- attachTimeline(timeline, fallback) {
8690
- const subscriptions = this.animations.map((animation) => {
8691
- if (supportsScrollTimeline() && animation.attachTimeline) {
8692
- return animation.attachTimeline(timeline);
8693
- }
8694
- else if (typeof fallback === "function") {
8695
- return fallback(animation);
8696
- }
8697
- });
8698
- return () => {
8699
- subscriptions.forEach((cancel, i) => {
8700
- cancel && cancel();
8701
- this.animations[i].stop();
8702
- });
8703
- };
8199
+ get duration() {
8200
+ return this.animation.duration;
8704
8201
  }
8705
8202
  get time() {
8706
- return this.getAll("time");
8203
+ return this.animation.time;
8707
8204
  }
8708
- set time(time) {
8709
- this.setAll("time", time);
8205
+ set time(newTime) {
8206
+ this.animation.time = newTime;
8710
8207
  }
8711
8208
  get speed() {
8712
- return this.getAll("speed");
8209
+ return this.animation.speed;
8713
8210
  }
8714
- set speed(speed) {
8715
- this.setAll("speed", speed);
8211
+ get state() {
8212
+ return this.animation.state;
8213
+ }
8214
+ set speed(newSpeed) {
8215
+ this.animation.speed = newSpeed;
8716
8216
  }
8717
8217
  get startTime() {
8718
- return this.getAll("startTime");
8218
+ return this.animation.startTime;
8719
8219
  }
8720
- get duration() {
8721
- let max = 0;
8722
- for (let i = 0; i < this.animations.length; i++) {
8723
- max = Math.max(max, this.animations[i].duration);
8220
+ attachTimeline(timeline) {
8221
+ if (this._animation) {
8222
+ this.stopTimeline = this.animation.attachTimeline(timeline);
8724
8223
  }
8725
- return max;
8726
- }
8727
- runAll(methodName) {
8728
- this.animations.forEach((controls) => controls[methodName]());
8729
- }
8730
- flatten() {
8731
- this.runAll("flatten");
8224
+ else {
8225
+ this.pendingTimeline = timeline;
8226
+ }
8227
+ return () => this.stop();
8732
8228
  }
8733
8229
  play() {
8734
- this.runAll("play");
8230
+ this.animation.play();
8735
8231
  }
8736
8232
  pause() {
8737
- this.runAll("pause");
8738
- }
8739
- cancel() {
8740
- this.runAll("cancel");
8233
+ this.animation.pause();
8741
8234
  }
8742
8235
  complete() {
8743
- this.runAll("complete");
8236
+ this.animation.complete();
8744
8237
  }
8745
- }
8746
-
8747
- class GroupAnimationWithThen extends GroupAnimation {
8748
- then(onResolve, _onReject) {
8749
- return this.finished.finally(onResolve).then(() => { });
8238
+ cancel() {
8239
+ this.animation.cancel();
8750
8240
  }
8751
8241
  }
8752
8242
 
@@ -8764,7 +8254,7 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
8764
8254
  */
8765
8255
  let { elapsed = 0 } = transition;
8766
8256
  elapsed = elapsed - secondsToMilliseconds(delay);
8767
- let options = {
8257
+ const options = {
8768
8258
  keyframes: Array.isArray(target) ? target : [null, target],
8769
8259
  ease: "easeOut",
8770
8260
  velocity: value.getVelocity(),
@@ -8787,22 +8277,18 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
8787
8277
  * unique transition settings for this value.
8788
8278
  */
8789
8279
  if (!isTransitionDefined(valueTransition)) {
8790
- options = {
8791
- ...options,
8792
- ...getDefaultTransition(name, options),
8793
- };
8280
+ Object.assign(options, getDefaultTransition(name, options));
8794
8281
  }
8795
8282
  /**
8796
8283
  * Both WAAPI and our internal animation functions use durations
8797
8284
  * as defined by milliseconds, while our external API defines them
8798
8285
  * as seconds.
8799
8286
  */
8800
- if (options.duration) {
8801
- options.duration = secondsToMilliseconds(options.duration);
8802
- }
8803
- if (options.repeatDelay) {
8804
- options.repeatDelay = secondsToMilliseconds(options.repeatDelay);
8805
- }
8287
+ options.duration && (options.duration = secondsToMilliseconds(options.duration));
8288
+ options.repeatDelay && (options.repeatDelay = secondsToMilliseconds(options.repeatDelay));
8289
+ /**
8290
+ * Support deprecated way to set initial value. Prefer keyframe syntax.
8291
+ */
8806
8292
  if (options.from !== undefined) {
8807
8293
  options.keyframes[0] = options.from;
8808
8294
  }
@@ -8814,6 +8300,12 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
8814
8300
  shouldSkip = true;
8815
8301
  }
8816
8302
  }
8303
+ if (MotionGlobalConfig.instantAnimations ||
8304
+ MotionGlobalConfig.skipAnimations) {
8305
+ shouldSkip = true;
8306
+ options.duration = 0;
8307
+ options.delay = 0;
8308
+ }
8817
8309
  /**
8818
8310
  * If the transition type or easing has been explicitly set by the user
8819
8311
  * then we don't want to allow flattening the animation.
@@ -8825,30 +8317,28 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
8825
8317
  * this early check prevents the need to create an animation at all.
8826
8318
  */
8827
8319
  if (shouldSkip && !isHandoff && value.get() !== undefined) {
8828
- const finalKeyframe = getFinalKeyframe(options.keyframes, valueTransition);
8320
+ const finalKeyframe = getFinalKeyframe$1(options.keyframes, valueTransition);
8829
8321
  if (finalKeyframe !== undefined) {
8830
8322
  frame.update(() => {
8831
8323
  options.onUpdate(finalKeyframe);
8832
8324
  options.onComplete();
8833
8325
  });
8834
- // We still want to return some animation controls here rather
8835
- // than returning undefined
8836
- return new GroupAnimationWithThen([]);
8326
+ return;
8837
8327
  }
8838
8328
  }
8839
- /**
8840
- * Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via
8841
- * WAAPI. Therefore, this animation must be JS to ensure it runs "under" the
8842
- * optimised animation.
8843
- */
8844
- if (!isHandoff && AcceleratedAnimation.supports(options)) {
8845
- return new AcceleratedAnimation(options);
8846
- }
8847
- else {
8848
- return new MainThreadAnimation(options);
8849
- }
8329
+ return new AsyncMotionValueAnimation(options);
8850
8330
  };
8851
8331
 
8332
+ const positionalKeys = new Set([
8333
+ "width",
8334
+ "height",
8335
+ "top",
8336
+ "left",
8337
+ "right",
8338
+ "bottom",
8339
+ ...transformPropOrder,
8340
+ ]);
8341
+
8852
8342
  /**
8853
8343
  * Decide whether we should block this animation. Previously, we achieved this
8854
8344
  * just by checking whether the key was listed in protectedKeys, but this
@@ -8880,6 +8370,17 @@ function animateTarget(visualElement, targetAndTransition, { delay = 0, transiti
8880
8370
  delay,
8881
8371
  ...getValueTransition(transition || {}, key),
8882
8372
  };
8373
+ /**
8374
+ * If the value is already at the defined target, skip the animation.
8375
+ */
8376
+ const currentValue = value.get();
8377
+ if (currentValue !== undefined &&
8378
+ !value.isAnimating &&
8379
+ !Array.isArray(valueTarget) &&
8380
+ valueTarget === currentValue &&
8381
+ !valueTransition.velocity) {
8382
+ continue;
8383
+ }
8883
8384
  /**
8884
8385
  * If this is the first time a value is being animated, check
8885
8386
  * to see if we're handling off from an existing animation.
@@ -10832,7 +10333,7 @@ function delay(callback, timeout) {
10832
10333
  callback(elapsed - timeout);
10833
10334
  }
10834
10335
  };
10835
- frame.read(checkElapsed, true);
10336
+ frame.setup(checkElapsed, true);
10836
10337
  return () => cancelFrame(checkElapsed);
10837
10338
  }
10838
10339
 
@@ -12126,9 +11627,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
12126
11627
  }
12127
11628
  setAnimationOrigin(delta, hasOnlyRelativeTargetChanged = false) {
12128
11629
  const snapshot = this.snapshot;
12129
- const snapshotLatestValues = snapshot
12130
- ? snapshot.latestValues
12131
- : {};
11630
+ const snapshotLatestValues = snapshot ? snapshot.latestValues : {};
12132
11631
  const mixedValues = { ...this.latestValues };
12133
11632
  const targetDelta = createDelta();
12134
11633
  if (!this.relativeParent ||
@@ -13196,15 +12695,6 @@ function initPrefersReducedMotion() {
13196
12695
  }
13197
12696
  }
13198
12697
 
13199
- /**
13200
- * A list of all ValueTypes
13201
- */
13202
- const valueTypes = [...dimensionValueTypes, color, complex];
13203
- /**
13204
- * Tests a value against the list of ValueTypes
13205
- */
13206
- const findValueType = (v) => valueTypes.find(testValueType(v));
13207
-
13208
12698
  const visualElementStore = new WeakMap();
13209
12699
 
13210
12700
  function updateMotionValuesFromProps(element, next, prev) {
@@ -13222,7 +12712,7 @@ function updateMotionValuesFromProps(element, next, prev) {
13222
12712
  * and warn against mismatches.
13223
12713
  */
13224
12714
  if (process.env.NODE_ENV === "development") {
13225
- warnOnce(nextValue.version === "12.7.4", `Attempting to mix Motion versions ${nextValue.version} with 12.7.4 may not work as expected.`);
12715
+ warnOnce(nextValue.version === "12.9.0", `Attempting to mix Motion versions ${nextValue.version} with 12.9.0 may not work as expected.`);
13226
12716
  }
13227
12717
  }
13228
12718
  else if (isMotionValue(prevValue)) {
@@ -13261,6 +12751,108 @@ function updateMotionValuesFromProps(element, next, prev) {
13261
12751
  return next;
13262
12752
  }
13263
12753
 
12754
+ /**
12755
+ * Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1"
12756
+ */
12757
+ const isNumericalString = (v) => /^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(v);
12758
+
12759
+ /**
12760
+ * Check if the value is a zero value string like "0px" or "0%"
12761
+ */
12762
+ const isZeroValueString = (v) => /^0[^.\s]+$/u.test(v);
12763
+
12764
+ /**
12765
+ * ValueType for "auto"
12766
+ */
12767
+ const auto = {
12768
+ test: (v) => v === "auto",
12769
+ parse: (v) => v,
12770
+ };
12771
+
12772
+ /**
12773
+ * Tests a provided value against a ValueType
12774
+ */
12775
+ const testValueType = (v) => (type) => type.test(v);
12776
+
12777
+ /**
12778
+ * A list of value types commonly used for dimensions
12779
+ */
12780
+ const dimensionValueTypes = [number, px, percent, degrees, vw, vh, auto];
12781
+ /**
12782
+ * Tests a dimensional value against the list of dimension ValueTypes
12783
+ */
12784
+ const findDimensionValueType = (v) => dimensionValueTypes.find(testValueType(v));
12785
+
12786
+ /**
12787
+ * A list of all ValueTypes
12788
+ */
12789
+ const valueTypes = [...dimensionValueTypes, color, complex];
12790
+ /**
12791
+ * Tests a value against the list of ValueTypes
12792
+ */
12793
+ const findValueType = (v) => valueTypes.find(testValueType(v));
12794
+
12795
+ /**
12796
+ * Properties that should default to 1 or 100%
12797
+ */
12798
+ const maxDefaults = new Set(["brightness", "contrast", "saturate", "opacity"]);
12799
+ function applyDefaultFilter(v) {
12800
+ const [name, value] = v.slice(0, -1).split("(");
12801
+ if (name === "drop-shadow")
12802
+ return v;
12803
+ const [number] = value.match(floatRegex) || [];
12804
+ if (!number)
12805
+ return v;
12806
+ const unit = value.replace(number, "");
12807
+ let defaultValue = maxDefaults.has(name) ? 1 : 0;
12808
+ if (number !== value)
12809
+ defaultValue *= 100;
12810
+ return name + "(" + defaultValue + unit + ")";
12811
+ }
12812
+ const functionRegex = /\b([a-z-]*)\(.*?\)/gu;
12813
+ const filter = {
12814
+ ...complex,
12815
+ getAnimatableNone: (v) => {
12816
+ const functions = v.match(functionRegex);
12817
+ return functions ? functions.map(applyDefaultFilter).join(" ") : v;
12818
+ },
12819
+ };
12820
+
12821
+ /**
12822
+ * A map of default value types for common values
12823
+ */
12824
+ const defaultValueTypes = {
12825
+ ...numberValueTypes,
12826
+ // Color props
12827
+ color,
12828
+ backgroundColor: color,
12829
+ outlineColor: color,
12830
+ fill: color,
12831
+ stroke: color,
12832
+ // Border props
12833
+ borderColor: color,
12834
+ borderTopColor: color,
12835
+ borderRightColor: color,
12836
+ borderBottomColor: color,
12837
+ borderLeftColor: color,
12838
+ filter,
12839
+ WebkitFilter: filter,
12840
+ };
12841
+ /**
12842
+ * Gets the default ValueType for the provided value key
12843
+ */
12844
+ const getDefaultValueType = (key) => defaultValueTypes[key];
12845
+
12846
+ function getAnimatableNone(key, value) {
12847
+ let defaultValueType = getDefaultValueType(key);
12848
+ if (defaultValueType !== filter)
12849
+ defaultValueType = complex;
12850
+ // If value is not recognised as animatable, ie "none", create an animatable version origin based on the target
12851
+ return defaultValueType.getAnimatableNone
12852
+ ? defaultValueType.getAnimatableNone(value)
12853
+ : undefined;
12854
+ }
12855
+
13264
12856
  const propEventHandlers = [
13265
12857
  "AnimationStart",
13266
12858
  "AnimationComplete",
@@ -13355,8 +12947,7 @@ class VisualElement {
13355
12947
  frame.render(this.render, false, true);
13356
12948
  }
13357
12949
  };
13358
- const { latestValues, renderState, onUpdate } = visualState;
13359
- this.onUpdate = onUpdate;
12950
+ const { latestValues, renderState } = visualState;
13360
12951
  this.latestValues = latestValues;
13361
12952
  this.baseTarget = { ...latestValues };
13362
12953
  this.initialValues = props.initial ? { ...latestValues } : {};
@@ -13558,7 +13149,6 @@ class VisualElement {
13558
13149
  if (this.handleChildMotionValue) {
13559
13150
  this.handleChildMotionValue();
13560
13151
  }
13561
- this.onUpdate && this.onUpdate(this);
13562
13152
  }
13563
13153
  getProps() {
13564
13154
  return this.props;
@@ -13718,6 +13308,202 @@ class VisualElement {
13718
13308
  }
13719
13309
  }
13720
13310
 
13311
+ /**
13312
+ * Parse Framer's special CSS variable format into a CSS token and a fallback.
13313
+ *
13314
+ * ```
13315
+ * `var(--foo, #fff)` => [`--foo`, '#fff']
13316
+ * ```
13317
+ *
13318
+ * @param current
13319
+ */
13320
+ const splitCSSVariableRegex =
13321
+ // eslint-disable-next-line redos-detector/no-unsafe-regex -- false positive, as it can match a lot of words
13322
+ /^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;
13323
+ function parseCSSVariable(current) {
13324
+ const match = splitCSSVariableRegex.exec(current);
13325
+ if (!match)
13326
+ return [,];
13327
+ const [, token1, token2, fallback] = match;
13328
+ return [`--${token1 ?? token2}`, fallback];
13329
+ }
13330
+ const maxDepth = 4;
13331
+ function getVariableValue(current, element, depth = 1) {
13332
+ invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`);
13333
+ const [token, fallback] = parseCSSVariable(current);
13334
+ // No CSS variable detected
13335
+ if (!token)
13336
+ return;
13337
+ // Attempt to read this CSS variable off the element
13338
+ const resolved = window.getComputedStyle(element).getPropertyValue(token);
13339
+ if (resolved) {
13340
+ const trimmed = resolved.trim();
13341
+ return isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed;
13342
+ }
13343
+ return isCSSVariableToken(fallback)
13344
+ ? getVariableValue(fallback, element, depth + 1)
13345
+ : fallback;
13346
+ }
13347
+
13348
+ function isNone(value) {
13349
+ if (typeof value === "number") {
13350
+ return value === 0;
13351
+ }
13352
+ else if (value !== null) {
13353
+ return value === "none" || value === "0" || isZeroValueString(value);
13354
+ }
13355
+ else {
13356
+ return true;
13357
+ }
13358
+ }
13359
+
13360
+ /**
13361
+ * If we encounter keyframes like "none" or "0" and we also have keyframes like
13362
+ * "#fff" or "200px 200px" we want to find a keyframe to serve as a template for
13363
+ * the "none" keyframes. In this case "#fff" or "200px 200px" - then these get turned into
13364
+ * zero equivalents, i.e. "#fff0" or "0px 0px".
13365
+ */
13366
+ const invalidTemplates = new Set(["auto", "none", "0"]);
13367
+ function makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name) {
13368
+ let i = 0;
13369
+ let animatableTemplate = undefined;
13370
+ while (i < unresolvedKeyframes.length && !animatableTemplate) {
13371
+ const keyframe = unresolvedKeyframes[i];
13372
+ if (typeof keyframe === "string" &&
13373
+ !invalidTemplates.has(keyframe) &&
13374
+ analyseComplexValue(keyframe).values.length) {
13375
+ animatableTemplate = unresolvedKeyframes[i];
13376
+ }
13377
+ i++;
13378
+ }
13379
+ if (animatableTemplate && name) {
13380
+ for (const noneIndex of noneKeyframeIndexes) {
13381
+ unresolvedKeyframes[noneIndex] = getAnimatableNone(name, animatableTemplate);
13382
+ }
13383
+ }
13384
+ }
13385
+
13386
+ class DOMKeyframesResolver extends KeyframeResolver {
13387
+ constructor(unresolvedKeyframes, onComplete, name, motionValue, element) {
13388
+ super(unresolvedKeyframes, onComplete, name, motionValue, element, true);
13389
+ }
13390
+ readKeyframes() {
13391
+ const { unresolvedKeyframes, element, name } = this;
13392
+ if (!element || !element.current)
13393
+ return;
13394
+ super.readKeyframes();
13395
+ /**
13396
+ * If any keyframe is a CSS variable, we need to find its value by sampling the element
13397
+ */
13398
+ for (let i = 0; i < unresolvedKeyframes.length; i++) {
13399
+ let keyframe = unresolvedKeyframes[i];
13400
+ if (typeof keyframe === "string") {
13401
+ keyframe = keyframe.trim();
13402
+ if (isCSSVariableToken(keyframe)) {
13403
+ const resolved = getVariableValue(keyframe, element.current);
13404
+ if (resolved !== undefined) {
13405
+ unresolvedKeyframes[i] = resolved;
13406
+ }
13407
+ if (i === unresolvedKeyframes.length - 1) {
13408
+ this.finalKeyframe = keyframe;
13409
+ }
13410
+ }
13411
+ }
13412
+ }
13413
+ /**
13414
+ * Resolve "none" values. We do this potentially twice - once before and once after measuring keyframes.
13415
+ * This could be seen as inefficient but it's a trade-off to avoid measurements in more situations, which
13416
+ * have a far bigger performance impact.
13417
+ */
13418
+ this.resolveNoneKeyframes();
13419
+ /**
13420
+ * Check to see if unit type has changed. If so schedule jobs that will
13421
+ * temporarily set styles to the destination keyframes.
13422
+ * Skip if we have more than two keyframes or this isn't a positional value.
13423
+ * TODO: We can throw if there are multiple keyframes and the value type changes.
13424
+ */
13425
+ if (!positionalKeys.has(name) || unresolvedKeyframes.length !== 2) {
13426
+ return;
13427
+ }
13428
+ const [origin, target] = unresolvedKeyframes;
13429
+ const originType = findDimensionValueType(origin);
13430
+ const targetType = findDimensionValueType(target);
13431
+ /**
13432
+ * Either we don't recognise these value types or we can animate between them.
13433
+ */
13434
+ if (originType === targetType)
13435
+ return;
13436
+ /**
13437
+ * If both values are numbers or pixels, we can animate between them by
13438
+ * converting them to numbers.
13439
+ */
13440
+ if (isNumOrPxType(originType) && isNumOrPxType(targetType)) {
13441
+ for (let i = 0; i < unresolvedKeyframes.length; i++) {
13442
+ const value = unresolvedKeyframes[i];
13443
+ if (typeof value === "string") {
13444
+ unresolvedKeyframes[i] = parseFloat(value);
13445
+ }
13446
+ }
13447
+ }
13448
+ else {
13449
+ /**
13450
+ * Else, the only way to resolve this is by measuring the element.
13451
+ */
13452
+ this.needsMeasurement = true;
13453
+ }
13454
+ }
13455
+ resolveNoneKeyframes() {
13456
+ const { unresolvedKeyframes, name } = this;
13457
+ const noneKeyframeIndexes = [];
13458
+ for (let i = 0; i < unresolvedKeyframes.length; i++) {
13459
+ if (unresolvedKeyframes[i] === null ||
13460
+ isNone(unresolvedKeyframes[i])) {
13461
+ noneKeyframeIndexes.push(i);
13462
+ }
13463
+ }
13464
+ if (noneKeyframeIndexes.length) {
13465
+ makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name);
13466
+ }
13467
+ }
13468
+ measureInitialState() {
13469
+ const { element, unresolvedKeyframes, name } = this;
13470
+ if (!element || !element.current)
13471
+ return;
13472
+ if (name === "height") {
13473
+ this.suspendedScrollY = window.pageYOffset;
13474
+ }
13475
+ this.measuredOrigin = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
13476
+ unresolvedKeyframes[0] = this.measuredOrigin;
13477
+ // Set final key frame to measure after next render
13478
+ const measureKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
13479
+ if (measureKeyframe !== undefined) {
13480
+ element.getValue(name, measureKeyframe).jump(measureKeyframe, false);
13481
+ }
13482
+ }
13483
+ measureEndState() {
13484
+ const { element, name, unresolvedKeyframes } = this;
13485
+ if (!element || !element.current)
13486
+ return;
13487
+ const value = element.getValue(name);
13488
+ value && value.jump(this.measuredOrigin, false);
13489
+ const finalKeyframeIndex = unresolvedKeyframes.length - 1;
13490
+ const finalKeyframe = unresolvedKeyframes[finalKeyframeIndex];
13491
+ unresolvedKeyframes[finalKeyframeIndex] = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
13492
+ if (finalKeyframe !== null && this.finalKeyframe === undefined) {
13493
+ this.finalKeyframe = finalKeyframe;
13494
+ }
13495
+ // If we removed transform values, reapply them before the next render
13496
+ if (this.removedTransforms?.length) {
13497
+ this.removedTransforms.forEach(([unsetTransformName, unsetTransformValue]) => {
13498
+ element
13499
+ .getValue(unsetTransformName)
13500
+ .set(unsetTransformValue);
13501
+ });
13502
+ }
13503
+ this.resolveNoneKeyframes();
13504
+ }
13505
+ }
13506
+
13721
13507
  class DOMVisualElement extends VisualElement {
13722
13508
  constructor() {
13723
13509
  super(...arguments);
@@ -13756,6 +13542,14 @@ class DOMVisualElement extends VisualElement {
13756
13542
  }
13757
13543
  }
13758
13544
 
13545
+ function renderHTML(element, { style, vars }, styleProp, projection) {
13546
+ Object.assign(element.style, style, projection && projection.getProjectionStyles(styleProp));
13547
+ // Loop over any CSS variables and assign those.
13548
+ for (const key in vars) {
13549
+ element.style.setProperty(key, vars[key]);
13550
+ }
13551
+ }
13552
+
13759
13553
  function getComputedStyle$1(element) {
13760
13554
  return window.getComputedStyle(element);
13761
13555
  }
@@ -13788,17 +13582,48 @@ class HTMLVisualElement extends DOMVisualElement {
13788
13582
  }
13789
13583
  }
13790
13584
 
13585
+ /**
13586
+ * A set of attribute names that are always read/written as camel case.
13587
+ */
13588
+ const camelCaseAttributes = new Set([
13589
+ "baseFrequency",
13590
+ "diffuseConstant",
13591
+ "kernelMatrix",
13592
+ "kernelUnitLength",
13593
+ "keySplines",
13594
+ "keyTimes",
13595
+ "limitingConeAngle",
13596
+ "markerHeight",
13597
+ "markerWidth",
13598
+ "numOctaves",
13599
+ "targetX",
13600
+ "targetY",
13601
+ "surfaceScale",
13602
+ "specularConstant",
13603
+ "specularExponent",
13604
+ "stdDeviation",
13605
+ "tableValues",
13606
+ "viewBox",
13607
+ "gradientTransform",
13608
+ "pathLength",
13609
+ "startOffset",
13610
+ "textLength",
13611
+ "lengthAdjust",
13612
+ ]);
13613
+
13614
+ function renderSVG(element, renderState, _styleProp, projection) {
13615
+ renderHTML(element, renderState, undefined, projection);
13616
+ for (const key in renderState.attrs) {
13617
+ element.setAttribute(!camelCaseAttributes.has(key) ? camelToDash(key) : key, renderState.attrs[key]);
13618
+ }
13619
+ }
13620
+
13791
13621
  class SVGVisualElement extends DOMVisualElement {
13792
13622
  constructor() {
13793
13623
  super(...arguments);
13794
13624
  this.type = "svg";
13795
13625
  this.isSVGTag = false;
13796
13626
  this.measureInstanceViewportBox = createBox;
13797
- this.updateDimensions = () => {
13798
- if (this.current && !this.renderState.dimensions) {
13799
- updateSVGDimensions(this.current, this.renderState);
13800
- }
13801
- };
13802
13627
  }
13803
13628
  getBaseTargetFromProps(props, key) {
13804
13629
  return props[key];
@@ -13814,11 +13639,6 @@ class SVGVisualElement extends DOMVisualElement {
13814
13639
  scrapeMotionValuesFromProps(props, prevProps, visualElement) {
13815
13640
  return scrapeMotionValuesFromProps(props, prevProps, visualElement);
13816
13641
  }
13817
- onBindTransform() {
13818
- if (this.current && !this.renderState.dimensions) {
13819
- frame.postRender(this.updateDimensions);
13820
- }
13821
- }
13822
13642
  build(renderState, latestValues, props) {
13823
13643
  buildSVGAttrs(renderState, latestValues, this.isSVGTag, props.transformTemplate);
13824
13644
  }
@@ -34145,20 +33965,20 @@ var initStripe = function initStripe(maybeStripe, args, startTime) {
34145
33965
  return stripe;
34146
33966
  }; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
34147
33967
 
34148
- var stripePromise$2;
33968
+ var stripePromise;
34149
33969
  var loadCalled = false;
34150
33970
 
34151
33971
  var getStripePromise = function getStripePromise() {
34152
- if (stripePromise$2) {
34153
- return stripePromise$2;
33972
+ if (stripePromise) {
33973
+ return stripePromise;
34154
33974
  }
34155
33975
 
34156
- stripePromise$2 = loadScript(null)["catch"](function (error) {
33976
+ stripePromise = loadScript(null)["catch"](function (error) {
34157
33977
  // clear cache on error
34158
- stripePromise$2 = null;
33978
+ stripePromise = null;
34159
33979
  return Promise.reject(error);
34160
33980
  });
34161
- return stripePromise$2;
33981
+ return stripePromise;
34162
33982
  }; // Execute our own script injection after a tick to give users time to do their
34163
33983
  // own script injection.
34164
33984
 
@@ -34228,9 +34048,8 @@ const CheckoutForm$1 = ({ onSuccess, onError, children, setSubmitting, }) => {
34228
34048
  };
34229
34049
  var CheckoutForm$2 = React.memo(CheckoutForm$1);
34230
34050
 
34231
- const publicStripeKey = "pk_live_51QvliXK5wvEuxX36GWLFgMtUrG2cGIjpW0eXoqVzjEr8S0PdGzAp4ydQa6ssxVW9u0zaLajod93YZnQIU5C8cgqp00Bb64X60b";
34232
- const stripePromise = loadStripe(publicStripeKey);
34233
- function PaymentElement({ paymentSecret, checkoutAppearance, locale, fonts, onSuccess, onError, children, setSubmitting, }) {
34051
+ function PaymentElement({ paymentSecret, publicKey, checkoutAppearance, locale, fonts, onSuccess, onError, children, setSubmitting, }) {
34052
+ const stripePromise = loadStripe(publicKey !== null && publicKey !== void 0 ? publicKey : "");
34234
34053
  const options = {
34235
34054
  locale: locale !== null && locale !== void 0 ? locale : "en",
34236
34055
  appearance: checkoutAppearance,
@@ -34242,7 +34061,7 @@ function PaymentElement({ paymentSecret, checkoutAppearance, locale, fonts, onSu
34242
34061
  }
34243
34062
  var PaymentElement$1 = React.memo(PaymentElement);
34244
34063
 
34245
- function PaymentForm({ paymentSecret, onSuccess, onError, onBack, onDoubleBack, contactEmail, shippingAddress, shippingName, shippingPrice, checkoutAppearance, fonts, locale, }) {
34064
+ function PaymentForm({ paymentSecret, onSuccess, onError, onBack, onDoubleBack, contactEmail, shippingAddress, shippingName, shippingPrice, checkoutAppearance, fonts, locale, publicKey, }) {
34246
34065
  const [isSubmitting, setIsSubmitting] = React.useState(false);
34247
34066
  const { t } = useTranslation();
34248
34067
  return (React.createElement("div", { className: "space-y-6" },
@@ -34271,7 +34090,7 @@ function PaymentForm({ paymentSecret, onSuccess, onError, onBack, onDoubleBack,
34271
34090
  " \u00B7 ",
34272
34091
  shippingPrice)),
34273
34092
  React.createElement(Button, { variant: "link", size: "link", onClick: onBack }, t("CheckoutEmbed.Shipping.change")))),
34274
- React.createElement("div", { className: "mt-8" }, paymentSecret && (React.createElement(PaymentElement$1, { fonts: fonts, checkoutAppearance: convertCheckoutAppearanceToStripeAppearance(checkoutAppearance, fonts), locale: locale, paymentSecret: paymentSecret, onSuccess: onSuccess, onError: onError, setSubmitting: setIsSubmitting },
34093
+ React.createElement("div", { className: "mt-8" }, paymentSecret && (React.createElement(PaymentElement$1, { fonts: fonts, checkoutAppearance: convertCheckoutAppearanceToStripeAppearance(checkoutAppearance, fonts), locale: locale, paymentSecret: paymentSecret, onSuccess: onSuccess, onError: onError, setSubmitting: setIsSubmitting, publicKey: publicKey },
34275
34094
  React.createElement("div", { className: "flex justify-between items-center pt-8" },
34276
34095
  React.createElement(Button, { type: "button", variant: "ghost", onClick: onBack },
34277
34096
  React.createElement(ChevronLeft, null),
@@ -34439,6 +34258,8 @@ const useFormStore = create()(persist((set) => ({
34439
34258
  setFormData: (formData) => set({ formData }),
34440
34259
  step: "customer",
34441
34260
  setStep: (step) => set({ step }),
34261
+ checkoutId: "",
34262
+ setCheckoutId: (checkoutId) => set({ checkoutId }),
34442
34263
  }), { name: `checkout` }));
34443
34264
 
34444
34265
  const motionSettings = {
@@ -34448,8 +34269,9 @@ const motionSettings = {
34448
34269
  transition: { duration: 0.2 },
34449
34270
  };
34450
34271
  function CheckoutForm({ storeClient, checkoutId, onSuccess, onError, cancelUrl, clientSecret, customer, currency, checkoutAppearance, fonts, locale, setShippingCost, exchangeRate, }) {
34451
- const { formData, setFormData, step, setStep } = useFormStore();
34272
+ const { formData, setFormData, step, setStep, checkoutId: storedCheckoutId, setCheckoutId, } = useFormStore();
34452
34273
  const [paymentSecret, setPaymentSecret] = React.useState(null);
34274
+ const [publicKey, setPublicKey] = React.useState(null);
34453
34275
  const [shippingRates, setShippingRates] = React.useState([]);
34454
34276
  const validateStep = React.useCallback(() => {
34455
34277
  if (step === "customer")
@@ -34471,21 +34293,53 @@ function CheckoutForm({ storeClient, checkoutId, onSuccess, onError, cancelUrl,
34471
34293
  validateStep();
34472
34294
  }, [step]);
34473
34295
  React.useEffect(() => {
34474
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
34475
- if (customer && !((_a = formData.customer) === null || _a === void 0 ? void 0 : _a.email)) {
34296
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19;
34297
+ if (checkoutId !== storedCheckoutId) {
34298
+ setStep("customer");
34299
+ setCheckoutId(checkoutId);
34300
+ if (customer) {
34301
+ setFormData({
34302
+ customerId: customer.id,
34303
+ customer: {
34304
+ firstName: (_c = (_b = (_a = customer.address) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.split(" ")[0]) !== null && _c !== void 0 ? _c : "",
34305
+ lastName: (_f = (_e = (_d = customer.address) === null || _d === void 0 ? void 0 : _d.name) === null || _e === void 0 ? void 0 : _e.split(" ")[1]) !== null && _f !== void 0 ? _f : "",
34306
+ phone: (_h = (_g = customer.address) === null || _g === void 0 ? void 0 : _g.phone) !== null && _h !== void 0 ? _h : "",
34307
+ email: (_j = customer.email) !== null && _j !== void 0 ? _j : "",
34308
+ address: {
34309
+ line1: (_l = (_k = customer.address) === null || _k === void 0 ? void 0 : _k.line1) !== null && _l !== void 0 ? _l : "",
34310
+ line2: (_o = (_m = customer.address) === null || _m === void 0 ? void 0 : _m.line2) !== null && _o !== void 0 ? _o : "",
34311
+ city: (_q = (_p = customer.address) === null || _p === void 0 ? void 0 : _p.city) !== null && _q !== void 0 ? _q : "",
34312
+ zipCode: (_s = (_r = customer.address) === null || _r === void 0 ? void 0 : _r.zipCode) !== null && _s !== void 0 ? _s : "",
34313
+ country: (_u = (_t = customer.address) === null || _t === void 0 ? void 0 : _t.country) !== null && _u !== void 0 ? _u : "",
34314
+ countryCode: (_w = (_v = customer.address) === null || _v === void 0 ? void 0 : _v.countryCode) !== null && _w !== void 0 ? _w : "",
34315
+ },
34316
+ },
34317
+ });
34318
+ }
34319
+ else if ((_x = formData.customer) === null || _x === void 0 ? void 0 : _x.email) {
34320
+ setFormData({
34321
+ customer: formData.customer,
34322
+ });
34323
+ }
34324
+ else {
34325
+ setFormData({});
34326
+ }
34327
+ return;
34328
+ }
34329
+ if (customer && !((_y = formData.customer) === null || _y === void 0 ? void 0 : _y.email)) {
34476
34330
  const step = customer.id ? "shipping" : "customer";
34477
34331
  setFormData(Object.assign(Object.assign({}, formData), { customerId: customer.id, customer: {
34478
- firstName: (_d = (_c = (_b = customer.address) === null || _b === void 0 ? void 0 : _b.name) === null || _c === void 0 ? void 0 : _c.split(" ")[0]) !== null && _d !== void 0 ? _d : "",
34479
- lastName: (_g = (_f = (_e = customer.address) === null || _e === void 0 ? void 0 : _e.name) === null || _f === void 0 ? void 0 : _f.split(" ")[1]) !== null && _g !== void 0 ? _g : "",
34480
- phone: (_j = (_h = customer.address) === null || _h === void 0 ? void 0 : _h.phone) !== null && _j !== void 0 ? _j : "",
34481
- email: (_k = customer.email) !== null && _k !== void 0 ? _k : "",
34332
+ firstName: (_1 = (_0 = (_z = customer.address) === null || _z === void 0 ? void 0 : _z.name) === null || _0 === void 0 ? void 0 : _0.split(" ")[0]) !== null && _1 !== void 0 ? _1 : "",
34333
+ lastName: (_4 = (_3 = (_2 = customer.address) === null || _2 === void 0 ? void 0 : _2.name) === null || _3 === void 0 ? void 0 : _3.split(" ")[1]) !== null && _4 !== void 0 ? _4 : "",
34334
+ phone: (_6 = (_5 = customer.address) === null || _5 === void 0 ? void 0 : _5.phone) !== null && _6 !== void 0 ? _6 : "",
34335
+ email: (_7 = customer.email) !== null && _7 !== void 0 ? _7 : "",
34482
34336
  address: {
34483
- line1: (_m = (_l = customer.address) === null || _l === void 0 ? void 0 : _l.line1) !== null && _m !== void 0 ? _m : "",
34484
- line2: (_p = (_o = customer.address) === null || _o === void 0 ? void 0 : _o.line2) !== null && _p !== void 0 ? _p : "",
34485
- city: (_r = (_q = customer.address) === null || _q === void 0 ? void 0 : _q.city) !== null && _r !== void 0 ? _r : "",
34486
- zipCode: (_t = (_s = customer.address) === null || _s === void 0 ? void 0 : _s.zipCode) !== null && _t !== void 0 ? _t : "",
34487
- country: (_v = (_u = customer.address) === null || _u === void 0 ? void 0 : _u.country) !== null && _v !== void 0 ? _v : "",
34488
- countryCode: (_x = (_w = customer.address) === null || _w === void 0 ? void 0 : _w.countryCode) !== null && _x !== void 0 ? _x : "",
34337
+ line1: (_9 = (_8 = customer.address) === null || _8 === void 0 ? void 0 : _8.line1) !== null && _9 !== void 0 ? _9 : "",
34338
+ line2: (_11 = (_10 = customer.address) === null || _10 === void 0 ? void 0 : _10.line2) !== null && _11 !== void 0 ? _11 : "",
34339
+ city: (_13 = (_12 = customer.address) === null || _12 === void 0 ? void 0 : _12.city) !== null && _13 !== void 0 ? _13 : "",
34340
+ zipCode: (_15 = (_14 = customer.address) === null || _14 === void 0 ? void 0 : _14.zipCode) !== null && _15 !== void 0 ? _15 : "",
34341
+ country: (_17 = (_16 = customer.address) === null || _16 === void 0 ? void 0 : _16.country) !== null && _17 !== void 0 ? _17 : "",
34342
+ countryCode: (_19 = (_18 = customer.address) === null || _18 === void 0 ? void 0 : _18.countryCode) !== null && _19 !== void 0 ? _19 : "",
34489
34343
  },
34490
34344
  } }));
34491
34345
  setStep(step);
@@ -34549,8 +34403,9 @@ function CheckoutForm({ storeClient, checkoutId, onSuccess, onError, cancelUrl,
34549
34403
  pickupPointId: data.pickupPointId,
34550
34404
  },
34551
34405
  });
34552
- const paymentSecret = yield storeClient.generateCheckoutsPaymentSecret(clientSecret, checkoutId);
34406
+ const { paymentSecret, publicKey } = yield storeClient.generateCheckoutsPaymentSecret(clientSecret, checkoutId);
34553
34407
  setPaymentSecret(paymentSecret);
34408
+ setPublicKey(publicKey);
34554
34409
  setShippingCost(data.price);
34555
34410
  setStep("payment");
34556
34411
  });
@@ -34569,8 +34424,9 @@ function CheckoutForm({ storeClient, checkoutId, onSuccess, onError, cancelUrl,
34569
34424
  };
34570
34425
  React.useEffect(() => {
34571
34426
  const asyncFunc = () => __awaiter(this, void 0, void 0, function* () {
34572
- const paymentSecret = yield storeClient.generateCheckoutsPaymentSecret(clientSecret, checkoutId);
34427
+ const { paymentSecret, publicKey } = yield storeClient.generateCheckoutsPaymentSecret(clientSecret, checkoutId);
34573
34428
  setPaymentSecret(paymentSecret);
34429
+ setPublicKey(publicKey);
34574
34430
  });
34575
34431
  if (!paymentSecret && step === "payment") {
34576
34432
  asyncFunc();
@@ -34583,7 +34439,7 @@ function CheckoutForm({ storeClient, checkoutId, onSuccess, onError, cancelUrl,
34583
34439
  step === "shipping" && formData.customer && (React.createElement(motion.div, Object.assign({ key: "shipping" }, motionSettings, { className: "absolute w-full" }),
34584
34440
  React.createElement(ShippingMethodForm, { setFormData: setFormData, formData: formData, shippingRates: shippingRates, initialData: formData.shipping, onSubmit: handleShippingSubmit, onBack: handleBack, contactEmail: formData.customer.email, shippingAddress: formatAddress(formData.customer.address), currency: currency, exchangeRate: exchangeRate, locale: locale, countryCode: formData.customer.address.countryCode }))),
34585
34441
  step === "payment" && formData.customer && formData.shipping && (React.createElement(motion.div, Object.assign({ key: "payment" }, motionSettings, { className: "absolute w-full" }),
34586
- React.createElement(PaymentForm, { locale: locale, fonts: fonts, checkoutAppearance: checkoutAppearance, paymentSecret: paymentSecret, onSuccess: onSuccess, onError: onError, onBack: handleBack, onDoubleBack: handleDoubleBack, contactEmail: formData.customer.email, shippingAddress: formatAddress(formData.customer.address), shippingName: formData.shipping.name, shippingPrice: storeHelpers.formatPrice(formData.shipping.price, currency, exchangeRate) }))))));
34442
+ React.createElement(PaymentForm, { locale: locale, fonts: fonts, checkoutAppearance: checkoutAppearance, paymentSecret: paymentSecret, onSuccess: onSuccess, onError: onError, onBack: handleBack, onDoubleBack: handleDoubleBack, contactEmail: formData.customer.email, shippingAddress: formatAddress(formData.customer.address), shippingName: formData.shipping.name, shippingPrice: storeHelpers.formatPrice(formData.shipping.price, currency, exchangeRate), publicKey: publicKey }))))));
34587
34443
  }
34588
34444
 
34589
34445
  function CheckoutFormLoading() {