@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.mjs CHANGED
@@ -3920,8 +3920,10 @@ const AnimatePresence = ({ children, custom, initial = true, onExitComplete, pre
3920
3920
  };
3921
3921
 
3922
3922
  const stepsOrder = [
3923
+ "setup", // Compute
3923
3924
  "read", // Read
3924
3925
  "resolveKeyframes", // Write/Read/Write/Read
3926
+ "preUpdate", // Compute
3925
3927
  "update", // Compute
3926
3928
  "preRender", // Compute
3927
3929
  "render", // Write
@@ -4020,9 +4022,7 @@ function createRenderStep(runNextFrame, stepName) {
4020
4022
  return step;
4021
4023
  }
4022
4024
 
4023
- const MotionGlobalConfig = {
4024
- useManualTiming: false,
4025
- };
4025
+ const MotionGlobalConfig = {};
4026
4026
 
4027
4027
  const maxElapsed = 40;
4028
4028
  function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
@@ -4038,11 +4038,13 @@ function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
4038
4038
  acc[key] = createRenderStep(flagRunNextFrame, allowKeepAlive ? key : undefined);
4039
4039
  return acc;
4040
4040
  }, {});
4041
- const { read, resolveKeyframes, update, preRender, render, postRender } = steps;
4041
+ const { setup, read, resolveKeyframes, preUpdate, update, preRender, render, postRender, } = steps;
4042
4042
  const processBatch = () => {
4043
- const timestamp = performance.now();
4043
+ const timestamp = MotionGlobalConfig.useManualTiming
4044
+ ? state.timestamp
4045
+ : performance.now();
4044
4046
  runNextFrame = false;
4045
- {
4047
+ if (!MotionGlobalConfig.useManualTiming) {
4046
4048
  state.delta = useDefaultElapsed
4047
4049
  ? 1000 / 60
4048
4050
  : Math.max(Math.min(timestamp - state.timestamp, maxElapsed), 1);
@@ -4050,8 +4052,10 @@ function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
4050
4052
  state.timestamp = timestamp;
4051
4053
  state.isProcessing = true;
4052
4054
  // Unrolled render loop for better per-frame performance
4055
+ setup.process(state);
4053
4056
  read.process(state);
4054
4057
  resolveKeyframes.process(state);
4058
+ preUpdate.process(state);
4055
4059
  update.process(state);
4056
4060
  preRender.process(state);
4057
4061
  render.process(state);
@@ -4666,7 +4670,7 @@ const transformPropOrder = [
4666
4670
  /**
4667
4671
  * A quick lookup for transform props.
4668
4672
  */
4669
- const transformProps = new Set(transformPropOrder);
4673
+ const transformProps = /*@__PURE__*/ (() => new Set(transformPropOrder))();
4670
4674
 
4671
4675
  function isForcedMotionValue(key, { layout, layoutId }) {
4672
4676
  return (transformProps.has(key) ||
@@ -4708,6 +4712,12 @@ const scale = {
4708
4712
  default: 1,
4709
4713
  };
4710
4714
 
4715
+ const int = {
4716
+ ...number,
4717
+ transform: Math.round,
4718
+ };
4719
+
4720
+ /*#__NO_SIDE_EFFECTS__*/
4711
4721
  const createUnitType = (unit) => ({
4712
4722
  test: (v) => typeof v === "string" && v.endsWith(unit) && v.split(" ").length === 1,
4713
4723
  parse: parseFloat,
@@ -4718,13 +4728,40 @@ const percent = /*@__PURE__*/ createUnitType("%");
4718
4728
  const px = /*@__PURE__*/ createUnitType("px");
4719
4729
  const vh = /*@__PURE__*/ createUnitType("vh");
4720
4730
  const vw = /*@__PURE__*/ createUnitType("vw");
4721
- const progressPercentage = {
4731
+ const progressPercentage = /*@__PURE__*/ (() => ({
4722
4732
  ...percent,
4723
4733
  parse: (v) => percent.parse(v) / 100,
4724
4734
  transform: (v) => percent.transform(v * 100),
4735
+ }))();
4736
+
4737
+ const transformValueTypes = {
4738
+ rotate: degrees,
4739
+ rotateX: degrees,
4740
+ rotateY: degrees,
4741
+ rotateZ: degrees,
4742
+ scale,
4743
+ scaleX: scale,
4744
+ scaleY: scale,
4745
+ scaleZ: scale,
4746
+ skew: degrees,
4747
+ skewX: degrees,
4748
+ skewY: degrees,
4749
+ distance: px,
4750
+ translateX: px,
4751
+ translateY: px,
4752
+ translateZ: px,
4753
+ x: px,
4754
+ y: px,
4755
+ z: px,
4756
+ perspective: px,
4757
+ transformPerspective: px,
4758
+ opacity: alpha,
4759
+ originX: progressPercentage,
4760
+ originY: progressPercentage,
4761
+ originZ: px,
4725
4762
  };
4726
4763
 
4727
- const browserNumberValueTypes = {
4764
+ const numberValueTypes = {
4728
4765
  // Border props
4729
4766
  borderWidth: px,
4730
4767
  borderTopWidth: px,
@@ -4760,45 +4797,8 @@ const browserNumberValueTypes = {
4760
4797
  // Misc
4761
4798
  backgroundPositionX: px,
4762
4799
  backgroundPositionY: px,
4763
- };
4764
-
4765
- const transformValueTypes = {
4766
- rotate: degrees,
4767
- rotateX: degrees,
4768
- rotateY: degrees,
4769
- rotateZ: degrees,
4770
- scale,
4771
- scaleX: scale,
4772
- scaleY: scale,
4773
- scaleZ: scale,
4774
- skew: degrees,
4775
- skewX: degrees,
4776
- skewY: degrees,
4777
- distance: px,
4778
- translateX: px,
4779
- translateY: px,
4780
- translateZ: px,
4781
- x: px,
4782
- y: px,
4783
- z: px,
4784
- perspective: px,
4785
- transformPerspective: px,
4786
- opacity: alpha,
4787
- originX: progressPercentage,
4788
- originY: progressPercentage,
4789
- originZ: px,
4790
- };
4791
-
4792
- const int = {
4793
- ...number,
4794
- transform: Math.round,
4795
- };
4796
-
4797
- const numberValueTypes = {
4798
- ...browserNumberValueTypes,
4799
4800
  ...transformValueTypes,
4800
4801
  zIndex: int,
4801
- size: px,
4802
4802
  // SVG
4803
4803
  fillOpacity: alpha,
4804
4804
  strokeOpacity: alpha,
@@ -5065,25 +5065,10 @@ function buildSVGPath(attrs, length, spacing = 1, offset = 0, useDashCase = true
5065
5065
  attrs[keys.array] = `${pathLength} ${pathSpacing}`;
5066
5066
  }
5067
5067
 
5068
- function calcOrigin$1(origin, offset, size) {
5069
- return typeof origin === "string"
5070
- ? origin
5071
- : px.transform(offset + size * origin);
5072
- }
5073
- /**
5074
- * The SVG transform origin defaults are different to CSS and is less intuitive,
5075
- * so we use the measured dimensions of the SVG to reconcile these.
5076
- */
5077
- function calcSVGTransformOrigin(dimensions, originX, originY) {
5078
- const pxOriginX = calcOrigin$1(originX, dimensions.x, dimensions.width);
5079
- const pxOriginY = calcOrigin$1(originY, dimensions.y, dimensions.height);
5080
- return `${pxOriginX} ${pxOriginY}`;
5081
- }
5082
-
5083
5068
  /**
5084
5069
  * Build SVG visual attrbutes, like cx and style.transform
5085
5070
  */
5086
- function buildSVGAttrs(state, { attrX, attrY, attrScale, originX, originY, pathLength, pathSpacing = 1, pathOffset = 0,
5071
+ function buildSVGAttrs(state, { attrX, attrY, attrScale, pathLength, pathSpacing = 1, pathOffset = 0,
5087
5072
  // This is object creation, which we try to avoid per-frame.
5088
5073
  ...latest }, isSVGTag, transformTemplate) {
5089
5074
  buildHTMLStyles(state, latest, transformTemplate);
@@ -5099,20 +5084,26 @@ function buildSVGAttrs(state, { attrX, attrY, attrScale, originX, originY, pathL
5099
5084
  }
5100
5085
  state.attrs = state.style;
5101
5086
  state.style = {};
5102
- const { attrs, style, dimensions } = state;
5087
+ const { attrs, style } = state;
5103
5088
  /**
5104
- * However, we apply transforms as CSS transforms. So if we detect a transform we take it from attrs
5105
- * and copy it into style.
5089
+ * However, we apply transforms as CSS transforms.
5090
+ * So if we detect a transform, transformOrigin we take it from attrs and copy it into style.
5106
5091
  */
5107
5092
  if (attrs.transform) {
5108
- if (dimensions)
5109
- style.transform = attrs.transform;
5093
+ style.transform = attrs.transform;
5110
5094
  delete attrs.transform;
5111
5095
  }
5112
- // Parse transformOrigin
5113
- if (dimensions &&
5114
- (originX !== undefined || originY !== undefined || style.transform)) {
5115
- style.transformOrigin = calcSVGTransformOrigin(dimensions, originX !== undefined ? originX : 0.5, originY !== undefined ? originY : 0.5);
5096
+ if (style.transform || attrs.transformOrigin) {
5097
+ style.transformOrigin = attrs.transformOrigin ?? "50% 50%";
5098
+ delete attrs.transformOrigin;
5099
+ }
5100
+ if (style.transform) {
5101
+ /**
5102
+ * SVG's element transform-origin uses its own median as a reference.
5103
+ * Therefore, transformBox becomes a fill-box
5104
+ */
5105
+ style.transformBox = "fill-box";
5106
+ delete attrs.transformBox;
5116
5107
  }
5117
5108
  // Render attrX/attrY/attrScale as attributes
5118
5109
  if (attrX !== undefined)
@@ -5211,44 +5202,20 @@ function resolveVariantFromProps(props, definition, custom, visualElement) {
5211
5202
  return definition;
5212
5203
  }
5213
5204
 
5214
- const isKeyframesTarget = (v) => {
5215
- return Array.isArray(v);
5216
- };
5217
-
5218
- const isCustomValue = (v) => {
5219
- return Boolean(v && typeof v === "object" && v.mix && v.toValue);
5220
- };
5221
- const resolveFinalValueInKeyframes = (v) => {
5222
- // TODO maybe throw if v.length - 1 is placeholder token?
5223
- return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v;
5224
- };
5225
-
5226
5205
  /**
5227
5206
  * If the provided value is a MotionValue, this returns the actual value, otherwise just the value itself
5228
5207
  *
5229
5208
  * TODO: Remove and move to library
5230
5209
  */
5231
5210
  function resolveMotionValue(value) {
5232
- const unwrappedValue = isMotionValue(value) ? value.get() : value;
5233
- return isCustomValue(unwrappedValue)
5234
- ? unwrappedValue.toValue()
5235
- : unwrappedValue;
5211
+ return isMotionValue(value) ? value.get() : value;
5236
5212
  }
5237
5213
 
5238
- function makeState({ scrapeMotionValuesFromProps, createRenderState, onUpdate, }, props, context, presenceContext) {
5214
+ function makeState({ scrapeMotionValuesFromProps, createRenderState, }, props, context, presenceContext) {
5239
5215
  const state = {
5240
5216
  latestValues: makeLatestValues(props, context, presenceContext, scrapeMotionValuesFromProps),
5241
5217
  renderState: createRenderState(),
5242
5218
  };
5243
- if (onUpdate) {
5244
- /**
5245
- * onMount works without the VisualElement because it could be
5246
- * called before the VisualElement payload has been hydrated.
5247
- * (e.g. if someone is using m components <m.circle />)
5248
- */
5249
- state.onMount = (instance) => onUpdate({ props, current: instance, ...state });
5250
- state.onUpdate = (visualElement) => onUpdate(visualElement);
5251
- }
5252
5219
  return state;
5253
5220
  }
5254
5221
  const makeUseVisualState = (config) => (props, isStatic) => {
@@ -5335,68 +5302,6 @@ const htmlMotionConfig = {
5335
5302
  }),
5336
5303
  };
5337
5304
 
5338
- function updateSVGDimensions(instance, renderState) {
5339
- try {
5340
- renderState.dimensions =
5341
- typeof instance.getBBox === "function"
5342
- ? instance.getBBox()
5343
- : instance.getBoundingClientRect();
5344
- }
5345
- catch (e) {
5346
- // Most likely trying to measure an unrendered element under Firefox
5347
- renderState.dimensions = {
5348
- x: 0,
5349
- y: 0,
5350
- width: 0,
5351
- height: 0,
5352
- };
5353
- }
5354
- }
5355
-
5356
- function renderHTML(element, { style, vars }, styleProp, projection) {
5357
- Object.assign(element.style, style, projection && projection.getProjectionStyles(styleProp));
5358
- // Loop over any CSS variables and assign those.
5359
- for (const key in vars) {
5360
- element.style.setProperty(key, vars[key]);
5361
- }
5362
- }
5363
-
5364
- /**
5365
- * A set of attribute names that are always read/written as camel case.
5366
- */
5367
- const camelCaseAttributes = new Set([
5368
- "baseFrequency",
5369
- "diffuseConstant",
5370
- "kernelMatrix",
5371
- "kernelUnitLength",
5372
- "keySplines",
5373
- "keyTimes",
5374
- "limitingConeAngle",
5375
- "markerHeight",
5376
- "markerWidth",
5377
- "numOctaves",
5378
- "targetX",
5379
- "targetY",
5380
- "surfaceScale",
5381
- "specularConstant",
5382
- "specularExponent",
5383
- "stdDeviation",
5384
- "tableValues",
5385
- "viewBox",
5386
- "gradientTransform",
5387
- "pathLength",
5388
- "startOffset",
5389
- "textLength",
5390
- "lengthAdjust",
5391
- ]);
5392
-
5393
- function renderSVG(element, renderState, _styleProp, projection) {
5394
- renderHTML(element, renderState, undefined, projection);
5395
- for (const key in renderState.attrs) {
5396
- element.setAttribute(!camelCaseAttributes.has(key) ? camelToDash(key) : key, renderState.attrs[key]);
5397
- }
5398
- }
5399
-
5400
5305
  function scrapeMotionValuesFromProps(props, prevProps, visualElement) {
5401
5306
  const newValues = scrapeMotionValuesFromProps$1(props, prevProps, visualElement);
5402
5307
  for (const key in props) {
@@ -5411,49 +5316,10 @@ function scrapeMotionValuesFromProps(props, prevProps, visualElement) {
5411
5316
  return newValues;
5412
5317
  }
5413
5318
 
5414
- const layoutProps = ["x", "y", "width", "height", "cx", "cy", "r"];
5415
5319
  const svgMotionConfig = {
5416
5320
  useVisualState: makeUseVisualState({
5417
5321
  scrapeMotionValuesFromProps: scrapeMotionValuesFromProps,
5418
5322
  createRenderState: createSvgRenderState,
5419
- onUpdate: ({ props, prevProps, current, renderState, latestValues, }) => {
5420
- if (!current)
5421
- return;
5422
- let hasTransform = !!props.drag;
5423
- if (!hasTransform) {
5424
- for (const key in latestValues) {
5425
- if (transformProps.has(key)) {
5426
- hasTransform = true;
5427
- break;
5428
- }
5429
- }
5430
- }
5431
- if (!hasTransform)
5432
- return;
5433
- let needsMeasure = !prevProps;
5434
- if (prevProps) {
5435
- /**
5436
- * Check the layout props for changes, if any are found we need to
5437
- * measure the element again.
5438
- */
5439
- for (let i = 0; i < layoutProps.length; i++) {
5440
- const key = layoutProps[i];
5441
- if (props[key] !==
5442
- prevProps[key]) {
5443
- needsMeasure = true;
5444
- }
5445
- }
5446
- }
5447
- if (!needsMeasure)
5448
- return;
5449
- frame.read(() => {
5450
- updateSVGDimensions(current, renderState);
5451
- frame.render(() => {
5452
- buildSVGAttrs(renderState, latestValues, isSVGTag(current.tagName), props.transformTemplate);
5453
- renderSVG(current, renderState);
5454
- });
5455
- });
5456
- },
5457
5323
  }),
5458
5324
  };
5459
5325
 
@@ -5478,15 +5344,9 @@ function resolveVariant(visualElement, definition, custom) {
5478
5344
  return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, visualElement);
5479
5345
  }
5480
5346
 
5481
- const positionalKeys = new Set([
5482
- "width",
5483
- "height",
5484
- "top",
5485
- "left",
5486
- "right",
5487
- "bottom",
5488
- ...transformPropOrder,
5489
- ]);
5347
+ const isKeyframesTarget = (v) => {
5348
+ return Array.isArray(v);
5349
+ };
5490
5350
 
5491
5351
  let now;
5492
5352
  function clearTime() {
@@ -5597,7 +5457,7 @@ class MotionValue {
5597
5457
  * This will be replaced by the build step with the latest version number.
5598
5458
  * When MotionValues are provided to motion components, warn if versions are mixed.
5599
5459
  */
5600
- this.version = "12.7.4";
5460
+ this.version = "12.9.0";
5601
5461
  /**
5602
5462
  * Tracks whether this value can output a velocity. Currently this is only true
5603
5463
  * if the value is numerical, but we might be able to widen the scope here and support
@@ -5623,12 +5483,12 @@ class MotionValue {
5623
5483
  this.prev = this.current;
5624
5484
  this.setCurrent(v);
5625
5485
  // Update update subscribers
5626
- if (this.current !== this.prev && this.events.change) {
5627
- this.events.change.notify(this.current);
5486
+ if (this.current !== this.prev) {
5487
+ this.events.change?.notify(this.current);
5628
5488
  }
5629
5489
  // Update render subscribers
5630
- if (render && this.events.renderRequest) {
5631
- this.events.renderRequest.notify(this.current);
5490
+ if (render) {
5491
+ this.events.renderRequest?.notify(this.current);
5632
5492
  }
5633
5493
  };
5634
5494
  this.hasAnimated = false;
@@ -5861,6 +5721,7 @@ class MotionValue {
5861
5721
  * @public
5862
5722
  */
5863
5723
  destroy() {
5724
+ this.events.destroy?.notify();
5864
5725
  this.clearListeners();
5865
5726
  this.stop();
5866
5727
  if (this.stopPassiveEffect) {
@@ -5884,6 +5745,10 @@ function setMotionValue(visualElement, key, value) {
5884
5745
  visualElement.addValue(key, motionValue(value));
5885
5746
  }
5886
5747
  }
5748
+ function resolveFinalValueInKeyframes(v) {
5749
+ // TODO maybe throw if v.length - 1 is placeholder token?
5750
+ return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v;
5751
+ }
5887
5752
  function setTarget(visualElement, definition) {
5888
5753
  const resolved = resolveVariant(visualElement, definition);
5889
5754
  let { transitionEnd = {}, transition = {}, ...target } = resolved || {};
@@ -5918,89 +5783,79 @@ function getOptimisedAppearId(visualElement) {
5918
5783
  return visualElement.props[optimizedAppearDataAttribute];
5919
5784
  }
5920
5785
 
5921
- /*
5922
- Bezier function generator
5923
- This has been modified from Gaëtan Renaudeau's BezierEasing
5924
- https://github.com/gre/bezier-easing/blob/master/src/index.js
5925
- https://github.com/gre/bezier-easing/blob/master/LICENSE
5926
-
5927
- I've removed the newtonRaphsonIterate algo because in benchmarking it
5928
- wasn't noticiably faster than binarySubdivision, indeed removing it
5929
- usually improved times, depending on the curve.
5930
- I also removed the lookup table, as for the added bundle size and loop we're
5931
- only cutting ~4 or so subdivision iterations. I bumped the max iterations up
5932
- to 12 to compensate and this still tended to be faster for no perceivable
5933
- loss in accuracy.
5934
- Usage
5935
- const easeOut = cubicBezier(.17,.67,.83,.67);
5936
- const x = easeOut(0.5); // returns 0.627...
5937
- */
5938
- // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
5939
- const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) *
5940
- t;
5941
- const subdivisionPrecision = 0.0000001;
5942
- const subdivisionMaxIterations = 12;
5943
- function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) {
5944
- let currentX;
5945
- let currentT;
5946
- let i = 0;
5947
- do {
5948
- currentT = lowerBound + (upperBound - lowerBound) / 2.0;
5949
- currentX = calcBezier(currentT, mX1, mX2) - x;
5950
- if (currentX > 0.0) {
5951
- upperBound = currentT;
5952
- }
5953
- else {
5954
- lowerBound = currentT;
5955
- }
5956
- } while (Math.abs(currentX) > subdivisionPrecision &&
5957
- ++i < subdivisionMaxIterations);
5958
- return currentT;
5959
- }
5960
- function cubicBezier(mX1, mY1, mX2, mY2) {
5961
- // If this is a linear gradient, return linear easing
5962
- if (mX1 === mY1 && mX2 === mY2)
5963
- return noop;
5964
- const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2);
5965
- // If animation is at start/end, return t without easing
5966
- return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
5786
+ const isNotNull$1 = (value) => value !== null;
5787
+ function getFinalKeyframe$1(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
5788
+ const resolvedKeyframes = keyframes.filter(isNotNull$1);
5789
+ const index = repeat && repeatType !== "loop" && repeat % 2 === 1
5790
+ ? 0
5791
+ : resolvedKeyframes.length - 1;
5792
+ return resolvedKeyframes[index]
5793
+ ;
5967
5794
  }
5968
5795
 
5969
- // Accepts an easing function and returns a new one that outputs mirrored values for
5970
- // the second half of the animation. Turns easeIn into easeInOut.
5971
- const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
5972
-
5973
- // Accepts an easing function and returns a new one that outputs reversed values.
5974
- // Turns easeIn into easeOut.
5975
- const reverseEasing = (easing) => (p) => 1 - easing(1 - p);
5976
-
5977
- const backOut = /*@__PURE__*/ cubicBezier(0.33, 1.53, 0.69, 0.99);
5978
- const backIn = /*@__PURE__*/ reverseEasing(backOut);
5979
- const backInOut = /*@__PURE__*/ mirrorEasing(backIn);
5980
-
5981
- const anticipate = (p) => (p *= 2) < 1 ? 0.5 * backIn(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
5982
-
5983
- const circIn = (p) => 1 - Math.sin(Math.acos(p));
5984
- const circOut = reverseEasing(circIn);
5985
- const circInOut = mirrorEasing(circIn);
5986
-
5796
+ const underDampedSpring = {
5797
+ type: "spring",
5798
+ stiffness: 500,
5799
+ damping: 25,
5800
+ restSpeed: 10,
5801
+ };
5802
+ const criticallyDampedSpring = (target) => ({
5803
+ type: "spring",
5804
+ stiffness: 550,
5805
+ damping: target === 0 ? 2 * Math.sqrt(550) : 30,
5806
+ restSpeed: 10,
5807
+ });
5808
+ const keyframesTransition = {
5809
+ type: "keyframes",
5810
+ duration: 0.8,
5811
+ };
5987
5812
  /**
5988
- * Check if the value is a zero value string like "0px" or "0%"
5989
- */
5990
- const isZeroValueString = (v) => /^0[^.\s]+$/u.test(v);
5991
-
5992
- function isNone(value) {
5993
- if (typeof value === "number") {
5994
- return value === 0;
5995
- }
5996
- else if (value !== null) {
5997
- return value === "none" || value === "0" || isZeroValueString(value);
5813
+ * Default easing curve is a slightly shallower version of
5814
+ * the default browser easing curve.
5815
+ */
5816
+ const ease = {
5817
+ type: "keyframes",
5818
+ ease: [0.25, 0.1, 0.35, 1],
5819
+ duration: 0.3,
5820
+ };
5821
+ const getDefaultTransition = (valueKey, { keyframes }) => {
5822
+ if (keyframes.length > 2) {
5823
+ return keyframesTransition;
5998
5824
  }
5999
- else {
6000
- return true;
5825
+ else if (transformProps.has(valueKey)) {
5826
+ return valueKey.startsWith("scale")
5827
+ ? criticallyDampedSpring(keyframes[1])
5828
+ : underDampedSpring;
6001
5829
  }
5830
+ return ease;
5831
+ };
5832
+
5833
+ /**
5834
+ * Decide whether a transition is defined on a given Transition.
5835
+ * This filters out orchestration options and returns true
5836
+ * if any options are left.
5837
+ */
5838
+ function isTransitionDefined({ when, delay: _delay, delayChildren, staggerChildren, staggerDirection, repeat, repeatType, repeatDelay, from, elapsed, ...transition }) {
5839
+ return !!Object.keys(transition).length;
5840
+ }
5841
+
5842
+ function getValueTransition(transition, key) {
5843
+ return (transition?.[key] ??
5844
+ transition?.["default"] ??
5845
+ transition);
6002
5846
  }
6003
5847
 
5848
+ /**
5849
+ * Converts seconds to milliseconds
5850
+ *
5851
+ * @param seconds - Time in seconds.
5852
+ * @return milliseconds - Converted time in milliseconds.
5853
+ */
5854
+ /*#__NO_SIDE_EFFECTS__*/
5855
+ const secondsToMilliseconds = (seconds) => seconds * 1000;
5856
+ /*#__NO_SIDE_EFFECTS__*/
5857
+ const millisecondsToSeconds = (milliseconds) => milliseconds / 1000;
5858
+
6004
5859
  // If this number is a decimal, make it just five decimal places
6005
5860
  // to avoid exponents
6006
5861
  const sanitize = (v) => Math.round(v * 100000) / 100000;
@@ -6217,848 +6072,129 @@ const complex = {
6217
6072
  getAnimatableNone: getAnimatableNone$1,
6218
6073
  };
6219
6074
 
6220
- /**
6221
- * Properties that should default to 1 or 100%
6222
- */
6223
- const maxDefaults = new Set(["brightness", "contrast", "saturate", "opacity"]);
6224
- function applyDefaultFilter(v) {
6225
- const [name, value] = v.slice(0, -1).split("(");
6226
- if (name === "drop-shadow")
6227
- return v;
6228
- const [number] = value.match(floatRegex) || [];
6229
- if (!number)
6230
- return v;
6231
- const unit = value.replace(number, "");
6232
- let defaultValue = maxDefaults.has(name) ? 1 : 0;
6233
- if (number !== value)
6234
- defaultValue *= 100;
6235
- return name + "(" + defaultValue + unit + ")";
6075
+ // Adapted from https://gist.github.com/mjackson/5311256
6076
+ function hueToRgb(p, q, t) {
6077
+ if (t < 0)
6078
+ t += 1;
6079
+ if (t > 1)
6080
+ t -= 1;
6081
+ if (t < 1 / 6)
6082
+ return p + (q - p) * 6 * t;
6083
+ if (t < 1 / 2)
6084
+ return q;
6085
+ if (t < 2 / 3)
6086
+ return p + (q - p) * (2 / 3 - t) * 6;
6087
+ return p;
6088
+ }
6089
+ function hslaToRgba({ hue, saturation, lightness, alpha }) {
6090
+ hue /= 360;
6091
+ saturation /= 100;
6092
+ lightness /= 100;
6093
+ let red = 0;
6094
+ let green = 0;
6095
+ let blue = 0;
6096
+ if (!saturation) {
6097
+ red = green = blue = lightness;
6098
+ }
6099
+ else {
6100
+ const q = lightness < 0.5
6101
+ ? lightness * (1 + saturation)
6102
+ : lightness + saturation - lightness * saturation;
6103
+ const p = 2 * lightness - q;
6104
+ red = hueToRgb(p, q, hue + 1 / 3);
6105
+ green = hueToRgb(p, q, hue);
6106
+ blue = hueToRgb(p, q, hue - 1 / 3);
6107
+ }
6108
+ return {
6109
+ red: Math.round(red * 255),
6110
+ green: Math.round(green * 255),
6111
+ blue: Math.round(blue * 255),
6112
+ alpha,
6113
+ };
6236
6114
  }
6237
- const functionRegex = /\b([a-z-]*)\(.*?\)/gu;
6238
- const filter = {
6239
- ...complex,
6240
- getAnimatableNone: (v) => {
6241
- const functions = v.match(functionRegex);
6242
- return functions ? functions.map(applyDefaultFilter).join(" ") : v;
6243
- },
6244
- };
6245
6115
 
6246
- /**
6247
- * A map of default value types for common values
6248
- */
6249
- const defaultValueTypes = {
6250
- ...numberValueTypes,
6251
- // Color props
6252
- color,
6253
- backgroundColor: color,
6254
- outlineColor: color,
6255
- fill: color,
6256
- stroke: color,
6257
- // Border props
6258
- borderColor: color,
6259
- borderTopColor: color,
6260
- borderRightColor: color,
6261
- borderBottomColor: color,
6262
- borderLeftColor: color,
6263
- filter,
6264
- WebkitFilter: filter,
6116
+ function mixImmediate(a, b) {
6117
+ return (p) => (p > 0 ? b : a);
6118
+ }
6119
+
6120
+ /*
6121
+ Value in range from progress
6122
+
6123
+ Given a lower limit and an upper limit, we return the value within
6124
+ that range as expressed by progress (usually a number from 0 to 1)
6125
+
6126
+ So progress = 0.5 would change
6127
+
6128
+ from -------- to
6129
+
6130
+ to
6131
+
6132
+ from ---- to
6133
+
6134
+ E.g. from = 10, to = 20, progress = 0.5 => 15
6135
+
6136
+ @param [number]: Lower limit of range
6137
+ @param [number]: Upper limit of range
6138
+ @param [number]: The progress between lower and upper limits expressed 0-1
6139
+ @return [number]: Value as calculated from progress within range (not limited within range)
6140
+ */
6141
+ const mixNumber$1 = (from, to, progress) => {
6142
+ return from + (to - from) * progress;
6265
6143
  };
6266
- /**
6267
- * Gets the default ValueType for the provided value key
6268
- */
6269
- const getDefaultValueType = (key) => defaultValueTypes[key];
6270
6144
 
6271
- function getAnimatableNone(key, value) {
6272
- let defaultValueType = getDefaultValueType(key);
6273
- if (defaultValueType !== filter)
6274
- defaultValueType = complex;
6275
- // If value is not recognised as animatable, ie "none", create an animatable version origin based on the target
6276
- return defaultValueType.getAnimatableNone
6277
- ? defaultValueType.getAnimatableNone(value)
6278
- : undefined;
6145
+ // Linear color space blending
6146
+ // Explained https://www.youtube.com/watch?v=LKnqECcg6Gw
6147
+ // Demonstrated http://codepen.io/osublake/pen/xGVVaN
6148
+ const mixLinearColor = (from, to, v) => {
6149
+ const fromExpo = from * from;
6150
+ const expo = v * (to * to - fromExpo) + fromExpo;
6151
+ return expo < 0 ? 0 : Math.sqrt(expo);
6152
+ };
6153
+ const colorTypes = [hex, rgba, hsla];
6154
+ const getColorType = (v) => colorTypes.find((type) => type.test(v));
6155
+ function asRGBA(color) {
6156
+ const type = getColorType(color);
6157
+ warning(Boolean(type), `'${color}' is not an animatable color. Use the equivalent color code instead.`);
6158
+ if (!Boolean(type))
6159
+ return false;
6160
+ let model = type.parse(color);
6161
+ if (type === hsla) {
6162
+ // TODO Remove this cast - needed since Motion's stricter typing
6163
+ model = hslaToRgba(model);
6164
+ }
6165
+ return model;
6279
6166
  }
6167
+ const mixColor = (from, to) => {
6168
+ const fromRGBA = asRGBA(from);
6169
+ const toRGBA = asRGBA(to);
6170
+ if (!fromRGBA || !toRGBA) {
6171
+ return mixImmediate(from, to);
6172
+ }
6173
+ const blended = { ...fromRGBA };
6174
+ return (v) => {
6175
+ blended.red = mixLinearColor(fromRGBA.red, toRGBA.red, v);
6176
+ blended.green = mixLinearColor(fromRGBA.green, toRGBA.green, v);
6177
+ blended.blue = mixLinearColor(fromRGBA.blue, toRGBA.blue, v);
6178
+ blended.alpha = mixNumber$1(fromRGBA.alpha, toRGBA.alpha, v);
6179
+ return rgba.transform(blended);
6180
+ };
6181
+ };
6280
6182
 
6183
+ const invisibleValues = new Set(["none", "hidden"]);
6281
6184
  /**
6282
- * If we encounter keyframes like "none" or "0" and we also have keyframes like
6283
- * "#fff" or "200px 200px" we want to find a keyframe to serve as a template for
6284
- * the "none" keyframes. In this case "#fff" or "200px 200px" - then these get turned into
6285
- * zero equivalents, i.e. "#fff0" or "0px 0px".
6185
+ * Returns a function that, when provided a progress value between 0 and 1,
6186
+ * will return the "none" or "hidden" string only when the progress is that of
6187
+ * the origin or target.
6286
6188
  */
6287
- const invalidTemplates = new Set(["auto", "none", "0"]);
6288
- function makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name) {
6289
- let i = 0;
6290
- let animatableTemplate = undefined;
6291
- while (i < unresolvedKeyframes.length && !animatableTemplate) {
6292
- const keyframe = unresolvedKeyframes[i];
6293
- if (typeof keyframe === "string" &&
6294
- !invalidTemplates.has(keyframe) &&
6295
- analyseComplexValue(keyframe).values.length) {
6296
- animatableTemplate = unresolvedKeyframes[i];
6297
- }
6298
- i++;
6189
+ function mixVisibility(origin, target) {
6190
+ if (invisibleValues.has(origin)) {
6191
+ return (p) => (p <= 0 ? origin : target);
6299
6192
  }
6300
- if (animatableTemplate && name) {
6301
- for (const noneIndex of noneKeyframeIndexes) {
6302
- unresolvedKeyframes[noneIndex] = getAnimatableNone(name, animatableTemplate);
6303
- }
6193
+ else {
6194
+ return (p) => (p >= 1 ? target : origin);
6304
6195
  }
6305
6196
  }
6306
6197
 
6307
- const radToDeg = (rad) => (rad * 180) / Math.PI;
6308
- const rotate = (v) => {
6309
- const angle = radToDeg(Math.atan2(v[1], v[0]));
6310
- return rebaseAngle(angle);
6311
- };
6312
- const matrix2dParsers = {
6313
- x: 4,
6314
- y: 5,
6315
- translateX: 4,
6316
- translateY: 5,
6317
- scaleX: 0,
6318
- scaleY: 3,
6319
- scale: (v) => (Math.abs(v[0]) + Math.abs(v[3])) / 2,
6320
- rotate,
6321
- rotateZ: rotate,
6322
- skewX: (v) => radToDeg(Math.atan(v[1])),
6323
- skewY: (v) => radToDeg(Math.atan(v[2])),
6324
- skew: (v) => (Math.abs(v[1]) + Math.abs(v[2])) / 2,
6325
- };
6326
- const rebaseAngle = (angle) => {
6327
- angle = angle % 360;
6328
- if (angle < 0)
6329
- angle += 360;
6330
- return angle;
6331
- };
6332
- const rotateZ = rotate;
6333
- const scaleX = (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1]);
6334
- const scaleY = (v) => Math.sqrt(v[4] * v[4] + v[5] * v[5]);
6335
- const matrix3dParsers = {
6336
- x: 12,
6337
- y: 13,
6338
- z: 14,
6339
- translateX: 12,
6340
- translateY: 13,
6341
- translateZ: 14,
6342
- scaleX,
6343
- scaleY,
6344
- scale: (v) => (scaleX(v) + scaleY(v)) / 2,
6345
- rotateX: (v) => rebaseAngle(radToDeg(Math.atan2(v[6], v[5]))),
6346
- rotateY: (v) => rebaseAngle(radToDeg(Math.atan2(-v[2], v[0]))),
6347
- rotateZ,
6348
- rotate: rotateZ,
6349
- skewX: (v) => radToDeg(Math.atan(v[4])),
6350
- skewY: (v) => radToDeg(Math.atan(v[1])),
6351
- skew: (v) => (Math.abs(v[1]) + Math.abs(v[4])) / 2,
6352
- };
6353
- function defaultTransformValue(name) {
6354
- return name.includes("scale") ? 1 : 0;
6355
- }
6356
- function parseValueFromTransform(transform, name) {
6357
- if (!transform || transform === "none") {
6358
- return defaultTransformValue(name);
6359
- }
6360
- const matrix3dMatch = transform.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);
6361
- let parsers;
6362
- let match;
6363
- if (matrix3dMatch) {
6364
- parsers = matrix3dParsers;
6365
- match = matrix3dMatch;
6366
- }
6367
- else {
6368
- const matrix2dMatch = transform.match(/^matrix\(([-\d.e\s,]+)\)$/u);
6369
- parsers = matrix2dParsers;
6370
- match = matrix2dMatch;
6371
- }
6372
- if (!match) {
6373
- return defaultTransformValue(name);
6374
- }
6375
- const valueParser = parsers[name];
6376
- const values = match[1].split(",").map(convertTransformToNumber);
6377
- return typeof valueParser === "function"
6378
- ? valueParser(values)
6379
- : values[valueParser];
6380
- }
6381
- const readTransformValue = (instance, name) => {
6382
- const { transform = "none" } = getComputedStyle(instance);
6383
- return parseValueFromTransform(transform, name);
6384
- };
6385
- function convertTransformToNumber(value) {
6386
- return parseFloat(value.trim());
6387
- }
6388
-
6389
- const isNumOrPxType = (v) => v === number || v === px;
6390
- const transformKeys = new Set(["x", "y", "z"]);
6391
- const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key));
6392
- function removeNonTranslationalTransform(visualElement) {
6393
- const removedTransforms = [];
6394
- nonTranslationalTransformKeys.forEach((key) => {
6395
- const value = visualElement.getValue(key);
6396
- if (value !== undefined) {
6397
- removedTransforms.push([key, value.get()]);
6398
- value.set(key.startsWith("scale") ? 1 : 0);
6399
- }
6400
- });
6401
- return removedTransforms;
6402
- }
6403
- const positionalValues = {
6404
- // Dimensions
6405
- width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight),
6406
- height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom),
6407
- top: (_bbox, { top }) => parseFloat(top),
6408
- left: (_bbox, { left }) => parseFloat(left),
6409
- bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min),
6410
- right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min),
6411
- // Transform
6412
- x: (_bbox, { transform }) => parseValueFromTransform(transform, "x"),
6413
- y: (_bbox, { transform }) => parseValueFromTransform(transform, "y"),
6414
- };
6415
- // Alias translate longform names
6416
- positionalValues.translateX = positionalValues.x;
6417
- positionalValues.translateY = positionalValues.y;
6418
-
6419
- const toResolve = new Set();
6420
- let isScheduled = false;
6421
- let anyNeedsMeasurement = false;
6422
- function measureAllKeyframes() {
6423
- if (anyNeedsMeasurement) {
6424
- const resolversToMeasure = Array.from(toResolve).filter((resolver) => resolver.needsMeasurement);
6425
- const elementsToMeasure = new Set(resolversToMeasure.map((resolver) => resolver.element));
6426
- const transformsToRestore = new Map();
6427
- /**
6428
- * Write pass
6429
- * If we're measuring elements we want to remove bounding box-changing transforms.
6430
- */
6431
- elementsToMeasure.forEach((element) => {
6432
- const removedTransforms = removeNonTranslationalTransform(element);
6433
- if (!removedTransforms.length)
6434
- return;
6435
- transformsToRestore.set(element, removedTransforms);
6436
- element.render();
6437
- });
6438
- // Read
6439
- resolversToMeasure.forEach((resolver) => resolver.measureInitialState());
6440
- // Write
6441
- elementsToMeasure.forEach((element) => {
6442
- element.render();
6443
- const restore = transformsToRestore.get(element);
6444
- if (restore) {
6445
- restore.forEach(([key, value]) => {
6446
- element.getValue(key)?.set(value);
6447
- });
6448
- }
6449
- });
6450
- // Read
6451
- resolversToMeasure.forEach((resolver) => resolver.measureEndState());
6452
- // Write
6453
- resolversToMeasure.forEach((resolver) => {
6454
- if (resolver.suspendedScrollY !== undefined) {
6455
- window.scrollTo(0, resolver.suspendedScrollY);
6456
- }
6457
- });
6458
- }
6459
- anyNeedsMeasurement = false;
6460
- isScheduled = false;
6461
- toResolve.forEach((resolver) => resolver.complete());
6462
- toResolve.clear();
6463
- }
6464
- function readAllKeyframes() {
6465
- toResolve.forEach((resolver) => {
6466
- resolver.readKeyframes();
6467
- if (resolver.needsMeasurement) {
6468
- anyNeedsMeasurement = true;
6469
- }
6470
- });
6471
- }
6472
- function flushKeyframeResolvers() {
6473
- readAllKeyframes();
6474
- measureAllKeyframes();
6475
- }
6476
- class KeyframeResolver {
6477
- constructor(unresolvedKeyframes, onComplete, name, motionValue, element, isAsync = false) {
6478
- /**
6479
- * Track whether this resolver has completed. Once complete, it never
6480
- * needs to attempt keyframe resolution again.
6481
- */
6482
- this.isComplete = false;
6483
- /**
6484
- * Track whether this resolver is async. If it is, it'll be added to the
6485
- * resolver queue and flushed in the next frame. Resolvers that aren't going
6486
- * to trigger read/write thrashing don't need to be async.
6487
- */
6488
- this.isAsync = false;
6489
- /**
6490
- * Track whether this resolver needs to perform a measurement
6491
- * to resolve its keyframes.
6492
- */
6493
- this.needsMeasurement = false;
6494
- /**
6495
- * Track whether this resolver is currently scheduled to resolve
6496
- * to allow it to be cancelled and resumed externally.
6497
- */
6498
- this.isScheduled = false;
6499
- this.unresolvedKeyframes = [...unresolvedKeyframes];
6500
- this.onComplete = onComplete;
6501
- this.name = name;
6502
- this.motionValue = motionValue;
6503
- this.element = element;
6504
- this.isAsync = isAsync;
6505
- }
6506
- scheduleResolve() {
6507
- this.isScheduled = true;
6508
- if (this.isAsync) {
6509
- toResolve.add(this);
6510
- if (!isScheduled) {
6511
- isScheduled = true;
6512
- frame.read(readAllKeyframes);
6513
- frame.resolveKeyframes(measureAllKeyframes);
6514
- }
6515
- }
6516
- else {
6517
- this.readKeyframes();
6518
- this.complete();
6519
- }
6520
- }
6521
- readKeyframes() {
6522
- const { unresolvedKeyframes, name, element, motionValue } = this;
6523
- /**
6524
- * If a keyframe is null, we hydrate it either by reading it from
6525
- * the instance, or propagating from previous keyframes.
6526
- */
6527
- for (let i = 0; i < unresolvedKeyframes.length; i++) {
6528
- if (unresolvedKeyframes[i] === null) {
6529
- /**
6530
- * If the first keyframe is null, we need to find its value by sampling the element
6531
- */
6532
- if (i === 0) {
6533
- const currentValue = motionValue?.get();
6534
- const finalKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
6535
- if (currentValue !== undefined) {
6536
- unresolvedKeyframes[0] = currentValue;
6537
- }
6538
- else if (element && name) {
6539
- const valueAsRead = element.readValue(name, finalKeyframe);
6540
- if (valueAsRead !== undefined && valueAsRead !== null) {
6541
- unresolvedKeyframes[0] = valueAsRead;
6542
- }
6543
- }
6544
- if (unresolvedKeyframes[0] === undefined) {
6545
- unresolvedKeyframes[0] = finalKeyframe;
6546
- }
6547
- if (motionValue && currentValue === undefined) {
6548
- motionValue.set(unresolvedKeyframes[0]);
6549
- }
6550
- }
6551
- else {
6552
- unresolvedKeyframes[i] = unresolvedKeyframes[i - 1];
6553
- }
6554
- }
6555
- }
6556
- }
6557
- setFinalKeyframe() { }
6558
- measureInitialState() { }
6559
- renderEndStyles() { }
6560
- measureEndState() { }
6561
- complete() {
6562
- this.isComplete = true;
6563
- this.onComplete(this.unresolvedKeyframes, this.finalKeyframe);
6564
- toResolve.delete(this);
6565
- }
6566
- cancel() {
6567
- if (!this.isComplete) {
6568
- this.isScheduled = false;
6569
- toResolve.delete(this);
6570
- }
6571
- }
6572
- resume() {
6573
- if (!this.isComplete)
6574
- this.scheduleResolve();
6575
- }
6576
- }
6577
-
6578
- /**
6579
- * Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1"
6580
- */
6581
- const isNumericalString = (v) => /^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(v);
6582
-
6583
- /**
6584
- * Parse Framer's special CSS variable format into a CSS token and a fallback.
6585
- *
6586
- * ```
6587
- * `var(--foo, #fff)` => [`--foo`, '#fff']
6588
- * ```
6589
- *
6590
- * @param current
6591
- */
6592
- const splitCSSVariableRegex =
6593
- // eslint-disable-next-line redos-detector/no-unsafe-regex -- false positive, as it can match a lot of words
6594
- /^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;
6595
- function parseCSSVariable(current) {
6596
- const match = splitCSSVariableRegex.exec(current);
6597
- if (!match)
6598
- return [,];
6599
- const [, token1, token2, fallback] = match;
6600
- return [`--${token1 ?? token2}`, fallback];
6601
- }
6602
- const maxDepth = 4;
6603
- function getVariableValue(current, element, depth = 1) {
6604
- invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`);
6605
- const [token, fallback] = parseCSSVariable(current);
6606
- // No CSS variable detected
6607
- if (!token)
6608
- return;
6609
- // Attempt to read this CSS variable off the element
6610
- const resolved = window.getComputedStyle(element).getPropertyValue(token);
6611
- if (resolved) {
6612
- const trimmed = resolved.trim();
6613
- return isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed;
6614
- }
6615
- return isCSSVariableToken(fallback)
6616
- ? getVariableValue(fallback, element, depth + 1)
6617
- : fallback;
6618
- }
6619
-
6620
- /**
6621
- * Tests a provided value against a ValueType
6622
- */
6623
- const testValueType = (v) => (type) => type.test(v);
6624
-
6625
- /**
6626
- * ValueType for "auto"
6627
- */
6628
- const auto = {
6629
- test: (v) => v === "auto",
6630
- parse: (v) => v,
6631
- };
6632
-
6633
- /**
6634
- * A list of value types commonly used for dimensions
6635
- */
6636
- const dimensionValueTypes = [number, px, percent, degrees, vw, vh, auto];
6637
- /**
6638
- * Tests a dimensional value against the list of dimension ValueTypes
6639
- */
6640
- const findDimensionValueType = (v) => dimensionValueTypes.find(testValueType(v));
6641
-
6642
- class DOMKeyframesResolver extends KeyframeResolver {
6643
- constructor(unresolvedKeyframes, onComplete, name, motionValue, element) {
6644
- super(unresolvedKeyframes, onComplete, name, motionValue, element, true);
6645
- }
6646
- readKeyframes() {
6647
- const { unresolvedKeyframes, element, name } = this;
6648
- if (!element || !element.current)
6649
- return;
6650
- super.readKeyframes();
6651
- /**
6652
- * If any keyframe is a CSS variable, we need to find its value by sampling the element
6653
- */
6654
- for (let i = 0; i < unresolvedKeyframes.length; i++) {
6655
- let keyframe = unresolvedKeyframes[i];
6656
- if (typeof keyframe === "string") {
6657
- keyframe = keyframe.trim();
6658
- if (isCSSVariableToken(keyframe)) {
6659
- const resolved = getVariableValue(keyframe, element.current);
6660
- if (resolved !== undefined) {
6661
- unresolvedKeyframes[i] = resolved;
6662
- }
6663
- if (i === unresolvedKeyframes.length - 1) {
6664
- this.finalKeyframe = keyframe;
6665
- }
6666
- }
6667
- }
6668
- }
6669
- /**
6670
- * Resolve "none" values. We do this potentially twice - once before and once after measuring keyframes.
6671
- * This could be seen as inefficient but it's a trade-off to avoid measurements in more situations, which
6672
- * have a far bigger performance impact.
6673
- */
6674
- this.resolveNoneKeyframes();
6675
- /**
6676
- * Check to see if unit type has changed. If so schedule jobs that will
6677
- * temporarily set styles to the destination keyframes.
6678
- * Skip if we have more than two keyframes or this isn't a positional value.
6679
- * TODO: We can throw if there are multiple keyframes and the value type changes.
6680
- */
6681
- if (!positionalKeys.has(name) || unresolvedKeyframes.length !== 2) {
6682
- return;
6683
- }
6684
- const [origin, target] = unresolvedKeyframes;
6685
- const originType = findDimensionValueType(origin);
6686
- const targetType = findDimensionValueType(target);
6687
- /**
6688
- * Either we don't recognise these value types or we can animate between them.
6689
- */
6690
- if (originType === targetType)
6691
- return;
6692
- /**
6693
- * If both values are numbers or pixels, we can animate between them by
6694
- * converting them to numbers.
6695
- */
6696
- if (isNumOrPxType(originType) && isNumOrPxType(targetType)) {
6697
- for (let i = 0; i < unresolvedKeyframes.length; i++) {
6698
- const value = unresolvedKeyframes[i];
6699
- if (typeof value === "string") {
6700
- unresolvedKeyframes[i] = parseFloat(value);
6701
- }
6702
- }
6703
- }
6704
- else {
6705
- /**
6706
- * Else, the only way to resolve this is by measuring the element.
6707
- */
6708
- this.needsMeasurement = true;
6709
- }
6710
- }
6711
- resolveNoneKeyframes() {
6712
- const { unresolvedKeyframes, name } = this;
6713
- const noneKeyframeIndexes = [];
6714
- for (let i = 0; i < unresolvedKeyframes.length; i++) {
6715
- if (isNone(unresolvedKeyframes[i])) {
6716
- noneKeyframeIndexes.push(i);
6717
- }
6718
- }
6719
- if (noneKeyframeIndexes.length) {
6720
- makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name);
6721
- }
6722
- }
6723
- measureInitialState() {
6724
- const { element, unresolvedKeyframes, name } = this;
6725
- if (!element || !element.current)
6726
- return;
6727
- if (name === "height") {
6728
- this.suspendedScrollY = window.pageYOffset;
6729
- }
6730
- this.measuredOrigin = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
6731
- unresolvedKeyframes[0] = this.measuredOrigin;
6732
- // Set final key frame to measure after next render
6733
- const measureKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
6734
- if (measureKeyframe !== undefined) {
6735
- element.getValue(name, measureKeyframe).jump(measureKeyframe, false);
6736
- }
6737
- }
6738
- measureEndState() {
6739
- const { element, name, unresolvedKeyframes } = this;
6740
- if (!element || !element.current)
6741
- return;
6742
- const value = element.getValue(name);
6743
- value && value.jump(this.measuredOrigin, false);
6744
- const finalKeyframeIndex = unresolvedKeyframes.length - 1;
6745
- const finalKeyframe = unresolvedKeyframes[finalKeyframeIndex];
6746
- unresolvedKeyframes[finalKeyframeIndex] = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
6747
- if (finalKeyframe !== null && this.finalKeyframe === undefined) {
6748
- this.finalKeyframe = finalKeyframe;
6749
- }
6750
- // If we removed transform values, reapply them before the next render
6751
- if (this.removedTransforms?.length) {
6752
- this.removedTransforms.forEach(([unsetTransformName, unsetTransformValue]) => {
6753
- element
6754
- .getValue(unsetTransformName)
6755
- .set(unsetTransformValue);
6756
- });
6757
- }
6758
- this.resolveNoneKeyframes();
6759
- }
6760
- }
6761
-
6762
- /**
6763
- * Check if a value is animatable. Examples:
6764
- *
6765
- * ✅: 100, "100px", "#fff"
6766
- * ❌: "block", "url(2.jpg)"
6767
- * @param value
6768
- *
6769
- * @internal
6770
- */
6771
- const isAnimatable = (value, name) => {
6772
- // If the list of keys tat might be non-animatable grows, replace with Set
6773
- if (name === "zIndex")
6774
- return false;
6775
- // If it's a number or a keyframes array, we can animate it. We might at some point
6776
- // need to do a deep isAnimatable check of keyframes, or let Popmotion handle this,
6777
- // but for now lets leave it like this for performance reasons
6778
- if (typeof value === "number" || Array.isArray(value))
6779
- return true;
6780
- if (typeof value === "string" && // It's animatable if we have a string
6781
- (complex.test(value) || value === "0") && // And it contains numbers and/or colors
6782
- !value.startsWith("url(") // Unless it starts with "url("
6783
- ) {
6784
- return true;
6785
- }
6786
- return false;
6787
- };
6788
-
6789
- function isGenerator(type) {
6790
- return typeof type === "function" && "applyToOptions" in type;
6791
- }
6792
-
6793
- function hasKeyframesChanged(keyframes) {
6794
- const current = keyframes[0];
6795
- if (keyframes.length === 1)
6796
- return true;
6797
- for (let i = 0; i < keyframes.length; i++) {
6798
- if (keyframes[i] !== current)
6799
- return true;
6800
- }
6801
- }
6802
- function canAnimate(keyframes, name, type, velocity) {
6803
- /**
6804
- * Check if we're able to animate between the start and end keyframes,
6805
- * and throw a warning if we're attempting to animate between one that's
6806
- * animatable and another that isn't.
6807
- */
6808
- const originKeyframe = keyframes[0];
6809
- if (originKeyframe === null)
6810
- return false;
6811
- /**
6812
- * These aren't traditionally animatable but we do support them.
6813
- * In future we could look into making this more generic or replacing
6814
- * this function with mix() === mixImmediate
6815
- */
6816
- if (name === "display" || name === "visibility")
6817
- return true;
6818
- const targetKeyframe = keyframes[keyframes.length - 1];
6819
- const isOriginAnimatable = isAnimatable(originKeyframe, name);
6820
- const isTargetAnimatable = isAnimatable(targetKeyframe, name);
6821
- 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.`);
6822
- // Always skip if any of these are true
6823
- if (!isOriginAnimatable || !isTargetAnimatable) {
6824
- return false;
6825
- }
6826
- return (hasKeyframesChanged(keyframes) ||
6827
- ((type === "spring" || isGenerator(type)) && velocity));
6828
- }
6829
-
6830
- const isNotNull = (value) => value !== null;
6831
- function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
6832
- const resolvedKeyframes = keyframes.filter(isNotNull);
6833
- const index = repeat && repeatType !== "loop" && repeat % 2 === 1
6834
- ? 0
6835
- : resolvedKeyframes.length - 1;
6836
- return !index || finalKeyframe === undefined
6837
- ? resolvedKeyframes[index]
6838
- : finalKeyframe;
6839
- }
6840
-
6841
- /**
6842
- * Maximum time allowed between an animation being created and it being
6843
- * resolved for us to use the latter as the start time.
6844
- *
6845
- * This is to ensure that while we prefer to "start" an animation as soon
6846
- * as it's triggered, we also want to avoid a visual jump if there's a big delay
6847
- * between these two moments.
6848
- */
6849
- const MAX_RESOLVE_DELAY = 40;
6850
- class BaseAnimation {
6851
- constructor({ autoplay = true, delay = 0, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", ...options }) {
6852
- // Track whether the animation has been stopped. Stopped animations won't restart.
6853
- this.isStopped = false;
6854
- this.hasAttemptedResolve = false;
6855
- this.createdAt = time.now();
6856
- this.options = {
6857
- autoplay,
6858
- delay,
6859
- type,
6860
- repeat,
6861
- repeatDelay,
6862
- repeatType,
6863
- ...options,
6864
- };
6865
- this.updateFinishedPromise();
6866
- }
6867
- /**
6868
- * This method uses the createdAt and resolvedAt to calculate the
6869
- * animation startTime. *Ideally*, we would use the createdAt time as t=0
6870
- * as the following frame would then be the first frame of the animation in
6871
- * progress, which would feel snappier.
6872
- *
6873
- * However, if there's a delay (main thread work) between the creation of
6874
- * the animation and the first commited frame, we prefer to use resolvedAt
6875
- * to avoid a sudden jump into the animation.
6876
- */
6877
- calcStartTime() {
6878
- if (!this.resolvedAt)
6879
- return this.createdAt;
6880
- return this.resolvedAt - this.createdAt > MAX_RESOLVE_DELAY
6881
- ? this.resolvedAt
6882
- : this.createdAt;
6883
- }
6884
- /**
6885
- * A getter for resolved data. If keyframes are not yet resolved, accessing
6886
- * this.resolved will synchronously flush all pending keyframe resolvers.
6887
- * This is a deoptimisation, but at its worst still batches read/writes.
6888
- */
6889
- get resolved() {
6890
- if (!this._resolved && !this.hasAttemptedResolve) {
6891
- flushKeyframeResolvers();
6892
- }
6893
- return this._resolved;
6894
- }
6895
- /**
6896
- * A method to be called when the keyframes resolver completes. This method
6897
- * will check if its possible to run the animation and, if not, skip it.
6898
- * Otherwise, it will call initPlayback on the implementing class.
6899
- */
6900
- onKeyframesResolved(keyframes, finalKeyframe) {
6901
- this.resolvedAt = time.now();
6902
- this.hasAttemptedResolve = true;
6903
- const { name, type, velocity, delay, onComplete, onUpdate, isGenerator, } = this.options;
6904
- /**
6905
- * If we can't animate this value with the resolved keyframes
6906
- * then we should complete it immediately.
6907
- */
6908
- if (!isGenerator && !canAnimate(keyframes, name, type, velocity)) {
6909
- // Finish immediately
6910
- if (!delay) {
6911
- onUpdate &&
6912
- onUpdate(getFinalKeyframe(keyframes, this.options, finalKeyframe));
6913
- onComplete && onComplete();
6914
- this.resolveFinishedPromise();
6915
- return;
6916
- }
6917
- // Finish after a delay
6918
- else {
6919
- this.options.duration = 0;
6920
- }
6921
- }
6922
- const resolvedAnimation = this.initPlayback(keyframes, finalKeyframe);
6923
- if (resolvedAnimation === false)
6924
- return;
6925
- this._resolved = {
6926
- keyframes,
6927
- finalKeyframe,
6928
- ...resolvedAnimation,
6929
- };
6930
- this.onPostResolved();
6931
- }
6932
- onPostResolved() { }
6933
- /**
6934
- * Allows the returned animation to be awaited or promise-chained. Currently
6935
- * resolves when the animation finishes at all but in a future update could/should
6936
- * reject if its cancels.
6937
- */
6938
- then(resolve, reject) {
6939
- return this.currentFinishedPromise.then(resolve, reject);
6940
- }
6941
- flatten() {
6942
- if (!this.options.allowFlatten)
6943
- return;
6944
- this.options.type = "keyframes";
6945
- this.options.ease = "linear";
6946
- }
6947
- updateFinishedPromise() {
6948
- this.currentFinishedPromise = new Promise((resolve) => {
6949
- this.resolveFinishedPromise = resolve;
6950
- });
6951
- }
6952
- }
6953
-
6954
- /*
6955
- Value in range from progress
6956
-
6957
- Given a lower limit and an upper limit, we return the value within
6958
- that range as expressed by progress (usually a number from 0 to 1)
6959
-
6960
- So progress = 0.5 would change
6961
-
6962
- from -------- to
6963
-
6964
- to
6965
-
6966
- from ---- to
6967
-
6968
- E.g. from = 10, to = 20, progress = 0.5 => 15
6969
-
6970
- @param [number]: Lower limit of range
6971
- @param [number]: Upper limit of range
6972
- @param [number]: The progress between lower and upper limits expressed 0-1
6973
- @return [number]: Value as calculated from progress within range (not limited within range)
6974
- */
6975
- const mixNumber$1 = (from, to, progress) => {
6976
- return from + (to - from) * progress;
6977
- };
6978
-
6979
- // Adapted from https://gist.github.com/mjackson/5311256
6980
- function hueToRgb(p, q, t) {
6981
- if (t < 0)
6982
- t += 1;
6983
- if (t > 1)
6984
- t -= 1;
6985
- if (t < 1 / 6)
6986
- return p + (q - p) * 6 * t;
6987
- if (t < 1 / 2)
6988
- return q;
6989
- if (t < 2 / 3)
6990
- return p + (q - p) * (2 / 3 - t) * 6;
6991
- return p;
6992
- }
6993
- function hslaToRgba({ hue, saturation, lightness, alpha }) {
6994
- hue /= 360;
6995
- saturation /= 100;
6996
- lightness /= 100;
6997
- let red = 0;
6998
- let green = 0;
6999
- let blue = 0;
7000
- if (!saturation) {
7001
- red = green = blue = lightness;
7002
- }
7003
- else {
7004
- const q = lightness < 0.5
7005
- ? lightness * (1 + saturation)
7006
- : lightness + saturation - lightness * saturation;
7007
- const p = 2 * lightness - q;
7008
- red = hueToRgb(p, q, hue + 1 / 3);
7009
- green = hueToRgb(p, q, hue);
7010
- blue = hueToRgb(p, q, hue - 1 / 3);
7011
- }
7012
- return {
7013
- red: Math.round(red * 255),
7014
- green: Math.round(green * 255),
7015
- blue: Math.round(blue * 255),
7016
- alpha,
7017
- };
7018
- }
7019
-
7020
- function mixImmediate(a, b) {
7021
- return (p) => (p > 0 ? b : a);
7022
- }
7023
-
7024
- // Linear color space blending
7025
- // Explained https://www.youtube.com/watch?v=LKnqECcg6Gw
7026
- // Demonstrated http://codepen.io/osublake/pen/xGVVaN
7027
- const mixLinearColor = (from, to, v) => {
7028
- const fromExpo = from * from;
7029
- const expo = v * (to * to - fromExpo) + fromExpo;
7030
- return expo < 0 ? 0 : Math.sqrt(expo);
7031
- };
7032
- const colorTypes = [hex, rgba, hsla];
7033
- const getColorType = (v) => colorTypes.find((type) => type.test(v));
7034
- function asRGBA(color) {
7035
- const type = getColorType(color);
7036
- warning(Boolean(type), `'${color}' is not an animatable color. Use the equivalent color code instead.`);
7037
- if (!Boolean(type))
7038
- return false;
7039
- let model = type.parse(color);
7040
- if (type === hsla) {
7041
- // TODO Remove this cast - needed since Motion's stricter typing
7042
- model = hslaToRgba(model);
7043
- }
7044
- return model;
7045
- }
7046
- const mixColor = (from, to) => {
7047
- const fromRGBA = asRGBA(from);
7048
- const toRGBA = asRGBA(to);
7049
- if (!fromRGBA || !toRGBA) {
7050
- return mixImmediate(from, to);
7051
- }
7052
- const blended = { ...fromRGBA };
7053
- return (v) => {
7054
- blended.red = mixLinearColor(fromRGBA.red, toRGBA.red, v);
7055
- blended.green = mixLinearColor(fromRGBA.green, toRGBA.green, v);
7056
- blended.blue = mixLinearColor(fromRGBA.blue, toRGBA.blue, v);
7057
- blended.alpha = mixNumber$1(fromRGBA.alpha, toRGBA.alpha, v);
7058
- return rgba.transform(blended);
7059
- };
7060
- };
7061
-
7062
6198
  /**
7063
6199
  * Pipe
7064
6200
  * Compose other transformers to run linearily
@@ -7069,21 +6205,6 @@ const mixColor = (from, to) => {
7069
6205
  const combineFunctions = (a, b) => (v) => b(a(v));
7070
6206
  const pipe = (...transformers) => transformers.reduce(combineFunctions);
7071
6207
 
7072
- const invisibleValues = new Set(["none", "hidden"]);
7073
- /**
7074
- * Returns a function that, when provided a progress value between 0 and 1,
7075
- * will return the "none" or "hidden" string only when the progress is that of
7076
- * the origin or target.
7077
- */
7078
- function mixVisibility(origin, target) {
7079
- if (invisibleValues.has(origin)) {
7080
- return (p) => (p <= 0 ? origin : target);
7081
- }
7082
- else {
7083
- return (p) => (p >= 1 ? target : origin);
7084
- }
7085
- }
7086
-
7087
6208
  function mixNumber(a, b) {
7088
6209
  return (p) => mixNumber$1(a, b, p);
7089
6210
  }
@@ -7164,16 +6285,71 @@ const mixComplex = (origin, target) => {
7164
6285
  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.`);
7165
6286
  return mixImmediate(origin, target);
7166
6287
  }
7167
- };
6288
+ };
6289
+
6290
+ function mix(from, to, p) {
6291
+ if (typeof from === "number" &&
6292
+ typeof to === "number" &&
6293
+ typeof p === "number") {
6294
+ return mixNumber$1(from, to, p);
6295
+ }
6296
+ const mixer = getMixer(from);
6297
+ return mixer(from, to);
6298
+ }
6299
+
6300
+ const frameloopDriver = (update) => {
6301
+ const passTimestamp = ({ timestamp }) => update(timestamp);
6302
+ return {
6303
+ start: () => frame.update(passTimestamp, true),
6304
+ stop: () => cancelFrame(passTimestamp),
6305
+ /**
6306
+ * If we're processing this frame we can use the
6307
+ * framelocked timestamp to keep things in sync.
6308
+ */
6309
+ now: () => (frameData.isProcessing ? frameData.timestamp : time.now()),
6310
+ };
6311
+ };
6312
+
6313
+ const generateLinearEasing = (easing, duration, // as milliseconds
6314
+ resolution = 10 // as milliseconds
6315
+ ) => {
6316
+ let points = "";
6317
+ const numPoints = Math.max(Math.round(duration / resolution), 2);
6318
+ for (let i = 0; i < numPoints; i++) {
6319
+ points += easing(i / (numPoints - 1)) + ", ";
6320
+ }
6321
+ return `linear(${points.substring(0, points.length - 2)})`;
6322
+ };
6323
+
6324
+ /**
6325
+ * Implement a practical max duration for keyframe generation
6326
+ * to prevent infinite loops
6327
+ */
6328
+ const maxGeneratorDuration = 20000;
6329
+ function calcGeneratorDuration(generator) {
6330
+ let duration = 0;
6331
+ const timeStep = 50;
6332
+ let state = generator.next(duration);
6333
+ while (!state.done && duration < maxGeneratorDuration) {
6334
+ duration += timeStep;
6335
+ state = generator.next(duration);
6336
+ }
6337
+ return duration >= maxGeneratorDuration ? Infinity : duration;
6338
+ }
7168
6339
 
7169
- function mix(from, to, p) {
7170
- if (typeof from === "number" &&
7171
- typeof to === "number" &&
7172
- typeof p === "number") {
7173
- return mixNumber$1(from, to, p);
7174
- }
7175
- const mixer = getMixer(from);
7176
- return mixer(from, to);
6340
+ /**
6341
+ * Create a progress => progress easing function from a generator.
6342
+ */
6343
+ function createGeneratorEasing(options, scale = 100, createGenerator) {
6344
+ const generator = createGenerator({ ...options, keyframes: [0, scale] });
6345
+ const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
6346
+ return {
6347
+ type: "keyframes",
6348
+ ease: (progress) => {
6349
+ return generator.next(duration * progress).value / scale;
6350
+ },
6351
+ duration: millisecondsToSeconds(duration),
6352
+ };
7177
6353
  }
7178
6354
 
7179
6355
  const velocitySampleDuration = 5; // ms
@@ -7208,17 +6384,6 @@ const springDefaults = {
7208
6384
  maxDamping: 1,
7209
6385
  };
7210
6386
 
7211
- /**
7212
- * Converts seconds to milliseconds
7213
- *
7214
- * @param seconds - Time in seconds.
7215
- * @return milliseconds - Converted time in milliseconds.
7216
- */
7217
- /*#__NO_SIDE_EFFECTS__*/
7218
- const secondsToMilliseconds = (seconds) => seconds * 1000;
7219
- /*#__NO_SIDE_EFFECTS__*/
7220
- const millisecondsToSeconds = (milliseconds) => milliseconds / 1000;
7221
-
7222
6387
  const safeMin = 0.001;
7223
6388
  function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, velocity = springDefaults.velocity, mass = springDefaults.mass, }) {
7224
6389
  let envelope;
@@ -7299,81 +6464,6 @@ function calcAngularFreq(undampedFreq, dampingRatio) {
7299
6464
  return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
7300
6465
  }
7301
6466
 
7302
- /**
7303
- * Implement a practical max duration for keyframe generation
7304
- * to prevent infinite loops
7305
- */
7306
- const maxGeneratorDuration = 20000;
7307
- function calcGeneratorDuration(generator) {
7308
- let duration = 0;
7309
- const timeStep = 50;
7310
- let state = generator.next(duration);
7311
- while (!state.done && duration < maxGeneratorDuration) {
7312
- duration += timeStep;
7313
- state = generator.next(duration);
7314
- }
7315
- return duration >= maxGeneratorDuration ? Infinity : duration;
7316
- }
7317
-
7318
- /**
7319
- * Create a progress => progress easing function from a generator.
7320
- */
7321
- function createGeneratorEasing(options, scale = 100, createGenerator) {
7322
- const generator = createGenerator({ ...options, keyframes: [0, scale] });
7323
- const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
7324
- return {
7325
- type: "keyframes",
7326
- ease: (progress) => {
7327
- return generator.next(duration * progress).value / scale;
7328
- },
7329
- duration: millisecondsToSeconds(duration),
7330
- };
7331
- }
7332
-
7333
- /**
7334
- * Add the ability for test suites to manually set support flags
7335
- * to better test more environments.
7336
- */
7337
- const supportsFlags = {};
7338
-
7339
- /*#__NO_SIDE_EFFECTS__*/
7340
- function memo(callback) {
7341
- let result;
7342
- return () => {
7343
- if (result === undefined)
7344
- result = callback();
7345
- return result;
7346
- };
7347
- }
7348
-
7349
- function memoSupports(callback, supportsFlag) {
7350
- const memoized = memo(callback);
7351
- return () => supportsFlags[supportsFlag] ?? memoized();
7352
- }
7353
-
7354
- const supportsLinearEasing = /*@__PURE__*/ memoSupports(() => {
7355
- try {
7356
- document
7357
- .createElement("div")
7358
- .animate({ opacity: 0 }, { easing: "linear(0, 1)" });
7359
- }
7360
- catch (e) {
7361
- return false;
7362
- }
7363
- return true;
7364
- }, "linearEasing");
7365
-
7366
- const generateLinearEasing = (easing, duration, // as milliseconds
7367
- resolution = 10 // as milliseconds
7368
- ) => {
7369
- let points = "";
7370
- const numPoints = Math.max(Math.round(duration / resolution), 2);
7371
- for (let i = 0; i < numPoints; i++) {
7372
- points += easing(i / (numPoints - 1)) + ", ";
7373
- }
7374
- return `linear(${points.substring(0, points.length - 2)})`;
7375
- };
7376
-
7377
6467
  const durationKeys = ["duration", "bounce"];
7378
6468
  const physicsKeys = ["stiffness", "damping", "mass"];
7379
6469
  function isSpringType(options, keys) {
@@ -7500,7 +6590,7 @@ function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce
7500
6590
  next: (t) => {
7501
6591
  const current = resolveSpring(t);
7502
6592
  if (!isResolvedFromDuration) {
7503
- let currentVelocity = 0.0;
6593
+ let currentVelocity = t === 0 ? initialVelocity : 0.0;
7504
6594
  /**
7505
6595
  * We only need to calculate velocity for under-damped springs
7506
6596
  * as over- and critically-damped springs can't overshoot, so
@@ -7534,7 +6624,7 @@ function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce
7534
6624
  }
7535
6625
  spring.applyToOptions = (options) => {
7536
6626
  const generatorOptions = createGeneratorEasing(options, 100, spring);
7537
- options.ease = supportsLinearEasing() ? generatorOptions.ease : "easeOut";
6627
+ options.ease = generatorOptions.ease;
7538
6628
  options.duration = secondsToMilliseconds(generatorOptions.duration);
7539
6629
  options.type = "keyframes";
7540
6630
  return options;
@@ -7623,44 +6713,6 @@ function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, b
7623
6713
  };
7624
6714
  }
7625
6715
 
7626
- const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1);
7627
- const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1);
7628
- const easeInOut = /*@__PURE__*/ cubicBezier(0.42, 0, 0.58, 1);
7629
-
7630
- const isEasingArray = (ease) => {
7631
- return Array.isArray(ease) && typeof ease[0] !== "number";
7632
- };
7633
-
7634
- const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number";
7635
-
7636
- const easingLookup = {
7637
- linear: noop,
7638
- easeIn,
7639
- easeInOut,
7640
- easeOut,
7641
- circIn,
7642
- circInOut,
7643
- circOut,
7644
- backIn,
7645
- backInOut,
7646
- backOut,
7647
- anticipate,
7648
- };
7649
- const easingDefinitionToFunction = (definition) => {
7650
- if (isBezierDefinition(definition)) {
7651
- // If cubic bezier definition, create bezier curve
7652
- invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`);
7653
- const [x1, y1, x2, y2] = definition;
7654
- return cubicBezier(x1, y1, x2, y2);
7655
- }
7656
- else if (typeof definition === "string") {
7657
- // Else lookup from table
7658
- invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`);
7659
- return easingLookup[definition];
7660
- }
7661
- return definition;
7662
- };
7663
-
7664
6716
  /*
7665
6717
  Progress within given range
7666
6718
 
@@ -7681,7 +6733,7 @@ const progress = (from, to, value) => {
7681
6733
 
7682
6734
  function createMixers(output, ease, customMixer) {
7683
6735
  const mixers = [];
7684
- const mixerFactory = customMixer || mix;
6736
+ const mixerFactory = customMixer || MotionGlobalConfig.mix || mix;
7685
6737
  const numMixers = output.length - 1;
7686
6738
  for (let i = 0; i < numMixers; i++) {
7687
6739
  let mixer = mixerFactory(output[i], output[i + 1]);
@@ -7755,17 +6807,124 @@ function fillOffset(offset, remaining) {
7755
6807
  const offsetProgress = progress(0, remaining, i);
7756
6808
  offset.push(mixNumber$1(min, 1, offsetProgress));
7757
6809
  }
7758
- }
7759
-
7760
- function defaultOffset(arr) {
7761
- const offset = [0];
7762
- fillOffset(offset, arr.length - 1);
7763
- return offset;
7764
- }
7765
-
7766
- function convertOffsetToTimes(offset, duration) {
7767
- return offset.map((o) => o * duration);
7768
- }
6810
+ }
6811
+
6812
+ function defaultOffset(arr) {
6813
+ const offset = [0];
6814
+ fillOffset(offset, arr.length - 1);
6815
+ return offset;
6816
+ }
6817
+
6818
+ function convertOffsetToTimes(offset, duration) {
6819
+ return offset.map((o) => o * duration);
6820
+ }
6821
+
6822
+ /*
6823
+ Bezier function generator
6824
+ This has been modified from Gaëtan Renaudeau's BezierEasing
6825
+ https://github.com/gre/bezier-easing/blob/master/src/index.js
6826
+ https://github.com/gre/bezier-easing/blob/master/LICENSE
6827
+
6828
+ I've removed the newtonRaphsonIterate algo because in benchmarking it
6829
+ wasn't noticiably faster than binarySubdivision, indeed removing it
6830
+ usually improved times, depending on the curve.
6831
+ I also removed the lookup table, as for the added bundle size and loop we're
6832
+ only cutting ~4 or so subdivision iterations. I bumped the max iterations up
6833
+ to 12 to compensate and this still tended to be faster for no perceivable
6834
+ loss in accuracy.
6835
+ Usage
6836
+ const easeOut = cubicBezier(.17,.67,.83,.67);
6837
+ const x = easeOut(0.5); // returns 0.627...
6838
+ */
6839
+ // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
6840
+ const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) *
6841
+ t;
6842
+ const subdivisionPrecision = 0.0000001;
6843
+ const subdivisionMaxIterations = 12;
6844
+ function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) {
6845
+ let currentX;
6846
+ let currentT;
6847
+ let i = 0;
6848
+ do {
6849
+ currentT = lowerBound + (upperBound - lowerBound) / 2.0;
6850
+ currentX = calcBezier(currentT, mX1, mX2) - x;
6851
+ if (currentX > 0.0) {
6852
+ upperBound = currentT;
6853
+ }
6854
+ else {
6855
+ lowerBound = currentT;
6856
+ }
6857
+ } while (Math.abs(currentX) > subdivisionPrecision &&
6858
+ ++i < subdivisionMaxIterations);
6859
+ return currentT;
6860
+ }
6861
+ function cubicBezier(mX1, mY1, mX2, mY2) {
6862
+ // If this is a linear gradient, return linear easing
6863
+ if (mX1 === mY1 && mX2 === mY2)
6864
+ return noop;
6865
+ const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2);
6866
+ // If animation is at start/end, return t without easing
6867
+ return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
6868
+ }
6869
+
6870
+ const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1);
6871
+ const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1);
6872
+ const easeInOut = /*@__PURE__*/ cubicBezier(0.42, 0, 0.58, 1);
6873
+
6874
+ const isEasingArray = (ease) => {
6875
+ return Array.isArray(ease) && typeof ease[0] !== "number";
6876
+ };
6877
+
6878
+ // Accepts an easing function and returns a new one that outputs mirrored values for
6879
+ // the second half of the animation. Turns easeIn into easeInOut.
6880
+ const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
6881
+
6882
+ // Accepts an easing function and returns a new one that outputs reversed values.
6883
+ // Turns easeIn into easeOut.
6884
+ const reverseEasing = (easing) => (p) => 1 - easing(1 - p);
6885
+
6886
+ const backOut = /*@__PURE__*/ cubicBezier(0.33, 1.53, 0.69, 0.99);
6887
+ const backIn = /*@__PURE__*/ reverseEasing(backOut);
6888
+ const backInOut = /*@__PURE__*/ mirrorEasing(backIn);
6889
+
6890
+ const anticipate = (p) => (p *= 2) < 1 ? 0.5 * backIn(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
6891
+
6892
+ const circIn = (p) => 1 - Math.sin(Math.acos(p));
6893
+ const circOut = reverseEasing(circIn);
6894
+ const circInOut = mirrorEasing(circIn);
6895
+
6896
+ const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number";
6897
+
6898
+ const easingLookup = {
6899
+ linear: noop,
6900
+ easeIn,
6901
+ easeInOut,
6902
+ easeOut,
6903
+ circIn,
6904
+ circInOut,
6905
+ circOut,
6906
+ backIn,
6907
+ backInOut,
6908
+ backOut,
6909
+ anticipate,
6910
+ };
6911
+ const isValidEasing = (easing) => {
6912
+ return typeof easing === "string";
6913
+ };
6914
+ const easingDefinitionToFunction = (definition) => {
6915
+ if (isBezierDefinition(definition)) {
6916
+ // If cubic bezier definition, create bezier curve
6917
+ invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`);
6918
+ const [x1, y1, x2, y2] = definition;
6919
+ return cubicBezier(x1, y1, x2, y2);
6920
+ }
6921
+ else if (isValidEasing(definition)) {
6922
+ // Else lookup from table
6923
+ invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`);
6924
+ return easingLookup[definition];
6925
+ }
6926
+ return definition;
6927
+ };
7769
6928
 
7770
6929
  function defaultEasing(values, easing) {
7771
6930
  return values.map(() => easing || easeInOut).splice(0, values.length - 1);
@@ -7810,68 +6969,84 @@ function keyframes({ duration = 300, keyframes: keyframeValues, times, ease = "e
7810
6969
  };
7811
6970
  }
7812
6971
 
7813
- const frameloopDriver = (update) => {
7814
- const passTimestamp = ({ timestamp }) => update(timestamp);
7815
- return {
7816
- start: () => frame.update(passTimestamp, true),
7817
- stop: () => cancelFrame(passTimestamp),
7818
- /**
7819
- * If we're processing this frame we can use the
7820
- * framelocked timestamp to keep things in sync.
7821
- */
7822
- now: () => (frameData.isProcessing ? frameData.timestamp : time.now()),
7823
- };
7824
- };
6972
+ const isNotNull = (value) => value !== null;
6973
+ function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe, speed = 1) {
6974
+ const resolvedKeyframes = keyframes.filter(isNotNull);
6975
+ const useFirstKeyframe = speed < 0 || (repeat && repeatType !== "loop" && repeat % 2 === 1);
6976
+ const index = useFirstKeyframe ? 0 : resolvedKeyframes.length - 1;
6977
+ return !index || finalKeyframe === undefined
6978
+ ? resolvedKeyframes[index]
6979
+ : finalKeyframe;
6980
+ }
7825
6981
 
7826
- const generators = {
6982
+ const transitionTypeMap = {
7827
6983
  decay: inertia,
7828
6984
  inertia,
7829
6985
  tween: keyframes,
7830
6986
  keyframes: keyframes,
7831
6987
  spring,
7832
6988
  };
6989
+ function replaceTransitionType(transition) {
6990
+ if (typeof transition.type === "string") {
6991
+ transition.type = transitionTypeMap[transition.type];
6992
+ }
6993
+ }
6994
+
6995
+ class WithPromise {
6996
+ constructor() {
6997
+ this.count = 0;
6998
+ this.updateFinished();
6999
+ }
7000
+ get finished() {
7001
+ return this._finished;
7002
+ }
7003
+ updateFinished() {
7004
+ this.count++;
7005
+ this._finished = new Promise((resolve) => {
7006
+ this.resolve = resolve;
7007
+ });
7008
+ }
7009
+ notifyFinished() {
7010
+ this.resolve();
7011
+ }
7012
+ /**
7013
+ * Allows the animation to be awaited.
7014
+ *
7015
+ * @deprecated Use `finished` instead.
7016
+ */
7017
+ then(onResolve, onReject) {
7018
+ return this.finished.then(onResolve, onReject);
7019
+ }
7020
+ }
7021
+
7833
7022
  const percentToProgress = (percent) => percent / 100;
7834
- /**
7835
- * Animation that runs on the main thread. Designed to be WAAPI-spec in the subset of
7836
- * features we expose publically. Mostly the compatibility is to ensure visual identity
7837
- * between both WAAPI and main thread animations.
7838
- */
7839
- class MainThreadAnimation extends BaseAnimation {
7023
+ class JSAnimation extends WithPromise {
7840
7024
  constructor(options) {
7841
- super(options);
7842
- /**
7843
- * The time at which the animation was paused.
7844
- */
7845
- this.holdTime = null;
7846
- /**
7847
- * The time at which the animation was cancelled.
7848
- */
7849
- this.cancelTime = null;
7025
+ super();
7026
+ this.state = "idle";
7027
+ this.startTime = null;
7028
+ this.isStopped = false;
7850
7029
  /**
7851
7030
  * The current time of the animation.
7852
7031
  */
7853
7032
  this.currentTime = 0;
7854
7033
  /**
7855
- * Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed.
7856
- */
7857
- this.playbackSpeed = 1;
7858
- /**
7859
- * The state of the animation to apply when the animation is resolved. This
7860
- * allows calls to the public API to control the animation before it is resolved,
7861
- * without us having to resolve it first.
7034
+ * The time at which the animation was paused.
7862
7035
  */
7863
- this.pendingPlayState = "running";
7036
+ this.holdTime = null;
7864
7037
  /**
7865
- * The time at which the animation was started.
7038
+ * Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed.
7866
7039
  */
7867
- this.startTime = null;
7868
- this.state = "idle";
7040
+ this.playbackSpeed = 1;
7869
7041
  /**
7870
7042
  * This method is bound to the instance to fix a pattern where
7871
7043
  * animation.stop is returned as a reference from a useEffect.
7872
7044
  */
7873
7045
  this.stop = () => {
7874
- this.resolver.cancel();
7046
+ const { motionValue } = this.options;
7047
+ if (motionValue && motionValue.updatedAt !== time.now()) {
7048
+ this.tick(time.now());
7049
+ }
7875
7050
  this.isStopped = true;
7876
7051
  if (this.state === "idle")
7877
7052
  return;
@@ -7879,49 +7054,35 @@ class MainThreadAnimation extends BaseAnimation {
7879
7054
  const { onStop } = this.options;
7880
7055
  onStop && onStop();
7881
7056
  };
7882
- const { name, motionValue, element, keyframes } = this.options;
7883
- const KeyframeResolver$1 = element?.KeyframeResolver || KeyframeResolver;
7884
- const onResolved = (resolvedKeyframes, finalKeyframe) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe);
7885
- this.resolver = new KeyframeResolver$1(keyframes, onResolved, name, motionValue, element);
7886
- this.resolver.scheduleResolve();
7887
- }
7888
- flatten() {
7889
- super.flatten();
7890
- // If we've already resolved the animation, re-initialise it
7891
- if (this._resolved) {
7892
- Object.assign(this._resolved, this.initPlayback(this._resolved.keyframes));
7893
- }
7894
- }
7895
- initPlayback(keyframes$1) {
7896
- const { type = "keyframes", repeat = 0, repeatDelay = 0, repeatType, velocity = 0, } = this.options;
7897
- const generatorFactory = isGenerator(type)
7898
- ? type
7899
- : generators[type] || keyframes;
7900
- /**
7901
- * If our generator doesn't support mixing numbers, we need to replace keyframes with
7902
- * [0, 100] and then make a function that maps that to the actual keyframes.
7903
- *
7904
- * 100 is chosen instead of 1 as it works nicer with spring animations.
7905
- */
7906
- let mapPercentToKeyframes;
7907
- let mirroredGenerator;
7057
+ this.options = options;
7058
+ this.initAnimation();
7059
+ this.play();
7060
+ if (options.autoplay === false)
7061
+ this.pause();
7062
+ }
7063
+ initAnimation() {
7064
+ const { options } = this;
7065
+ replaceTransitionType(options);
7066
+ const { type = keyframes, repeat = 0, repeatDelay = 0, repeatType, velocity = 0, } = options;
7067
+ let { keyframes: keyframes$1 } = options;
7068
+ const generatorFactory = type || keyframes;
7908
7069
  if (process.env.NODE_ENV !== "production" &&
7909
7070
  generatorFactory !== keyframes) {
7910
7071
  invariant(keyframes$1.length <= 2, `Only two keyframes currently supported with spring and inertia animations. Trying to animate ${keyframes$1}`);
7911
7072
  }
7912
7073
  if (generatorFactory !== keyframes &&
7913
7074
  typeof keyframes$1[0] !== "number") {
7914
- mapPercentToKeyframes = pipe(percentToProgress, mix(keyframes$1[0], keyframes$1[1]));
7075
+ this.mixKeyframes = pipe(percentToProgress, mix(keyframes$1[0], keyframes$1[1]));
7915
7076
  keyframes$1 = [0, 100];
7916
7077
  }
7917
- const generator = generatorFactory({ ...this.options, keyframes: keyframes$1 });
7078
+ const generator = generatorFactory({ ...options, keyframes: keyframes$1 });
7918
7079
  /**
7919
7080
  * If we have a mirror repeat type we need to create a second generator that outputs the
7920
7081
  * mirrored (not reversed) animation and later ping pong between the two generators.
7921
7082
  */
7922
7083
  if (repeatType === "mirror") {
7923
- mirroredGenerator = generatorFactory({
7924
- ...this.options,
7084
+ this.mirroredGenerator = generatorFactory({
7085
+ ...options,
7925
7086
  keyframes: [...keyframes$1].reverse(),
7926
7087
  velocity: -velocity,
7927
7088
  });
@@ -7938,38 +7099,29 @@ class MainThreadAnimation extends BaseAnimation {
7938
7099
  generator.calculatedDuration = calcGeneratorDuration(generator);
7939
7100
  }
7940
7101
  const { calculatedDuration } = generator;
7941
- const resolvedDuration = calculatedDuration + repeatDelay;
7942
- const totalDuration = resolvedDuration * (repeat + 1) - repeatDelay;
7943
- return {
7944
- generator,
7945
- mirroredGenerator,
7946
- mapPercentToKeyframes,
7947
- calculatedDuration,
7948
- resolvedDuration,
7949
- totalDuration,
7950
- };
7102
+ this.calculatedDuration = calculatedDuration;
7103
+ this.resolvedDuration = calculatedDuration + repeatDelay;
7104
+ this.totalDuration = this.resolvedDuration * (repeat + 1) - repeatDelay;
7105
+ this.generator = generator;
7951
7106
  }
7952
- onPostResolved() {
7953
- const { autoplay = true } = this.options;
7954
- this.play();
7955
- if (this.pendingPlayState === "paused" || !autoplay) {
7956
- this.pause();
7107
+ updateTime(timestamp) {
7108
+ const animationTime = Math.round(timestamp - this.startTime) * this.playbackSpeed;
7109
+ // Update currentTime
7110
+ if (this.holdTime !== null) {
7111
+ this.currentTime = this.holdTime;
7957
7112
  }
7958
7113
  else {
7959
- this.state = this.pendingPlayState;
7114
+ // Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 =
7115
+ // 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for
7116
+ // example.
7117
+ this.currentTime = animationTime;
7960
7118
  }
7961
7119
  }
7962
7120
  tick(timestamp, sample = false) {
7963
- const { resolved } = this;
7964
- // If the animations has failed to resolve, return the final keyframe.
7965
- if (!resolved) {
7966
- const { keyframes } = this.options;
7967
- return { done: true, value: keyframes[keyframes.length - 1] };
7968
- }
7969
- const { finalKeyframe, generator, mirroredGenerator, mapPercentToKeyframes, keyframes, calculatedDuration, totalDuration, resolvedDuration, } = resolved;
7121
+ const { generator, totalDuration, mixKeyframes, mirroredGenerator, resolvedDuration, calculatedDuration, } = this;
7970
7122
  if (this.startTime === null)
7971
7123
  return generator.next(0);
7972
- const { delay, repeat, repeatType, repeatDelay, onUpdate } = this.options;
7124
+ const { delay = 0, keyframes, repeat, repeatType, repeatDelay, type, onUpdate, finalKeyframe, } = this.options;
7973
7125
  /**
7974
7126
  * requestAnimationFrame timestamps can come through as lower than
7975
7127
  * the startTime as set by performance.now(). Here we prevent this,
@@ -7982,23 +7134,15 @@ class MainThreadAnimation extends BaseAnimation {
7982
7134
  else if (this.speed < 0) {
7983
7135
  this.startTime = Math.min(timestamp - totalDuration / this.speed, this.startTime);
7984
7136
  }
7985
- // Update currentTime
7986
7137
  if (sample) {
7987
7138
  this.currentTime = timestamp;
7988
7139
  }
7989
- else if (this.holdTime !== null) {
7990
- this.currentTime = this.holdTime;
7991
- }
7992
7140
  else {
7993
- // Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 =
7994
- // 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for
7995
- // example.
7996
- this.currentTime =
7997
- Math.round(timestamp - this.startTime) * this.speed;
7141
+ this.updateTime(timestamp);
7998
7142
  }
7999
7143
  // Rebase on delay
8000
- const timeWithoutDelay = this.currentTime - delay * (this.speed >= 0 ? 1 : -1);
8001
- const isInDelayPhase = this.speed >= 0
7144
+ const timeWithoutDelay = this.currentTime - delay * (this.playbackSpeed >= 0 ? 1 : -1);
7145
+ const isInDelayPhase = this.playbackSpeed >= 0
8002
7146
  ? timeWithoutDelay < 0
8003
7147
  : timeWithoutDelay > totalDuration;
8004
7148
  this.currentTime = Math.max(timeWithoutDelay, 0);
@@ -8059,20 +7203,21 @@ class MainThreadAnimation extends BaseAnimation {
8059
7203
  const state = isInDelayPhase
8060
7204
  ? { done: false, value: keyframes[0] }
8061
7205
  : frameGenerator.next(elapsed);
8062
- if (mapPercentToKeyframes) {
8063
- state.value = mapPercentToKeyframes(state.value);
7206
+ if (mixKeyframes) {
7207
+ state.value = mixKeyframes(state.value);
8064
7208
  }
8065
7209
  let { done } = state;
8066
7210
  if (!isInDelayPhase && calculatedDuration !== null) {
8067
7211
  done =
8068
- this.speed >= 0
7212
+ this.playbackSpeed >= 0
8069
7213
  ? this.currentTime >= totalDuration
8070
7214
  : this.currentTime <= 0;
8071
7215
  }
8072
7216
  const isAnimationFinished = this.holdTime === null &&
8073
7217
  (this.state === "finished" || (this.state === "running" && done));
8074
- if (isAnimationFinished && finalKeyframe !== undefined) {
8075
- state.value = getFinalKeyframe(keyframes, this.options, finalKeyframe);
7218
+ // TODO: The exception for inertia could be cleaner here
7219
+ if (isAnimationFinished && type !== inertia) {
7220
+ state.value = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
8076
7221
  }
8077
7222
  if (onUpdate) {
8078
7223
  onUpdate(state.value);
@@ -8082,9 +7227,16 @@ class MainThreadAnimation extends BaseAnimation {
8082
7227
  }
8083
7228
  return state;
8084
7229
  }
7230
+ /**
7231
+ * Allows the returned animation to be awaited or promise-chained. Currently
7232
+ * resolves when the animation finishes at all but in a future update could/should
7233
+ * reject if its cancels.
7234
+ */
7235
+ then(resolve, reject) {
7236
+ return this.finished.then(resolve, reject);
7237
+ }
8085
7238
  get duration() {
8086
- const { resolved } = this;
8087
- return resolved ? millisecondsToSeconds(resolved.calculatedDuration) : 0;
7239
+ return millisecondsToSeconds(this.calculatedDuration);
8088
7240
  }
8089
7241
  get time() {
8090
7242
  return millisecondsToSeconds(this.currentTime);
@@ -8092,125 +7244,417 @@ class MainThreadAnimation extends BaseAnimation {
8092
7244
  set time(newTime) {
8093
7245
  newTime = secondsToMilliseconds(newTime);
8094
7246
  this.currentTime = newTime;
8095
- if (this.holdTime !== null || this.speed === 0) {
7247
+ if (this.startTime === null ||
7248
+ this.holdTime !== null ||
7249
+ this.playbackSpeed === 0) {
8096
7250
  this.holdTime = newTime;
8097
7251
  }
8098
7252
  else if (this.driver) {
8099
- this.startTime = this.driver.now() - newTime / this.speed;
7253
+ this.startTime = this.driver.now() - newTime / this.playbackSpeed;
7254
+ }
7255
+ }
7256
+ get speed() {
7257
+ return this.playbackSpeed;
7258
+ }
7259
+ set speed(newSpeed) {
7260
+ this.updateTime(time.now());
7261
+ const hasChanged = this.playbackSpeed !== newSpeed;
7262
+ this.playbackSpeed = newSpeed;
7263
+ if (hasChanged) {
7264
+ this.time = millisecondsToSeconds(this.currentTime);
8100
7265
  }
8101
7266
  }
8102
- get speed() {
8103
- return this.playbackSpeed;
7267
+ play() {
7268
+ if (this.isStopped)
7269
+ return;
7270
+ const { driver = frameloopDriver, onPlay, startTime } = this.options;
7271
+ if (!this.driver) {
7272
+ this.driver = driver((timestamp) => this.tick(timestamp));
7273
+ }
7274
+ onPlay && onPlay();
7275
+ const now = this.driver.now();
7276
+ if (this.holdTime !== null) {
7277
+ this.startTime = now - this.holdTime;
7278
+ }
7279
+ else if (this.state === "finished") {
7280
+ this.updateFinished();
7281
+ this.startTime = now;
7282
+ }
7283
+ else if (!this.startTime) {
7284
+ this.startTime = startTime ?? now;
7285
+ }
7286
+ if (this.state === "finished" && this.speed < 0) {
7287
+ this.startTime += this.calculatedDuration;
7288
+ }
7289
+ this.holdTime = null;
7290
+ /**
7291
+ * Set playState to running only after we've used it in
7292
+ * the previous logic.
7293
+ */
7294
+ this.state = "running";
7295
+ this.driver.start();
7296
+ }
7297
+ pause() {
7298
+ this.state = "paused";
7299
+ this.updateTime(time.now());
7300
+ this.holdTime = this.currentTime;
7301
+ }
7302
+ complete() {
7303
+ if (this.state !== "running") {
7304
+ this.play();
7305
+ }
7306
+ this.state = "finished";
7307
+ this.holdTime = null;
7308
+ }
7309
+ finish() {
7310
+ this.teardown();
7311
+ this.state = "finished";
7312
+ const { onComplete } = this.options;
7313
+ onComplete && onComplete();
7314
+ }
7315
+ cancel() {
7316
+ this.holdTime = null;
7317
+ this.startTime = 0;
7318
+ this.tick(0);
7319
+ this.teardown();
7320
+ }
7321
+ teardown() {
7322
+ this.notifyFinished();
7323
+ this.state = "idle";
7324
+ this.stopDriver();
7325
+ this.startTime = this.holdTime = null;
7326
+ }
7327
+ stopDriver() {
7328
+ if (!this.driver)
7329
+ return;
7330
+ this.driver.stop();
7331
+ this.driver = undefined;
7332
+ }
7333
+ sample(sampleTime) {
7334
+ this.startTime = 0;
7335
+ return this.tick(sampleTime, true);
7336
+ }
7337
+ attachTimeline(timeline) {
7338
+ if (this.options.allowFlatten) {
7339
+ this.options.type = "keyframes";
7340
+ this.options.ease = "linear";
7341
+ this.initAnimation();
7342
+ }
7343
+ return timeline.observe(this);
7344
+ }
7345
+ }
7346
+
7347
+ function fillWildcards(keyframes) {
7348
+ for (let i = 1; i < keyframes.length; i++) {
7349
+ keyframes[i] ?? (keyframes[i] = keyframes[i - 1]);
7350
+ }
7351
+ }
7352
+
7353
+ const radToDeg = (rad) => (rad * 180) / Math.PI;
7354
+ const rotate = (v) => {
7355
+ const angle = radToDeg(Math.atan2(v[1], v[0]));
7356
+ return rebaseAngle(angle);
7357
+ };
7358
+ const matrix2dParsers = {
7359
+ x: 4,
7360
+ y: 5,
7361
+ translateX: 4,
7362
+ translateY: 5,
7363
+ scaleX: 0,
7364
+ scaleY: 3,
7365
+ scale: (v) => (Math.abs(v[0]) + Math.abs(v[3])) / 2,
7366
+ rotate,
7367
+ rotateZ: rotate,
7368
+ skewX: (v) => radToDeg(Math.atan(v[1])),
7369
+ skewY: (v) => radToDeg(Math.atan(v[2])),
7370
+ skew: (v) => (Math.abs(v[1]) + Math.abs(v[2])) / 2,
7371
+ };
7372
+ const rebaseAngle = (angle) => {
7373
+ angle = angle % 360;
7374
+ if (angle < 0)
7375
+ angle += 360;
7376
+ return angle;
7377
+ };
7378
+ const rotateZ = rotate;
7379
+ const scaleX = (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1]);
7380
+ const scaleY = (v) => Math.sqrt(v[4] * v[4] + v[5] * v[5]);
7381
+ const matrix3dParsers = {
7382
+ x: 12,
7383
+ y: 13,
7384
+ z: 14,
7385
+ translateX: 12,
7386
+ translateY: 13,
7387
+ translateZ: 14,
7388
+ scaleX,
7389
+ scaleY,
7390
+ scale: (v) => (scaleX(v) + scaleY(v)) / 2,
7391
+ rotateX: (v) => rebaseAngle(radToDeg(Math.atan2(v[6], v[5]))),
7392
+ rotateY: (v) => rebaseAngle(radToDeg(Math.atan2(-v[2], v[0]))),
7393
+ rotateZ,
7394
+ rotate: rotateZ,
7395
+ skewX: (v) => radToDeg(Math.atan(v[4])),
7396
+ skewY: (v) => radToDeg(Math.atan(v[1])),
7397
+ skew: (v) => (Math.abs(v[1]) + Math.abs(v[4])) / 2,
7398
+ };
7399
+ function defaultTransformValue(name) {
7400
+ return name.includes("scale") ? 1 : 0;
7401
+ }
7402
+ function parseValueFromTransform(transform, name) {
7403
+ if (!transform || transform === "none") {
7404
+ return defaultTransformValue(name);
7405
+ }
7406
+ const matrix3dMatch = transform.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);
7407
+ let parsers;
7408
+ let match;
7409
+ if (matrix3dMatch) {
7410
+ parsers = matrix3dParsers;
7411
+ match = matrix3dMatch;
7412
+ }
7413
+ else {
7414
+ const matrix2dMatch = transform.match(/^matrix\(([-\d.e\s,]+)\)$/u);
7415
+ parsers = matrix2dParsers;
7416
+ match = matrix2dMatch;
7417
+ }
7418
+ if (!match) {
7419
+ return defaultTransformValue(name);
8104
7420
  }
8105
- set speed(newSpeed) {
8106
- const hasChanged = this.playbackSpeed !== newSpeed;
8107
- this.playbackSpeed = newSpeed;
8108
- if (hasChanged) {
8109
- this.time = millisecondsToSeconds(this.currentTime);
7421
+ const valueParser = parsers[name];
7422
+ const values = match[1].split(",").map(convertTransformToNumber);
7423
+ return typeof valueParser === "function"
7424
+ ? valueParser(values)
7425
+ : values[valueParser];
7426
+ }
7427
+ const readTransformValue = (instance, name) => {
7428
+ const { transform = "none" } = getComputedStyle(instance);
7429
+ return parseValueFromTransform(transform, name);
7430
+ };
7431
+ function convertTransformToNumber(value) {
7432
+ return parseFloat(value.trim());
7433
+ }
7434
+
7435
+ const isNumOrPxType = (v) => v === number || v === px;
7436
+ const transformKeys = new Set(["x", "y", "z"]);
7437
+ const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key));
7438
+ function removeNonTranslationalTransform(visualElement) {
7439
+ const removedTransforms = [];
7440
+ nonTranslationalTransformKeys.forEach((key) => {
7441
+ const value = visualElement.getValue(key);
7442
+ if (value !== undefined) {
7443
+ removedTransforms.push([key, value.get()]);
7444
+ value.set(key.startsWith("scale") ? 1 : 0);
8110
7445
  }
7446
+ });
7447
+ return removedTransforms;
7448
+ }
7449
+ const positionalValues = {
7450
+ // Dimensions
7451
+ width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight),
7452
+ height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom),
7453
+ top: (_bbox, { top }) => parseFloat(top),
7454
+ left: (_bbox, { left }) => parseFloat(left),
7455
+ bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min),
7456
+ right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min),
7457
+ // Transform
7458
+ x: (_bbox, { transform }) => parseValueFromTransform(transform, "x"),
7459
+ y: (_bbox, { transform }) => parseValueFromTransform(transform, "y"),
7460
+ };
7461
+ // Alias translate longform names
7462
+ positionalValues.translateX = positionalValues.x;
7463
+ positionalValues.translateY = positionalValues.y;
7464
+
7465
+ const toResolve = new Set();
7466
+ let isScheduled = false;
7467
+ let anyNeedsMeasurement = false;
7468
+ let isForced = false;
7469
+ function measureAllKeyframes() {
7470
+ if (anyNeedsMeasurement) {
7471
+ const resolversToMeasure = Array.from(toResolve).filter((resolver) => resolver.needsMeasurement);
7472
+ const elementsToMeasure = new Set(resolversToMeasure.map((resolver) => resolver.element));
7473
+ const transformsToRestore = new Map();
7474
+ /**
7475
+ * Write pass
7476
+ * If we're measuring elements we want to remove bounding box-changing transforms.
7477
+ */
7478
+ elementsToMeasure.forEach((element) => {
7479
+ const removedTransforms = removeNonTranslationalTransform(element);
7480
+ if (!removedTransforms.length)
7481
+ return;
7482
+ transformsToRestore.set(element, removedTransforms);
7483
+ element.render();
7484
+ });
7485
+ // Read
7486
+ resolversToMeasure.forEach((resolver) => resolver.measureInitialState());
7487
+ // Write
7488
+ elementsToMeasure.forEach((element) => {
7489
+ element.render();
7490
+ const restore = transformsToRestore.get(element);
7491
+ if (restore) {
7492
+ restore.forEach(([key, value]) => {
7493
+ element.getValue(key)?.set(value);
7494
+ });
7495
+ }
7496
+ });
7497
+ // Read
7498
+ resolversToMeasure.forEach((resolver) => resolver.measureEndState());
7499
+ // Write
7500
+ resolversToMeasure.forEach((resolver) => {
7501
+ if (resolver.suspendedScrollY !== undefined) {
7502
+ window.scrollTo(0, resolver.suspendedScrollY);
7503
+ }
7504
+ });
8111
7505
  }
8112
- play() {
8113
- if (!this.resolver.isScheduled) {
8114
- this.resolver.resume();
8115
- }
8116
- if (!this._resolved) {
8117
- this.pendingPlayState = "running";
8118
- return;
8119
- }
8120
- if (this.isStopped)
8121
- return;
8122
- const { driver = frameloopDriver, onPlay, startTime } = this.options;
8123
- if (!this.driver) {
8124
- this.driver = driver((timestamp) => this.tick(timestamp));
8125
- }
8126
- onPlay && onPlay();
8127
- const now = this.driver.now();
8128
- if (this.holdTime !== null) {
8129
- this.startTime = now - this.holdTime;
8130
- }
8131
- else if (!this.startTime) {
8132
- this.startTime = startTime ?? this.calcStartTime();
8133
- }
8134
- else if (this.state === "finished") {
8135
- this.startTime = now;
8136
- }
8137
- if (this.state === "finished") {
8138
- this.updateFinishedPromise();
7506
+ anyNeedsMeasurement = false;
7507
+ isScheduled = false;
7508
+ toResolve.forEach((resolver) => resolver.complete(isForced));
7509
+ toResolve.clear();
7510
+ }
7511
+ function readAllKeyframes() {
7512
+ toResolve.forEach((resolver) => {
7513
+ resolver.readKeyframes();
7514
+ if (resolver.needsMeasurement) {
7515
+ anyNeedsMeasurement = true;
8139
7516
  }
8140
- this.cancelTime = this.startTime;
8141
- this.holdTime = null;
7517
+ });
7518
+ }
7519
+ function flushKeyframeResolvers() {
7520
+ isForced = true;
7521
+ readAllKeyframes();
7522
+ measureAllKeyframes();
7523
+ isForced = false;
7524
+ }
7525
+ class KeyframeResolver {
7526
+ constructor(unresolvedKeyframes, onComplete, name, motionValue, element, isAsync = false) {
8142
7527
  /**
8143
- * Set playState to running only after we've used it in
8144
- * the previous logic.
7528
+ * Track whether this resolver has completed. Once complete, it never
7529
+ * needs to attempt keyframe resolution again.
8145
7530
  */
8146
- this.state = "running";
8147
- this.driver.start();
7531
+ this.isComplete = false;
7532
+ /**
7533
+ * Track whether this resolver is async. If it is, it'll be added to the
7534
+ * resolver queue and flushed in the next frame. Resolvers that aren't going
7535
+ * to trigger read/write thrashing don't need to be async.
7536
+ */
7537
+ this.isAsync = false;
7538
+ /**
7539
+ * Track whether this resolver needs to perform a measurement
7540
+ * to resolve its keyframes.
7541
+ */
7542
+ this.needsMeasurement = false;
7543
+ /**
7544
+ * Track whether this resolver is currently scheduled to resolve
7545
+ * to allow it to be cancelled and resumed externally.
7546
+ */
7547
+ this.isScheduled = false;
7548
+ this.unresolvedKeyframes = [...unresolvedKeyframes];
7549
+ this.onComplete = onComplete;
7550
+ this.name = name;
7551
+ this.motionValue = motionValue;
7552
+ this.element = element;
7553
+ this.isAsync = isAsync;
8148
7554
  }
8149
- pause() {
8150
- if (!this._resolved) {
8151
- this.pendingPlayState = "paused";
8152
- return;
7555
+ scheduleResolve() {
7556
+ this.isScheduled = true;
7557
+ if (this.isAsync) {
7558
+ toResolve.add(this);
7559
+ if (!isScheduled) {
7560
+ isScheduled = true;
7561
+ frame.read(readAllKeyframes);
7562
+ frame.resolveKeyframes(measureAllKeyframes);
7563
+ }
7564
+ }
7565
+ else {
7566
+ this.readKeyframes();
7567
+ this.complete();
8153
7568
  }
8154
- this.state = "paused";
8155
- this.holdTime = this.currentTime ?? 0;
8156
7569
  }
8157
- complete() {
8158
- if (this.state !== "running") {
8159
- this.play();
7570
+ readKeyframes() {
7571
+ const { unresolvedKeyframes, name, element, motionValue } = this;
7572
+ // If initial keyframe is null we need to read it from the DOM
7573
+ if (unresolvedKeyframes[0] === null) {
7574
+ const currentValue = motionValue?.get();
7575
+ // TODO: This doesn't work if the final keyframe is a wildcard
7576
+ const finalKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
7577
+ if (currentValue !== undefined) {
7578
+ unresolvedKeyframes[0] = currentValue;
7579
+ }
7580
+ else if (element && name) {
7581
+ const valueAsRead = element.readValue(name, finalKeyframe);
7582
+ if (valueAsRead !== undefined && valueAsRead !== null) {
7583
+ unresolvedKeyframes[0] = valueAsRead;
7584
+ }
7585
+ }
7586
+ if (unresolvedKeyframes[0] === undefined) {
7587
+ unresolvedKeyframes[0] = finalKeyframe;
7588
+ }
7589
+ if (motionValue && currentValue === undefined) {
7590
+ motionValue.set(unresolvedKeyframes[0]);
7591
+ }
8160
7592
  }
8161
- this.pendingPlayState = this.state = "finished";
8162
- this.holdTime = null;
7593
+ fillWildcards(unresolvedKeyframes);
8163
7594
  }
8164
- finish() {
8165
- this.teardown();
8166
- this.state = "finished";
8167
- const { onComplete } = this.options;
8168
- onComplete && onComplete();
7595
+ setFinalKeyframe() { }
7596
+ measureInitialState() { }
7597
+ renderEndStyles() { }
7598
+ measureEndState() { }
7599
+ complete(isForced = false) {
7600
+ this.isComplete = true;
7601
+ this.onComplete(this.unresolvedKeyframes, this.finalKeyframe, isForced);
7602
+ toResolve.delete(this);
8169
7603
  }
8170
7604
  cancel() {
8171
- if (this.cancelTime !== null) {
8172
- this.tick(this.cancelTime);
7605
+ if (!this.isComplete) {
7606
+ this.isScheduled = false;
7607
+ toResolve.delete(this);
8173
7608
  }
8174
- this.teardown();
8175
- this.updateFinishedPromise();
8176
- }
8177
- teardown() {
8178
- this.state = "idle";
8179
- this.stopDriver();
8180
- this.resolveFinishedPromise();
8181
- this.updateFinishedPromise();
8182
- this.startTime = this.cancelTime = null;
8183
- this.resolver.cancel();
8184
7609
  }
8185
- stopDriver() {
8186
- if (!this.driver)
8187
- return;
8188
- this.driver.stop();
8189
- this.driver = undefined;
8190
- }
8191
- sample(time) {
8192
- this.startTime = 0;
8193
- return this.tick(time, true);
8194
- }
8195
- get finished() {
8196
- return this.currentFinishedPromise;
7610
+ resume() {
7611
+ if (!this.isComplete)
7612
+ this.scheduleResolve();
8197
7613
  }
8198
7614
  }
8199
7615
 
7616
+ const isCSSVar = (name) => name.startsWith("--");
7617
+
7618
+ function setStyle(element, name, value) {
7619
+ isCSSVar(name)
7620
+ ? element.style.setProperty(name, value)
7621
+ : (element.style[name] = value);
7622
+ }
7623
+
7624
+ /*#__NO_SIDE_EFFECTS__*/
7625
+ function memo(callback) {
7626
+ let result;
7627
+ return () => {
7628
+ if (result === undefined)
7629
+ result = callback();
7630
+ return result;
7631
+ };
7632
+ }
7633
+
7634
+ const supportsScrollTimeline = /* @__PURE__ */ memo(() => window.ScrollTimeline !== undefined);
7635
+
8200
7636
  /**
8201
- * A list of values that can be hardware-accelerated.
7637
+ * Add the ability for test suites to manually set support flags
7638
+ * to better test more environments.
8202
7639
  */
8203
- const acceleratedValues = new Set([
8204
- "opacity",
8205
- "clipPath",
8206
- "filter",
8207
- "transform",
8208
- // TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved
8209
- // or until we implement support for linear() easing.
8210
- // "background-color"
8211
- ]);
7640
+ const supportsFlags = {};
8212
7641
 
8213
- const supportsWaapi = /*@__PURE__*/ memo(() => Object.hasOwnProperty.call(Element.prototype, "animate"));
7642
+ function memoSupports(callback, supportsFlag) {
7643
+ const memoized = memo(callback);
7644
+ return () => supportsFlags[supportsFlag] ?? memoized();
7645
+ }
7646
+
7647
+ const supportsLinearEasing = /*@__PURE__*/ memoSupports(() => {
7648
+ try {
7649
+ document
7650
+ .createElement("div")
7651
+ .animate({ opacity: 0 }, { easing: "linear(0, 1)" });
7652
+ }
7653
+ catch (e) {
7654
+ return false;
7655
+ }
7656
+ return true;
7657
+ }, "linearEasing");
8214
7658
 
8215
7659
  const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
8216
7660
 
@@ -8230,8 +7674,10 @@ function mapEasingToNativeEasing(easing, duration) {
8230
7674
  if (!easing) {
8231
7675
  return undefined;
8232
7676
  }
8233
- else if (typeof easing === "function" && supportsLinearEasing()) {
8234
- return generateLinearEasing(easing, duration);
7677
+ else if (typeof easing === "function") {
7678
+ return supportsLinearEasing()
7679
+ ? generateLinearEasing(easing, duration)
7680
+ : "ease-out";
8235
7681
  }
8236
7682
  else if (isBezierDefinition(easing)) {
8237
7683
  return cubicBezierAsString(easing);
@@ -8245,7 +7691,7 @@ function mapEasingToNativeEasing(easing, duration) {
8245
7691
  }
8246
7692
  }
8247
7693
 
8248
- function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duration = 300, repeat = 0, repeatType = "loop", ease = "easeInOut", times, } = {}, pseudoElement = undefined) {
7694
+ function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duration = 300, repeat = 0, repeatType = "loop", ease = "easeOut", times, } = {}, pseudoElement = undefined) {
8249
7695
  const keyframeOptions = {
8250
7696
  [valueName]: keyframes,
8251
7697
  };
@@ -8257,473 +7703,517 @@ function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duratio
8257
7703
  */
8258
7704
  if (Array.isArray(easing))
8259
7705
  keyframeOptions.easing = easing;
8260
- const animation = element.animate(keyframeOptions, {
7706
+ const options = {
8261
7707
  delay,
8262
7708
  duration,
8263
7709
  easing: !Array.isArray(easing) ? easing : "linear",
8264
7710
  fill: "both",
8265
7711
  iterations: repeat + 1,
8266
7712
  direction: repeatType === "reverse" ? "alternate" : "normal",
8267
- pseudoElement,
8268
- });
7713
+ };
7714
+ if (pseudoElement)
7715
+ options.pseudoElement = pseudoElement;
7716
+ const animation = element.animate(keyframeOptions, options);
8269
7717
  return animation;
8270
7718
  }
8271
7719
 
8272
- function attachTimeline(animation, timeline) {
8273
- animation.timeline = timeline;
8274
- animation.onfinish = null;
7720
+ function isGenerator(type) {
7721
+ return typeof type === "function" && "applyToOptions" in type;
8275
7722
  }
8276
7723
 
8277
- function isWaapiSupportedEasing(easing) {
8278
- return Boolean((typeof easing === "function" && supportsLinearEasing()) ||
8279
- !easing ||
8280
- (typeof easing === "string" &&
8281
- (easing in supportedWaapiEasing || supportsLinearEasing())) ||
8282
- isBezierDefinition(easing) ||
8283
- (Array.isArray(easing) && easing.every(isWaapiSupportedEasing)));
7724
+ function applyGeneratorOptions({ type, ...options }) {
7725
+ if (isGenerator(type) && supportsLinearEasing()) {
7726
+ return type.applyToOptions(options);
7727
+ }
7728
+ else {
7729
+ options.duration ?? (options.duration = 300);
7730
+ options.ease ?? (options.ease = "easeOut");
7731
+ }
7732
+ return options;
8284
7733
  }
8285
7734
 
8286
7735
  /**
8287
- * 10ms is chosen here as it strikes a balance between smooth
8288
- * results (more than one keyframe per frame at 60fps) and
8289
- * keyframe quantity.
8290
- */
8291
- const sampleDelta = 10; //ms
8292
- /**
8293
- * Implement a practical max duration for keyframe generation
8294
- * to prevent infinite loops
8295
- */
8296
- const maxDuration = 20000;
8297
- /**
8298
- * Check if an animation can run natively via WAAPI or requires pregenerated keyframes.
8299
- * WAAPI doesn't support spring or function easings so we run these as JS animation before
8300
- * handing off.
7736
+ * NativeAnimation implements AnimationPlaybackControls for the browser's Web Animations API.
8301
7737
  */
8302
- function requiresPregeneratedKeyframes(options) {
8303
- return (isGenerator(options.type) ||
8304
- options.type === "spring" ||
8305
- !isWaapiSupportedEasing(options.ease));
8306
- }
8307
- function pregenerateKeyframes(keyframes, options) {
8308
- /**
8309
- * Create a main-thread animation to pregenerate keyframes.
8310
- * We sample this at regular intervals to generate keyframes that we then
8311
- * linearly interpolate between.
8312
- */
8313
- const sampleAnimation = new MainThreadAnimation({
8314
- ...options,
8315
- keyframes,
8316
- repeat: 0,
8317
- delay: 0,
8318
- isGenerator: true,
8319
- });
8320
- let state = { done: false, value: keyframes[0] };
8321
- const pregeneratedKeyframes = [];
8322
- /**
8323
- * Bail after 20 seconds of pre-generated keyframes as it's likely
8324
- * we're heading for an infinite loop.
8325
- */
8326
- let t = 0;
8327
- while (!state.done && t < maxDuration) {
8328
- state = sampleAnimation.sample(t);
8329
- pregeneratedKeyframes.push(state.value);
8330
- t += sampleDelta;
8331
- }
8332
- return {
8333
- times: undefined,
8334
- keyframes: pregeneratedKeyframes,
8335
- duration: t - sampleDelta,
8336
- ease: "linear",
8337
- };
8338
- }
8339
- const unsupportedEasingFunctions = {
8340
- anticipate,
8341
- backInOut,
8342
- circInOut,
8343
- };
8344
- function isUnsupportedEase(key) {
8345
- return key in unsupportedEasingFunctions;
8346
- }
8347
- class AcceleratedAnimation extends BaseAnimation {
7738
+ class NativeAnimation extends WithPromise {
8348
7739
  constructor(options) {
8349
- super(options);
8350
- const { name, motionValue, element, keyframes } = this.options;
8351
- this.resolver = new DOMKeyframesResolver(keyframes, (resolvedKeyframes, finalKeyframe) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe), name, motionValue, element);
8352
- this.resolver.scheduleResolve();
8353
- }
8354
- initPlayback(keyframes, finalKeyframe) {
8355
- let { duration = 300, times, ease, type, motionValue, name, startTime, } = this.options;
7740
+ super();
7741
+ this.finishedTime = null;
7742
+ this.isStopped = false;
7743
+ if (!options)
7744
+ return;
7745
+ const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, onComplete, } = options;
7746
+ this.isPseudoElement = Boolean(pseudoElement);
7747
+ this.allowFlatten = allowFlatten;
7748
+ this.options = options;
7749
+ invariant(typeof options.type !== "string", `animateMini doesn't support "type" as a string. Did you mean to import { spring } from "motion"?`);
7750
+ const transition = applyGeneratorOptions(options);
7751
+ this.animation = startWaapiAnimation(element, name, keyframes, transition, pseudoElement);
7752
+ if (transition.autoplay === false) {
7753
+ this.animation.pause();
7754
+ }
7755
+ this.animation.onfinish = () => {
7756
+ this.finishedTime = this.time;
7757
+ if (!pseudoElement) {
7758
+ const keyframe = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
7759
+ if (this.updateMotionValue) {
7760
+ this.updateMotionValue(keyframe);
7761
+ }
7762
+ else {
7763
+ /**
7764
+ * If we can, we want to commit the final style as set by the user,
7765
+ * rather than the computed keyframe value supplied by the animation.
7766
+ */
7767
+ setStyle(element, name, keyframe);
7768
+ }
7769
+ this.animation.cancel();
7770
+ }
7771
+ onComplete?.();
7772
+ this.notifyFinished();
7773
+ };
8356
7774
  /**
8357
- * If element has since been unmounted, return false to indicate
8358
- * the animation failed to initialised.
7775
+ * TODO: In a breaking change, we should replace this with `.notifyCancel()`
8359
7776
  */
8360
- if (!motionValue.owner || !motionValue.owner.current) {
8361
- return false;
7777
+ this.animation.oncancel = () => this.notifyFinished();
7778
+ }
7779
+ play() {
7780
+ if (this.isStopped)
7781
+ return;
7782
+ this.animation.play();
7783
+ if (this.state === "finished") {
7784
+ this.updateFinished();
8362
7785
  }
8363
- /**
8364
- * If the user has provided an easing function name that isn't supported
8365
- * by WAAPI (like "anticipate"), we need to provide the corressponding
8366
- * function. This will later get converted to a linear() easing function.
8367
- */
8368
- if (typeof ease === "string" &&
8369
- supportsLinearEasing() &&
8370
- isUnsupportedEase(ease)) {
8371
- ease = unsupportedEasingFunctions[ease];
7786
+ }
7787
+ pause() {
7788
+ this.animation.pause();
7789
+ }
7790
+ complete() {
7791
+ this.animation.finish?.();
7792
+ }
7793
+ cancel() {
7794
+ try {
7795
+ this.animation.cancel();
8372
7796
  }
8373
- /**
8374
- * If this animation needs pre-generated keyframes then generate.
8375
- */
8376
- if (requiresPregeneratedKeyframes(this.options)) {
8377
- const { onComplete, onUpdate, motionValue, element, ...options } = this.options;
8378
- const pregeneratedAnimation = pregenerateKeyframes(keyframes, options);
8379
- keyframes = pregeneratedAnimation.keyframes;
8380
- // If this is a very short animation, ensure we have
8381
- // at least two keyframes to animate between as older browsers
8382
- // can't animate between a single keyframe.
8383
- if (keyframes.length === 1) {
8384
- keyframes[1] = keyframes[0];
8385
- }
8386
- duration = pregeneratedAnimation.duration;
8387
- times = pregeneratedAnimation.times;
8388
- ease = pregeneratedAnimation.ease;
8389
- type = "keyframes";
8390
- }
8391
- const animation = startWaapiAnimation(motionValue.owner.current, name, keyframes, { ...this.options, duration, times, ease });
8392
- // Override the browser calculated startTime with one synchronised to other JS
8393
- // and WAAPI animations starting this event loop.
8394
- animation.startTime = startTime ?? this.calcStartTime();
8395
- if (this.pendingTimeline) {
8396
- attachTimeline(animation, this.pendingTimeline);
8397
- this.pendingTimeline = undefined;
7797
+ catch (e) { }
7798
+ }
7799
+ stop() {
7800
+ if (this.isStopped)
7801
+ return;
7802
+ this.isStopped = true;
7803
+ const { state } = this;
7804
+ if (state === "idle" || state === "finished") {
7805
+ return;
7806
+ }
7807
+ if (this.updateMotionValue) {
7808
+ this.updateMotionValue();
8398
7809
  }
8399
7810
  else {
8400
- /**
8401
- * Prefer the `onfinish` prop as it's more widely supported than
8402
- * the `finished` promise.
8403
- *
8404
- * Here, we synchronously set the provided MotionValue to the end
8405
- * keyframe. If we didn't, when the WAAPI animation is finished it would
8406
- * be removed from the element which would then revert to its old styles.
8407
- */
8408
- animation.onfinish = () => {
8409
- const { onComplete } = this.options;
8410
- motionValue.set(getFinalKeyframe(keyframes, this.options, finalKeyframe));
8411
- onComplete && onComplete();
8412
- this.cancel();
8413
- this.resolveFinishedPromise();
8414
- };
7811
+ this.commitStyles();
7812
+ }
7813
+ if (!this.isPseudoElement)
7814
+ this.cancel();
7815
+ }
7816
+ /**
7817
+ * WAAPI doesn't natively have any interruption capabilities.
7818
+ *
7819
+ * In this method, we commit styles back to the DOM before cancelling
7820
+ * the animation.
7821
+ *
7822
+ * This is designed to be overridden by NativeAnimationExtended, which
7823
+ * will create a renderless JS animation and sample it twice to calculate
7824
+ * its current value, "previous" value, and therefore allow
7825
+ * Motion to also correctly calculate velocity for any subsequent animation
7826
+ * while deferring the commit until the next animation frame.
7827
+ */
7828
+ commitStyles() {
7829
+ if (!this.isPseudoElement) {
7830
+ this.animation.commitStyles?.();
8415
7831
  }
8416
- return {
8417
- animation,
8418
- duration,
8419
- times,
8420
- type,
8421
- ease,
8422
- keyframes: keyframes,
8423
- };
8424
7832
  }
8425
7833
  get duration() {
8426
- const { resolved } = this;
8427
- if (!resolved)
8428
- return 0;
8429
- const { duration } = resolved;
8430
- return millisecondsToSeconds(duration);
7834
+ const duration = this.animation.effect?.getComputedTiming?.().duration || 0;
7835
+ return millisecondsToSeconds(Number(duration));
8431
7836
  }
8432
7837
  get time() {
8433
- const { resolved } = this;
8434
- if (!resolved)
8435
- return 0;
8436
- const { animation } = resolved;
8437
- return millisecondsToSeconds(animation.currentTime || 0);
7838
+ return millisecondsToSeconds(Number(this.animation.currentTime) || 0);
8438
7839
  }
8439
7840
  set time(newTime) {
8440
- const { resolved } = this;
8441
- if (!resolved)
8442
- return;
8443
- const { animation } = resolved;
8444
- animation.currentTime = secondsToMilliseconds(newTime);
7841
+ this.finishedTime = null;
7842
+ this.animation.currentTime = secondsToMilliseconds(newTime);
8445
7843
  }
7844
+ /**
7845
+ * The playback speed of the animation.
7846
+ * 1 = normal speed, 2 = double speed, 0.5 = half speed.
7847
+ */
8446
7848
  get speed() {
8447
- const { resolved } = this;
8448
- if (!resolved)
8449
- return 1;
8450
- const { animation } = resolved;
8451
- return animation.playbackRate;
8452
- }
8453
- get finished() {
8454
- return this.resolved.animation.finished;
7849
+ return this.animation.playbackRate;
8455
7850
  }
8456
7851
  set speed(newSpeed) {
8457
- const { resolved } = this;
8458
- if (!resolved)
8459
- return;
8460
- const { animation } = resolved;
8461
- animation.playbackRate = newSpeed;
7852
+ // Allow backwards playback after finishing
7853
+ if (newSpeed < 0)
7854
+ this.finishedTime = null;
7855
+ this.animation.playbackRate = newSpeed;
8462
7856
  }
8463
7857
  get state() {
8464
- const { resolved } = this;
8465
- if (!resolved)
8466
- return "idle";
8467
- const { animation } = resolved;
8468
- return animation.playState;
7858
+ return this.finishedTime !== null
7859
+ ? "finished"
7860
+ : this.animation.playState;
8469
7861
  }
8470
7862
  get startTime() {
8471
- const { resolved } = this;
8472
- if (!resolved)
8473
- return null;
8474
- const { animation } = resolved;
8475
- // Coerce to number as TypeScript incorrectly types this
8476
- // as CSSNumberish
8477
- return animation.startTime;
7863
+ return Number(this.animation.startTime);
7864
+ }
7865
+ set startTime(newStartTime) {
7866
+ this.animation.startTime = newStartTime;
8478
7867
  }
8479
7868
  /**
8480
- * Replace the default DocumentTimeline with another AnimationTimeline.
8481
- * Currently used for scroll animations.
7869
+ * Attaches a timeline to the animation, for instance the `ScrollTimeline`.
8482
7870
  */
8483
- attachTimeline(timeline) {
8484
- if (!this._resolved) {
8485
- this.pendingTimeline = timeline;
7871
+ attachTimeline({ timeline, observe }) {
7872
+ if (this.allowFlatten) {
7873
+ this.animation.effect?.updateTiming({ easing: "linear" });
8486
7874
  }
8487
- else {
8488
- const { resolved } = this;
8489
- if (!resolved)
8490
- return noop;
8491
- const { animation } = resolved;
8492
- attachTimeline(animation, timeline);
7875
+ this.animation.onfinish = null;
7876
+ if (timeline && supportsScrollTimeline()) {
7877
+ this.animation.timeline = timeline;
7878
+ return noop;
8493
7879
  }
8494
- return noop;
8495
- }
8496
- play() {
8497
- if (this.isStopped)
8498
- return;
8499
- const { resolved } = this;
8500
- if (!resolved)
8501
- return;
8502
- const { animation } = resolved;
8503
- if (animation.playState === "finished") {
8504
- this.updateFinishedPromise();
7880
+ else {
7881
+ return observe(this);
8505
7882
  }
8506
- animation.play();
8507
7883
  }
8508
- pause() {
8509
- const { resolved } = this;
8510
- if (!resolved)
8511
- return;
8512
- const { animation } = resolved;
8513
- animation.pause();
7884
+ }
7885
+
7886
+ const unsupportedEasingFunctions = {
7887
+ anticipate,
7888
+ backInOut,
7889
+ circInOut,
7890
+ };
7891
+ function isUnsupportedEase(key) {
7892
+ return key in unsupportedEasingFunctions;
7893
+ }
7894
+ function replaceStringEasing(transition) {
7895
+ if (typeof transition.ease === "string" &&
7896
+ isUnsupportedEase(transition.ease)) {
7897
+ transition.ease = unsupportedEasingFunctions[transition.ease];
8514
7898
  }
8515
- stop() {
8516
- this.resolver.cancel();
8517
- this.isStopped = true;
8518
- if (this.state === "idle")
8519
- return;
8520
- this.resolveFinishedPromise();
8521
- this.updateFinishedPromise();
8522
- const { resolved } = this;
8523
- if (!resolved)
8524
- return;
8525
- const { animation, keyframes, duration, type, ease, times } = resolved;
8526
- if (animation.playState === "idle" ||
8527
- animation.playState === "finished") {
8528
- return;
8529
- }
7899
+ }
7900
+
7901
+ /**
7902
+ * 10ms is chosen here as it strikes a balance between smooth
7903
+ * results (more than one keyframe per frame at 60fps) and
7904
+ * keyframe quantity.
7905
+ */
7906
+ const sampleDelta = 10; //ms
7907
+ class NativeAnimationExtended extends NativeAnimation {
7908
+ constructor(options) {
8530
7909
  /**
8531
- * WAAPI doesn't natively have any interruption capabilities.
7910
+ * The base NativeAnimation function only supports a subset
7911
+ * of Motion easings, and WAAPI also only supports some
7912
+ * easing functions via string/cubic-bezier definitions.
8532
7913
  *
8533
- * Rather than read commited styles back out of the DOM, we can
8534
- * create a renderless JS animation and sample it twice to calculate
8535
- * its current value, "previous" value, and therefore allow
8536
- * Motion to calculate velocity for any subsequent animation.
7914
+ * This function replaces those unsupported easing functions
7915
+ * with a JS easing function. This will later get compiled
7916
+ * to a linear() easing function.
8537
7917
  */
8538
- if (this.time) {
8539
- const { motionValue, onUpdate, onComplete, element, ...options } = this.options;
8540
- const sampleAnimation = new MainThreadAnimation({
8541
- ...options,
8542
- keyframes,
8543
- duration,
8544
- type,
8545
- ease,
8546
- times,
8547
- isGenerator: true,
8548
- });
8549
- const sampleTime = secondsToMilliseconds(this.time);
8550
- motionValue.setWithVelocity(sampleAnimation.sample(sampleTime - sampleDelta).value, sampleAnimation.sample(sampleTime).value, sampleDelta);
7918
+ replaceStringEasing(options);
7919
+ /**
7920
+ * Ensure we replace the transition type with a generator function
7921
+ * before passing to WAAPI.
7922
+ *
7923
+ * TODO: Does this have a better home? It could be shared with
7924
+ * JSAnimation.
7925
+ */
7926
+ replaceTransitionType(options);
7927
+ super(options);
7928
+ if (options.startTime) {
7929
+ this.startTime = options.startTime;
8551
7930
  }
8552
- const { onStop } = this.options;
8553
- onStop && onStop();
8554
- this.cancel();
7931
+ this.options = options;
8555
7932
  }
8556
- complete() {
8557
- const { resolved } = this;
8558
- if (!resolved)
7933
+ /**
7934
+ * WAAPI doesn't natively have any interruption capabilities.
7935
+ *
7936
+ * Rather than read commited styles back out of the DOM, we can
7937
+ * create a renderless JS animation and sample it twice to calculate
7938
+ * its current value, "previous" value, and therefore allow
7939
+ * Motion to calculate velocity for any subsequent animation.
7940
+ */
7941
+ updateMotionValue(value) {
7942
+ const { motionValue, onUpdate, onComplete, element, ...options } = this.options;
7943
+ if (!motionValue)
8559
7944
  return;
8560
- resolved.animation.finish();
8561
- }
8562
- cancel() {
8563
- const { resolved } = this;
8564
- if (!resolved)
7945
+ if (value !== undefined) {
7946
+ motionValue.set(value);
8565
7947
  return;
8566
- resolved.animation.cancel();
8567
- }
8568
- static supports(options) {
8569
- const { motionValue, name, repeatDelay, repeatType, damping, type } = options;
8570
- if (!motionValue ||
8571
- !motionValue.owner ||
8572
- !(motionValue.owner.current instanceof HTMLElement)) {
8573
- return false;
8574
7948
  }
8575
- const { onUpdate, transformTemplate } = motionValue.owner.getProps();
8576
- return (supportsWaapi() &&
8577
- name &&
8578
- acceleratedValues.has(name) &&
8579
- (name !== "transform" || !transformTemplate) &&
8580
- /**
8581
- * If we're outputting values to onUpdate then we can't use WAAPI as there's
8582
- * no way to read the value from WAAPI every frame.
8583
- */
8584
- !onUpdate &&
8585
- !repeatDelay &&
8586
- repeatType !== "mirror" &&
8587
- damping !== 0 &&
8588
- type !== "inertia");
7949
+ const sampleAnimation = new JSAnimation({
7950
+ ...options,
7951
+ autoplay: false,
7952
+ });
7953
+ const sampleTime = secondsToMilliseconds(this.finishedTime ?? this.time);
7954
+ motionValue.setWithVelocity(sampleAnimation.sample(sampleTime - sampleDelta).value, sampleAnimation.sample(sampleTime).value, sampleDelta);
7955
+ sampleAnimation.stop();
8589
7956
  }
8590
7957
  }
8591
7958
 
8592
- const underDampedSpring = {
8593
- type: "spring",
8594
- stiffness: 500,
8595
- damping: 25,
8596
- restSpeed: 10,
8597
- };
8598
- const criticallyDampedSpring = (target) => ({
8599
- type: "spring",
8600
- stiffness: 550,
8601
- damping: target === 0 ? 2 * Math.sqrt(550) : 30,
8602
- restSpeed: 10,
8603
- });
8604
- const keyframesTransition = {
8605
- type: "keyframes",
8606
- duration: 0.8,
8607
- };
8608
7959
  /**
8609
- * Default easing curve is a slightly shallower version of
8610
- * the default browser easing curve.
7960
+ * Check if a value is animatable. Examples:
7961
+ *
7962
+ * ✅: 100, "100px", "#fff"
7963
+ * ❌: "block", "url(2.jpg)"
7964
+ * @param value
7965
+ *
7966
+ * @internal
8611
7967
  */
8612
- const ease = {
8613
- type: "keyframes",
8614
- ease: [0.25, 0.1, 0.35, 1],
8615
- duration: 0.3,
8616
- };
8617
- const getDefaultTransition = (valueKey, { keyframes }) => {
8618
- if (keyframes.length > 2) {
8619
- return keyframesTransition;
8620
- }
8621
- else if (transformProps.has(valueKey)) {
8622
- return valueKey.startsWith("scale")
8623
- ? criticallyDampedSpring(keyframes[1])
8624
- : underDampedSpring;
7968
+ const isAnimatable = (value, name) => {
7969
+ // If the list of keys tat might be non-animatable grows, replace with Set
7970
+ if (name === "zIndex")
7971
+ return false;
7972
+ // If it's a number or a keyframes array, we can animate it. We might at some point
7973
+ // need to do a deep isAnimatable check of keyframes, or let Popmotion handle this,
7974
+ // but for now lets leave it like this for performance reasons
7975
+ if (typeof value === "number" || Array.isArray(value))
7976
+ return true;
7977
+ if (typeof value === "string" && // It's animatable if we have a string
7978
+ (complex.test(value) || value === "0") && // And it contains numbers and/or colors
7979
+ !value.startsWith("url(") // Unless it starts with "url("
7980
+ ) {
7981
+ return true;
8625
7982
  }
8626
- return ease;
7983
+ return false;
8627
7984
  };
8628
7985
 
7986
+ function hasKeyframesChanged(keyframes) {
7987
+ const current = keyframes[0];
7988
+ if (keyframes.length === 1)
7989
+ return true;
7990
+ for (let i = 0; i < keyframes.length; i++) {
7991
+ if (keyframes[i] !== current)
7992
+ return true;
7993
+ }
7994
+ }
7995
+ function canAnimate(keyframes, name, type, velocity) {
7996
+ /**
7997
+ * Check if we're able to animate between the start and end keyframes,
7998
+ * and throw a warning if we're attempting to animate between one that's
7999
+ * animatable and another that isn't.
8000
+ */
8001
+ const originKeyframe = keyframes[0];
8002
+ if (originKeyframe === null)
8003
+ return false;
8004
+ /**
8005
+ * These aren't traditionally animatable but we do support them.
8006
+ * In future we could look into making this more generic or replacing
8007
+ * this function with mix() === mixImmediate
8008
+ */
8009
+ if (name === "display" || name === "visibility")
8010
+ return true;
8011
+ const targetKeyframe = keyframes[keyframes.length - 1];
8012
+ const isOriginAnimatable = isAnimatable(originKeyframe, name);
8013
+ const isTargetAnimatable = isAnimatable(targetKeyframe, name);
8014
+ 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.`);
8015
+ // Always skip if any of these are true
8016
+ if (!isOriginAnimatable || !isTargetAnimatable) {
8017
+ return false;
8018
+ }
8019
+ return (hasKeyframesChanged(keyframes) ||
8020
+ ((type === "spring" || isGenerator(type)) && velocity));
8021
+ }
8022
+
8629
8023
  /**
8630
- * Decide whether a transition is defined on a given Transition.
8631
- * This filters out orchestration options and returns true
8632
- * if any options are left.
8024
+ * A list of values that can be hardware-accelerated.
8633
8025
  */
8634
- function isTransitionDefined({ when, delay: _delay, delayChildren, staggerChildren, staggerDirection, repeat, repeatType, repeatDelay, from, elapsed, ...transition }) {
8635
- return !!Object.keys(transition).length;
8636
- }
8637
-
8638
- function getValueTransition(transition, key) {
8639
- return (transition?.[key] ??
8640
- transition?.["default"] ??
8641
- transition);
8026
+ const acceleratedValues = new Set([
8027
+ "opacity",
8028
+ "clipPath",
8029
+ "filter",
8030
+ "transform",
8031
+ // TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved
8032
+ // or until we implement support for linear() easing.
8033
+ // "background-color"
8034
+ ]);
8035
+ const supportsWaapi = /*@__PURE__*/ memo(() => Object.hasOwnProperty.call(Element.prototype, "animate"));
8036
+ function supportsBrowserAnimation(options) {
8037
+ const { motionValue, name, repeatDelay, repeatType, damping, type } = options;
8038
+ if (!motionValue ||
8039
+ !motionValue.owner ||
8040
+ !(motionValue.owner.current instanceof HTMLElement)) {
8041
+ return false;
8042
+ }
8043
+ const { onUpdate, transformTemplate } = motionValue.owner.getProps();
8044
+ return (supportsWaapi() &&
8045
+ name &&
8046
+ acceleratedValues.has(name) &&
8047
+ (name !== "transform" || !transformTemplate) &&
8048
+ /**
8049
+ * If we're outputting values to onUpdate then we can't use WAAPI as there's
8050
+ * no way to read the value from WAAPI every frame.
8051
+ */
8052
+ !onUpdate &&
8053
+ !repeatDelay &&
8054
+ repeatType !== "mirror" &&
8055
+ damping !== 0 &&
8056
+ type !== "inertia");
8642
8057
  }
8643
8058
 
8644
- const supportsScrollTimeline = /* @__PURE__ */ memo(() => window.ScrollTimeline !== undefined);
8645
-
8646
- class GroupAnimation {
8647
- constructor(animations) {
8648
- // Bound to accomodate common `return animation.stop` pattern
8649
- this.stop = () => this.runAll("stop");
8650
- this.animations = animations.filter(Boolean);
8059
+ /**
8060
+ * Maximum time allowed between an animation being created and it being
8061
+ * resolved for us to use the latter as the start time.
8062
+ *
8063
+ * This is to ensure that while we prefer to "start" an animation as soon
8064
+ * as it's triggered, we also want to avoid a visual jump if there's a big delay
8065
+ * between these two moments.
8066
+ */
8067
+ const MAX_RESOLVE_DELAY = 40;
8068
+ class AsyncMotionValueAnimation extends WithPromise {
8069
+ constructor({ autoplay = true, delay = 0, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", keyframes, name, motionValue, element, ...options }) {
8070
+ super();
8071
+ /**
8072
+ * Bound to support return animation.stop pattern
8073
+ */
8074
+ this.stop = () => {
8075
+ if (this._animation) {
8076
+ this._animation.stop();
8077
+ this.stopTimeline?.();
8078
+ }
8079
+ else {
8080
+ this.keyframeResolver?.cancel();
8081
+ }
8082
+ };
8083
+ this.createdAt = time.now();
8084
+ const optionsWithDefaults = {
8085
+ autoplay,
8086
+ delay,
8087
+ type,
8088
+ repeat,
8089
+ repeatDelay,
8090
+ repeatType,
8091
+ name,
8092
+ motionValue,
8093
+ element,
8094
+ ...options,
8095
+ };
8096
+ const KeyframeResolver$1 = element?.KeyframeResolver || KeyframeResolver;
8097
+ this.keyframeResolver = new KeyframeResolver$1(keyframes, (resolvedKeyframes, finalKeyframe, forced) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe, optionsWithDefaults, !forced), name, motionValue, element);
8098
+ this.keyframeResolver?.scheduleResolve();
8099
+ }
8100
+ onKeyframesResolved(keyframes, finalKeyframe, options, sync) {
8101
+ this.keyframeResolver = undefined;
8102
+ const { name, type, velocity, delay, isHandoff, onUpdate } = options;
8103
+ this.resolvedAt = time.now();
8104
+ /**
8105
+ * If we can't animate this value with the resolved keyframes
8106
+ * then we should complete it immediately.
8107
+ */
8108
+ if (!canAnimate(keyframes, name, type, velocity)) {
8109
+ if (MotionGlobalConfig.instantAnimations || !delay) {
8110
+ onUpdate?.(getFinalKeyframe(keyframes, options, finalKeyframe));
8111
+ }
8112
+ keyframes[0] = keyframes[keyframes.length - 1];
8113
+ options.duration = 0;
8114
+ options.repeat = 0;
8115
+ }
8116
+ /**
8117
+ * Resolve startTime for the animation.
8118
+ *
8119
+ * This method uses the createdAt and resolvedAt to calculate the
8120
+ * animation startTime. *Ideally*, we would use the createdAt time as t=0
8121
+ * as the following frame would then be the first frame of the animation in
8122
+ * progress, which would feel snappier.
8123
+ *
8124
+ * However, if there's a delay (main thread work) between the creation of
8125
+ * the animation and the first commited frame, we prefer to use resolvedAt
8126
+ * to avoid a sudden jump into the animation.
8127
+ */
8128
+ const startTime = sync
8129
+ ? !this.resolvedAt
8130
+ ? this.createdAt
8131
+ : this.resolvedAt - this.createdAt > MAX_RESOLVE_DELAY
8132
+ ? this.resolvedAt
8133
+ : this.createdAt
8134
+ : undefined;
8135
+ const resolvedOptions = {
8136
+ startTime,
8137
+ finalKeyframe,
8138
+ ...options,
8139
+ keyframes,
8140
+ };
8141
+ /**
8142
+ * Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via
8143
+ * WAAPI. Therefore, this animation must be JS to ensure it runs "under" the
8144
+ * optimised animation.
8145
+ */
8146
+ const animation = !isHandoff && supportsBrowserAnimation(resolvedOptions)
8147
+ ? new NativeAnimationExtended({
8148
+ ...resolvedOptions,
8149
+ element: resolvedOptions.motionValue.owner.current,
8150
+ })
8151
+ : new JSAnimation(resolvedOptions);
8152
+ animation.finished.then(() => this.notifyFinished()).catch(noop);
8153
+ if (this.pendingTimeline) {
8154
+ this.stopTimeline = animation.attachTimeline(this.pendingTimeline);
8155
+ this.pendingTimeline = undefined;
8156
+ }
8157
+ this._animation = animation;
8651
8158
  }
8652
8159
  get finished() {
8653
- return Promise.all(this.animations.map((animation) => animation.finished));
8160
+ if (!this._animation) {
8161
+ return this._finished;
8162
+ }
8163
+ else {
8164
+ return this.animation.finished;
8165
+ }
8654
8166
  }
8655
- /**
8656
- * TODO: Filter out cancelled or stopped animations before returning
8657
- */
8658
- getAll(propName) {
8659
- return this.animations[0][propName];
8167
+ then(onResolve, _onReject) {
8168
+ return this.finished.finally(onResolve).then(() => { });
8660
8169
  }
8661
- setAll(propName, newValue) {
8662
- for (let i = 0; i < this.animations.length; i++) {
8663
- this.animations[i][propName] = newValue;
8170
+ get animation() {
8171
+ if (!this._animation) {
8172
+ flushKeyframeResolvers();
8664
8173
  }
8174
+ return this._animation;
8665
8175
  }
8666
- attachTimeline(timeline, fallback) {
8667
- const subscriptions = this.animations.map((animation) => {
8668
- if (supportsScrollTimeline() && animation.attachTimeline) {
8669
- return animation.attachTimeline(timeline);
8670
- }
8671
- else if (typeof fallback === "function") {
8672
- return fallback(animation);
8673
- }
8674
- });
8675
- return () => {
8676
- subscriptions.forEach((cancel, i) => {
8677
- cancel && cancel();
8678
- this.animations[i].stop();
8679
- });
8680
- };
8176
+ get duration() {
8177
+ return this.animation.duration;
8681
8178
  }
8682
8179
  get time() {
8683
- return this.getAll("time");
8180
+ return this.animation.time;
8684
8181
  }
8685
- set time(time) {
8686
- this.setAll("time", time);
8182
+ set time(newTime) {
8183
+ this.animation.time = newTime;
8687
8184
  }
8688
8185
  get speed() {
8689
- return this.getAll("speed");
8186
+ return this.animation.speed;
8690
8187
  }
8691
- set speed(speed) {
8692
- this.setAll("speed", speed);
8188
+ get state() {
8189
+ return this.animation.state;
8190
+ }
8191
+ set speed(newSpeed) {
8192
+ this.animation.speed = newSpeed;
8693
8193
  }
8694
8194
  get startTime() {
8695
- return this.getAll("startTime");
8195
+ return this.animation.startTime;
8696
8196
  }
8697
- get duration() {
8698
- let max = 0;
8699
- for (let i = 0; i < this.animations.length; i++) {
8700
- max = Math.max(max, this.animations[i].duration);
8197
+ attachTimeline(timeline) {
8198
+ if (this._animation) {
8199
+ this.stopTimeline = this.animation.attachTimeline(timeline);
8701
8200
  }
8702
- return max;
8703
- }
8704
- runAll(methodName) {
8705
- this.animations.forEach((controls) => controls[methodName]());
8706
- }
8707
- flatten() {
8708
- this.runAll("flatten");
8201
+ else {
8202
+ this.pendingTimeline = timeline;
8203
+ }
8204
+ return () => this.stop();
8709
8205
  }
8710
8206
  play() {
8711
- this.runAll("play");
8207
+ this.animation.play();
8712
8208
  }
8713
8209
  pause() {
8714
- this.runAll("pause");
8715
- }
8716
- cancel() {
8717
- this.runAll("cancel");
8210
+ this.animation.pause();
8718
8211
  }
8719
8212
  complete() {
8720
- this.runAll("complete");
8213
+ this.animation.complete();
8721
8214
  }
8722
- }
8723
-
8724
- class GroupAnimationWithThen extends GroupAnimation {
8725
- then(onResolve, _onReject) {
8726
- return this.finished.finally(onResolve).then(() => { });
8215
+ cancel() {
8216
+ this.animation.cancel();
8727
8217
  }
8728
8218
  }
8729
8219
 
@@ -8741,7 +8231,7 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
8741
8231
  */
8742
8232
  let { elapsed = 0 } = transition;
8743
8233
  elapsed = elapsed - secondsToMilliseconds(delay);
8744
- let options = {
8234
+ const options = {
8745
8235
  keyframes: Array.isArray(target) ? target : [null, target],
8746
8236
  ease: "easeOut",
8747
8237
  velocity: value.getVelocity(),
@@ -8764,22 +8254,18 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
8764
8254
  * unique transition settings for this value.
8765
8255
  */
8766
8256
  if (!isTransitionDefined(valueTransition)) {
8767
- options = {
8768
- ...options,
8769
- ...getDefaultTransition(name, options),
8770
- };
8257
+ Object.assign(options, getDefaultTransition(name, options));
8771
8258
  }
8772
8259
  /**
8773
8260
  * Both WAAPI and our internal animation functions use durations
8774
8261
  * as defined by milliseconds, while our external API defines them
8775
8262
  * as seconds.
8776
8263
  */
8777
- if (options.duration) {
8778
- options.duration = secondsToMilliseconds(options.duration);
8779
- }
8780
- if (options.repeatDelay) {
8781
- options.repeatDelay = secondsToMilliseconds(options.repeatDelay);
8782
- }
8264
+ options.duration && (options.duration = secondsToMilliseconds(options.duration));
8265
+ options.repeatDelay && (options.repeatDelay = secondsToMilliseconds(options.repeatDelay));
8266
+ /**
8267
+ * Support deprecated way to set initial value. Prefer keyframe syntax.
8268
+ */
8783
8269
  if (options.from !== undefined) {
8784
8270
  options.keyframes[0] = options.from;
8785
8271
  }
@@ -8791,6 +8277,12 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
8791
8277
  shouldSkip = true;
8792
8278
  }
8793
8279
  }
8280
+ if (MotionGlobalConfig.instantAnimations ||
8281
+ MotionGlobalConfig.skipAnimations) {
8282
+ shouldSkip = true;
8283
+ options.duration = 0;
8284
+ options.delay = 0;
8285
+ }
8794
8286
  /**
8795
8287
  * If the transition type or easing has been explicitly set by the user
8796
8288
  * then we don't want to allow flattening the animation.
@@ -8802,30 +8294,28 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
8802
8294
  * this early check prevents the need to create an animation at all.
8803
8295
  */
8804
8296
  if (shouldSkip && !isHandoff && value.get() !== undefined) {
8805
- const finalKeyframe = getFinalKeyframe(options.keyframes, valueTransition);
8297
+ const finalKeyframe = getFinalKeyframe$1(options.keyframes, valueTransition);
8806
8298
  if (finalKeyframe !== undefined) {
8807
8299
  frame.update(() => {
8808
8300
  options.onUpdate(finalKeyframe);
8809
8301
  options.onComplete();
8810
8302
  });
8811
- // We still want to return some animation controls here rather
8812
- // than returning undefined
8813
- return new GroupAnimationWithThen([]);
8303
+ return;
8814
8304
  }
8815
8305
  }
8816
- /**
8817
- * Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via
8818
- * WAAPI. Therefore, this animation must be JS to ensure it runs "under" the
8819
- * optimised animation.
8820
- */
8821
- if (!isHandoff && AcceleratedAnimation.supports(options)) {
8822
- return new AcceleratedAnimation(options);
8823
- }
8824
- else {
8825
- return new MainThreadAnimation(options);
8826
- }
8306
+ return new AsyncMotionValueAnimation(options);
8827
8307
  };
8828
8308
 
8309
+ const positionalKeys = new Set([
8310
+ "width",
8311
+ "height",
8312
+ "top",
8313
+ "left",
8314
+ "right",
8315
+ "bottom",
8316
+ ...transformPropOrder,
8317
+ ]);
8318
+
8829
8319
  /**
8830
8320
  * Decide whether we should block this animation. Previously, we achieved this
8831
8321
  * just by checking whether the key was listed in protectedKeys, but this
@@ -8857,6 +8347,17 @@ function animateTarget(visualElement, targetAndTransition, { delay = 0, transiti
8857
8347
  delay,
8858
8348
  ...getValueTransition(transition || {}, key),
8859
8349
  };
8350
+ /**
8351
+ * If the value is already at the defined target, skip the animation.
8352
+ */
8353
+ const currentValue = value.get();
8354
+ if (currentValue !== undefined &&
8355
+ !value.isAnimating &&
8356
+ !Array.isArray(valueTarget) &&
8357
+ valueTarget === currentValue &&
8358
+ !valueTransition.velocity) {
8359
+ continue;
8360
+ }
8860
8361
  /**
8861
8362
  * If this is the first time a value is being animated, check
8862
8363
  * to see if we're handling off from an existing animation.
@@ -10809,7 +10310,7 @@ function delay(callback, timeout) {
10809
10310
  callback(elapsed - timeout);
10810
10311
  }
10811
10312
  };
10812
- frame.read(checkElapsed, true);
10313
+ frame.setup(checkElapsed, true);
10813
10314
  return () => cancelFrame(checkElapsed);
10814
10315
  }
10815
10316
 
@@ -12103,9 +11604,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
12103
11604
  }
12104
11605
  setAnimationOrigin(delta, hasOnlyRelativeTargetChanged = false) {
12105
11606
  const snapshot = this.snapshot;
12106
- const snapshotLatestValues = snapshot
12107
- ? snapshot.latestValues
12108
- : {};
11607
+ const snapshotLatestValues = snapshot ? snapshot.latestValues : {};
12109
11608
  const mixedValues = { ...this.latestValues };
12110
11609
  const targetDelta = createDelta();
12111
11610
  if (!this.relativeParent ||
@@ -13173,15 +12672,6 @@ function initPrefersReducedMotion() {
13173
12672
  }
13174
12673
  }
13175
12674
 
13176
- /**
13177
- * A list of all ValueTypes
13178
- */
13179
- const valueTypes = [...dimensionValueTypes, color, complex];
13180
- /**
13181
- * Tests a value against the list of ValueTypes
13182
- */
13183
- const findValueType = (v) => valueTypes.find(testValueType(v));
13184
-
13185
12675
  const visualElementStore = new WeakMap();
13186
12676
 
13187
12677
  function updateMotionValuesFromProps(element, next, prev) {
@@ -13199,7 +12689,7 @@ function updateMotionValuesFromProps(element, next, prev) {
13199
12689
  * and warn against mismatches.
13200
12690
  */
13201
12691
  if (process.env.NODE_ENV === "development") {
13202
- warnOnce(nextValue.version === "12.7.4", `Attempting to mix Motion versions ${nextValue.version} with 12.7.4 may not work as expected.`);
12692
+ warnOnce(nextValue.version === "12.9.0", `Attempting to mix Motion versions ${nextValue.version} with 12.9.0 may not work as expected.`);
13203
12693
  }
13204
12694
  }
13205
12695
  else if (isMotionValue(prevValue)) {
@@ -13238,6 +12728,108 @@ function updateMotionValuesFromProps(element, next, prev) {
13238
12728
  return next;
13239
12729
  }
13240
12730
 
12731
+ /**
12732
+ * Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1"
12733
+ */
12734
+ const isNumericalString = (v) => /^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(v);
12735
+
12736
+ /**
12737
+ * Check if the value is a zero value string like "0px" or "0%"
12738
+ */
12739
+ const isZeroValueString = (v) => /^0[^.\s]+$/u.test(v);
12740
+
12741
+ /**
12742
+ * ValueType for "auto"
12743
+ */
12744
+ const auto = {
12745
+ test: (v) => v === "auto",
12746
+ parse: (v) => v,
12747
+ };
12748
+
12749
+ /**
12750
+ * Tests a provided value against a ValueType
12751
+ */
12752
+ const testValueType = (v) => (type) => type.test(v);
12753
+
12754
+ /**
12755
+ * A list of value types commonly used for dimensions
12756
+ */
12757
+ const dimensionValueTypes = [number, px, percent, degrees, vw, vh, auto];
12758
+ /**
12759
+ * Tests a dimensional value against the list of dimension ValueTypes
12760
+ */
12761
+ const findDimensionValueType = (v) => dimensionValueTypes.find(testValueType(v));
12762
+
12763
+ /**
12764
+ * A list of all ValueTypes
12765
+ */
12766
+ const valueTypes = [...dimensionValueTypes, color, complex];
12767
+ /**
12768
+ * Tests a value against the list of ValueTypes
12769
+ */
12770
+ const findValueType = (v) => valueTypes.find(testValueType(v));
12771
+
12772
+ /**
12773
+ * Properties that should default to 1 or 100%
12774
+ */
12775
+ const maxDefaults = new Set(["brightness", "contrast", "saturate", "opacity"]);
12776
+ function applyDefaultFilter(v) {
12777
+ const [name, value] = v.slice(0, -1).split("(");
12778
+ if (name === "drop-shadow")
12779
+ return v;
12780
+ const [number] = value.match(floatRegex) || [];
12781
+ if (!number)
12782
+ return v;
12783
+ const unit = value.replace(number, "");
12784
+ let defaultValue = maxDefaults.has(name) ? 1 : 0;
12785
+ if (number !== value)
12786
+ defaultValue *= 100;
12787
+ return name + "(" + defaultValue + unit + ")";
12788
+ }
12789
+ const functionRegex = /\b([a-z-]*)\(.*?\)/gu;
12790
+ const filter = {
12791
+ ...complex,
12792
+ getAnimatableNone: (v) => {
12793
+ const functions = v.match(functionRegex);
12794
+ return functions ? functions.map(applyDefaultFilter).join(" ") : v;
12795
+ },
12796
+ };
12797
+
12798
+ /**
12799
+ * A map of default value types for common values
12800
+ */
12801
+ const defaultValueTypes = {
12802
+ ...numberValueTypes,
12803
+ // Color props
12804
+ color,
12805
+ backgroundColor: color,
12806
+ outlineColor: color,
12807
+ fill: color,
12808
+ stroke: color,
12809
+ // Border props
12810
+ borderColor: color,
12811
+ borderTopColor: color,
12812
+ borderRightColor: color,
12813
+ borderBottomColor: color,
12814
+ borderLeftColor: color,
12815
+ filter,
12816
+ WebkitFilter: filter,
12817
+ };
12818
+ /**
12819
+ * Gets the default ValueType for the provided value key
12820
+ */
12821
+ const getDefaultValueType = (key) => defaultValueTypes[key];
12822
+
12823
+ function getAnimatableNone(key, value) {
12824
+ let defaultValueType = getDefaultValueType(key);
12825
+ if (defaultValueType !== filter)
12826
+ defaultValueType = complex;
12827
+ // If value is not recognised as animatable, ie "none", create an animatable version origin based on the target
12828
+ return defaultValueType.getAnimatableNone
12829
+ ? defaultValueType.getAnimatableNone(value)
12830
+ : undefined;
12831
+ }
12832
+
13241
12833
  const propEventHandlers = [
13242
12834
  "AnimationStart",
13243
12835
  "AnimationComplete",
@@ -13332,8 +12924,7 @@ class VisualElement {
13332
12924
  frame.render(this.render, false, true);
13333
12925
  }
13334
12926
  };
13335
- const { latestValues, renderState, onUpdate } = visualState;
13336
- this.onUpdate = onUpdate;
12927
+ const { latestValues, renderState } = visualState;
13337
12928
  this.latestValues = latestValues;
13338
12929
  this.baseTarget = { ...latestValues };
13339
12930
  this.initialValues = props.initial ? { ...latestValues } : {};
@@ -13535,7 +13126,6 @@ class VisualElement {
13535
13126
  if (this.handleChildMotionValue) {
13536
13127
  this.handleChildMotionValue();
13537
13128
  }
13538
- this.onUpdate && this.onUpdate(this);
13539
13129
  }
13540
13130
  getProps() {
13541
13131
  return this.props;
@@ -13695,6 +13285,202 @@ class VisualElement {
13695
13285
  }
13696
13286
  }
13697
13287
 
13288
+ /**
13289
+ * Parse Framer's special CSS variable format into a CSS token and a fallback.
13290
+ *
13291
+ * ```
13292
+ * `var(--foo, #fff)` => [`--foo`, '#fff']
13293
+ * ```
13294
+ *
13295
+ * @param current
13296
+ */
13297
+ const splitCSSVariableRegex =
13298
+ // eslint-disable-next-line redos-detector/no-unsafe-regex -- false positive, as it can match a lot of words
13299
+ /^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;
13300
+ function parseCSSVariable(current) {
13301
+ const match = splitCSSVariableRegex.exec(current);
13302
+ if (!match)
13303
+ return [,];
13304
+ const [, token1, token2, fallback] = match;
13305
+ return [`--${token1 ?? token2}`, fallback];
13306
+ }
13307
+ const maxDepth = 4;
13308
+ function getVariableValue(current, element, depth = 1) {
13309
+ invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`);
13310
+ const [token, fallback] = parseCSSVariable(current);
13311
+ // No CSS variable detected
13312
+ if (!token)
13313
+ return;
13314
+ // Attempt to read this CSS variable off the element
13315
+ const resolved = window.getComputedStyle(element).getPropertyValue(token);
13316
+ if (resolved) {
13317
+ const trimmed = resolved.trim();
13318
+ return isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed;
13319
+ }
13320
+ return isCSSVariableToken(fallback)
13321
+ ? getVariableValue(fallback, element, depth + 1)
13322
+ : fallback;
13323
+ }
13324
+
13325
+ function isNone(value) {
13326
+ if (typeof value === "number") {
13327
+ return value === 0;
13328
+ }
13329
+ else if (value !== null) {
13330
+ return value === "none" || value === "0" || isZeroValueString(value);
13331
+ }
13332
+ else {
13333
+ return true;
13334
+ }
13335
+ }
13336
+
13337
+ /**
13338
+ * If we encounter keyframes like "none" or "0" and we also have keyframes like
13339
+ * "#fff" or "200px 200px" we want to find a keyframe to serve as a template for
13340
+ * the "none" keyframes. In this case "#fff" or "200px 200px" - then these get turned into
13341
+ * zero equivalents, i.e. "#fff0" or "0px 0px".
13342
+ */
13343
+ const invalidTemplates = new Set(["auto", "none", "0"]);
13344
+ function makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name) {
13345
+ let i = 0;
13346
+ let animatableTemplate = undefined;
13347
+ while (i < unresolvedKeyframes.length && !animatableTemplate) {
13348
+ const keyframe = unresolvedKeyframes[i];
13349
+ if (typeof keyframe === "string" &&
13350
+ !invalidTemplates.has(keyframe) &&
13351
+ analyseComplexValue(keyframe).values.length) {
13352
+ animatableTemplate = unresolvedKeyframes[i];
13353
+ }
13354
+ i++;
13355
+ }
13356
+ if (animatableTemplate && name) {
13357
+ for (const noneIndex of noneKeyframeIndexes) {
13358
+ unresolvedKeyframes[noneIndex] = getAnimatableNone(name, animatableTemplate);
13359
+ }
13360
+ }
13361
+ }
13362
+
13363
+ class DOMKeyframesResolver extends KeyframeResolver {
13364
+ constructor(unresolvedKeyframes, onComplete, name, motionValue, element) {
13365
+ super(unresolvedKeyframes, onComplete, name, motionValue, element, true);
13366
+ }
13367
+ readKeyframes() {
13368
+ const { unresolvedKeyframes, element, name } = this;
13369
+ if (!element || !element.current)
13370
+ return;
13371
+ super.readKeyframes();
13372
+ /**
13373
+ * If any keyframe is a CSS variable, we need to find its value by sampling the element
13374
+ */
13375
+ for (let i = 0; i < unresolvedKeyframes.length; i++) {
13376
+ let keyframe = unresolvedKeyframes[i];
13377
+ if (typeof keyframe === "string") {
13378
+ keyframe = keyframe.trim();
13379
+ if (isCSSVariableToken(keyframe)) {
13380
+ const resolved = getVariableValue(keyframe, element.current);
13381
+ if (resolved !== undefined) {
13382
+ unresolvedKeyframes[i] = resolved;
13383
+ }
13384
+ if (i === unresolvedKeyframes.length - 1) {
13385
+ this.finalKeyframe = keyframe;
13386
+ }
13387
+ }
13388
+ }
13389
+ }
13390
+ /**
13391
+ * Resolve "none" values. We do this potentially twice - once before and once after measuring keyframes.
13392
+ * This could be seen as inefficient but it's a trade-off to avoid measurements in more situations, which
13393
+ * have a far bigger performance impact.
13394
+ */
13395
+ this.resolveNoneKeyframes();
13396
+ /**
13397
+ * Check to see if unit type has changed. If so schedule jobs that will
13398
+ * temporarily set styles to the destination keyframes.
13399
+ * Skip if we have more than two keyframes or this isn't a positional value.
13400
+ * TODO: We can throw if there are multiple keyframes and the value type changes.
13401
+ */
13402
+ if (!positionalKeys.has(name) || unresolvedKeyframes.length !== 2) {
13403
+ return;
13404
+ }
13405
+ const [origin, target] = unresolvedKeyframes;
13406
+ const originType = findDimensionValueType(origin);
13407
+ const targetType = findDimensionValueType(target);
13408
+ /**
13409
+ * Either we don't recognise these value types or we can animate between them.
13410
+ */
13411
+ if (originType === targetType)
13412
+ return;
13413
+ /**
13414
+ * If both values are numbers or pixels, we can animate between them by
13415
+ * converting them to numbers.
13416
+ */
13417
+ if (isNumOrPxType(originType) && isNumOrPxType(targetType)) {
13418
+ for (let i = 0; i < unresolvedKeyframes.length; i++) {
13419
+ const value = unresolvedKeyframes[i];
13420
+ if (typeof value === "string") {
13421
+ unresolvedKeyframes[i] = parseFloat(value);
13422
+ }
13423
+ }
13424
+ }
13425
+ else {
13426
+ /**
13427
+ * Else, the only way to resolve this is by measuring the element.
13428
+ */
13429
+ this.needsMeasurement = true;
13430
+ }
13431
+ }
13432
+ resolveNoneKeyframes() {
13433
+ const { unresolvedKeyframes, name } = this;
13434
+ const noneKeyframeIndexes = [];
13435
+ for (let i = 0; i < unresolvedKeyframes.length; i++) {
13436
+ if (unresolvedKeyframes[i] === null ||
13437
+ isNone(unresolvedKeyframes[i])) {
13438
+ noneKeyframeIndexes.push(i);
13439
+ }
13440
+ }
13441
+ if (noneKeyframeIndexes.length) {
13442
+ makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name);
13443
+ }
13444
+ }
13445
+ measureInitialState() {
13446
+ const { element, unresolvedKeyframes, name } = this;
13447
+ if (!element || !element.current)
13448
+ return;
13449
+ if (name === "height") {
13450
+ this.suspendedScrollY = window.pageYOffset;
13451
+ }
13452
+ this.measuredOrigin = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
13453
+ unresolvedKeyframes[0] = this.measuredOrigin;
13454
+ // Set final key frame to measure after next render
13455
+ const measureKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
13456
+ if (measureKeyframe !== undefined) {
13457
+ element.getValue(name, measureKeyframe).jump(measureKeyframe, false);
13458
+ }
13459
+ }
13460
+ measureEndState() {
13461
+ const { element, name, unresolvedKeyframes } = this;
13462
+ if (!element || !element.current)
13463
+ return;
13464
+ const value = element.getValue(name);
13465
+ value && value.jump(this.measuredOrigin, false);
13466
+ const finalKeyframeIndex = unresolvedKeyframes.length - 1;
13467
+ const finalKeyframe = unresolvedKeyframes[finalKeyframeIndex];
13468
+ unresolvedKeyframes[finalKeyframeIndex] = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
13469
+ if (finalKeyframe !== null && this.finalKeyframe === undefined) {
13470
+ this.finalKeyframe = finalKeyframe;
13471
+ }
13472
+ // If we removed transform values, reapply them before the next render
13473
+ if (this.removedTransforms?.length) {
13474
+ this.removedTransforms.forEach(([unsetTransformName, unsetTransformValue]) => {
13475
+ element
13476
+ .getValue(unsetTransformName)
13477
+ .set(unsetTransformValue);
13478
+ });
13479
+ }
13480
+ this.resolveNoneKeyframes();
13481
+ }
13482
+ }
13483
+
13698
13484
  class DOMVisualElement extends VisualElement {
13699
13485
  constructor() {
13700
13486
  super(...arguments);
@@ -13733,6 +13519,14 @@ class DOMVisualElement extends VisualElement {
13733
13519
  }
13734
13520
  }
13735
13521
 
13522
+ function renderHTML(element, { style, vars }, styleProp, projection) {
13523
+ Object.assign(element.style, style, projection && projection.getProjectionStyles(styleProp));
13524
+ // Loop over any CSS variables and assign those.
13525
+ for (const key in vars) {
13526
+ element.style.setProperty(key, vars[key]);
13527
+ }
13528
+ }
13529
+
13736
13530
  function getComputedStyle$1(element) {
13737
13531
  return window.getComputedStyle(element);
13738
13532
  }
@@ -13765,17 +13559,48 @@ class HTMLVisualElement extends DOMVisualElement {
13765
13559
  }
13766
13560
  }
13767
13561
 
13562
+ /**
13563
+ * A set of attribute names that are always read/written as camel case.
13564
+ */
13565
+ const camelCaseAttributes = new Set([
13566
+ "baseFrequency",
13567
+ "diffuseConstant",
13568
+ "kernelMatrix",
13569
+ "kernelUnitLength",
13570
+ "keySplines",
13571
+ "keyTimes",
13572
+ "limitingConeAngle",
13573
+ "markerHeight",
13574
+ "markerWidth",
13575
+ "numOctaves",
13576
+ "targetX",
13577
+ "targetY",
13578
+ "surfaceScale",
13579
+ "specularConstant",
13580
+ "specularExponent",
13581
+ "stdDeviation",
13582
+ "tableValues",
13583
+ "viewBox",
13584
+ "gradientTransform",
13585
+ "pathLength",
13586
+ "startOffset",
13587
+ "textLength",
13588
+ "lengthAdjust",
13589
+ ]);
13590
+
13591
+ function renderSVG(element, renderState, _styleProp, projection) {
13592
+ renderHTML(element, renderState, undefined, projection);
13593
+ for (const key in renderState.attrs) {
13594
+ element.setAttribute(!camelCaseAttributes.has(key) ? camelToDash(key) : key, renderState.attrs[key]);
13595
+ }
13596
+ }
13597
+
13768
13598
  class SVGVisualElement extends DOMVisualElement {
13769
13599
  constructor() {
13770
13600
  super(...arguments);
13771
13601
  this.type = "svg";
13772
13602
  this.isSVGTag = false;
13773
13603
  this.measureInstanceViewportBox = createBox;
13774
- this.updateDimensions = () => {
13775
- if (this.current && !this.renderState.dimensions) {
13776
- updateSVGDimensions(this.current, this.renderState);
13777
- }
13778
- };
13779
13604
  }
13780
13605
  getBaseTargetFromProps(props, key) {
13781
13606
  return props[key];
@@ -13791,11 +13616,6 @@ class SVGVisualElement extends DOMVisualElement {
13791
13616
  scrapeMotionValuesFromProps(props, prevProps, visualElement) {
13792
13617
  return scrapeMotionValuesFromProps(props, prevProps, visualElement);
13793
13618
  }
13794
- onBindTransform() {
13795
- if (this.current && !this.renderState.dimensions) {
13796
- frame.postRender(this.updateDimensions);
13797
- }
13798
- }
13799
13619
  build(renderState, latestValues, props) {
13800
13620
  buildSVGAttrs(renderState, latestValues, this.isSVGTag, props.transformTemplate);
13801
13621
  }
@@ -34122,20 +33942,20 @@ var initStripe = function initStripe(maybeStripe, args, startTime) {
34122
33942
  return stripe;
34123
33943
  }; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
34124
33944
 
34125
- var stripePromise$2;
33945
+ var stripePromise;
34126
33946
  var loadCalled = false;
34127
33947
 
34128
33948
  var getStripePromise = function getStripePromise() {
34129
- if (stripePromise$2) {
34130
- return stripePromise$2;
33949
+ if (stripePromise) {
33950
+ return stripePromise;
34131
33951
  }
34132
33952
 
34133
- stripePromise$2 = loadScript(null)["catch"](function (error) {
33953
+ stripePromise = loadScript(null)["catch"](function (error) {
34134
33954
  // clear cache on error
34135
- stripePromise$2 = null;
33955
+ stripePromise = null;
34136
33956
  return Promise.reject(error);
34137
33957
  });
34138
- return stripePromise$2;
33958
+ return stripePromise;
34139
33959
  }; // Execute our own script injection after a tick to give users time to do their
34140
33960
  // own script injection.
34141
33961
 
@@ -34205,9 +34025,8 @@ const CheckoutForm$1 = ({ onSuccess, onError, children, setSubmitting, }) => {
34205
34025
  };
34206
34026
  var CheckoutForm$2 = memo$1(CheckoutForm$1);
34207
34027
 
34208
- const publicStripeKey = "pk_live_51QvliXK5wvEuxX36GWLFgMtUrG2cGIjpW0eXoqVzjEr8S0PdGzAp4ydQa6ssxVW9u0zaLajod93YZnQIU5C8cgqp00Bb64X60b";
34209
- const stripePromise = loadStripe(publicStripeKey);
34210
- function PaymentElement({ paymentSecret, checkoutAppearance, locale, fonts, onSuccess, onError, children, setSubmitting, }) {
34028
+ function PaymentElement({ paymentSecret, publicKey, checkoutAppearance, locale, fonts, onSuccess, onError, children, setSubmitting, }) {
34029
+ const stripePromise = loadStripe(publicKey !== null && publicKey !== void 0 ? publicKey : "");
34211
34030
  const options = {
34212
34031
  locale: locale !== null && locale !== void 0 ? locale : "en",
34213
34032
  appearance: checkoutAppearance,
@@ -34219,7 +34038,7 @@ function PaymentElement({ paymentSecret, checkoutAppearance, locale, fonts, onSu
34219
34038
  }
34220
34039
  var PaymentElement$1 = memo$1(PaymentElement);
34221
34040
 
34222
- function PaymentForm({ paymentSecret, onSuccess, onError, onBack, onDoubleBack, contactEmail, shippingAddress, shippingName, shippingPrice, checkoutAppearance, fonts, locale, }) {
34041
+ function PaymentForm({ paymentSecret, onSuccess, onError, onBack, onDoubleBack, contactEmail, shippingAddress, shippingName, shippingPrice, checkoutAppearance, fonts, locale, publicKey, }) {
34223
34042
  const [isSubmitting, setIsSubmitting] = useState(false);
34224
34043
  const { t } = useTranslation();
34225
34044
  return (React__default.createElement("div", { className: "space-y-6" },
@@ -34248,7 +34067,7 @@ function PaymentForm({ paymentSecret, onSuccess, onError, onBack, onDoubleBack,
34248
34067
  " \u00B7 ",
34249
34068
  shippingPrice)),
34250
34069
  React__default.createElement(Button, { variant: "link", size: "link", onClick: onBack }, t("CheckoutEmbed.Shipping.change")))),
34251
- React__default.createElement("div", { className: "mt-8" }, paymentSecret && (React__default.createElement(PaymentElement$1, { fonts: fonts, checkoutAppearance: convertCheckoutAppearanceToStripeAppearance(checkoutAppearance, fonts), locale: locale, paymentSecret: paymentSecret, onSuccess: onSuccess, onError: onError, setSubmitting: setIsSubmitting },
34070
+ React__default.createElement("div", { className: "mt-8" }, paymentSecret && (React__default.createElement(PaymentElement$1, { fonts: fonts, checkoutAppearance: convertCheckoutAppearanceToStripeAppearance(checkoutAppearance, fonts), locale: locale, paymentSecret: paymentSecret, onSuccess: onSuccess, onError: onError, setSubmitting: setIsSubmitting, publicKey: publicKey },
34252
34071
  React__default.createElement("div", { className: "flex justify-between items-center pt-8" },
34253
34072
  React__default.createElement(Button, { type: "button", variant: "ghost", onClick: onBack },
34254
34073
  React__default.createElement(ChevronLeft, null),
@@ -34416,6 +34235,8 @@ const useFormStore = create()(persist((set) => ({
34416
34235
  setFormData: (formData) => set({ formData }),
34417
34236
  step: "customer",
34418
34237
  setStep: (step) => set({ step }),
34238
+ checkoutId: "",
34239
+ setCheckoutId: (checkoutId) => set({ checkoutId }),
34419
34240
  }), { name: `checkout` }));
34420
34241
 
34421
34242
  const motionSettings = {
@@ -34425,8 +34246,9 @@ const motionSettings = {
34425
34246
  transition: { duration: 0.2 },
34426
34247
  };
34427
34248
  function CheckoutForm({ storeClient, checkoutId, onSuccess, onError, cancelUrl, clientSecret, customer, currency, checkoutAppearance, fonts, locale, setShippingCost, exchangeRate, }) {
34428
- const { formData, setFormData, step, setStep } = useFormStore();
34249
+ const { formData, setFormData, step, setStep, checkoutId: storedCheckoutId, setCheckoutId, } = useFormStore();
34429
34250
  const [paymentSecret, setPaymentSecret] = useState(null);
34251
+ const [publicKey, setPublicKey] = useState(null);
34430
34252
  const [shippingRates, setShippingRates] = useState([]);
34431
34253
  const validateStep = useCallback(() => {
34432
34254
  if (step === "customer")
@@ -34448,21 +34270,53 @@ function CheckoutForm({ storeClient, checkoutId, onSuccess, onError, cancelUrl,
34448
34270
  validateStep();
34449
34271
  }, [step]);
34450
34272
  useEffect(() => {
34451
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
34452
- if (customer && !((_a = formData.customer) === null || _a === void 0 ? void 0 : _a.email)) {
34273
+ 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;
34274
+ if (checkoutId !== storedCheckoutId) {
34275
+ setStep("customer");
34276
+ setCheckoutId(checkoutId);
34277
+ if (customer) {
34278
+ setFormData({
34279
+ customerId: customer.id,
34280
+ customer: {
34281
+ 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 : "",
34282
+ 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 : "",
34283
+ phone: (_h = (_g = customer.address) === null || _g === void 0 ? void 0 : _g.phone) !== null && _h !== void 0 ? _h : "",
34284
+ email: (_j = customer.email) !== null && _j !== void 0 ? _j : "",
34285
+ address: {
34286
+ line1: (_l = (_k = customer.address) === null || _k === void 0 ? void 0 : _k.line1) !== null && _l !== void 0 ? _l : "",
34287
+ line2: (_o = (_m = customer.address) === null || _m === void 0 ? void 0 : _m.line2) !== null && _o !== void 0 ? _o : "",
34288
+ city: (_q = (_p = customer.address) === null || _p === void 0 ? void 0 : _p.city) !== null && _q !== void 0 ? _q : "",
34289
+ zipCode: (_s = (_r = customer.address) === null || _r === void 0 ? void 0 : _r.zipCode) !== null && _s !== void 0 ? _s : "",
34290
+ country: (_u = (_t = customer.address) === null || _t === void 0 ? void 0 : _t.country) !== null && _u !== void 0 ? _u : "",
34291
+ countryCode: (_w = (_v = customer.address) === null || _v === void 0 ? void 0 : _v.countryCode) !== null && _w !== void 0 ? _w : "",
34292
+ },
34293
+ },
34294
+ });
34295
+ }
34296
+ else if ((_x = formData.customer) === null || _x === void 0 ? void 0 : _x.email) {
34297
+ setFormData({
34298
+ customer: formData.customer,
34299
+ });
34300
+ }
34301
+ else {
34302
+ setFormData({});
34303
+ }
34304
+ return;
34305
+ }
34306
+ if (customer && !((_y = formData.customer) === null || _y === void 0 ? void 0 : _y.email)) {
34453
34307
  const step = customer.id ? "shipping" : "customer";
34454
34308
  setFormData(Object.assign(Object.assign({}, formData), { customerId: customer.id, customer: {
34455
- 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 : "",
34456
- 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 : "",
34457
- phone: (_j = (_h = customer.address) === null || _h === void 0 ? void 0 : _h.phone) !== null && _j !== void 0 ? _j : "",
34458
- email: (_k = customer.email) !== null && _k !== void 0 ? _k : "",
34309
+ 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 : "",
34310
+ 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 : "",
34311
+ phone: (_6 = (_5 = customer.address) === null || _5 === void 0 ? void 0 : _5.phone) !== null && _6 !== void 0 ? _6 : "",
34312
+ email: (_7 = customer.email) !== null && _7 !== void 0 ? _7 : "",
34459
34313
  address: {
34460
- line1: (_m = (_l = customer.address) === null || _l === void 0 ? void 0 : _l.line1) !== null && _m !== void 0 ? _m : "",
34461
- line2: (_p = (_o = customer.address) === null || _o === void 0 ? void 0 : _o.line2) !== null && _p !== void 0 ? _p : "",
34462
- city: (_r = (_q = customer.address) === null || _q === void 0 ? void 0 : _q.city) !== null && _r !== void 0 ? _r : "",
34463
- zipCode: (_t = (_s = customer.address) === null || _s === void 0 ? void 0 : _s.zipCode) !== null && _t !== void 0 ? _t : "",
34464
- country: (_v = (_u = customer.address) === null || _u === void 0 ? void 0 : _u.country) !== null && _v !== void 0 ? _v : "",
34465
- countryCode: (_x = (_w = customer.address) === null || _w === void 0 ? void 0 : _w.countryCode) !== null && _x !== void 0 ? _x : "",
34314
+ line1: (_9 = (_8 = customer.address) === null || _8 === void 0 ? void 0 : _8.line1) !== null && _9 !== void 0 ? _9 : "",
34315
+ line2: (_11 = (_10 = customer.address) === null || _10 === void 0 ? void 0 : _10.line2) !== null && _11 !== void 0 ? _11 : "",
34316
+ city: (_13 = (_12 = customer.address) === null || _12 === void 0 ? void 0 : _12.city) !== null && _13 !== void 0 ? _13 : "",
34317
+ zipCode: (_15 = (_14 = customer.address) === null || _14 === void 0 ? void 0 : _14.zipCode) !== null && _15 !== void 0 ? _15 : "",
34318
+ country: (_17 = (_16 = customer.address) === null || _16 === void 0 ? void 0 : _16.country) !== null && _17 !== void 0 ? _17 : "",
34319
+ countryCode: (_19 = (_18 = customer.address) === null || _18 === void 0 ? void 0 : _18.countryCode) !== null && _19 !== void 0 ? _19 : "",
34466
34320
  },
34467
34321
  } }));
34468
34322
  setStep(step);
@@ -34526,8 +34380,9 @@ function CheckoutForm({ storeClient, checkoutId, onSuccess, onError, cancelUrl,
34526
34380
  pickupPointId: data.pickupPointId,
34527
34381
  },
34528
34382
  });
34529
- const paymentSecret = yield storeClient.generateCheckoutsPaymentSecret(clientSecret, checkoutId);
34383
+ const { paymentSecret, publicKey } = yield storeClient.generateCheckoutsPaymentSecret(clientSecret, checkoutId);
34530
34384
  setPaymentSecret(paymentSecret);
34385
+ setPublicKey(publicKey);
34531
34386
  setShippingCost(data.price);
34532
34387
  setStep("payment");
34533
34388
  });
@@ -34546,8 +34401,9 @@ function CheckoutForm({ storeClient, checkoutId, onSuccess, onError, cancelUrl,
34546
34401
  };
34547
34402
  useEffect(() => {
34548
34403
  const asyncFunc = () => __awaiter(this, void 0, void 0, function* () {
34549
- const paymentSecret = yield storeClient.generateCheckoutsPaymentSecret(clientSecret, checkoutId);
34404
+ const { paymentSecret, publicKey } = yield storeClient.generateCheckoutsPaymentSecret(clientSecret, checkoutId);
34550
34405
  setPaymentSecret(paymentSecret);
34406
+ setPublicKey(publicKey);
34551
34407
  });
34552
34408
  if (!paymentSecret && step === "payment") {
34553
34409
  asyncFunc();
@@ -34560,7 +34416,7 @@ function CheckoutForm({ storeClient, checkoutId, onSuccess, onError, cancelUrl,
34560
34416
  step === "shipping" && formData.customer && (React__default.createElement(motion.div, Object.assign({ key: "shipping" }, motionSettings, { className: "absolute w-full" }),
34561
34417
  React__default.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 }))),
34562
34418
  step === "payment" && formData.customer && formData.shipping && (React__default.createElement(motion.div, Object.assign({ key: "payment" }, motionSettings, { className: "absolute w-full" }),
34563
- React__default.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) }))))));
34419
+ React__default.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 }))))));
34564
34420
  }
34565
34421
 
34566
34422
  function CheckoutFormLoading() {