motion 12.27.4 → 12.28.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/motion.dev.js +280 -319
- 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 = (any) => any;
|
|
84
|
+
const noop$1 = (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$1;
|
|
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$1,
|
|
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, true);
|
|
482
|
+
const { schedule: frame, cancel: cancelFrame, state: frameData, steps: frameSteps, } = /* @__PURE__ */ createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop$1, 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 : ease;
|
|
1432
|
+
const easingFunction = Array.isArray(ease) ? ease[i] || noop$1 : 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$1;
|
|
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$1);
|
|
2787
2787
|
if (this.pendingTimeline) {
|
|
2788
2788
|
this.stopTimeline = animation.attachTimeline(this.pendingTimeline);
|
|
2789
2789
|
this.pendingTimeline = undefined;
|
|
@@ -4425,21 +4425,17 @@
|
|
|
4425
4425
|
const styleEffect = /*@__PURE__*/ createSelectorEffect(
|
|
4426
4426
|
/*@__PURE__*/ createEffect(addStyleValue));
|
|
4427
4427
|
|
|
4428
|
+
const toPx = px.transform;
|
|
4428
4429
|
function addSVGPathValue(element, state, key, value) {
|
|
4429
4430
|
frame.render(() => element.setAttribute("pathLength", "1"));
|
|
4430
4431
|
if (key === "pathOffset") {
|
|
4431
|
-
return state.set(key, value, () =>
|
|
4432
|
-
// Use unitless value to avoid Safari zoom bug
|
|
4433
|
-
const offset = state.latest[key];
|
|
4434
|
-
element.setAttribute("stroke-dashoffset", `${-offset}`);
|
|
4435
|
-
});
|
|
4432
|
+
return state.set(key, value, () => element.setAttribute("stroke-dashoffset", toPx(-state.latest[key])));
|
|
4436
4433
|
}
|
|
4437
4434
|
else {
|
|
4438
4435
|
if (!state.get("stroke-dasharray")) {
|
|
4439
4436
|
state.set("stroke-dasharray", new MotionValue("1 1"), () => {
|
|
4440
4437
|
const { pathLength = 1, pathSpacing } = state.latest;
|
|
4441
|
-
|
|
4442
|
-
element.setAttribute("stroke-dasharray", `${pathLength} ${pathSpacing ?? 1 - Number(pathLength)}`);
|
|
4438
|
+
element.setAttribute("stroke-dasharray", `${toPx(pathLength)} ${toPx(pathSpacing ?? 1 - Number(pathLength))}`);
|
|
4443
4439
|
});
|
|
4444
4440
|
}
|
|
4445
4441
|
return state.set(key, value, undefined, state.get("stroke-dasharray"));
|
|
@@ -5406,7 +5402,7 @@
|
|
|
5406
5402
|
constructor(update, options = {}) {
|
|
5407
5403
|
this.currentSubject = "root";
|
|
5408
5404
|
this.targets = new Map();
|
|
5409
|
-
this.notifyReady = noop;
|
|
5405
|
+
this.notifyReady = noop$1;
|
|
5410
5406
|
this.readyPromise = new Promise((resolve) => {
|
|
5411
5407
|
this.notifyReady = resolve;
|
|
5412
5408
|
});
|
|
@@ -6642,9 +6638,6 @@
|
|
|
6642
6638
|
* and stroke-dasharray attributes.
|
|
6643
6639
|
*
|
|
6644
6640
|
* This function is mutative to reduce per-frame GC.
|
|
6645
|
-
*
|
|
6646
|
-
* Note: We use unitless values for stroke-dasharray and stroke-dashoffset
|
|
6647
|
-
* because Safari incorrectly scales px values when the page is zoomed.
|
|
6648
6641
|
*/
|
|
6649
6642
|
function buildSVGPath(attrs, length, spacing = 1, offset = 0, useDashCase = true) {
|
|
6650
6643
|
// Normalise path length by setting SVG attribute pathLength to 1
|
|
@@ -6652,10 +6645,12 @@
|
|
|
6652
6645
|
// We use dash case when setting attributes directly to the DOM node and camel case
|
|
6653
6646
|
// when defining props on a React component.
|
|
6654
6647
|
const keys = useDashCase ? dashKeys : camelKeys;
|
|
6655
|
-
// Build the dash offset
|
|
6656
|
-
attrs[keys.offset] =
|
|
6657
|
-
// Build the dash array
|
|
6658
|
-
|
|
6648
|
+
// Build the dash offset
|
|
6649
|
+
attrs[keys.offset] = px.transform(-offset);
|
|
6650
|
+
// Build the dash array
|
|
6651
|
+
const pathLength = px.transform(length);
|
|
6652
|
+
const pathSpacing = px.transform(spacing);
|
|
6653
|
+
attrs[keys.array] = `${pathLength} ${pathSpacing}`;
|
|
6659
6654
|
}
|
|
6660
6655
|
|
|
6661
6656
|
/**
|
|
@@ -7462,7 +7457,7 @@
|
|
|
7462
7457
|
: values.borderRadius;
|
|
7463
7458
|
}
|
|
7464
7459
|
const easeCrossfadeIn = /*@__PURE__*/ compress(0, 0.5, circOut);
|
|
7465
|
-
const easeCrossfadeOut = /*@__PURE__*/ compress(0.5, 0.95, noop);
|
|
7460
|
+
const easeCrossfadeOut = /*@__PURE__*/ compress(0.5, 0.95, noop$1);
|
|
7466
7461
|
function compress(min, max, easing) {
|
|
7467
7462
|
return (p) => {
|
|
7468
7463
|
// Could replace ifs with clamp
|
|
@@ -7580,6 +7575,13 @@
|
|
|
7580
7575
|
const prevLead = this.lead;
|
|
7581
7576
|
if (node === prevLead)
|
|
7582
7577
|
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
|
+
});
|
|
7583
7585
|
this.prevLead = prevLead;
|
|
7584
7586
|
this.lead = node;
|
|
7585
7587
|
node.show();
|
|
@@ -7688,7 +7690,7 @@
|
|
|
7688
7690
|
cancelTreeOptimisedTransformAnimations(parent);
|
|
7689
7691
|
}
|
|
7690
7692
|
}
|
|
7691
|
-
function createProjectionNode
|
|
7693
|
+
function createProjectionNode({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) {
|
|
7692
7694
|
return class ProjectionNode {
|
|
7693
7695
|
constructor(latestValues = {}, parent = defaultParent?.()) {
|
|
7694
7696
|
/**
|
|
@@ -9226,7 +9228,7 @@
|
|
|
9226
9228
|
*/
|
|
9227
9229
|
const roundPoint = userAgentContains("applewebkit/") && !userAgentContains("chrome/")
|
|
9228
9230
|
? Math.round
|
|
9229
|
-
: noop;
|
|
9231
|
+
: noop$1;
|
|
9230
9232
|
function roundAxis(axis) {
|
|
9231
9233
|
// Round to the nearest .5 pixels to support subpixel layouts
|
|
9232
9234
|
axis.min = roundPoint(axis.min);
|
|
@@ -9245,7 +9247,7 @@
|
|
|
9245
9247
|
return node !== node.root && node.scroll?.wasRoot;
|
|
9246
9248
|
}
|
|
9247
9249
|
|
|
9248
|
-
const DocumentProjectionNode = createProjectionNode
|
|
9250
|
+
const DocumentProjectionNode = createProjectionNode({
|
|
9249
9251
|
attachResizeListener: (ref, notify) => addDomEvent(ref, "resize", notify),
|
|
9250
9252
|
measureScroll: () => ({
|
|
9251
9253
|
x: document.documentElement.scrollLeft || document.body.scrollLeft,
|
|
@@ -9280,7 +9282,7 @@
|
|
|
9280
9282
|
const rootProjectionNode = {
|
|
9281
9283
|
current: undefined,
|
|
9282
9284
|
};
|
|
9283
|
-
const HTMLProjectionNode = createProjectionNode
|
|
9285
|
+
const HTMLProjectionNode = createProjectionNode({
|
|
9284
9286
|
measureScroll: (instance) => ({
|
|
9285
9287
|
x: instance.scrollLeft,
|
|
9286
9288
|
y: instance.scrollTop,
|
|
@@ -9300,315 +9302,160 @@
|
|
|
9300
9302
|
checkIsScrollRoot: (instance) => Boolean(window.getComputedStyle(instance).position === "fixed"),
|
|
9301
9303
|
});
|
|
9302
9304
|
|
|
9303
|
-
const
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
if (
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
return element.getAttribute("data-layout-id");
|
|
9314
|
-
}
|
|
9315
|
-
function hasLayout(element) {
|
|
9316
|
-
return (element.hasAttribute("data-layout") ||
|
|
9317
|
-
element.hasAttribute("data-layout-id"));
|
|
9318
|
-
}
|
|
9319
|
-
|
|
9320
|
-
let scaleCorrectorAdded = false;
|
|
9321
|
-
/**
|
|
9322
|
-
* Track active projection nodes per element to handle animation interruption.
|
|
9323
|
-
* When a new animation starts on an element that already has an active animation,
|
|
9324
|
-
* we need to stop the old animation so the new one can start from the current
|
|
9325
|
-
* visual position.
|
|
9326
|
-
*/
|
|
9327
|
-
const activeProjectionNodes = new WeakMap();
|
|
9328
|
-
function ensureScaleCorrectors() {
|
|
9329
|
-
if (scaleCorrectorAdded)
|
|
9330
|
-
return;
|
|
9331
|
-
scaleCorrectorAdded = true;
|
|
9332
|
-
addScaleCorrector({
|
|
9333
|
-
borderRadius: {
|
|
9334
|
-
...correctBorderRadius,
|
|
9335
|
-
applyTo: [
|
|
9336
|
-
"borderTopLeftRadius",
|
|
9337
|
-
"borderTopRightRadius",
|
|
9338
|
-
"borderBottomLeftRadius",
|
|
9339
|
-
"borderBottomRightRadius",
|
|
9340
|
-
],
|
|
9341
|
-
},
|
|
9342
|
-
borderTopLeftRadius: correctBorderRadius,
|
|
9343
|
-
borderTopRightRadius: correctBorderRadius,
|
|
9344
|
-
borderBottomLeftRadius: correctBorderRadius,
|
|
9345
|
-
borderBottomRightRadius: correctBorderRadius,
|
|
9346
|
-
boxShadow: correctBoxShadow,
|
|
9347
|
-
});
|
|
9348
|
-
}
|
|
9349
|
-
/**
|
|
9350
|
-
* Get DOM depth of an element
|
|
9351
|
-
*/
|
|
9352
|
-
function getDepth(element) {
|
|
9353
|
-
let depth = 0;
|
|
9354
|
-
let current = element.parentElement;
|
|
9355
|
-
while (current) {
|
|
9356
|
-
depth++;
|
|
9357
|
-
current = current.parentElement;
|
|
9358
|
-
}
|
|
9359
|
-
return depth;
|
|
9360
|
-
}
|
|
9361
|
-
/**
|
|
9362
|
-
* Find the closest projection parent for an element
|
|
9363
|
-
*/
|
|
9364
|
-
function findProjectionParent(element, nodeCache) {
|
|
9365
|
-
let parent = element.parentElement;
|
|
9366
|
-
while (parent) {
|
|
9367
|
-
const node = nodeCache.get(parent);
|
|
9368
|
-
if (node)
|
|
9369
|
-
return node;
|
|
9370
|
-
parent = parent.parentElement;
|
|
9371
|
-
}
|
|
9372
|
-
return undefined;
|
|
9373
|
-
}
|
|
9374
|
-
/**
|
|
9375
|
-
* Create or reuse a projection node for an element
|
|
9376
|
-
*/
|
|
9377
|
-
function createProjectionNode(element, parent, options, transition) {
|
|
9378
|
-
// Check for existing active node - reuse it to preserve animation state
|
|
9379
|
-
const existingNode = activeProjectionNodes.get(element);
|
|
9380
|
-
if (existingNode) {
|
|
9381
|
-
const visualElement = existingNode.options.visualElement;
|
|
9382
|
-
// Update transition options for the new animation
|
|
9383
|
-
const nodeTransition = transition
|
|
9384
|
-
? { duration: transition.duration, ease: transition.ease }
|
|
9385
|
-
: { duration: 0.3, ease: "easeOut" };
|
|
9386
|
-
existingNode.setOptions({
|
|
9387
|
-
...existingNode.options,
|
|
9388
|
-
animate: true,
|
|
9389
|
-
transition: nodeTransition,
|
|
9390
|
-
...options,
|
|
9391
|
-
});
|
|
9392
|
-
// Re-mount the node if it was previously unmounted
|
|
9393
|
-
// This re-adds it to root.nodes so didUpdate() will process it
|
|
9394
|
-
if (!existingNode.instance) {
|
|
9395
|
-
existingNode.mount(element);
|
|
9396
|
-
}
|
|
9397
|
-
return { node: existingNode, visualElement };
|
|
9398
|
-
}
|
|
9399
|
-
// No existing node - create a new one
|
|
9400
|
-
const latestValues = {};
|
|
9401
|
-
const visualElement = new HTMLVisualElement({
|
|
9402
|
-
visualState: {
|
|
9403
|
-
latestValues,
|
|
9404
|
-
renderState: {
|
|
9405
|
-
transformOrigin: {},
|
|
9406
|
-
transform: {},
|
|
9407
|
-
style: {},
|
|
9408
|
-
vars: {},
|
|
9409
|
-
},
|
|
9410
|
-
},
|
|
9411
|
-
presenceContext: null,
|
|
9412
|
-
props: {},
|
|
9413
|
-
});
|
|
9414
|
-
const node = new HTMLProjectionNode(latestValues, parent);
|
|
9415
|
-
// Convert AnimationOptions to transition format for the projection system
|
|
9416
|
-
const nodeTransition = transition
|
|
9417
|
-
? { duration: transition.duration, ease: transition.ease }
|
|
9418
|
-
: { duration: 0.3, ease: "easeOut" };
|
|
9419
|
-
node.setOptions({
|
|
9420
|
-
visualElement,
|
|
9421
|
-
layout: true,
|
|
9422
|
-
animate: true,
|
|
9423
|
-
transition: nodeTransition,
|
|
9424
|
-
...options,
|
|
9425
|
-
});
|
|
9426
|
-
node.mount(element);
|
|
9427
|
-
visualElement.projection = node;
|
|
9428
|
-
// Track this node as the active one for this element
|
|
9429
|
-
activeProjectionNodes.set(element, node);
|
|
9430
|
-
return { node, visualElement };
|
|
9431
|
-
}
|
|
9432
|
-
/**
|
|
9433
|
-
* Build a projection tree from a list of elements
|
|
9434
|
-
*/
|
|
9435
|
-
function buildProjectionTree(elements, existingContext, options) {
|
|
9436
|
-
ensureScaleCorrectors();
|
|
9437
|
-
const nodes = existingContext?.nodes ?? new Map();
|
|
9438
|
-
const visualElements = existingContext?.visualElements ?? new Map();
|
|
9439
|
-
const group = existingContext?.group ?? nodeGroup();
|
|
9440
|
-
const defaultTransition = options?.defaultTransition;
|
|
9441
|
-
const sharedTransitions = options?.sharedTransitions;
|
|
9442
|
-
// Sort elements by DOM depth (parents before children)
|
|
9443
|
-
const sorted = [...elements].sort((a, b) => getDepth(a) - getDepth(b));
|
|
9444
|
-
let root = existingContext?.root;
|
|
9445
|
-
for (const element of sorted) {
|
|
9446
|
-
// Skip if already has a node
|
|
9447
|
-
if (nodes.has(element))
|
|
9448
|
-
continue;
|
|
9449
|
-
const parent = findProjectionParent(element, nodes);
|
|
9450
|
-
const layoutId = getLayoutId(element);
|
|
9451
|
-
const layoutMode = element.getAttribute("data-layout");
|
|
9452
|
-
const nodeOptions = {
|
|
9453
|
-
layoutId: layoutId ?? undefined,
|
|
9454
|
-
animationType: parseLayoutMode(layoutMode),
|
|
9455
|
-
};
|
|
9456
|
-
// Use layoutId-specific transition if available, otherwise use default
|
|
9457
|
-
const transition = layoutId && sharedTransitions?.get(layoutId)
|
|
9458
|
-
? sharedTransitions.get(layoutId)
|
|
9459
|
-
: defaultTransition;
|
|
9460
|
-
const { node, visualElement } = createProjectionNode(element, parent, nodeOptions, transition);
|
|
9461
|
-
nodes.set(element, node);
|
|
9462
|
-
visualElements.set(element, visualElement);
|
|
9463
|
-
group.add(node);
|
|
9464
|
-
if (!root) {
|
|
9465
|
-
root = node.root;
|
|
9466
|
-
}
|
|
9467
|
-
}
|
|
9305
|
+
const layoutSelector = "[data-layout], [data-layout-id]";
|
|
9306
|
+
const noop = () => { };
|
|
9307
|
+
function snapshotFromTarget(projection) {
|
|
9308
|
+
const target = projection.targetWithTransforms || projection.target;
|
|
9309
|
+
if (!target)
|
|
9310
|
+
return undefined;
|
|
9311
|
+
const measuredBox = createBox();
|
|
9312
|
+
const layoutBox = createBox();
|
|
9313
|
+
copyBoxInto(measuredBox, target);
|
|
9314
|
+
copyBoxInto(layoutBox, target);
|
|
9468
9315
|
return {
|
|
9469
|
-
|
|
9470
|
-
|
|
9471
|
-
|
|
9472
|
-
|
|
9316
|
+
animationId: projection.root?.animationId ?? 0,
|
|
9317
|
+
measuredBox,
|
|
9318
|
+
layoutBox,
|
|
9319
|
+
latestValues: projection.animationValues || projection.latestValues || {},
|
|
9320
|
+
source: projection.id,
|
|
9473
9321
|
};
|
|
9474
9322
|
}
|
|
9475
|
-
/**
|
|
9476
|
-
* Parse the data-layout attribute value
|
|
9477
|
-
*/
|
|
9478
|
-
function parseLayoutMode(value) {
|
|
9479
|
-
if (value === "position")
|
|
9480
|
-
return "position";
|
|
9481
|
-
if (value === "size")
|
|
9482
|
-
return "size";
|
|
9483
|
-
if (value === "preserve-aspect")
|
|
9484
|
-
return "preserve-aspect";
|
|
9485
|
-
return "both";
|
|
9486
|
-
}
|
|
9487
|
-
/**
|
|
9488
|
-
* Clean up projection nodes for specific elements.
|
|
9489
|
-
* If elementsToCleanup is provided, only those elements are cleaned up.
|
|
9490
|
-
* If not provided, all nodes are cleaned up.
|
|
9491
|
-
*
|
|
9492
|
-
* This allows persisting elements to keep their nodes between animations,
|
|
9493
|
-
* matching React's behavior where nodes persist for elements that remain in the DOM.
|
|
9494
|
-
*/
|
|
9495
|
-
function cleanupProjectionTree(context, elementsToCleanup) {
|
|
9496
|
-
const elementsToProcess = elementsToCleanup
|
|
9497
|
-
? [...context.nodes.entries()].filter(([el]) => elementsToCleanup.has(el))
|
|
9498
|
-
: [...context.nodes.entries()];
|
|
9499
|
-
for (const [element, node] of elementsToProcess) {
|
|
9500
|
-
context.group.remove(node);
|
|
9501
|
-
node.unmount();
|
|
9502
|
-
// Only clear from activeProjectionNodes if this is still the active node.
|
|
9503
|
-
// A newer animation might have already taken over.
|
|
9504
|
-
if (activeProjectionNodes.get(element) === node) {
|
|
9505
|
-
activeProjectionNodes.delete(element);
|
|
9506
|
-
}
|
|
9507
|
-
context.nodes.delete(element);
|
|
9508
|
-
context.visualElements.delete(element);
|
|
9509
|
-
}
|
|
9510
|
-
}
|
|
9511
|
-
|
|
9512
9323
|
class LayoutAnimationBuilder {
|
|
9513
9324
|
constructor(scope, updateDom, defaultOptions) {
|
|
9514
9325
|
this.sharedTransitions = new Map();
|
|
9515
9326
|
this.notifyReady = noop;
|
|
9516
|
-
this.
|
|
9327
|
+
this.rejectReady = noop;
|
|
9517
9328
|
this.scope = scope;
|
|
9518
9329
|
this.updateDom = updateDom;
|
|
9519
9330
|
this.defaultOptions = defaultOptions;
|
|
9520
|
-
this.readyPromise = new Promise((resolve) => {
|
|
9331
|
+
this.readyPromise = new Promise((resolve, reject) => {
|
|
9521
9332
|
this.notifyReady = resolve;
|
|
9333
|
+
this.rejectReady = reject;
|
|
9334
|
+
});
|
|
9335
|
+
frame.postRender(() => {
|
|
9336
|
+
this.start().then(this.notifyReady).catch(this.rejectReady);
|
|
9522
9337
|
});
|
|
9523
|
-
// Queue execution on microtask to allow builder methods to be called
|
|
9524
|
-
queueMicrotask(() => this.execute());
|
|
9525
9338
|
}
|
|
9526
|
-
shared(id,
|
|
9527
|
-
this.sharedTransitions.set(id,
|
|
9339
|
+
shared(id, transition) {
|
|
9340
|
+
this.sharedTransitions.set(id, transition);
|
|
9528
9341
|
return this;
|
|
9529
9342
|
}
|
|
9530
|
-
then(
|
|
9531
|
-
return this.readyPromise.then(
|
|
9343
|
+
then(resolve, reject) {
|
|
9344
|
+
return this.readyPromise.then(resolve, reject);
|
|
9532
9345
|
}
|
|
9533
|
-
async
|
|
9534
|
-
|
|
9535
|
-
|
|
9536
|
-
|
|
9537
|
-
|
|
9538
|
-
|
|
9539
|
-
|
|
9540
|
-
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
|
|
9545
|
-
|
|
9546
|
-
|
|
9547
|
-
}
|
|
9548
|
-
// Phase 2: Execute DOM update
|
|
9549
|
-
this.updateDom();
|
|
9550
|
-
// Phase 3: Post-mutation - Compare before/after elements
|
|
9551
|
-
const afterElements = getLayoutElements(this.scope);
|
|
9552
|
-
const beforeSet = new Set(beforeElements);
|
|
9553
|
-
const afterSet = new Set(afterElements);
|
|
9554
|
-
const entering = afterElements.filter((el) => !beforeSet.has(el));
|
|
9555
|
-
const exiting = beforeElements.filter((el) => !afterSet.has(el));
|
|
9556
|
-
// Build projection nodes for entering elements
|
|
9557
|
-
if (entering.length > 0) {
|
|
9558
|
-
context = buildProjectionTree(entering, context, this.getBuildOptions());
|
|
9559
|
-
}
|
|
9560
|
-
// No layout elements - return empty animation
|
|
9561
|
-
if (!context) {
|
|
9562
|
-
this.notifyReady(new GroupAnimation([]));
|
|
9563
|
-
return;
|
|
9564
|
-
}
|
|
9565
|
-
// Handle shared elements
|
|
9566
|
-
for (const element of exiting) {
|
|
9567
|
-
const node = context.nodes.get(element);
|
|
9568
|
-
node?.getStack()?.remove(node);
|
|
9569
|
-
}
|
|
9570
|
-
for (const element of entering) {
|
|
9571
|
-
context.nodes.get(element)?.promote();
|
|
9572
|
-
}
|
|
9573
|
-
// Phase 4: Animate
|
|
9574
|
-
context.root.didUpdate();
|
|
9575
|
-
await new Promise((resolve) => frame.postRender(() => resolve()));
|
|
9576
|
-
const animations = [];
|
|
9577
|
-
for (const node of context.nodes.values()) {
|
|
9578
|
-
if (node.currentAnimation) {
|
|
9579
|
-
animations.push(node.currentAnimation);
|
|
9580
|
-
}
|
|
9581
|
-
}
|
|
9582
|
-
const groupAnimation = new GroupAnimation(animations);
|
|
9583
|
-
groupAnimation.finished.then(() => {
|
|
9584
|
-
// Only clean up nodes for elements no longer in the document.
|
|
9585
|
-
// Elements still in DOM keep their nodes so subsequent animations
|
|
9586
|
-
// can use the stored position snapshots (A→B→A pattern).
|
|
9587
|
-
const elementsToCleanup = new Set();
|
|
9588
|
-
for (const element of context.nodes.keys()) {
|
|
9589
|
-
if (!document.contains(element)) {
|
|
9590
|
-
elementsToCleanup.add(element);
|
|
9346
|
+
async start() {
|
|
9347
|
+
const beforeElements = collectLayoutElements(this.scope);
|
|
9348
|
+
const beforeRecords = this.buildRecords(beforeElements);
|
|
9349
|
+
const exitCandidates = collectExitCandidates(beforeRecords);
|
|
9350
|
+
beforeRecords.forEach(({ projection }) => {
|
|
9351
|
+
const hasCurrentAnimation = Boolean(projection.currentAnimation);
|
|
9352
|
+
const isSharedLayout = Boolean(projection.options.layoutId);
|
|
9353
|
+
if (hasCurrentAnimation && isSharedLayout) {
|
|
9354
|
+
const snapshot = snapshotFromTarget(projection);
|
|
9355
|
+
if (snapshot) {
|
|
9356
|
+
projection.snapshot = snapshot;
|
|
9357
|
+
}
|
|
9358
|
+
else if (projection.snapshot) {
|
|
9359
|
+
projection.snapshot = undefined;
|
|
9591
9360
|
}
|
|
9592
9361
|
}
|
|
9593
|
-
|
|
9362
|
+
else if (projection.snapshot &&
|
|
9363
|
+
(projection.currentAnimation || projection.isProjecting())) {
|
|
9364
|
+
projection.snapshot = undefined;
|
|
9365
|
+
}
|
|
9366
|
+
projection.isPresent = true;
|
|
9367
|
+
projection.willUpdate();
|
|
9368
|
+
});
|
|
9369
|
+
await this.updateDom();
|
|
9370
|
+
const afterElements = collectLayoutElements(this.scope);
|
|
9371
|
+
const afterRecords = this.buildRecords(afterElements);
|
|
9372
|
+
const exitRecords = this.handleExitingElements(beforeRecords, afterRecords, exitCandidates);
|
|
9373
|
+
afterRecords.forEach(({ projection }) => {
|
|
9374
|
+
const instance = projection.instance;
|
|
9375
|
+
const resumeFromInstance = projection.resumeFrom
|
|
9376
|
+
?.instance;
|
|
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
|
+
}
|
|
9594
9389
|
});
|
|
9595
|
-
|
|
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);
|
|
9411
|
+
}
|
|
9412
|
+
return animation;
|
|
9596
9413
|
}
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
}
|
|
9603
|
-
|
|
9604
|
-
? this.sharedTransitions
|
|
9605
|
-
: undefined
|
|
9606
|
-
|
|
9414
|
+
buildRecords(elements) {
|
|
9415
|
+
const records = [];
|
|
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);
|
|
9432
|
+
}
|
|
9433
|
+
return records;
|
|
9434
|
+
}
|
|
9435
|
+
handleExitingElements(beforeRecords, afterRecords, exitCandidates) {
|
|
9436
|
+
const afterElementsSet = new Set(afterRecords.map((record) => record.element));
|
|
9437
|
+
const exiting = [];
|
|
9438
|
+
beforeRecords.forEach((record) => {
|
|
9439
|
+
if (afterElementsSet.has(record.element))
|
|
9440
|
+
return;
|
|
9441
|
+
const exitRecord = exitCandidates.get(record.element);
|
|
9442
|
+
if (!exitRecord) {
|
|
9443
|
+
record.visualElement.unmount();
|
|
9444
|
+
visualElementStore.delete(record.element);
|
|
9445
|
+
return;
|
|
9446
|
+
}
|
|
9447
|
+
if (!exitRecord.element.isConnected) {
|
|
9448
|
+
reinstateExitElement(exitRecord);
|
|
9449
|
+
}
|
|
9450
|
+
record.projection.isPresent = false;
|
|
9451
|
+
if (record.projection.options.layoutId) {
|
|
9452
|
+
record.projection.relegate();
|
|
9453
|
+
}
|
|
9454
|
+
exiting.push(record);
|
|
9455
|
+
});
|
|
9456
|
+
return exiting;
|
|
9607
9457
|
}
|
|
9608
9458
|
}
|
|
9609
|
-
/**
|
|
9610
|
-
* Parse arguments for animateLayout overloads
|
|
9611
|
-
*/
|
|
9612
9459
|
function parseAnimateLayoutArgs(scopeOrUpdateDom, updateDomOrOptions, options) {
|
|
9613
9460
|
// animateLayout(updateDom)
|
|
9614
9461
|
if (typeof scopeOrUpdateDom === "function") {
|
|
@@ -9622,11 +9469,125 @@
|
|
|
9622
9469
|
const elements = resolveElements(scopeOrUpdateDom);
|
|
9623
9470
|
const scope = elements[0] || document;
|
|
9624
9471
|
return {
|
|
9625
|
-
scope
|
|
9472
|
+
scope,
|
|
9626
9473
|
updateDom: updateDomOrOptions,
|
|
9627
9474
|
defaultOptions: options,
|
|
9628
9475
|
};
|
|
9629
9476
|
}
|
|
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
|
+
}
|
|
9630
9591
|
|
|
9631
9592
|
/**
|
|
9632
9593
|
* @deprecated
|
|
@@ -10520,7 +10481,7 @@
|
|
|
10520
10481
|
const getEventTarget = (element) => element === document.scrollingElement ? window : element;
|
|
10521
10482
|
function scrollInfo(onScroll, { container = document.scrollingElement, ...options } = {}) {
|
|
10522
10483
|
if (!container)
|
|
10523
|
-
return noop;
|
|
10484
|
+
return noop$1;
|
|
10524
10485
|
let containerHandlers = onScrollHandlers.get(container);
|
|
10525
10486
|
/**
|
|
10526
10487
|
* Get the onScroll handlers for this container.
|
|
@@ -10648,7 +10609,7 @@
|
|
|
10648
10609
|
|
|
10649
10610
|
function scroll(onScroll, { axis = "y", container = document.scrollingElement, ...options } = {}) {
|
|
10650
10611
|
if (!container)
|
|
10651
|
-
return noop;
|
|
10612
|
+
return noop$1;
|
|
10652
10613
|
const optionsWithDefaults = { axis, container, ...options };
|
|
10653
10614
|
return typeof onScroll === "function"
|
|
10654
10615
|
? attachToFunction(onScroll, optionsWithDefaults)
|
|
@@ -10809,7 +10770,7 @@
|
|
|
10809
10770
|
exports.createBox = createBox;
|
|
10810
10771
|
exports.createDelta = createDelta;
|
|
10811
10772
|
exports.createGeneratorEasing = createGeneratorEasing;
|
|
10812
|
-
exports.createProjectionNode = createProjectionNode
|
|
10773
|
+
exports.createProjectionNode = createProjectionNode;
|
|
10813
10774
|
exports.createRenderBatcher = createRenderBatcher;
|
|
10814
10775
|
exports.createScopedAnimate = createScopedAnimate;
|
|
10815
10776
|
exports.cubicBezier = cubicBezier;
|
|
@@ -10923,7 +10884,7 @@
|
|
|
10923
10884
|
exports.motionValue = motionValue;
|
|
10924
10885
|
exports.moveItem = moveItem;
|
|
10925
10886
|
exports.nodeGroup = nodeGroup;
|
|
10926
|
-
exports.noop = noop;
|
|
10887
|
+
exports.noop = noop$1;
|
|
10927
10888
|
exports.number = number;
|
|
10928
10889
|
exports.numberValueTypes = numberValueTypes;
|
|
10929
10890
|
exports.observeTimeline = observeTimeline;
|