motion 12.28.1-alpha.0 → 12.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/motion.dev.js +450 -344
- package/dist/motion.js +1 -1
- package/package.json +3 -3
package/dist/motion.dev.js
CHANGED
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/*#__NO_SIDE_EFFECTS__*/
|
|
84
|
-
const noop
|
|
84
|
+
const noop = (any) => any;
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
87
|
* Pipe
|
|
@@ -227,7 +227,7 @@
|
|
|
227
227
|
function cubicBezier(mX1, mY1, mX2, mY2) {
|
|
228
228
|
// If this is a linear gradient, return linear easing
|
|
229
229
|
if (mX1 === mY1 && mX2 === mY2)
|
|
230
|
-
return noop
|
|
230
|
+
return noop;
|
|
231
231
|
const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2);
|
|
232
232
|
// If animation is at start/end, return t without easing
|
|
233
233
|
return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
|
|
@@ -278,7 +278,7 @@
|
|
|
278
278
|
const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number";
|
|
279
279
|
|
|
280
280
|
const easingLookup = {
|
|
281
|
-
linear: noop
|
|
281
|
+
linear: noop,
|
|
282
282
|
easeIn,
|
|
283
283
|
easeInOut,
|
|
284
284
|
easeOut,
|
|
@@ -479,7 +479,7 @@
|
|
|
479
479
|
return { schedule, cancel, state, steps };
|
|
480
480
|
}
|
|
481
481
|
|
|
482
|
-
const { schedule: frame, cancel: cancelFrame, state: frameData, steps: frameSteps, } = /* @__PURE__ */ createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop
|
|
482
|
+
const { schedule: frame, cancel: cancelFrame, state: frameData, steps: frameSteps, } = /* @__PURE__ */ createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop, true);
|
|
483
483
|
|
|
484
484
|
let now;
|
|
485
485
|
function clearTime() {
|
|
@@ -1429,7 +1429,7 @@
|
|
|
1429
1429
|
for (let i = 0; i < numMixers; i++) {
|
|
1430
1430
|
let mixer = mixerFactory(output[i], output[i + 1]);
|
|
1431
1431
|
if (ease) {
|
|
1432
|
-
const easingFunction = Array.isArray(ease) ? ease[i] || noop
|
|
1432
|
+
const easingFunction = Array.isArray(ease) ? ease[i] || noop : ease;
|
|
1433
1433
|
mixer = pipe(easingFunction, mixer);
|
|
1434
1434
|
}
|
|
1435
1435
|
mixers.push(mixer);
|
|
@@ -2491,7 +2491,7 @@
|
|
|
2491
2491
|
this.animation.onfinish = null;
|
|
2492
2492
|
if (timeline && supportsScrollTimeline()) {
|
|
2493
2493
|
this.animation.timeline = timeline;
|
|
2494
|
-
return noop
|
|
2494
|
+
return noop;
|
|
2495
2495
|
}
|
|
2496
2496
|
else {
|
|
2497
2497
|
return observe(this);
|
|
@@ -2783,7 +2783,7 @@
|
|
|
2783
2783
|
: new JSAnimation(resolvedOptions);
|
|
2784
2784
|
animation.finished.then(() => {
|
|
2785
2785
|
this.notifyFinished();
|
|
2786
|
-
}).catch(noop
|
|
2786
|
+
}).catch(noop);
|
|
2787
2787
|
if (this.pendingTimeline) {
|
|
2788
2788
|
this.stopTimeline = animation.attachTimeline(this.pendingTimeline);
|
|
2789
2789
|
this.pendingTimeline = undefined;
|
|
@@ -4370,7 +4370,8 @@
|
|
|
4370
4370
|
valueIsDefault = value === (key.startsWith("scale") ? 1 : 0);
|
|
4371
4371
|
}
|
|
4372
4372
|
else {
|
|
4373
|
-
|
|
4373
|
+
const parsed = parseFloat(value);
|
|
4374
|
+
valueIsDefault = key.startsWith("scale") ? parsed === 1 : parsed === 0;
|
|
4374
4375
|
}
|
|
4375
4376
|
if (!valueIsDefault) {
|
|
4376
4377
|
transformIsDefault = false;
|
|
@@ -4425,17 +4426,21 @@
|
|
|
4425
4426
|
const styleEffect = /*@__PURE__*/ createSelectorEffect(
|
|
4426
4427
|
/*@__PURE__*/ createEffect(addStyleValue));
|
|
4427
4428
|
|
|
4428
|
-
const toPx = px.transform;
|
|
4429
4429
|
function addSVGPathValue(element, state, key, value) {
|
|
4430
4430
|
frame.render(() => element.setAttribute("pathLength", "1"));
|
|
4431
4431
|
if (key === "pathOffset") {
|
|
4432
|
-
return state.set(key, value, () =>
|
|
4432
|
+
return state.set(key, value, () => {
|
|
4433
|
+
// Use unitless value to avoid Safari zoom bug
|
|
4434
|
+
const offset = state.latest[key];
|
|
4435
|
+
element.setAttribute("stroke-dashoffset", `${-offset}`);
|
|
4436
|
+
});
|
|
4433
4437
|
}
|
|
4434
4438
|
else {
|
|
4435
4439
|
if (!state.get("stroke-dasharray")) {
|
|
4436
4440
|
state.set("stroke-dasharray", new MotionValue("1 1"), () => {
|
|
4437
4441
|
const { pathLength = 1, pathSpacing } = state.latest;
|
|
4438
|
-
|
|
4442
|
+
// Use unitless values to avoid Safari zoom bug
|
|
4443
|
+
element.setAttribute("stroke-dasharray", `${pathLength} ${pathSpacing ?? 1 - Number(pathLength)}`);
|
|
4439
4444
|
});
|
|
4440
4445
|
}
|
|
4441
4446
|
return state.set(key, value, undefined, state.get("stroke-dasharray"));
|
|
@@ -4981,6 +4986,101 @@
|
|
|
4981
4986
|
return useImmediate ? interpolator(inputValue) : interpolator;
|
|
4982
4987
|
}
|
|
4983
4988
|
|
|
4989
|
+
/**
|
|
4990
|
+
* Create a `MotionValue` that animates to its latest value using any transition type.
|
|
4991
|
+
* Can either be a value or track another `MotionValue`.
|
|
4992
|
+
*
|
|
4993
|
+
* ```jsx
|
|
4994
|
+
* const x = motionValue(0)
|
|
4995
|
+
* const y = followValue(x, { type: "spring", stiffness: 300 })
|
|
4996
|
+
* // or with tween
|
|
4997
|
+
* const z = followValue(x, { type: "tween", duration: 0.5, ease: "easeOut" })
|
|
4998
|
+
* ```
|
|
4999
|
+
*
|
|
5000
|
+
* @param source - Initial value or MotionValue to track
|
|
5001
|
+
* @param options - Animation transition options
|
|
5002
|
+
* @returns `MotionValue`
|
|
5003
|
+
*
|
|
5004
|
+
* @public
|
|
5005
|
+
*/
|
|
5006
|
+
function followValue(source, options) {
|
|
5007
|
+
const initialValue = isMotionValue(source) ? source.get() : source;
|
|
5008
|
+
const value = motionValue(initialValue);
|
|
5009
|
+
attachFollow(value, source, options);
|
|
5010
|
+
return value;
|
|
5011
|
+
}
|
|
5012
|
+
/**
|
|
5013
|
+
* Attach an animation to a MotionValue that will animate whenever the value changes.
|
|
5014
|
+
* Similar to attachSpring but supports any transition type (spring, tween, inertia, etc.)
|
|
5015
|
+
*
|
|
5016
|
+
* @param value - The MotionValue to animate
|
|
5017
|
+
* @param source - Initial value or MotionValue to track
|
|
5018
|
+
* @param options - Animation transition options
|
|
5019
|
+
* @returns Cleanup function
|
|
5020
|
+
*
|
|
5021
|
+
* @public
|
|
5022
|
+
*/
|
|
5023
|
+
function attachFollow(value, source, options = {}) {
|
|
5024
|
+
const initialValue = value.get();
|
|
5025
|
+
let activeAnimation = null;
|
|
5026
|
+
let latestValue = initialValue;
|
|
5027
|
+
let latestSetter;
|
|
5028
|
+
const unit = typeof initialValue === "string"
|
|
5029
|
+
? initialValue.replace(/[\d.-]/g, "")
|
|
5030
|
+
: undefined;
|
|
5031
|
+
const stopAnimation = () => {
|
|
5032
|
+
if (activeAnimation) {
|
|
5033
|
+
activeAnimation.stop();
|
|
5034
|
+
activeAnimation = null;
|
|
5035
|
+
}
|
|
5036
|
+
};
|
|
5037
|
+
const startAnimation = () => {
|
|
5038
|
+
stopAnimation();
|
|
5039
|
+
const currentValue = asNumber$1(value.get());
|
|
5040
|
+
const targetValue = asNumber$1(latestValue);
|
|
5041
|
+
// Don't animate if we're already at the target
|
|
5042
|
+
if (currentValue === targetValue) {
|
|
5043
|
+
return;
|
|
5044
|
+
}
|
|
5045
|
+
activeAnimation = new JSAnimation({
|
|
5046
|
+
keyframes: [currentValue, targetValue],
|
|
5047
|
+
velocity: value.getVelocity(),
|
|
5048
|
+
// Default to spring if no type specified (matches useSpring behavior)
|
|
5049
|
+
type: "spring",
|
|
5050
|
+
restDelta: 0.001,
|
|
5051
|
+
restSpeed: 0.01,
|
|
5052
|
+
...options,
|
|
5053
|
+
onUpdate: latestSetter,
|
|
5054
|
+
});
|
|
5055
|
+
};
|
|
5056
|
+
value.attach((v, set) => {
|
|
5057
|
+
latestValue = v;
|
|
5058
|
+
latestSetter = (latest) => set(parseValue(latest, unit));
|
|
5059
|
+
frame.postRender(() => {
|
|
5060
|
+
startAnimation();
|
|
5061
|
+
value["events"].animationStart?.notify();
|
|
5062
|
+
activeAnimation?.then(() => {
|
|
5063
|
+
value["events"].animationComplete?.notify();
|
|
5064
|
+
});
|
|
5065
|
+
});
|
|
5066
|
+
}, stopAnimation);
|
|
5067
|
+
if (isMotionValue(source)) {
|
|
5068
|
+
const removeSourceOnChange = source.on("change", (v) => value.set(parseValue(v, unit)));
|
|
5069
|
+
const removeValueOnDestroy = value.on("destroy", removeSourceOnChange);
|
|
5070
|
+
return () => {
|
|
5071
|
+
removeSourceOnChange();
|
|
5072
|
+
removeValueOnDestroy();
|
|
5073
|
+
};
|
|
5074
|
+
}
|
|
5075
|
+
return stopAnimation;
|
|
5076
|
+
}
|
|
5077
|
+
function parseValue(v, unit) {
|
|
5078
|
+
return unit ? v + unit : v;
|
|
5079
|
+
}
|
|
5080
|
+
function asNumber$1(v) {
|
|
5081
|
+
return typeof v === "number" ? v : parseFloat(v);
|
|
5082
|
+
}
|
|
5083
|
+
|
|
4984
5084
|
function subscribeValue(inputValues, outputValue, getLatest) {
|
|
4985
5085
|
const update = () => outputValue.set(getLatest());
|
|
4986
5086
|
const scheduleUpdate = () => frame.preRender(update, false, true);
|
|
@@ -5070,72 +5170,30 @@
|
|
|
5070
5170
|
*
|
|
5071
5171
|
* ```jsx
|
|
5072
5172
|
* const x = motionValue(0)
|
|
5073
|
-
* const y =
|
|
5173
|
+
* const y = springValue(x, { stiffness: 300 })
|
|
5074
5174
|
* ```
|
|
5075
5175
|
*
|
|
5076
|
-
* @param
|
|
5176
|
+
* @param source - Initial value or MotionValue to track
|
|
5177
|
+
* @param options - Spring configuration options
|
|
5077
5178
|
* @returns `MotionValue`
|
|
5078
5179
|
*
|
|
5079
5180
|
* @public
|
|
5080
5181
|
*/
|
|
5081
5182
|
function springValue(source, options) {
|
|
5082
|
-
|
|
5083
|
-
const value = motionValue(initialValue);
|
|
5084
|
-
attachSpring(value, source, options);
|
|
5085
|
-
return value;
|
|
5183
|
+
return followValue(source, { type: "spring", ...options });
|
|
5086
5184
|
}
|
|
5185
|
+
/**
|
|
5186
|
+
* Attach a spring animation to a MotionValue that will animate whenever the value changes.
|
|
5187
|
+
*
|
|
5188
|
+
* @param value - The MotionValue to animate
|
|
5189
|
+
* @param source - Initial value or MotionValue to track
|
|
5190
|
+
* @param options - Spring configuration options
|
|
5191
|
+
* @returns Cleanup function
|
|
5192
|
+
*
|
|
5193
|
+
* @public
|
|
5194
|
+
*/
|
|
5087
5195
|
function attachSpring(value, source, options) {
|
|
5088
|
-
|
|
5089
|
-
let activeAnimation = null;
|
|
5090
|
-
let latestValue = initialValue;
|
|
5091
|
-
let latestSetter;
|
|
5092
|
-
const unit = typeof initialValue === "string"
|
|
5093
|
-
? initialValue.replace(/[\d.-]/g, "")
|
|
5094
|
-
: undefined;
|
|
5095
|
-
const stopAnimation = () => {
|
|
5096
|
-
if (activeAnimation) {
|
|
5097
|
-
activeAnimation.stop();
|
|
5098
|
-
activeAnimation = null;
|
|
5099
|
-
}
|
|
5100
|
-
};
|
|
5101
|
-
const startAnimation = () => {
|
|
5102
|
-
stopAnimation();
|
|
5103
|
-
activeAnimation = new JSAnimation({
|
|
5104
|
-
keyframes: [asNumber$1(value.get()), asNumber$1(latestValue)],
|
|
5105
|
-
velocity: value.getVelocity(),
|
|
5106
|
-
type: "spring",
|
|
5107
|
-
restDelta: 0.001,
|
|
5108
|
-
restSpeed: 0.01,
|
|
5109
|
-
...options,
|
|
5110
|
-
onUpdate: latestSetter,
|
|
5111
|
-
});
|
|
5112
|
-
};
|
|
5113
|
-
value.attach((v, set) => {
|
|
5114
|
-
latestValue = v;
|
|
5115
|
-
latestSetter = (latest) => set(parseValue(latest, unit));
|
|
5116
|
-
frame.postRender(() => {
|
|
5117
|
-
startAnimation();
|
|
5118
|
-
value['events'].animationStart?.notify();
|
|
5119
|
-
activeAnimation?.then(() => {
|
|
5120
|
-
value['events'].animationComplete?.notify();
|
|
5121
|
-
});
|
|
5122
|
-
});
|
|
5123
|
-
}, stopAnimation);
|
|
5124
|
-
if (isMotionValue(source)) {
|
|
5125
|
-
const removeSourceOnChange = source.on("change", (v) => value.set(parseValue(v, unit)));
|
|
5126
|
-
const removeValueOnDestroy = value.on("destroy", removeSourceOnChange);
|
|
5127
|
-
return () => {
|
|
5128
|
-
removeSourceOnChange();
|
|
5129
|
-
removeValueOnDestroy();
|
|
5130
|
-
};
|
|
5131
|
-
}
|
|
5132
|
-
return stopAnimation;
|
|
5133
|
-
}
|
|
5134
|
-
function parseValue(v, unit) {
|
|
5135
|
-
return unit ? v + unit : v;
|
|
5136
|
-
}
|
|
5137
|
-
function asNumber$1(v) {
|
|
5138
|
-
return typeof v === "number" ? v : parseFloat(v);
|
|
5196
|
+
return attachFollow(value, source, { type: "spring", ...options });
|
|
5139
5197
|
}
|
|
5140
5198
|
|
|
5141
5199
|
/**
|
|
@@ -5402,7 +5460,7 @@
|
|
|
5402
5460
|
constructor(update, options = {}) {
|
|
5403
5461
|
this.currentSubject = "root";
|
|
5404
5462
|
this.targets = new Map();
|
|
5405
|
-
this.notifyReady = noop
|
|
5463
|
+
this.notifyReady = noop;
|
|
5406
5464
|
this.readyPromise = new Promise((resolve) => {
|
|
5407
5465
|
this.notifyReady = resolve;
|
|
5408
5466
|
});
|
|
@@ -6335,7 +6393,8 @@
|
|
|
6335
6393
|
valueIsDefault = value === (key.startsWith("scale") ? 1 : 0);
|
|
6336
6394
|
}
|
|
6337
6395
|
else {
|
|
6338
|
-
|
|
6396
|
+
const parsed = parseFloat(value);
|
|
6397
|
+
valueIsDefault = key.startsWith("scale") ? parsed === 1 : parsed === 0;
|
|
6339
6398
|
}
|
|
6340
6399
|
if (!valueIsDefault || transformTemplate) {
|
|
6341
6400
|
const valueAsType = getValueAsType(value, numberValueTypes[key]);
|
|
@@ -6638,6 +6697,9 @@
|
|
|
6638
6697
|
* and stroke-dasharray attributes.
|
|
6639
6698
|
*
|
|
6640
6699
|
* This function is mutative to reduce per-frame GC.
|
|
6700
|
+
*
|
|
6701
|
+
* Note: We use unitless values for stroke-dasharray and stroke-dashoffset
|
|
6702
|
+
* because Safari incorrectly scales px values when the page is zoomed.
|
|
6641
6703
|
*/
|
|
6642
6704
|
function buildSVGPath(attrs, length, spacing = 1, offset = 0, useDashCase = true) {
|
|
6643
6705
|
// Normalise path length by setting SVG attribute pathLength to 1
|
|
@@ -6645,12 +6707,10 @@
|
|
|
6645
6707
|
// We use dash case when setting attributes directly to the DOM node and camel case
|
|
6646
6708
|
// when defining props on a React component.
|
|
6647
6709
|
const keys = useDashCase ? dashKeys : camelKeys;
|
|
6648
|
-
// Build the dash offset
|
|
6649
|
-
attrs[keys.offset] =
|
|
6650
|
-
// Build the dash array
|
|
6651
|
-
|
|
6652
|
-
const pathSpacing = px.transform(spacing);
|
|
6653
|
-
attrs[keys.array] = `${pathLength} ${pathSpacing}`;
|
|
6710
|
+
// Build the dash offset (unitless to avoid Safari zoom bug)
|
|
6711
|
+
attrs[keys.offset] = `${-offset}`;
|
|
6712
|
+
// Build the dash array (unitless to avoid Safari zoom bug)
|
|
6713
|
+
attrs[keys.array] = `${length} ${spacing}`;
|
|
6654
6714
|
}
|
|
6655
6715
|
|
|
6656
6716
|
/**
|
|
@@ -7457,7 +7517,7 @@
|
|
|
7457
7517
|
: values.borderRadius;
|
|
7458
7518
|
}
|
|
7459
7519
|
const easeCrossfadeIn = /*@__PURE__*/ compress(0, 0.5, circOut);
|
|
7460
|
-
const easeCrossfadeOut = /*@__PURE__*/ compress(0.5, 0.95, noop
|
|
7520
|
+
const easeCrossfadeOut = /*@__PURE__*/ compress(0.5, 0.95, noop);
|
|
7461
7521
|
function compress(min, max, easing) {
|
|
7462
7522
|
return (p) => {
|
|
7463
7523
|
// Could replace ifs with clamp
|
|
@@ -7575,13 +7635,6 @@
|
|
|
7575
7635
|
const prevLead = this.lead;
|
|
7576
7636
|
if (node === prevLead)
|
|
7577
7637
|
return;
|
|
7578
|
-
console.log("[projection] promote", {
|
|
7579
|
-
layoutId: node.options.layoutId,
|
|
7580
|
-
node: node.id,
|
|
7581
|
-
prevLead: prevLead?.id,
|
|
7582
|
-
prevLeadSnapshot: Boolean(prevLead?.snapshot),
|
|
7583
|
-
prevLeadResumeFrom: Boolean(prevLead?.resumeFrom),
|
|
7584
|
-
});
|
|
7585
7638
|
this.prevLead = prevLead;
|
|
7586
7639
|
this.lead = node;
|
|
7587
7640
|
node.show();
|
|
@@ -7690,7 +7743,7 @@
|
|
|
7690
7743
|
cancelTreeOptimisedTransformAnimations(parent);
|
|
7691
7744
|
}
|
|
7692
7745
|
}
|
|
7693
|
-
function createProjectionNode({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) {
|
|
7746
|
+
function createProjectionNode$1({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) {
|
|
7694
7747
|
return class ProjectionNode {
|
|
7695
7748
|
constructor(latestValues = {}, parent = defaultParent?.()) {
|
|
7696
7749
|
/**
|
|
@@ -9228,7 +9281,7 @@
|
|
|
9228
9281
|
*/
|
|
9229
9282
|
const roundPoint = userAgentContains("applewebkit/") && !userAgentContains("chrome/")
|
|
9230
9283
|
? Math.round
|
|
9231
|
-
: noop
|
|
9284
|
+
: noop;
|
|
9232
9285
|
function roundAxis(axis) {
|
|
9233
9286
|
// Round to the nearest .5 pixels to support subpixel layouts
|
|
9234
9287
|
axis.min = roundPoint(axis.min);
|
|
@@ -9247,11 +9300,11 @@
|
|
|
9247
9300
|
return node !== node.root && node.scroll?.wasRoot;
|
|
9248
9301
|
}
|
|
9249
9302
|
|
|
9250
|
-
const DocumentProjectionNode = createProjectionNode({
|
|
9303
|
+
const DocumentProjectionNode = createProjectionNode$1({
|
|
9251
9304
|
attachResizeListener: (ref, notify) => addDomEvent(ref, "resize", notify),
|
|
9252
9305
|
measureScroll: () => ({
|
|
9253
|
-
x: document.documentElement.scrollLeft || document.body
|
|
9254
|
-
y: document.documentElement.scrollTop || document.body
|
|
9306
|
+
x: document.documentElement.scrollLeft || document.body?.scrollLeft || 0,
|
|
9307
|
+
y: document.documentElement.scrollTop || document.body?.scrollTop || 0,
|
|
9255
9308
|
}),
|
|
9256
9309
|
checkIsScrollRoot: () => true,
|
|
9257
9310
|
});
|
|
@@ -9282,7 +9335,7 @@
|
|
|
9282
9335
|
const rootProjectionNode = {
|
|
9283
9336
|
current: undefined,
|
|
9284
9337
|
};
|
|
9285
|
-
const HTMLProjectionNode = createProjectionNode({
|
|
9338
|
+
const HTMLProjectionNode = createProjectionNode$1({
|
|
9286
9339
|
measureScroll: (instance) => ({
|
|
9287
9340
|
x: instance.scrollLeft,
|
|
9288
9341
|
y: instance.scrollTop,
|
|
@@ -9302,160 +9355,315 @@
|
|
|
9302
9355
|
checkIsScrollRoot: (instance) => Boolean(window.getComputedStyle(instance).position === "fixed"),
|
|
9303
9356
|
});
|
|
9304
9357
|
|
|
9305
|
-
const
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
if (
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9358
|
+
const LAYOUT_SELECTOR = "[data-layout], [data-layout-id]";
|
|
9359
|
+
function getLayoutElements(scope) {
|
|
9360
|
+
const elements = Array.from(scope.querySelectorAll(LAYOUT_SELECTOR));
|
|
9361
|
+
// Include scope itself if it's an Element (not Document) and has layout attributes
|
|
9362
|
+
if (scope instanceof Element && hasLayout(scope)) {
|
|
9363
|
+
elements.unshift(scope);
|
|
9364
|
+
}
|
|
9365
|
+
return elements;
|
|
9366
|
+
}
|
|
9367
|
+
function getLayoutId(element) {
|
|
9368
|
+
return element.getAttribute("data-layout-id");
|
|
9369
|
+
}
|
|
9370
|
+
function hasLayout(element) {
|
|
9371
|
+
return (element.hasAttribute("data-layout") ||
|
|
9372
|
+
element.hasAttribute("data-layout-id"));
|
|
9373
|
+
}
|
|
9374
|
+
|
|
9375
|
+
let scaleCorrectorAdded = false;
|
|
9376
|
+
/**
|
|
9377
|
+
* Track active projection nodes per element to handle animation interruption.
|
|
9378
|
+
* When a new animation starts on an element that already has an active animation,
|
|
9379
|
+
* we need to stop the old animation so the new one can start from the current
|
|
9380
|
+
* visual position.
|
|
9381
|
+
*/
|
|
9382
|
+
const activeProjectionNodes = new WeakMap();
|
|
9383
|
+
function ensureScaleCorrectors() {
|
|
9384
|
+
if (scaleCorrectorAdded)
|
|
9385
|
+
return;
|
|
9386
|
+
scaleCorrectorAdded = true;
|
|
9387
|
+
addScaleCorrector({
|
|
9388
|
+
borderRadius: {
|
|
9389
|
+
...correctBorderRadius,
|
|
9390
|
+
applyTo: [
|
|
9391
|
+
"borderTopLeftRadius",
|
|
9392
|
+
"borderTopRightRadius",
|
|
9393
|
+
"borderBottomLeftRadius",
|
|
9394
|
+
"borderBottomRightRadius",
|
|
9395
|
+
],
|
|
9396
|
+
},
|
|
9397
|
+
borderTopLeftRadius: correctBorderRadius,
|
|
9398
|
+
borderTopRightRadius: correctBorderRadius,
|
|
9399
|
+
borderBottomLeftRadius: correctBorderRadius,
|
|
9400
|
+
borderBottomRightRadius: correctBorderRadius,
|
|
9401
|
+
boxShadow: correctBoxShadow,
|
|
9402
|
+
});
|
|
9403
|
+
}
|
|
9404
|
+
/**
|
|
9405
|
+
* Get DOM depth of an element
|
|
9406
|
+
*/
|
|
9407
|
+
function getDepth(element) {
|
|
9408
|
+
let depth = 0;
|
|
9409
|
+
let current = element.parentElement;
|
|
9410
|
+
while (current) {
|
|
9411
|
+
depth++;
|
|
9412
|
+
current = current.parentElement;
|
|
9413
|
+
}
|
|
9414
|
+
return depth;
|
|
9415
|
+
}
|
|
9416
|
+
/**
|
|
9417
|
+
* Find the closest projection parent for an element
|
|
9418
|
+
*/
|
|
9419
|
+
function findProjectionParent(element, nodeCache) {
|
|
9420
|
+
let parent = element.parentElement;
|
|
9421
|
+
while (parent) {
|
|
9422
|
+
const node = nodeCache.get(parent);
|
|
9423
|
+
if (node)
|
|
9424
|
+
return node;
|
|
9425
|
+
parent = parent.parentElement;
|
|
9426
|
+
}
|
|
9427
|
+
return undefined;
|
|
9428
|
+
}
|
|
9429
|
+
/**
|
|
9430
|
+
* Create or reuse a projection node for an element
|
|
9431
|
+
*/
|
|
9432
|
+
function createProjectionNode(element, parent, options, transition) {
|
|
9433
|
+
// Check for existing active node - reuse it to preserve animation state
|
|
9434
|
+
const existingNode = activeProjectionNodes.get(element);
|
|
9435
|
+
if (existingNode) {
|
|
9436
|
+
const visualElement = existingNode.options.visualElement;
|
|
9437
|
+
// Update transition options for the new animation
|
|
9438
|
+
const nodeTransition = transition
|
|
9439
|
+
? { duration: transition.duration, ease: transition.ease }
|
|
9440
|
+
: { duration: 0.3, ease: "easeOut" };
|
|
9441
|
+
existingNode.setOptions({
|
|
9442
|
+
...existingNode.options,
|
|
9443
|
+
animate: true,
|
|
9444
|
+
transition: nodeTransition,
|
|
9445
|
+
...options,
|
|
9446
|
+
});
|
|
9447
|
+
// Re-mount the node if it was previously unmounted
|
|
9448
|
+
// This re-adds it to root.nodes so didUpdate() will process it
|
|
9449
|
+
if (!existingNode.instance) {
|
|
9450
|
+
existingNode.mount(element);
|
|
9451
|
+
}
|
|
9452
|
+
return { node: existingNode, visualElement };
|
|
9453
|
+
}
|
|
9454
|
+
// No existing node - create a new one
|
|
9455
|
+
const latestValues = {};
|
|
9456
|
+
const visualElement = new HTMLVisualElement({
|
|
9457
|
+
visualState: {
|
|
9458
|
+
latestValues,
|
|
9459
|
+
renderState: {
|
|
9460
|
+
transformOrigin: {},
|
|
9461
|
+
transform: {},
|
|
9462
|
+
style: {},
|
|
9463
|
+
vars: {},
|
|
9464
|
+
},
|
|
9465
|
+
},
|
|
9466
|
+
presenceContext: null,
|
|
9467
|
+
props: {},
|
|
9468
|
+
});
|
|
9469
|
+
const node = new HTMLProjectionNode(latestValues, parent);
|
|
9470
|
+
// Convert AnimationOptions to transition format for the projection system
|
|
9471
|
+
const nodeTransition = transition
|
|
9472
|
+
? { duration: transition.duration, ease: transition.ease }
|
|
9473
|
+
: { duration: 0.3, ease: "easeOut" };
|
|
9474
|
+
node.setOptions({
|
|
9475
|
+
visualElement,
|
|
9476
|
+
layout: true,
|
|
9477
|
+
animate: true,
|
|
9478
|
+
transition: nodeTransition,
|
|
9479
|
+
...options,
|
|
9480
|
+
});
|
|
9481
|
+
node.mount(element);
|
|
9482
|
+
visualElement.projection = node;
|
|
9483
|
+
// Track this node as the active one for this element
|
|
9484
|
+
activeProjectionNodes.set(element, node);
|
|
9485
|
+
return { node, visualElement };
|
|
9486
|
+
}
|
|
9487
|
+
/**
|
|
9488
|
+
* Build a projection tree from a list of elements
|
|
9489
|
+
*/
|
|
9490
|
+
function buildProjectionTree(elements, existingContext, options) {
|
|
9491
|
+
ensureScaleCorrectors();
|
|
9492
|
+
const nodes = existingContext?.nodes ?? new Map();
|
|
9493
|
+
const visualElements = existingContext?.visualElements ?? new Map();
|
|
9494
|
+
const group = existingContext?.group ?? nodeGroup();
|
|
9495
|
+
const defaultTransition = options?.defaultTransition;
|
|
9496
|
+
const sharedTransitions = options?.sharedTransitions;
|
|
9497
|
+
// Sort elements by DOM depth (parents before children)
|
|
9498
|
+
const sorted = [...elements].sort((a, b) => getDepth(a) - getDepth(b));
|
|
9499
|
+
let root = existingContext?.root;
|
|
9500
|
+
for (const element of sorted) {
|
|
9501
|
+
// Skip if already has a node
|
|
9502
|
+
if (nodes.has(element))
|
|
9503
|
+
continue;
|
|
9504
|
+
const parent = findProjectionParent(element, nodes);
|
|
9505
|
+
const layoutId = getLayoutId(element);
|
|
9506
|
+
const layoutMode = element.getAttribute("data-layout");
|
|
9507
|
+
const nodeOptions = {
|
|
9508
|
+
layoutId: layoutId ?? undefined,
|
|
9509
|
+
animationType: parseLayoutMode(layoutMode),
|
|
9510
|
+
};
|
|
9511
|
+
// Use layoutId-specific transition if available, otherwise use default
|
|
9512
|
+
const transition = layoutId && sharedTransitions?.get(layoutId)
|
|
9513
|
+
? sharedTransitions.get(layoutId)
|
|
9514
|
+
: defaultTransition;
|
|
9515
|
+
const { node, visualElement } = createProjectionNode(element, parent, nodeOptions, transition);
|
|
9516
|
+
nodes.set(element, node);
|
|
9517
|
+
visualElements.set(element, visualElement);
|
|
9518
|
+
group.add(node);
|
|
9519
|
+
if (!root) {
|
|
9520
|
+
root = node.root;
|
|
9521
|
+
}
|
|
9522
|
+
}
|
|
9315
9523
|
return {
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
source: projection.id,
|
|
9524
|
+
nodes,
|
|
9525
|
+
visualElements,
|
|
9526
|
+
group,
|
|
9527
|
+
root: root,
|
|
9321
9528
|
};
|
|
9322
9529
|
}
|
|
9530
|
+
/**
|
|
9531
|
+
* Parse the data-layout attribute value
|
|
9532
|
+
*/
|
|
9533
|
+
function parseLayoutMode(value) {
|
|
9534
|
+
if (value === "position")
|
|
9535
|
+
return "position";
|
|
9536
|
+
if (value === "size")
|
|
9537
|
+
return "size";
|
|
9538
|
+
if (value === "preserve-aspect")
|
|
9539
|
+
return "preserve-aspect";
|
|
9540
|
+
return "both";
|
|
9541
|
+
}
|
|
9542
|
+
/**
|
|
9543
|
+
* Clean up projection nodes for specific elements.
|
|
9544
|
+
* If elementsToCleanup is provided, only those elements are cleaned up.
|
|
9545
|
+
* If not provided, all nodes are cleaned up.
|
|
9546
|
+
*
|
|
9547
|
+
* This allows persisting elements to keep their nodes between animations,
|
|
9548
|
+
* matching React's behavior where nodes persist for elements that remain in the DOM.
|
|
9549
|
+
*/
|
|
9550
|
+
function cleanupProjectionTree(context, elementsToCleanup) {
|
|
9551
|
+
const elementsToProcess = elementsToCleanup
|
|
9552
|
+
? [...context.nodes.entries()].filter(([el]) => elementsToCleanup.has(el))
|
|
9553
|
+
: [...context.nodes.entries()];
|
|
9554
|
+
for (const [element, node] of elementsToProcess) {
|
|
9555
|
+
context.group.remove(node);
|
|
9556
|
+
node.unmount();
|
|
9557
|
+
// Only clear from activeProjectionNodes if this is still the active node.
|
|
9558
|
+
// A newer animation might have already taken over.
|
|
9559
|
+
if (activeProjectionNodes.get(element) === node) {
|
|
9560
|
+
activeProjectionNodes.delete(element);
|
|
9561
|
+
}
|
|
9562
|
+
context.nodes.delete(element);
|
|
9563
|
+
context.visualElements.delete(element);
|
|
9564
|
+
}
|
|
9565
|
+
}
|
|
9566
|
+
|
|
9323
9567
|
class LayoutAnimationBuilder {
|
|
9324
9568
|
constructor(scope, updateDom, defaultOptions) {
|
|
9325
9569
|
this.sharedTransitions = new Map();
|
|
9326
9570
|
this.notifyReady = noop;
|
|
9327
|
-
this.
|
|
9571
|
+
this.executed = false;
|
|
9328
9572
|
this.scope = scope;
|
|
9329
9573
|
this.updateDom = updateDom;
|
|
9330
9574
|
this.defaultOptions = defaultOptions;
|
|
9331
|
-
this.readyPromise = new Promise((resolve
|
|
9575
|
+
this.readyPromise = new Promise((resolve) => {
|
|
9332
9576
|
this.notifyReady = resolve;
|
|
9333
|
-
this.rejectReady = reject;
|
|
9334
|
-
});
|
|
9335
|
-
frame.postRender(() => {
|
|
9336
|
-
this.start().then(this.notifyReady).catch(this.rejectReady);
|
|
9337
9577
|
});
|
|
9578
|
+
// Queue execution on microtask to allow builder methods to be called
|
|
9579
|
+
queueMicrotask(() => this.execute());
|
|
9338
9580
|
}
|
|
9339
|
-
shared(id,
|
|
9340
|
-
this.sharedTransitions.set(id,
|
|
9581
|
+
shared(id, options) {
|
|
9582
|
+
this.sharedTransitions.set(id, options);
|
|
9341
9583
|
return this;
|
|
9342
9584
|
}
|
|
9343
|
-
then(
|
|
9344
|
-
return this.readyPromise.then(
|
|
9585
|
+
then(onfulfilled, onrejected) {
|
|
9586
|
+
return this.readyPromise.then(onfulfilled, onrejected);
|
|
9345
9587
|
}
|
|
9346
|
-
async
|
|
9347
|
-
|
|
9348
|
-
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
|
|
9355
|
-
|
|
9356
|
-
|
|
9357
|
-
|
|
9358
|
-
|
|
9359
|
-
|
|
9360
|
-
|
|
9361
|
-
|
|
9362
|
-
|
|
9363
|
-
|
|
9364
|
-
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
if (!instance || !resumeFromInstance)
|
|
9378
|
-
return;
|
|
9379
|
-
if (!("style" in instance))
|
|
9380
|
-
return;
|
|
9381
|
-
const currentTransform = instance.style.transform;
|
|
9382
|
-
const resumeFromTransform = resumeFromInstance.style.transform;
|
|
9383
|
-
if (currentTransform &&
|
|
9384
|
-
resumeFromTransform &&
|
|
9385
|
-
currentTransform === resumeFromTransform) {
|
|
9386
|
-
instance.style.transform = "";
|
|
9387
|
-
instance.style.transformOrigin = "";
|
|
9388
|
-
}
|
|
9389
|
-
});
|
|
9390
|
-
afterRecords.forEach(({ projection }) => {
|
|
9391
|
-
projection.isPresent = true;
|
|
9392
|
-
});
|
|
9393
|
-
const root = getProjectionRoot(afterRecords, beforeRecords);
|
|
9394
|
-
root?.didUpdate();
|
|
9395
|
-
await new Promise((resolve) => {
|
|
9396
|
-
frame.postRender(() => resolve());
|
|
9397
|
-
});
|
|
9398
|
-
const animations = collectAnimations(afterRecords, exitRecords);
|
|
9399
|
-
const animation = new GroupAnimation(animations);
|
|
9400
|
-
if (exitRecords.length) {
|
|
9401
|
-
const cleanup = () => {
|
|
9402
|
-
exitRecords.forEach(({ element, visualElement }) => {
|
|
9403
|
-
if (element.isConnected) {
|
|
9404
|
-
element.remove();
|
|
9405
|
-
}
|
|
9406
|
-
visualElement.unmount();
|
|
9407
|
-
visualElementStore.delete(element);
|
|
9408
|
-
});
|
|
9409
|
-
};
|
|
9410
|
-
animation.finished.then(cleanup, cleanup);
|
|
9588
|
+
async execute() {
|
|
9589
|
+
if (this.executed)
|
|
9590
|
+
return;
|
|
9591
|
+
this.executed = true;
|
|
9592
|
+
let context;
|
|
9593
|
+
// Phase 1: Pre-mutation - Build projection tree and take snapshots
|
|
9594
|
+
const beforeElements = getLayoutElements(this.scope);
|
|
9595
|
+
if (beforeElements.length > 0) {
|
|
9596
|
+
context = buildProjectionTree(beforeElements, undefined, this.getBuildOptions());
|
|
9597
|
+
context.root.startUpdate();
|
|
9598
|
+
for (const node of context.nodes.values()) {
|
|
9599
|
+
node.isLayoutDirty = false;
|
|
9600
|
+
node.willUpdate();
|
|
9601
|
+
}
|
|
9602
|
+
}
|
|
9603
|
+
// Phase 2: Execute DOM update
|
|
9604
|
+
this.updateDom();
|
|
9605
|
+
// Phase 3: Post-mutation - Compare before/after elements
|
|
9606
|
+
const afterElements = getLayoutElements(this.scope);
|
|
9607
|
+
const beforeSet = new Set(beforeElements);
|
|
9608
|
+
const afterSet = new Set(afterElements);
|
|
9609
|
+
const entering = afterElements.filter((el) => !beforeSet.has(el));
|
|
9610
|
+
const exiting = beforeElements.filter((el) => !afterSet.has(el));
|
|
9611
|
+
// Build projection nodes for entering elements
|
|
9612
|
+
if (entering.length > 0) {
|
|
9613
|
+
context = buildProjectionTree(entering, context, this.getBuildOptions());
|
|
9614
|
+
}
|
|
9615
|
+
// No layout elements - return empty animation
|
|
9616
|
+
if (!context) {
|
|
9617
|
+
this.notifyReady(new GroupAnimation([]));
|
|
9618
|
+
return;
|
|
9411
9619
|
}
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
const recordMap = new Map();
|
|
9417
|
-
for (const element of elements) {
|
|
9418
|
-
const parentRecord = findParentRecord(element, recordMap, this.scope);
|
|
9419
|
-
const { layout, layoutId } = readLayoutAttributes(element);
|
|
9420
|
-
const override = layoutId
|
|
9421
|
-
? this.sharedTransitions.get(layoutId)
|
|
9422
|
-
: undefined;
|
|
9423
|
-
const transition = override || this.defaultOptions;
|
|
9424
|
-
const record = getOrCreateRecord(element, parentRecord?.projection, {
|
|
9425
|
-
layout,
|
|
9426
|
-
layoutId,
|
|
9427
|
-
animationType: typeof layout === "string" ? layout : "both",
|
|
9428
|
-
transition: transition,
|
|
9429
|
-
});
|
|
9430
|
-
recordMap.set(element, record);
|
|
9431
|
-
records.push(record);
|
|
9620
|
+
// Handle shared elements
|
|
9621
|
+
for (const element of exiting) {
|
|
9622
|
+
const node = context.nodes.get(element);
|
|
9623
|
+
node?.getStack()?.remove(node);
|
|
9432
9624
|
}
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
|
|
9438
|
-
|
|
9439
|
-
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9625
|
+
for (const element of entering) {
|
|
9626
|
+
context.nodes.get(element)?.promote();
|
|
9627
|
+
}
|
|
9628
|
+
// Phase 4: Animate
|
|
9629
|
+
context.root.didUpdate();
|
|
9630
|
+
await new Promise((resolve) => frame.postRender(() => resolve()));
|
|
9631
|
+
const animations = [];
|
|
9632
|
+
for (const node of context.nodes.values()) {
|
|
9633
|
+
if (node.currentAnimation) {
|
|
9634
|
+
animations.push(node.currentAnimation);
|
|
9635
|
+
}
|
|
9636
|
+
}
|
|
9637
|
+
const groupAnimation = new GroupAnimation(animations);
|
|
9638
|
+
groupAnimation.finished.then(() => {
|
|
9639
|
+
// Only clean up nodes for elements no longer in the document.
|
|
9640
|
+
// Elements still in DOM keep their nodes so subsequent animations
|
|
9641
|
+
// can use the stored position snapshots (A→B→A pattern).
|
|
9642
|
+
const elementsToCleanup = new Set();
|
|
9643
|
+
for (const element of context.nodes.keys()) {
|
|
9644
|
+
if (!document.contains(element)) {
|
|
9645
|
+
elementsToCleanup.add(element);
|
|
9646
|
+
}
|
|
9453
9647
|
}
|
|
9454
|
-
|
|
9648
|
+
cleanupProjectionTree(context, elementsToCleanup);
|
|
9455
9649
|
});
|
|
9456
|
-
|
|
9650
|
+
this.notifyReady(groupAnimation);
|
|
9651
|
+
}
|
|
9652
|
+
getBuildOptions() {
|
|
9653
|
+
return {
|
|
9654
|
+
defaultTransition: this.defaultOptions || {
|
|
9655
|
+
duration: 0.3,
|
|
9656
|
+
ease: "easeOut",
|
|
9657
|
+
},
|
|
9658
|
+
sharedTransitions: this.sharedTransitions.size > 0
|
|
9659
|
+
? this.sharedTransitions
|
|
9660
|
+
: undefined,
|
|
9661
|
+
};
|
|
9457
9662
|
}
|
|
9458
9663
|
}
|
|
9664
|
+
/**
|
|
9665
|
+
* Parse arguments for animateLayout overloads
|
|
9666
|
+
*/
|
|
9459
9667
|
function parseAnimateLayoutArgs(scopeOrUpdateDom, updateDomOrOptions, options) {
|
|
9460
9668
|
// animateLayout(updateDom)
|
|
9461
9669
|
if (typeof scopeOrUpdateDom === "function") {
|
|
@@ -9469,125 +9677,11 @@
|
|
|
9469
9677
|
const elements = resolveElements(scopeOrUpdateDom);
|
|
9470
9678
|
const scope = elements[0] || document;
|
|
9471
9679
|
return {
|
|
9472
|
-
scope,
|
|
9680
|
+
scope: scope instanceof Document ? scope : scope,
|
|
9473
9681
|
updateDom: updateDomOrOptions,
|
|
9474
9682
|
defaultOptions: options,
|
|
9475
9683
|
};
|
|
9476
9684
|
}
|
|
9477
|
-
function collectLayoutElements(scope) {
|
|
9478
|
-
const elements = Array.from(scope.querySelectorAll(layoutSelector));
|
|
9479
|
-
if (scope instanceof Element && scope.matches(layoutSelector)) {
|
|
9480
|
-
if (!elements.includes(scope)) {
|
|
9481
|
-
elements.unshift(scope);
|
|
9482
|
-
}
|
|
9483
|
-
}
|
|
9484
|
-
return elements;
|
|
9485
|
-
}
|
|
9486
|
-
function readLayoutAttributes(element) {
|
|
9487
|
-
const layoutId = element.getAttribute("data-layout-id") || undefined;
|
|
9488
|
-
const rawLayout = element.getAttribute("data-layout");
|
|
9489
|
-
let layout;
|
|
9490
|
-
if (rawLayout === "" || rawLayout === "true") {
|
|
9491
|
-
layout = true;
|
|
9492
|
-
}
|
|
9493
|
-
else if (rawLayout) {
|
|
9494
|
-
layout = rawLayout;
|
|
9495
|
-
}
|
|
9496
|
-
return {
|
|
9497
|
-
layout,
|
|
9498
|
-
layoutId,
|
|
9499
|
-
layoutExit: element.hasAttribute("data-layout-exit"),
|
|
9500
|
-
};
|
|
9501
|
-
}
|
|
9502
|
-
function createVisualState() {
|
|
9503
|
-
return {
|
|
9504
|
-
latestValues: {},
|
|
9505
|
-
renderState: {
|
|
9506
|
-
transform: {},
|
|
9507
|
-
transformOrigin: {},
|
|
9508
|
-
style: {},
|
|
9509
|
-
vars: {},
|
|
9510
|
-
},
|
|
9511
|
-
};
|
|
9512
|
-
}
|
|
9513
|
-
function getOrCreateRecord(element, parentProjection, projectionOptions) {
|
|
9514
|
-
const existing = visualElementStore.get(element);
|
|
9515
|
-
const visualElement = existing ??
|
|
9516
|
-
new HTMLVisualElement({
|
|
9517
|
-
props: {},
|
|
9518
|
-
presenceContext: null,
|
|
9519
|
-
visualState: createVisualState(),
|
|
9520
|
-
}, { allowProjection: true });
|
|
9521
|
-
if (!existing || !visualElement.projection) {
|
|
9522
|
-
visualElement.projection = new HTMLProjectionNode(visualElement.latestValues, parentProjection);
|
|
9523
|
-
}
|
|
9524
|
-
visualElement.projection.setOptions({
|
|
9525
|
-
...projectionOptions,
|
|
9526
|
-
visualElement,
|
|
9527
|
-
});
|
|
9528
|
-
if (!visualElement.current) {
|
|
9529
|
-
visualElement.mount(element);
|
|
9530
|
-
}
|
|
9531
|
-
if (!existing) {
|
|
9532
|
-
visualElementStore.set(element, visualElement);
|
|
9533
|
-
}
|
|
9534
|
-
return {
|
|
9535
|
-
element,
|
|
9536
|
-
visualElement,
|
|
9537
|
-
projection: visualElement.projection,
|
|
9538
|
-
};
|
|
9539
|
-
}
|
|
9540
|
-
function findParentRecord(element, recordMap, scope) {
|
|
9541
|
-
let parent = element.parentElement;
|
|
9542
|
-
while (parent) {
|
|
9543
|
-
const record = recordMap.get(parent);
|
|
9544
|
-
if (record)
|
|
9545
|
-
return record;
|
|
9546
|
-
if (parent === scope)
|
|
9547
|
-
break;
|
|
9548
|
-
parent = parent.parentElement;
|
|
9549
|
-
}
|
|
9550
|
-
return undefined;
|
|
9551
|
-
}
|
|
9552
|
-
function collectExitCandidates(records) {
|
|
9553
|
-
const exitCandidates = new Map();
|
|
9554
|
-
records.forEach((record) => {
|
|
9555
|
-
const { layoutExit } = readLayoutAttributes(record.element);
|
|
9556
|
-
if (!layoutExit)
|
|
9557
|
-
return;
|
|
9558
|
-
exitCandidates.set(record.element, {
|
|
9559
|
-
...record,
|
|
9560
|
-
parent: record.element.parentNode,
|
|
9561
|
-
nextSibling: record.element.nextSibling,
|
|
9562
|
-
});
|
|
9563
|
-
});
|
|
9564
|
-
return exitCandidates;
|
|
9565
|
-
}
|
|
9566
|
-
function reinstateExitElement(record) {
|
|
9567
|
-
if (!record.parent)
|
|
9568
|
-
return;
|
|
9569
|
-
if (record.nextSibling && record.nextSibling.parentNode === record.parent) {
|
|
9570
|
-
record.parent.insertBefore(record.element, record.nextSibling);
|
|
9571
|
-
}
|
|
9572
|
-
else {
|
|
9573
|
-
record.parent.appendChild(record.element);
|
|
9574
|
-
}
|
|
9575
|
-
}
|
|
9576
|
-
function getProjectionRoot(afterRecords, beforeRecords) {
|
|
9577
|
-
const record = afterRecords[0] || beforeRecords[0];
|
|
9578
|
-
return record?.projection.root;
|
|
9579
|
-
}
|
|
9580
|
-
function collectAnimations(afterRecords, exitRecords) {
|
|
9581
|
-
const animations = new Set();
|
|
9582
|
-
const addAnimation = (record) => {
|
|
9583
|
-
const animation = record.projection.currentAnimation;
|
|
9584
|
-
if (animation)
|
|
9585
|
-
animations.add(animation);
|
|
9586
|
-
};
|
|
9587
|
-
afterRecords.forEach(addAnimation);
|
|
9588
|
-
exitRecords.forEach(addAnimation);
|
|
9589
|
-
return Array.from(animations);
|
|
9590
|
-
}
|
|
9591
9685
|
|
|
9592
9686
|
/**
|
|
9593
9687
|
* @deprecated
|
|
@@ -9748,7 +9842,7 @@
|
|
|
9748
9842
|
let maxDuration = 0;
|
|
9749
9843
|
const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numSubjects = 0) => {
|
|
9750
9844
|
const valueKeyframesAsList = keyframesAsList(valueKeyframes);
|
|
9751
|
-
const { delay = 0, times = defaultOffset$1(valueKeyframesAsList), type = "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition;
|
|
9845
|
+
const { delay = 0, times = defaultOffset$1(valueKeyframesAsList), type = defaultTransition.type || "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition;
|
|
9752
9846
|
let { ease = defaultTransition.ease || "easeOut", duration } = valueTransition;
|
|
9753
9847
|
/**
|
|
9754
9848
|
* Resolve stagger() if defined.
|
|
@@ -9776,7 +9870,10 @@
|
|
|
9776
9870
|
const delta = valueKeyframesAsList[1] - valueKeyframesAsList[0];
|
|
9777
9871
|
absoluteDelta = Math.abs(delta);
|
|
9778
9872
|
}
|
|
9779
|
-
const springTransition = {
|
|
9873
|
+
const springTransition = {
|
|
9874
|
+
...defaultTransition,
|
|
9875
|
+
...remainingTransition,
|
|
9876
|
+
};
|
|
9780
9877
|
if (duration !== undefined) {
|
|
9781
9878
|
springTransition.duration = secondsToMilliseconds(duration);
|
|
9782
9879
|
}
|
|
@@ -9909,8 +10006,15 @@
|
|
|
9909
10006
|
}
|
|
9910
10007
|
const definition = animationDefinitions.get(element);
|
|
9911
10008
|
definition.keyframes[key] = keyframes;
|
|
10009
|
+
/**
|
|
10010
|
+
* Exclude `type` from defaultTransition since springs have been
|
|
10011
|
+
* converted to duration-based easing functions in resolveValueSequence.
|
|
10012
|
+
* Including `type: "spring"` would cause JSAnimation to error when
|
|
10013
|
+
* the merged keyframes array has more than 2 keyframes.
|
|
10014
|
+
*/
|
|
10015
|
+
const { type: _type, ...remainingDefaultTransition } = defaultTransition;
|
|
9912
10016
|
definition.transition[key] = {
|
|
9913
|
-
...
|
|
10017
|
+
...remainingDefaultTransition,
|
|
9914
10018
|
duration: totalDuration,
|
|
9915
10019
|
ease: valueEasing,
|
|
9916
10020
|
times: valueOffset,
|
|
@@ -10481,7 +10585,7 @@
|
|
|
10481
10585
|
const getEventTarget = (element) => element === document.scrollingElement ? window : element;
|
|
10482
10586
|
function scrollInfo(onScroll, { container = document.scrollingElement, ...options } = {}) {
|
|
10483
10587
|
if (!container)
|
|
10484
|
-
return noop
|
|
10588
|
+
return noop;
|
|
10485
10589
|
let containerHandlers = onScrollHandlers.get(container);
|
|
10486
10590
|
/**
|
|
10487
10591
|
* Get the onScroll handlers for this container.
|
|
@@ -10609,7 +10713,7 @@
|
|
|
10609
10713
|
|
|
10610
10714
|
function scroll(onScroll, { axis = "y", container = document.scrollingElement, ...options } = {}) {
|
|
10611
10715
|
if (!container)
|
|
10612
|
-
return noop
|
|
10716
|
+
return noop;
|
|
10613
10717
|
const optionsWithDefaults = { axis, container, ...options };
|
|
10614
10718
|
return typeof onScroll === "function"
|
|
10615
10719
|
? attachToFunction(onScroll, optionsWithDefaults)
|
|
@@ -10716,6 +10820,7 @@
|
|
|
10716
10820
|
exports.applyPxDefaults = applyPxDefaults;
|
|
10717
10821
|
exports.applyTreeDeltas = applyTreeDeltas;
|
|
10718
10822
|
exports.aspectRatio = aspectRatio;
|
|
10823
|
+
exports.attachFollow = attachFollow;
|
|
10719
10824
|
exports.attachSpring = attachSpring;
|
|
10720
10825
|
exports.attrEffect = attrEffect;
|
|
10721
10826
|
exports.axisDeltaEquals = axisDeltaEquals;
|
|
@@ -10770,7 +10875,7 @@
|
|
|
10770
10875
|
exports.createBox = createBox;
|
|
10771
10876
|
exports.createDelta = createDelta;
|
|
10772
10877
|
exports.createGeneratorEasing = createGeneratorEasing;
|
|
10773
|
-
exports.createProjectionNode = createProjectionNode;
|
|
10878
|
+
exports.createProjectionNode = createProjectionNode$1;
|
|
10774
10879
|
exports.createRenderBatcher = createRenderBatcher;
|
|
10775
10880
|
exports.createScopedAnimate = createScopedAnimate;
|
|
10776
10881
|
exports.cubicBezier = cubicBezier;
|
|
@@ -10795,6 +10900,7 @@
|
|
|
10795
10900
|
exports.findDimensionValueType = findDimensionValueType;
|
|
10796
10901
|
exports.findValueType = findValueType;
|
|
10797
10902
|
exports.flushKeyframeResolvers = flushKeyframeResolvers;
|
|
10903
|
+
exports.followValue = followValue;
|
|
10798
10904
|
exports.frame = frame;
|
|
10799
10905
|
exports.frameData = frameData;
|
|
10800
10906
|
exports.frameSteps = frameSteps;
|
|
@@ -10884,7 +10990,7 @@
|
|
|
10884
10990
|
exports.motionValue = motionValue;
|
|
10885
10991
|
exports.moveItem = moveItem;
|
|
10886
10992
|
exports.nodeGroup = nodeGroup;
|
|
10887
|
-
exports.noop = noop
|
|
10993
|
+
exports.noop = noop;
|
|
10888
10994
|
exports.number = number;
|
|
10889
10995
|
exports.numberValueTypes = numberValueTypes;
|
|
10890
10996
|
exports.observeTimeline = observeTimeline;
|