motion 12.29.0 → 12.29.2

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.
@@ -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;
@@ -3612,6 +3612,7 @@
3612
3612
  }
3613
3613
  function animateTarget(visualElement, targetAndTransition, { delay = 0, transitionOverride, type } = {}) {
3614
3614
  let { transition = visualElement.getDefaultTransition(), transitionEnd, ...target } = targetAndTransition;
3615
+ const reduceMotion = transition?.reduceMotion;
3615
3616
  if (transitionOverride)
3616
3617
  transition = transitionOverride;
3617
3618
  const animations = [];
@@ -3657,7 +3658,8 @@
3657
3658
  }
3658
3659
  }
3659
3660
  addValueToWillChange(visualElement, key);
3660
- value.start(animateMotionValue(key, value, valueTarget, visualElement.shouldReduceMotion && positionalKeys.has(key)
3661
+ const shouldReduceMotion = reduceMotion ?? visualElement.shouldReduceMotion;
3662
+ value.start(animateMotionValue(key, value, valueTarget, shouldReduceMotion && positionalKeys.has(key)
3661
3663
  ? { type: false }
3662
3664
  : valueTransition, visualElement, isHandoff));
3663
3665
  const animation = value.animation;
@@ -5462,7 +5464,7 @@
5462
5464
  constructor(update, options = {}) {
5463
5465
  this.currentSubject = "root";
5464
5466
  this.targets = new Map();
5465
- this.notifyReady = noop;
5467
+ this.notifyReady = noop$1;
5466
5468
  this.readyPromise = new Promise((resolve) => {
5467
5469
  this.notifyReady = resolve;
5468
5470
  });
@@ -7519,7 +7521,7 @@
7519
7521
  : values.borderRadius;
7520
7522
  }
7521
7523
  const easeCrossfadeIn = /*@__PURE__*/ compress(0, 0.5, circOut);
7522
- const easeCrossfadeOut = /*@__PURE__*/ compress(0.5, 0.95, noop);
7524
+ const easeCrossfadeOut = /*@__PURE__*/ compress(0.5, 0.95, noop$1);
7523
7525
  function compress(min, max, easing) {
7524
7526
  return (p) => {
7525
7527
  // Could replace ifs with clamp
@@ -7758,7 +7760,7 @@
7758
7760
  cancelTreeOptimisedTransformAnimations(parent);
7759
7761
  }
7760
7762
  }
7761
- function createProjectionNode$1({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) {
7763
+ function createProjectionNode({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) {
7762
7764
  return class ProjectionNode {
7763
7765
  constructor(latestValues = {}, parent = defaultParent?.()) {
7764
7766
  /**
@@ -9296,7 +9298,7 @@
9296
9298
  */
9297
9299
  const roundPoint = userAgentContains("applewebkit/") && !userAgentContains("chrome/")
9298
9300
  ? Math.round
9299
- : noop;
9301
+ : noop$1;
9300
9302
  function roundAxis(axis) {
9301
9303
  // Round to the nearest .5 pixels to support subpixel layouts
9302
9304
  axis.min = roundPoint(axis.min);
@@ -9315,7 +9317,7 @@
9315
9317
  return node !== node.root && node.scroll?.wasRoot;
9316
9318
  }
9317
9319
 
9318
- const DocumentProjectionNode = createProjectionNode$1({
9320
+ const DocumentProjectionNode = createProjectionNode({
9319
9321
  attachResizeListener: (ref, notify) => addDomEvent(ref, "resize", notify),
9320
9322
  measureScroll: () => ({
9321
9323
  x: document.documentElement.scrollLeft || document.body?.scrollLeft || 0,
@@ -9350,7 +9352,7 @@
9350
9352
  const rootProjectionNode = {
9351
9353
  current: undefined,
9352
9354
  };
9353
- const HTMLProjectionNode = createProjectionNode$1({
9355
+ const HTMLProjectionNode = createProjectionNode({
9354
9356
  measureScroll: (instance) => ({
9355
9357
  x: instance.scrollLeft,
9356
9358
  y: instance.scrollTop,
@@ -9370,315 +9372,153 @@
9370
9372
  checkIsScrollRoot: (instance) => Boolean(window.getComputedStyle(instance).position === "fixed"),
9371
9373
  });
9372
9374
 
9373
- const LAYOUT_SELECTOR = "[data-layout], [data-layout-id]";
9374
- function getLayoutElements(scope) {
9375
- const elements = Array.from(scope.querySelectorAll(LAYOUT_SELECTOR));
9376
- // Include scope itself if it's an Element (not Document) and has layout attributes
9377
- if (scope instanceof Element && hasLayout(scope)) {
9378
- elements.unshift(scope);
9379
- }
9380
- return elements;
9381
- }
9382
- function getLayoutId(element) {
9383
- return element.getAttribute("data-layout-id");
9384
- }
9385
- function hasLayout(element) {
9386
- return (element.hasAttribute("data-layout") ||
9387
- element.hasAttribute("data-layout-id"));
9388
- }
9389
-
9390
- let scaleCorrectorAdded = false;
9391
- /**
9392
- * Track active projection nodes per element to handle animation interruption.
9393
- * When a new animation starts on an element that already has an active animation,
9394
- * we need to stop the old animation so the new one can start from the current
9395
- * visual position.
9396
- */
9397
- const activeProjectionNodes = new WeakMap();
9398
- function ensureScaleCorrectors() {
9399
- if (scaleCorrectorAdded)
9400
- return;
9401
- scaleCorrectorAdded = true;
9402
- addScaleCorrector({
9403
- borderRadius: {
9404
- ...correctBorderRadius,
9405
- applyTo: [
9406
- "borderTopLeftRadius",
9407
- "borderTopRightRadius",
9408
- "borderBottomLeftRadius",
9409
- "borderBottomRightRadius",
9410
- ],
9411
- },
9412
- borderTopLeftRadius: correctBorderRadius,
9413
- borderTopRightRadius: correctBorderRadius,
9414
- borderBottomLeftRadius: correctBorderRadius,
9415
- borderBottomRightRadius: correctBorderRadius,
9416
- boxShadow: correctBoxShadow,
9417
- });
9418
- }
9419
- /**
9420
- * Get DOM depth of an element
9421
- */
9422
- function getDepth(element) {
9423
- let depth = 0;
9424
- let current = element.parentElement;
9425
- while (current) {
9426
- depth++;
9427
- current = current.parentElement;
9428
- }
9429
- return depth;
9430
- }
9431
- /**
9432
- * Find the closest projection parent for an element
9433
- */
9434
- function findProjectionParent(element, nodeCache) {
9435
- let parent = element.parentElement;
9436
- while (parent) {
9437
- const node = nodeCache.get(parent);
9438
- if (node)
9439
- return node;
9440
- parent = parent.parentElement;
9441
- }
9442
- return undefined;
9443
- }
9444
- /**
9445
- * Create or reuse a projection node for an element
9446
- */
9447
- function createProjectionNode(element, parent, options, transition) {
9448
- // Check for existing active node - reuse it to preserve animation state
9449
- const existingNode = activeProjectionNodes.get(element);
9450
- if (existingNode) {
9451
- const visualElement = existingNode.options.visualElement;
9452
- // Update transition options for the new animation
9453
- const nodeTransition = transition
9454
- ? { duration: transition.duration, ease: transition.ease }
9455
- : { duration: 0.3, ease: "easeOut" };
9456
- existingNode.setOptions({
9457
- ...existingNode.options,
9458
- animate: true,
9459
- transition: nodeTransition,
9460
- ...options,
9461
- });
9462
- // Re-mount the node if it was previously unmounted
9463
- // This re-adds it to root.nodes so didUpdate() will process it
9464
- if (!existingNode.instance) {
9465
- existingNode.mount(element);
9466
- }
9467
- return { node: existingNode, visualElement };
9468
- }
9469
- // No existing node - create a new one
9470
- const latestValues = {};
9471
- const visualElement = new HTMLVisualElement({
9472
- visualState: {
9473
- latestValues,
9474
- renderState: {
9475
- transformOrigin: {},
9476
- transform: {},
9477
- style: {},
9478
- vars: {},
9479
- },
9480
- },
9481
- presenceContext: null,
9482
- props: {},
9483
- });
9484
- const node = new HTMLProjectionNode(latestValues, parent);
9485
- // Convert AnimationOptions to transition format for the projection system
9486
- const nodeTransition = transition
9487
- ? { duration: transition.duration, ease: transition.ease }
9488
- : { duration: 0.3, ease: "easeOut" };
9489
- node.setOptions({
9490
- visualElement,
9491
- layout: true,
9492
- animate: true,
9493
- transition: nodeTransition,
9494
- ...options,
9495
- });
9496
- node.mount(element);
9497
- visualElement.projection = node;
9498
- // Track this node as the active one for this element
9499
- activeProjectionNodes.set(element, node);
9500
- return { node, visualElement };
9501
- }
9502
- /**
9503
- * Build a projection tree from a list of elements
9504
- */
9505
- function buildProjectionTree(elements, existingContext, options) {
9506
- ensureScaleCorrectors();
9507
- const nodes = existingContext?.nodes ?? new Map();
9508
- const visualElements = existingContext?.visualElements ?? new Map();
9509
- const group = existingContext?.group ?? nodeGroup();
9510
- const defaultTransition = options?.defaultTransition;
9511
- const sharedTransitions = options?.sharedTransitions;
9512
- // Sort elements by DOM depth (parents before children)
9513
- const sorted = [...elements].sort((a, b) => getDepth(a) - getDepth(b));
9514
- let root = existingContext?.root;
9515
- for (const element of sorted) {
9516
- // Skip if already has a node
9517
- if (nodes.has(element))
9518
- continue;
9519
- const parent = findProjectionParent(element, nodes);
9520
- const layoutId = getLayoutId(element);
9521
- const layoutMode = element.getAttribute("data-layout");
9522
- const nodeOptions = {
9523
- layoutId: layoutId ?? undefined,
9524
- animationType: parseLayoutMode(layoutMode),
9525
- };
9526
- // Use layoutId-specific transition if available, otherwise use default
9527
- const transition = layoutId && sharedTransitions?.get(layoutId)
9528
- ? sharedTransitions.get(layoutId)
9529
- : defaultTransition;
9530
- const { node, visualElement } = createProjectionNode(element, parent, nodeOptions, transition);
9531
- nodes.set(element, node);
9532
- visualElements.set(element, visualElement);
9533
- group.add(node);
9534
- if (!root) {
9535
- root = node.root;
9536
- }
9537
- }
9375
+ const layoutSelector = "[data-layout], [data-layout-id]";
9376
+ const noop = () => { };
9377
+ function snapshotFromTarget(projection) {
9378
+ const target = projection.targetWithTransforms || projection.target;
9379
+ if (!target)
9380
+ return undefined;
9381
+ const measuredBox = createBox();
9382
+ const layoutBox = createBox();
9383
+ copyBoxInto(measuredBox, target);
9384
+ copyBoxInto(layoutBox, target);
9538
9385
  return {
9539
- nodes,
9540
- visualElements,
9541
- group,
9542
- root: root,
9386
+ animationId: projection.root?.animationId ?? 0,
9387
+ measuredBox,
9388
+ layoutBox,
9389
+ latestValues: projection.animationValues || projection.latestValues || {},
9390
+ source: projection.id,
9543
9391
  };
9544
9392
  }
9545
- /**
9546
- * Parse the data-layout attribute value
9547
- */
9548
- function parseLayoutMode(value) {
9549
- if (value === "position")
9550
- return "position";
9551
- if (value === "size")
9552
- return "size";
9553
- if (value === "preserve-aspect")
9554
- return "preserve-aspect";
9555
- return "both";
9556
- }
9557
- /**
9558
- * Clean up projection nodes for specific elements.
9559
- * If elementsToCleanup is provided, only those elements are cleaned up.
9560
- * If not provided, all nodes are cleaned up.
9561
- *
9562
- * This allows persisting elements to keep their nodes between animations,
9563
- * matching React's behavior where nodes persist for elements that remain in the DOM.
9564
- */
9565
- function cleanupProjectionTree(context, elementsToCleanup) {
9566
- const elementsToProcess = elementsToCleanup
9567
- ? [...context.nodes.entries()].filter(([el]) => elementsToCleanup.has(el))
9568
- : [...context.nodes.entries()];
9569
- for (const [element, node] of elementsToProcess) {
9570
- context.group.remove(node);
9571
- node.unmount();
9572
- // Only clear from activeProjectionNodes if this is still the active node.
9573
- // A newer animation might have already taken over.
9574
- if (activeProjectionNodes.get(element) === node) {
9575
- activeProjectionNodes.delete(element);
9576
- }
9577
- context.nodes.delete(element);
9578
- context.visualElements.delete(element);
9579
- }
9580
- }
9581
-
9582
9393
  class LayoutAnimationBuilder {
9583
9394
  constructor(scope, updateDom, defaultOptions) {
9584
9395
  this.sharedTransitions = new Map();
9585
9396
  this.notifyReady = noop;
9586
- this.executed = false;
9397
+ this.rejectReady = noop;
9587
9398
  this.scope = scope;
9588
9399
  this.updateDom = updateDom;
9589
9400
  this.defaultOptions = defaultOptions;
9590
- this.readyPromise = new Promise((resolve) => {
9401
+ this.readyPromise = new Promise((resolve, reject) => {
9591
9402
  this.notifyReady = resolve;
9403
+ this.rejectReady = reject;
9404
+ });
9405
+ frame.postRender(() => {
9406
+ this.start().then(this.notifyReady).catch(this.rejectReady);
9592
9407
  });
9593
- // Queue execution on microtask to allow builder methods to be called
9594
- queueMicrotask(() => this.execute());
9595
9408
  }
9596
- shared(id, options) {
9597
- this.sharedTransitions.set(id, options);
9409
+ shared(id, transition) {
9410
+ this.sharedTransitions.set(id, transition);
9598
9411
  return this;
9599
9412
  }
9600
- then(onfulfilled, onrejected) {
9601
- return this.readyPromise.then(onfulfilled, onrejected);
9413
+ then(resolve, reject) {
9414
+ return this.readyPromise.then(resolve, reject);
9602
9415
  }
9603
- async execute() {
9604
- if (this.executed)
9605
- return;
9606
- this.executed = true;
9607
- let context;
9608
- // Phase 1: Pre-mutation - Build projection tree and take snapshots
9609
- const beforeElements = getLayoutElements(this.scope);
9610
- if (beforeElements.length > 0) {
9611
- context = buildProjectionTree(beforeElements, undefined, this.getBuildOptions());
9612
- context.root.startUpdate();
9613
- for (const node of context.nodes.values()) {
9614
- node.isLayoutDirty = false;
9615
- node.willUpdate();
9616
- }
9617
- }
9618
- // Phase 2: Execute DOM update
9619
- this.updateDom();
9620
- // Phase 3: Post-mutation - Compare before/after elements
9621
- const afterElements = getLayoutElements(this.scope);
9622
- const beforeSet = new Set(beforeElements);
9623
- const afterSet = new Set(afterElements);
9624
- const entering = afterElements.filter((el) => !beforeSet.has(el));
9625
- const exiting = beforeElements.filter((el) => !afterSet.has(el));
9626
- // Build projection nodes for entering elements
9627
- if (entering.length > 0) {
9628
- context = buildProjectionTree(entering, context, this.getBuildOptions());
9629
- }
9630
- // No layout elements - return empty animation
9631
- if (!context) {
9632
- this.notifyReady(new GroupAnimation([]));
9633
- return;
9634
- }
9635
- // Handle shared elements
9636
- for (const element of exiting) {
9637
- const node = context.nodes.get(element);
9638
- node?.getStack()?.remove(node);
9639
- }
9640
- for (const element of entering) {
9641
- context.nodes.get(element)?.promote();
9642
- }
9643
- // Phase 4: Animate
9644
- context.root.didUpdate();
9645
- await new Promise((resolve) => frame.postRender(() => resolve()));
9646
- const animations = [];
9647
- for (const node of context.nodes.values()) {
9648
- if (node.currentAnimation) {
9649
- animations.push(node.currentAnimation);
9650
- }
9651
- }
9652
- const groupAnimation = new GroupAnimation(animations);
9653
- groupAnimation.finished.then(() => {
9654
- // Only clean up nodes for elements no longer in the document.
9655
- // Elements still in DOM keep their nodes so subsequent animations
9656
- // can use the stored position snapshots (A→B→A pattern).
9657
- const elementsToCleanup = new Set();
9658
- for (const element of context.nodes.keys()) {
9659
- if (!document.contains(element)) {
9660
- elementsToCleanup.add(element);
9416
+ async start() {
9417
+ const beforeElements = collectLayoutElements(this.scope);
9418
+ const beforeRecords = this.buildRecords(beforeElements);
9419
+ beforeRecords.forEach(({ projection }) => {
9420
+ const hasCurrentAnimation = Boolean(projection.currentAnimation);
9421
+ const isSharedLayout = Boolean(projection.options.layoutId);
9422
+ if (hasCurrentAnimation && isSharedLayout) {
9423
+ const snapshot = snapshotFromTarget(projection);
9424
+ if (snapshot) {
9425
+ projection.snapshot = snapshot;
9661
9426
  }
9427
+ else if (projection.snapshot) {
9428
+ projection.snapshot = undefined;
9429
+ }
9430
+ }
9431
+ else if (projection.snapshot &&
9432
+ (projection.currentAnimation || projection.isProjecting())) {
9433
+ projection.snapshot = undefined;
9434
+ }
9435
+ projection.isPresent = true;
9436
+ projection.willUpdate();
9437
+ });
9438
+ await this.updateDom();
9439
+ const afterElements = collectLayoutElements(this.scope);
9440
+ const afterRecords = this.buildRecords(afterElements);
9441
+ this.handleExitingElements(beforeRecords, afterRecords);
9442
+ afterRecords.forEach(({ projection }) => {
9443
+ const instance = projection.instance;
9444
+ const resumeFromInstance = projection.resumeFrom
9445
+ ?.instance;
9446
+ if (!instance || !resumeFromInstance)
9447
+ return;
9448
+ if (!("style" in instance))
9449
+ return;
9450
+ const currentTransform = instance.style.transform;
9451
+ const resumeFromTransform = resumeFromInstance.style.transform;
9452
+ if (currentTransform &&
9453
+ resumeFromTransform &&
9454
+ currentTransform === resumeFromTransform) {
9455
+ instance.style.transform = "";
9456
+ instance.style.transformOrigin = "";
9662
9457
  }
9663
- cleanupProjectionTree(context, elementsToCleanup);
9664
9458
  });
9665
- this.notifyReady(groupAnimation);
9459
+ afterRecords.forEach(({ projection }) => {
9460
+ projection.isPresent = true;
9461
+ });
9462
+ const root = getProjectionRoot(afterRecords, beforeRecords);
9463
+ root?.didUpdate();
9464
+ await new Promise((resolve) => {
9465
+ frame.postRender(() => resolve());
9466
+ });
9467
+ const animations = collectAnimations(afterRecords);
9468
+ const animation = new GroupAnimation(animations);
9469
+ return animation;
9666
9470
  }
9667
- getBuildOptions() {
9668
- return {
9669
- defaultTransition: this.defaultOptions || {
9670
- duration: 0.3,
9671
- ease: "easeOut",
9672
- },
9673
- sharedTransitions: this.sharedTransitions.size > 0
9674
- ? this.sharedTransitions
9675
- : undefined,
9676
- };
9471
+ buildRecords(elements) {
9472
+ const records = [];
9473
+ const recordMap = new Map();
9474
+ for (const element of elements) {
9475
+ const parentRecord = findParentRecord(element, recordMap, this.scope);
9476
+ const { layout, layoutId } = readLayoutAttributes(element);
9477
+ const override = layoutId
9478
+ ? this.sharedTransitions.get(layoutId)
9479
+ : undefined;
9480
+ const transition = override || this.defaultOptions;
9481
+ const record = getOrCreateRecord(element, parentRecord?.projection, {
9482
+ layout,
9483
+ layoutId,
9484
+ animationType: typeof layout === "string" ? layout : "both",
9485
+ transition: transition,
9486
+ });
9487
+ recordMap.set(element, record);
9488
+ records.push(record);
9489
+ }
9490
+ return records;
9491
+ }
9492
+ handleExitingElements(beforeRecords, afterRecords) {
9493
+ const afterElementsSet = new Set(afterRecords.map((record) => record.element));
9494
+ beforeRecords.forEach((record) => {
9495
+ if (afterElementsSet.has(record.element))
9496
+ return;
9497
+ // For shared layout elements, relegate to set up resumeFrom
9498
+ // so the remaining element animates from this position
9499
+ if (record.projection.options.layoutId) {
9500
+ record.projection.isPresent = false;
9501
+ record.projection.relegate();
9502
+ }
9503
+ record.visualElement.unmount();
9504
+ visualElementStore.delete(record.element);
9505
+ });
9506
+ // Clear resumeFrom on EXISTING nodes that point to unmounted projections
9507
+ // This prevents crossfade animation when the source element was removed entirely
9508
+ // But preserve resumeFrom for NEW nodes so they can animate from the old position
9509
+ // Also preserve resumeFrom for lead nodes that were just promoted via relegate
9510
+ const beforeElementsSet = new Set(beforeRecords.map((record) => record.element));
9511
+ afterRecords.forEach(({ element, projection }) => {
9512
+ if (beforeElementsSet.has(element) &&
9513
+ projection.resumeFrom &&
9514
+ !projection.resumeFrom.instance &&
9515
+ !projection.isLead()) {
9516
+ projection.resumeFrom = undefined;
9517
+ projection.snapshot = undefined;
9518
+ }
9519
+ });
9677
9520
  }
9678
9521
  }
9679
- /**
9680
- * Parse arguments for animateLayout overloads
9681
- */
9682
9522
  function parseAnimateLayoutArgs(scopeOrUpdateDom, updateDomOrOptions, options) {
9683
9523
  // animateLayout(updateDom)
9684
9524
  if (typeof scopeOrUpdateDom === "function") {
@@ -9692,11 +9532,98 @@
9692
9532
  const elements = resolveElements(scopeOrUpdateDom);
9693
9533
  const scope = elements[0] || document;
9694
9534
  return {
9695
- scope: scope instanceof Document ? scope : scope,
9535
+ scope,
9696
9536
  updateDom: updateDomOrOptions,
9697
9537
  defaultOptions: options,
9698
9538
  };
9699
9539
  }
9540
+ function collectLayoutElements(scope) {
9541
+ const elements = Array.from(scope.querySelectorAll(layoutSelector));
9542
+ if (scope instanceof Element && scope.matches(layoutSelector)) {
9543
+ if (!elements.includes(scope)) {
9544
+ elements.unshift(scope);
9545
+ }
9546
+ }
9547
+ return elements;
9548
+ }
9549
+ function readLayoutAttributes(element) {
9550
+ const layoutId = element.getAttribute("data-layout-id") || undefined;
9551
+ const rawLayout = element.getAttribute("data-layout");
9552
+ let layout;
9553
+ if (rawLayout === "" || rawLayout === "true") {
9554
+ layout = true;
9555
+ }
9556
+ else if (rawLayout) {
9557
+ layout = rawLayout;
9558
+ }
9559
+ return {
9560
+ layout,
9561
+ layoutId,
9562
+ };
9563
+ }
9564
+ function createVisualState() {
9565
+ return {
9566
+ latestValues: {},
9567
+ renderState: {
9568
+ transform: {},
9569
+ transformOrigin: {},
9570
+ style: {},
9571
+ vars: {},
9572
+ },
9573
+ };
9574
+ }
9575
+ function getOrCreateRecord(element, parentProjection, projectionOptions) {
9576
+ const existing = visualElementStore.get(element);
9577
+ const visualElement = existing ??
9578
+ new HTMLVisualElement({
9579
+ props: {},
9580
+ presenceContext: null,
9581
+ visualState: createVisualState(),
9582
+ }, { allowProjection: true });
9583
+ if (!existing || !visualElement.projection) {
9584
+ visualElement.projection = new HTMLProjectionNode(visualElement.latestValues, parentProjection);
9585
+ }
9586
+ visualElement.projection.setOptions({
9587
+ ...projectionOptions,
9588
+ visualElement,
9589
+ });
9590
+ if (!visualElement.current) {
9591
+ visualElement.mount(element);
9592
+ }
9593
+ if (!existing) {
9594
+ visualElementStore.set(element, visualElement);
9595
+ }
9596
+ return {
9597
+ element,
9598
+ visualElement,
9599
+ projection: visualElement.projection,
9600
+ };
9601
+ }
9602
+ function findParentRecord(element, recordMap, scope) {
9603
+ let parent = element.parentElement;
9604
+ while (parent) {
9605
+ const record = recordMap.get(parent);
9606
+ if (record)
9607
+ return record;
9608
+ if (parent === scope)
9609
+ break;
9610
+ parent = parent.parentElement;
9611
+ }
9612
+ return undefined;
9613
+ }
9614
+ function getProjectionRoot(afterRecords, beforeRecords) {
9615
+ const record = afterRecords[0] || beforeRecords[0];
9616
+ return record?.projection.root;
9617
+ }
9618
+ function collectAnimations(afterRecords) {
9619
+ const animations = new Set();
9620
+ afterRecords.forEach((record) => {
9621
+ const animation = record.projection.currentAnimation;
9622
+ if (animation)
9623
+ animations.add(animation);
9624
+ });
9625
+ return Array.from(animations);
9626
+ }
9700
9627
 
9701
9628
  /**
9702
9629
  * @deprecated
@@ -10161,7 +10088,8 @@
10161
10088
  * Creates an animation function that is optionally scoped
10162
10089
  * to a specific element.
10163
10090
  */
10164
- function createScopedAnimate(scope) {
10091
+ function createScopedAnimate(options = {}) {
10092
+ const { scope, reduceMotion } = options;
10165
10093
  /**
10166
10094
  * Implementation
10167
10095
  */
@@ -10169,7 +10097,9 @@
10169
10097
  let animations = [];
10170
10098
  let animationOnComplete;
10171
10099
  if (isSequence(subjectOrSequence)) {
10172
- animations = animateSequence(subjectOrSequence, optionsOrKeyframes, scope);
10100
+ animations = animateSequence(subjectOrSequence, reduceMotion !== undefined
10101
+ ? { reduceMotion, ...optionsOrKeyframes }
10102
+ : optionsOrKeyframes, scope);
10173
10103
  }
10174
10104
  else {
10175
10105
  // Extract top-level onComplete so it doesn't get applied per-value
@@ -10177,7 +10107,9 @@
10177
10107
  if (typeof onComplete === "function") {
10178
10108
  animationOnComplete = onComplete;
10179
10109
  }
10180
- animations = animateSubject(subjectOrSequence, optionsOrKeyframes, rest, scope);
10110
+ animations = animateSubject(subjectOrSequence, optionsOrKeyframes, (reduceMotion !== undefined
10111
+ ? { reduceMotion, ...rest }
10112
+ : rest), scope);
10181
10113
  }
10182
10114
  const animation = new GroupAnimationWithThen(animations);
10183
10115
  if (animationOnComplete) {
@@ -10602,7 +10534,7 @@
10602
10534
  const getEventTarget = (element) => element === document.scrollingElement ? window : element;
10603
10535
  function scrollInfo(onScroll, { container = document.scrollingElement, trackContentSize = false, ...options } = {}) {
10604
10536
  if (!container)
10605
- return noop;
10537
+ return noop$1;
10606
10538
  let containerHandlers = onScrollHandlers.get(container);
10607
10539
  /**
10608
10540
  * Get the onScroll handlers for this container.
@@ -10762,7 +10694,7 @@
10762
10694
 
10763
10695
  function scroll(onScroll, { axis = "y", container = document.scrollingElement, ...options } = {}) {
10764
10696
  if (!container)
10765
- return noop;
10697
+ return noop$1;
10766
10698
  const optionsWithDefaults = { axis, container, ...options };
10767
10699
  return typeof onScroll === "function"
10768
10700
  ? attachToFunction(onScroll, optionsWithDefaults)
@@ -10924,7 +10856,7 @@
10924
10856
  exports.createBox = createBox;
10925
10857
  exports.createDelta = createDelta;
10926
10858
  exports.createGeneratorEasing = createGeneratorEasing;
10927
- exports.createProjectionNode = createProjectionNode$1;
10859
+ exports.createProjectionNode = createProjectionNode;
10928
10860
  exports.createRenderBatcher = createRenderBatcher;
10929
10861
  exports.createScopedAnimate = createScopedAnimate;
10930
10862
  exports.cubicBezier = cubicBezier;
@@ -11039,7 +10971,7 @@
11039
10971
  exports.motionValue = motionValue;
11040
10972
  exports.moveItem = moveItem;
11041
10973
  exports.nodeGroup = nodeGroup;
11042
- exports.noop = noop;
10974
+ exports.noop = noop$1;
11043
10975
  exports.number = number;
11044
10976
  exports.numberValueTypes = numberValueTypes;
11045
10977
  exports.observeTimeline = observeTimeline;