motion 12.27.2 → 12.27.3-alpha.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.
@@ -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;
@@ -5402,7 +5402,7 @@
5402
5402
  constructor(update, options = {}) {
5403
5403
  this.currentSubject = "root";
5404
5404
  this.targets = new Map();
5405
- this.notifyReady = noop;
5405
+ this.notifyReady = noop$1;
5406
5406
  this.readyPromise = new Promise((resolve) => {
5407
5407
  this.notifyReady = resolve;
5408
5408
  });
@@ -7457,7 +7457,7 @@
7457
7457
  : values.borderRadius;
7458
7458
  }
7459
7459
  const easeCrossfadeIn = /*@__PURE__*/ compress(0, 0.5, circOut);
7460
- const easeCrossfadeOut = /*@__PURE__*/ compress(0.5, 0.95, noop);
7460
+ const easeCrossfadeOut = /*@__PURE__*/ compress(0.5, 0.95, noop$1);
7461
7461
  function compress(min, max, easing) {
7462
7462
  return (p) => {
7463
7463
  // Could replace ifs with clamp
@@ -7683,7 +7683,7 @@
7683
7683
  cancelTreeOptimisedTransformAnimations(parent);
7684
7684
  }
7685
7685
  }
7686
- function createProjectionNode$1({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) {
7686
+ function createProjectionNode({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) {
7687
7687
  return class ProjectionNode {
7688
7688
  constructor(latestValues = {}, parent = defaultParent?.()) {
7689
7689
  /**
@@ -9221,7 +9221,7 @@
9221
9221
  */
9222
9222
  const roundPoint = userAgentContains("applewebkit/") && !userAgentContains("chrome/")
9223
9223
  ? Math.round
9224
- : noop;
9224
+ : noop$1;
9225
9225
  function roundAxis(axis) {
9226
9226
  // Round to the nearest .5 pixels to support subpixel layouts
9227
9227
  axis.min = roundPoint(axis.min);
@@ -9240,7 +9240,7 @@
9240
9240
  return node !== node.root && node.scroll?.wasRoot;
9241
9241
  }
9242
9242
 
9243
- const DocumentProjectionNode = createProjectionNode$1({
9243
+ const DocumentProjectionNode = createProjectionNode({
9244
9244
  attachResizeListener: (ref, notify) => addDomEvent(ref, "resize", notify),
9245
9245
  measureScroll: () => ({
9246
9246
  x: document.documentElement.scrollLeft || document.body.scrollLeft,
@@ -9275,7 +9275,7 @@
9275
9275
  const rootProjectionNode = {
9276
9276
  current: undefined,
9277
9277
  };
9278
- const HTMLProjectionNode = createProjectionNode$1({
9278
+ const HTMLProjectionNode = createProjectionNode({
9279
9279
  measureScroll: (instance) => ({
9280
9280
  x: instance.scrollLeft,
9281
9281
  y: instance.scrollTop,
@@ -9295,315 +9295,113 @@
9295
9295
  checkIsScrollRoot: (instance) => Boolean(window.getComputedStyle(instance).position === "fixed"),
9296
9296
  });
9297
9297
 
9298
- const LAYOUT_SELECTOR = "[data-layout], [data-layout-id]";
9299
- function getLayoutElements(scope) {
9300
- const elements = Array.from(scope.querySelectorAll(LAYOUT_SELECTOR));
9301
- // Include scope itself if it's an Element (not Document) and has layout attributes
9302
- if (scope instanceof Element && hasLayout(scope)) {
9303
- elements.unshift(scope);
9304
- }
9305
- return elements;
9306
- }
9307
- function getLayoutId(element) {
9308
- return element.getAttribute("data-layout-id");
9309
- }
9310
- function hasLayout(element) {
9311
- return (element.hasAttribute("data-layout") ||
9312
- element.hasAttribute("data-layout-id"));
9313
- }
9314
-
9315
- let scaleCorrectorAdded = false;
9316
- /**
9317
- * Track active projection nodes per element to handle animation interruption.
9318
- * When a new animation starts on an element that already has an active animation,
9319
- * we need to stop the old animation so the new one can start from the current
9320
- * visual position.
9321
- */
9322
- const activeProjectionNodes = new WeakMap();
9323
- function ensureScaleCorrectors() {
9324
- if (scaleCorrectorAdded)
9325
- return;
9326
- scaleCorrectorAdded = true;
9327
- addScaleCorrector({
9328
- borderRadius: {
9329
- ...correctBorderRadius,
9330
- applyTo: [
9331
- "borderTopLeftRadius",
9332
- "borderTopRightRadius",
9333
- "borderBottomLeftRadius",
9334
- "borderBottomRightRadius",
9335
- ],
9336
- },
9337
- borderTopLeftRadius: correctBorderRadius,
9338
- borderTopRightRadius: correctBorderRadius,
9339
- borderBottomLeftRadius: correctBorderRadius,
9340
- borderBottomRightRadius: correctBorderRadius,
9341
- boxShadow: correctBoxShadow,
9342
- });
9343
- }
9344
- /**
9345
- * Get DOM depth of an element
9346
- */
9347
- function getDepth(element) {
9348
- let depth = 0;
9349
- let current = element.parentElement;
9350
- while (current) {
9351
- depth++;
9352
- current = current.parentElement;
9353
- }
9354
- return depth;
9355
- }
9356
- /**
9357
- * Find the closest projection parent for an element
9358
- */
9359
- function findProjectionParent(element, nodeCache) {
9360
- let parent = element.parentElement;
9361
- while (parent) {
9362
- const node = nodeCache.get(parent);
9363
- if (node)
9364
- return node;
9365
- parent = parent.parentElement;
9366
- }
9367
- return undefined;
9368
- }
9369
- /**
9370
- * Create or reuse a projection node for an element
9371
- */
9372
- function createProjectionNode(element, parent, options, transition) {
9373
- // Check for existing active node - reuse it to preserve animation state
9374
- const existingNode = activeProjectionNodes.get(element);
9375
- if (existingNode) {
9376
- const visualElement = existingNode.options.visualElement;
9377
- // Update transition options for the new animation
9378
- const nodeTransition = transition
9379
- ? { duration: transition.duration, ease: transition.ease }
9380
- : { duration: 0.3, ease: "easeOut" };
9381
- existingNode.setOptions({
9382
- ...existingNode.options,
9383
- animate: true,
9384
- transition: nodeTransition,
9385
- ...options,
9386
- });
9387
- // Re-mount the node if it was previously unmounted
9388
- // This re-adds it to root.nodes so didUpdate() will process it
9389
- if (!existingNode.instance) {
9390
- existingNode.mount(element);
9391
- }
9392
- return { node: existingNode, visualElement };
9393
- }
9394
- // No existing node - create a new one
9395
- const latestValues = {};
9396
- const visualElement = new HTMLVisualElement({
9397
- visualState: {
9398
- latestValues,
9399
- renderState: {
9400
- transformOrigin: {},
9401
- transform: {},
9402
- style: {},
9403
- vars: {},
9404
- },
9405
- },
9406
- presenceContext: null,
9407
- props: {},
9408
- });
9409
- const node = new HTMLProjectionNode(latestValues, parent);
9410
- // Convert AnimationOptions to transition format for the projection system
9411
- const nodeTransition = transition
9412
- ? { duration: transition.duration, ease: transition.ease }
9413
- : { duration: 0.3, ease: "easeOut" };
9414
- node.setOptions({
9415
- visualElement,
9416
- layout: true,
9417
- animate: true,
9418
- transition: nodeTransition,
9419
- ...options,
9420
- });
9421
- node.mount(element);
9422
- visualElement.projection = node;
9423
- // Track this node as the active one for this element
9424
- activeProjectionNodes.set(element, node);
9425
- return { node, visualElement };
9426
- }
9427
- /**
9428
- * Build a projection tree from a list of elements
9429
- */
9430
- function buildProjectionTree(elements, existingContext, options) {
9431
- ensureScaleCorrectors();
9432
- const nodes = existingContext?.nodes ?? new Map();
9433
- const visualElements = existingContext?.visualElements ?? new Map();
9434
- const group = existingContext?.group ?? nodeGroup();
9435
- const defaultTransition = options?.defaultTransition;
9436
- const sharedTransitions = options?.sharedTransitions;
9437
- // Sort elements by DOM depth (parents before children)
9438
- const sorted = [...elements].sort((a, b) => getDepth(a) - getDepth(b));
9439
- let root = existingContext?.root;
9440
- for (const element of sorted) {
9441
- // Skip if already has a node
9442
- if (nodes.has(element))
9443
- continue;
9444
- const parent = findProjectionParent(element, nodes);
9445
- const layoutId = getLayoutId(element);
9446
- const layoutMode = element.getAttribute("data-layout");
9447
- const nodeOptions = {
9448
- layoutId: layoutId ?? undefined,
9449
- animationType: parseLayoutMode(layoutMode),
9450
- };
9451
- // Use layoutId-specific transition if available, otherwise use default
9452
- const transition = layoutId && sharedTransitions?.get(layoutId)
9453
- ? sharedTransitions.get(layoutId)
9454
- : defaultTransition;
9455
- const { node, visualElement } = createProjectionNode(element, parent, nodeOptions, transition);
9456
- nodes.set(element, node);
9457
- visualElements.set(element, visualElement);
9458
- group.add(node);
9459
- if (!root) {
9460
- root = node.root;
9461
- }
9462
- }
9463
- return {
9464
- nodes,
9465
- visualElements,
9466
- group,
9467
- root: root,
9468
- };
9469
- }
9470
- /**
9471
- * Parse the data-layout attribute value
9472
- */
9473
- function parseLayoutMode(value) {
9474
- if (value === "position")
9475
- return "position";
9476
- if (value === "size")
9477
- return "size";
9478
- if (value === "preserve-aspect")
9479
- return "preserve-aspect";
9480
- return "both";
9481
- }
9482
- /**
9483
- * Clean up projection nodes for specific elements.
9484
- * If elementsToCleanup is provided, only those elements are cleaned up.
9485
- * If not provided, all nodes are cleaned up.
9486
- *
9487
- * This allows persisting elements to keep their nodes between animations,
9488
- * matching React's behavior where nodes persist for elements that remain in the DOM.
9489
- */
9490
- function cleanupProjectionTree(context, elementsToCleanup) {
9491
- const elementsToProcess = elementsToCleanup
9492
- ? [...context.nodes.entries()].filter(([el]) => elementsToCleanup.has(el))
9493
- : [...context.nodes.entries()];
9494
- for (const [element, node] of elementsToProcess) {
9495
- context.group.remove(node);
9496
- node.unmount();
9497
- // Only clear from activeProjectionNodes if this is still the active node.
9498
- // A newer animation might have already taken over.
9499
- if (activeProjectionNodes.get(element) === node) {
9500
- activeProjectionNodes.delete(element);
9501
- }
9502
- context.nodes.delete(element);
9503
- context.visualElements.delete(element);
9504
- }
9505
- }
9506
-
9298
+ const layoutSelector = "[data-layout], [data-layout-id]";
9299
+ const noop = () => { };
9507
9300
  class LayoutAnimationBuilder {
9508
9301
  constructor(scope, updateDom, defaultOptions) {
9509
9302
  this.sharedTransitions = new Map();
9510
9303
  this.notifyReady = noop;
9511
- this.executed = false;
9304
+ this.rejectReady = noop;
9512
9305
  this.scope = scope;
9513
9306
  this.updateDom = updateDom;
9514
9307
  this.defaultOptions = defaultOptions;
9515
- this.readyPromise = new Promise((resolve) => {
9308
+ this.readyPromise = new Promise((resolve, reject) => {
9516
9309
  this.notifyReady = resolve;
9310
+ this.rejectReady = reject;
9311
+ });
9312
+ microtask.read(() => {
9313
+ this.start().then(this.notifyReady).catch(this.rejectReady);
9517
9314
  });
9518
- // Queue execution on microtask to allow builder methods to be called
9519
- queueMicrotask(() => this.execute());
9520
9315
  }
9521
- shared(id, options) {
9522
- this.sharedTransitions.set(id, options);
9316
+ shared(id, transition) {
9317
+ this.sharedTransitions.set(id, transition);
9523
9318
  return this;
9524
9319
  }
9525
- then(onfulfilled, onrejected) {
9526
- return this.readyPromise.then(onfulfilled, onrejected);
9320
+ then(resolve, reject) {
9321
+ return this.readyPromise.then(resolve, reject);
9527
9322
  }
9528
- async execute() {
9529
- if (this.executed)
9530
- return;
9531
- this.executed = true;
9532
- let context;
9533
- // Phase 1: Pre-mutation - Build projection tree and take snapshots
9534
- const beforeElements = getLayoutElements(this.scope);
9535
- if (beforeElements.length > 0) {
9536
- context = buildProjectionTree(beforeElements, undefined, this.getBuildOptions());
9537
- context.root.startUpdate();
9538
- for (const node of context.nodes.values()) {
9539
- node.isLayoutDirty = false;
9540
- node.willUpdate();
9541
- }
9542
- }
9543
- // Phase 2: Execute DOM update
9544
- this.updateDom();
9545
- // Phase 3: Post-mutation - Compare before/after elements
9546
- const afterElements = getLayoutElements(this.scope);
9547
- const beforeSet = new Set(beforeElements);
9548
- const afterSet = new Set(afterElements);
9549
- const entering = afterElements.filter((el) => !beforeSet.has(el));
9550
- const exiting = beforeElements.filter((el) => !afterSet.has(el));
9551
- // Build projection nodes for entering elements
9552
- if (entering.length > 0) {
9553
- context = buildProjectionTree(entering, context, this.getBuildOptions());
9554
- }
9555
- // No layout elements - return empty animation
9556
- if (!context) {
9557
- this.notifyReady(new GroupAnimation([]));
9558
- return;
9559
- }
9560
- // Handle shared elements
9561
- for (const element of exiting) {
9562
- const node = context.nodes.get(element);
9563
- node?.getStack()?.remove(node);
9323
+ async start() {
9324
+ const beforeElements = collectLayoutElements(this.scope);
9325
+ const beforeRecords = this.buildRecords(beforeElements);
9326
+ const exitCandidates = collectExitCandidates(beforeRecords);
9327
+ beforeRecords.forEach(({ projection }) => {
9328
+ seedProjectionSnapshot(projection);
9329
+ projection.isPresent = true;
9330
+ projection.willUpdate();
9331
+ });
9332
+ await this.updateDom();
9333
+ const afterElements = collectLayoutElements(this.scope);
9334
+ const afterRecords = this.buildRecords(afterElements);
9335
+ const exitRecords = this.handleExitingElements(beforeRecords, afterRecords, exitCandidates);
9336
+ afterRecords.forEach(({ projection }) => {
9337
+ projection.isPresent = true;
9338
+ });
9339
+ const root = getProjectionRoot(afterRecords, beforeRecords);
9340
+ root?.didUpdate();
9341
+ await new Promise((resolve) => {
9342
+ frame.postRender(() => resolve());
9343
+ });
9344
+ const animations = collectAnimations(afterRecords, exitRecords);
9345
+ const animation = new GroupAnimation(animations);
9346
+ if (exitRecords.length) {
9347
+ const cleanup = () => {
9348
+ exitRecords.forEach(({ element, visualElement }) => {
9349
+ if (element.isConnected) {
9350
+ element.remove();
9351
+ }
9352
+ visualElement.unmount();
9353
+ visualElementStore.delete(element);
9354
+ });
9355
+ };
9356
+ animation.finished.then(cleanup, cleanup);
9564
9357
  }
9565
- for (const element of entering) {
9566
- context.nodes.get(element)?.promote();
9358
+ return animation;
9359
+ }
9360
+ buildRecords(elements) {
9361
+ const records = [];
9362
+ const recordMap = new Map();
9363
+ for (const element of elements) {
9364
+ const parentRecord = findParentRecord(element, recordMap, this.scope);
9365
+ const { layout, layoutId } = readLayoutAttributes(element);
9366
+ const override = layoutId
9367
+ ? this.sharedTransitions.get(layoutId)
9368
+ : undefined;
9369
+ const transition = override || this.defaultOptions;
9370
+ const record = getOrCreateRecord(element, parentRecord?.projection, {
9371
+ layout,
9372
+ layoutId,
9373
+ animationType: typeof layout === "string" ? layout : "both",
9374
+ transition: transition,
9375
+ });
9376
+ recordMap.set(element, record);
9377
+ records.push(record);
9567
9378
  }
9568
- // Phase 4: Animate
9569
- context.root.didUpdate();
9570
- await new Promise((resolve) => frame.postRender(() => resolve()));
9571
- const animations = [];
9572
- for (const node of context.nodes.values()) {
9573
- if (node.currentAnimation) {
9574
- animations.push(node.currentAnimation);
9575
- }
9576
- }
9577
- const groupAnimation = new GroupAnimation(animations);
9578
- groupAnimation.finished.then(() => {
9579
- // Only clean up nodes for elements no longer in the document.
9580
- // Elements still in DOM keep their nodes so subsequent animations
9581
- // can use the stored position snapshots (A→B→A pattern).
9582
- const elementsToCleanup = new Set();
9583
- for (const element of context.nodes.keys()) {
9584
- if (!document.contains(element)) {
9585
- elementsToCleanup.add(element);
9586
- }
9379
+ return records;
9380
+ }
9381
+ handleExitingElements(beforeRecords, afterRecords, exitCandidates) {
9382
+ const afterElementsSet = new Set(afterRecords.map((record) => record.element));
9383
+ const exiting = [];
9384
+ beforeRecords.forEach((record) => {
9385
+ if (afterElementsSet.has(record.element))
9386
+ return;
9387
+ const exitRecord = exitCandidates.get(record.element);
9388
+ if (!exitRecord) {
9389
+ record.visualElement.unmount();
9390
+ visualElementStore.delete(record.element);
9391
+ return;
9392
+ }
9393
+ if (!exitRecord.element.isConnected) {
9394
+ reinstateExitElement(exitRecord);
9395
+ }
9396
+ record.projection.isPresent = false;
9397
+ if (record.projection.options.layoutId) {
9398
+ record.projection.relegate();
9587
9399
  }
9588
- cleanupProjectionTree(context, elementsToCleanup);
9400
+ exiting.push(record);
9589
9401
  });
9590
- this.notifyReady(groupAnimation);
9591
- }
9592
- getBuildOptions() {
9593
- return {
9594
- defaultTransition: this.defaultOptions || {
9595
- duration: 0.3,
9596
- ease: "easeOut",
9597
- },
9598
- sharedTransitions: this.sharedTransitions.size > 0
9599
- ? this.sharedTransitions
9600
- : undefined,
9601
- };
9402
+ return exiting;
9602
9403
  }
9603
9404
  }
9604
- /**
9605
- * Parse arguments for animateLayout overloads
9606
- */
9607
9405
  function parseAnimateLayoutArgs(scopeOrUpdateDom, updateDomOrOptions, options) {
9608
9406
  // animateLayout(updateDom)
9609
9407
  if (typeof scopeOrUpdateDom === "function") {
@@ -9617,11 +9415,135 @@
9617
9415
  const elements = resolveElements(scopeOrUpdateDom);
9618
9416
  const scope = elements[0] || document;
9619
9417
  return {
9620
- scope: scope instanceof Document ? scope : scope,
9418
+ scope,
9621
9419
  updateDom: updateDomOrOptions,
9622
9420
  defaultOptions: options,
9623
9421
  };
9624
9422
  }
9423
+ function collectLayoutElements(scope) {
9424
+ const elements = Array.from(scope.querySelectorAll(layoutSelector));
9425
+ if (scope instanceof Element && scope.matches(layoutSelector)) {
9426
+ if (!elements.includes(scope)) {
9427
+ elements.unshift(scope);
9428
+ }
9429
+ }
9430
+ return elements;
9431
+ }
9432
+ function readLayoutAttributes(element) {
9433
+ const layoutId = element.getAttribute("data-layout-id") || undefined;
9434
+ const rawLayout = element.getAttribute("data-layout");
9435
+ let layout;
9436
+ if (rawLayout === "" || rawLayout === "true") {
9437
+ layout = true;
9438
+ }
9439
+ else if (rawLayout) {
9440
+ layout = rawLayout;
9441
+ }
9442
+ return {
9443
+ layout,
9444
+ layoutId,
9445
+ layoutExit: element.hasAttribute("data-layout-exit"),
9446
+ };
9447
+ }
9448
+ function createVisualState() {
9449
+ return {
9450
+ latestValues: {},
9451
+ renderState: {
9452
+ transform: {},
9453
+ transformOrigin: {},
9454
+ style: {},
9455
+ vars: {},
9456
+ },
9457
+ };
9458
+ }
9459
+ function seedProjectionSnapshot(projection) {
9460
+ const projectionNode = projection;
9461
+ if (!projectionNode.currentAnimation && !projectionNode.pendingAnimation) {
9462
+ return;
9463
+ }
9464
+ const snapshot = projection.measure(false);
9465
+ snapshot.latestValues =
9466
+ projectionNode.animationValues || projection.latestValues;
9467
+ projection.snapshot = snapshot;
9468
+ }
9469
+ function getOrCreateRecord(element, parentProjection, projectionOptions) {
9470
+ const existing = visualElementStore.get(element);
9471
+ const visualElement = existing ??
9472
+ new HTMLVisualElement({
9473
+ props: {},
9474
+ presenceContext: null,
9475
+ visualState: createVisualState(),
9476
+ }, { allowProjection: true });
9477
+ if (!existing || !visualElement.projection) {
9478
+ visualElement.projection = new HTMLProjectionNode(visualElement.latestValues, parentProjection);
9479
+ }
9480
+ visualElement.projection.setOptions({
9481
+ ...projectionOptions,
9482
+ visualElement,
9483
+ });
9484
+ if (!visualElement.current) {
9485
+ visualElement.mount(element);
9486
+ }
9487
+ if (!existing) {
9488
+ visualElementStore.set(element, visualElement);
9489
+ }
9490
+ return {
9491
+ element,
9492
+ visualElement,
9493
+ projection: visualElement.projection,
9494
+ };
9495
+ }
9496
+ function findParentRecord(element, recordMap, scope) {
9497
+ let parent = element.parentElement;
9498
+ while (parent) {
9499
+ const record = recordMap.get(parent);
9500
+ if (record)
9501
+ return record;
9502
+ if (parent === scope)
9503
+ break;
9504
+ parent = parent.parentElement;
9505
+ }
9506
+ return undefined;
9507
+ }
9508
+ function collectExitCandidates(records) {
9509
+ const exitCandidates = new Map();
9510
+ records.forEach((record) => {
9511
+ const { layoutExit } = readLayoutAttributes(record.element);
9512
+ if (!layoutExit)
9513
+ return;
9514
+ exitCandidates.set(record.element, {
9515
+ ...record,
9516
+ parent: record.element.parentNode,
9517
+ nextSibling: record.element.nextSibling,
9518
+ });
9519
+ });
9520
+ return exitCandidates;
9521
+ }
9522
+ function reinstateExitElement(record) {
9523
+ if (!record.parent)
9524
+ return;
9525
+ if (record.nextSibling && record.nextSibling.parentNode === record.parent) {
9526
+ record.parent.insertBefore(record.element, record.nextSibling);
9527
+ }
9528
+ else {
9529
+ record.parent.appendChild(record.element);
9530
+ }
9531
+ }
9532
+ function getProjectionRoot(afterRecords, beforeRecords) {
9533
+ const record = afterRecords[0] || beforeRecords[0];
9534
+ return record?.projection.root;
9535
+ }
9536
+ function collectAnimations(afterRecords, exitRecords) {
9537
+ const animations = new Set();
9538
+ const addAnimation = (record) => {
9539
+ const animation = record.projection.currentAnimation;
9540
+ if (animation)
9541
+ animations.add(animation);
9542
+ };
9543
+ afterRecords.forEach(addAnimation);
9544
+ exitRecords.forEach(addAnimation);
9545
+ return Array.from(animations);
9546
+ }
9625
9547
 
9626
9548
  /**
9627
9549
  * @deprecated
@@ -10515,7 +10437,7 @@
10515
10437
  const getEventTarget = (element) => element === document.scrollingElement ? window : element;
10516
10438
  function scrollInfo(onScroll, { container = document.scrollingElement, ...options } = {}) {
10517
10439
  if (!container)
10518
- return noop;
10440
+ return noop$1;
10519
10441
  let containerHandlers = onScrollHandlers.get(container);
10520
10442
  /**
10521
10443
  * Get the onScroll handlers for this container.
@@ -10643,7 +10565,7 @@
10643
10565
 
10644
10566
  function scroll(onScroll, { axis = "y", container = document.scrollingElement, ...options } = {}) {
10645
10567
  if (!container)
10646
- return noop;
10568
+ return noop$1;
10647
10569
  const optionsWithDefaults = { axis, container, ...options };
10648
10570
  return typeof onScroll === "function"
10649
10571
  ? attachToFunction(onScroll, optionsWithDefaults)
@@ -10804,7 +10726,7 @@
10804
10726
  exports.createBox = createBox;
10805
10727
  exports.createDelta = createDelta;
10806
10728
  exports.createGeneratorEasing = createGeneratorEasing;
10807
- exports.createProjectionNode = createProjectionNode$1;
10729
+ exports.createProjectionNode = createProjectionNode;
10808
10730
  exports.createRenderBatcher = createRenderBatcher;
10809
10731
  exports.createScopedAnimate = createScopedAnimate;
10810
10732
  exports.cubicBezier = cubicBezier;
@@ -10918,7 +10840,7 @@
10918
10840
  exports.motionValue = motionValue;
10919
10841
  exports.moveItem = moveItem;
10920
10842
  exports.nodeGroup = nodeGroup;
10921
- exports.noop = noop;
10843
+ exports.noop = noop$1;
10922
10844
  exports.number = number;
10923
10845
  exports.numberValueTypes = numberValueTypes;
10924
10846
  exports.observeTimeline = observeTimeline;