UNPKG

@tanstack/charts

Version:

A chart grammar for TypeScript and JavaScript. Marks consume your data directly, channels describe visual encodings, and the engine compiles them into a renderer-neutral keyed scene. TanStack's compact scales cover common numeric and categorical mappings.

2,829 lines 98.8 kB
import { focusedNodeKeys, resolveFocusScene } from "./focus-layer.js";
import { resolveFocusGuides } from "./focus-presentation.js";
import { resolveMarkStateScene } from "./mark-state.js";
import { reconcileChartSvg, reconcileChartSvgFragment } from "./reconcile.js";
import { chartSceneSource } from "./scene-source.js";
import {
  sceneMotionNode
} from "./scene-motion-internal.js";
import { viewportTranslationChanged } from "./scene-point-map.js";
import { createChartSpring } from "./spring.js";
import { renderChartSvgWithResources } from "./svg-resources.js";
import { svgClientToScene } from "./svg-coordinates.js";
import { valueKey } from "./scales.js";
import { resolveRollingPathPlan } from "./motion-path.js";
import { renderFocusGuideLayer } from "./svg-renderer.js";
import {
  detachSvgFocusGuideLayers,
  ensureSvgFocusGuideLayer,
  removeSvgFocusGuideLayer,
  restoreSvgFocusGuideLayers
} from "./svg-focus-guide-layer.js";
import { stagger } from "./motion-definition.js";
const defaultDuration = 1100;
const defaultStaggerRatio = 0.4;
const defaultEasing = cubicBezier(0.85, 0, 0.15, 1);
const springSafetyLimit = 1e4;
let clipId = 0;
function createSvgMotionDriver(options = {}) {
  const transition = resolveTransition(options.transition, defaultDuration);
  const resolved = {
    transition
  };
  return createSvgMotionRuntime(resolved, {
    initial: options.initial ?? true,
    resize: options.resize ?? false,
    respectReducedMotion: options.respectReducedMotion ?? true
  });
}
function createSvgMotionRuntime(options, policy) {
  const runtimes = /* @__PURE__ */ new WeakMap();
  return {
    id: "svg-motion",
    ...policy,
    createTooltip: (context) => createTooltipMotionController(options, policy, context),
    animateSvg(context) {
      let runtime = runtimes.get(context.container);
      if (!runtime) {
        runtime = {
          elements: /* @__PURE__ */ new WeakMap(),
          points: /* @__PURE__ */ new Map()
        };
        runtimes.set(context.container, runtime);
      }
      const timing = createTimingResolver(
        options,
        context.scene,
        context.transition || context.markTransitions ? {
          ...context.transition ? { default: { transition: context.transition } } : {},
          ...context.markTransitions ? {
            marks: Object.fromEntries(
              Object.entries(context.markTransitions).map(
                ([markId, transition]) => [markId, { transition }]
              )
            )
          } : {}
        } : void 0
      );
      if (context.phase === "update" && context.markup) {
        return reconcileMotionSvg(context, options, timing, runtime);
      }
      const root = context.container.querySelector("svg.ts-chart");
      if (!root) return () => {
      };
      const points = new Map(
        context.scene.points.map((point) => [point.key, point])
      );
      const tracks = [
        ...createBarTracks(root, context.scene, points, timing, runtime),
        ...createCartesianPathTracks(root, context.scene, timing),
        ...createRadialPathTracks(root, context.scene, timing),
        ...createArcTracks(root, context.scene, timing)
      ];
      const presentation = createPresentationTracks(
        root,
        context.scene,
        context.presentationPoints ?? [],
        timing,
        context.setPresentationPoints,
        "enter",
        runtime
      );
      return runTracks(root, [...tracks, ...presentation.tracks], {
        publish: presentation.publish,
        finish: () => context.setPresentationPoints?.(context.scene.points)
      });
    },
    animateSvgFragment(context) {
      let runtime = runtimes.get(context.container);
      if (!runtime) {
        runtime = {
          elements: /* @__PURE__ */ new WeakMap(),
          points: /* @__PURE__ */ new Map()
        };
        runtimes.set(context.container, runtime);
      }
      const nextRoot = parseSvgFragment(context.root, context.markup);
      if (!nextRoot || context.root.namespaceURI !== nextRoot.namespaceURI || context.root.localName !== nextRoot.localName) {
        if (nextRoot) context.root.replaceWith(nextRoot);
        return () => {
        };
      }
      const tracks = [];
      reconcileMotionElement(context.root, nextRoot, tracks, {
        scene: context.scene,
        previousScene: context.previousScene,
        timingFor: createTimingResolver(
          options,
          context.scene,
          context.transition ? { default: { transition: context.transition } } : void 0
        ),
        options,
        runtime,
        pathPlans: {
          elements: /* @__PURE__ */ new Map(),
          points: /* @__PURE__ */ new Map()
        }
      });
      return runTracks(context.root, tracks);
    }
  };
}
function createTooltipMotionController(options, policy, context) {
  const view = context.container.ownerDocument.defaultView;
  let presenceAnimation;
  let presencePhase;
  let movementAnimation;
  let movementFrame;
  let springMovement;
  let hideGeneration = 0;
  const prefersReducedMotion = () => policy.respectReducedMotion && Boolean(view?.matchMedia?.("(prefers-reduced-motion: reduce)").matches);
  const transition = (override) => {
    if (override === false || prefersReducedMotion()) return void 0;
    const inherited = override ?? context.transition();
    if (inherited === false) return void 0;
    return resolveTransition(
      inherited,
      defaultDuration,
      void 0,
      options.transition
    );
  };
  const now = () => view?.performance.now() ?? Date.now();
  const stopMovement = (element) => {
    if (movementFrame !== void 0) {
      view?.cancelAnimationFrame?.(movementFrame);
      movementFrame = void 0;
    }
    movementAnimation?.cancel();
    movementAnimation = void 0;
    springMovement = void 0;
    element?.style.removeProperty("translate");
  };
  const sampleSpringMovement = (timestamp, element) => {
    const movement = springMovement;
    if (!movement) return emptyTooltipMovement;
    const elapsed = Math.max(0, timestamp - movement.startedAt);
    const x = movement.spring.sample(elapsed, {
      from: movement.fromX,
      to: 0,
      velocity: movement.velocityX
    });
    const y = movement.spring.sample(elapsed, {
      from: movement.fromY,
      to: 0,
      velocity: movement.velocityY
    });
    const snapshot = {
      x: x.value,
      y: y.value,
      velocityX: x.velocity,
      velocityY: y.velocity
    };
    element.style.translate = `${snapshot.x}px ${snapshot.y}px`;
    if (x.done && y.done) {
      springMovement = void 0;
      element.style.removeProperty("translate");
      return emptyTooltipMovement;
    }
    return snapshot;
  };
  const sampleSpringFrame = (timestamp, element) => {
    movementFrame = void 0;
    sampleSpringMovement(timestamp, element);
    if (!springMovement) return;
    movementFrame = view?.requestAnimationFrame((nextTimestamp) => {
      sampleSpringFrame(nextTimestamp, element);
    });
  };
  const captureMovement = (element) => {
    let snapshot;
    if (springMovement) {
      snapshot = sampleSpringMovement(now(), element);
    } else if (movementAnimation) {
      snapshot = {
        ...readTooltipTranslate(element),
        velocityX: 0,
        velocityY: 0
      };
    } else {
      snapshot = emptyTooltipMovement;
    }
    stopMovement(element);
    return snapshot;
  };
  const animateMovement = (element, resolved, movement) => {
    const { x, y, velocityX, velocityY } = movement;
    if (Math.abs(x) < 0.5 && Math.abs(y) < 0.5) {
      element.style.removeProperty("translate");
      return;
    }
    if (resolved.type === "tween") {
      element.style.translate = `${x}px ${y}px`;
      if (typeof element.animate !== "function") {
        element.style.removeProperty("translate");
        return;
      }
      const sampled = tooltipMotionSamples(resolved);
      const animation = element.animate(
        sampled.values.map((progress, index) => ({
          offset: sampled.offsets[index],
          translate: `${interpolate(x, 0, progress)}px ${interpolate(y, 0, progress)}px`
        })),
        {
          duration: sampled.duration,
          easing: "linear",
          fill: "both"
        }
      );
      movementAnimation = animation;
      animation.onfinish = () => {
        if (movementAnimation !== animation) return;
        element.style.removeProperty("translate");
        movementAnimation = void 0;
      };
      return;
    }
    if (!view?.requestAnimationFrame) {
      element.style.removeProperty("translate");
      return;
    }
    springMovement = {
      spring: resolved.spring,
      startedAt: now(),
      fromX: x,
      fromY: y,
      velocityX,
      velocityY
    };
    element.style.translate = `${x}px ${y}px`;
    movementFrame = view.requestAnimationFrame((timestamp) => {
      sampleSpringFrame(timestamp, element);
    });
  };
  const animatePresence = (element, resolved, from, to) => {
    if (typeof element.animate !== "function") return void 0;
    const sampled = tooltipMotionSamples(resolved);
    return element.animate(
      sampled.values.map((progress, index) => ({
        offset: sampled.offsets[index],
        opacity: interpolate(from.opacity, to.opacity, progress),
        transform: `scale(${interpolate(from.scale, to.scale, progress)})`
      })),
      {
        duration: sampled.duration,
        easing: "linear",
        fill: "both"
      }
    );
  };
  return {
    beforePaint(element) {
      const wasHidden = element.hasAttribute("hidden");
      const resumingExit = presencePhase === "exit";
      const previousLeft = finiteStyleNumber(element.style.left);
      const previousTop = finiteStyleNumber(element.style.top);
      const movement = captureMovement(element);
      const presence = resumingExit ? readTooltipPresenceState(element) : void 0;
      if (resumingExit) {
        presenceAnimation?.cancel();
        presenceAnimation = void 0;
        presencePhase = void 0;
      }
      hideGeneration += 1;
      return {
        wasHidden,
        showPresence: wasHidden || resumingExit,
        previousLeft,
        previousTop,
        movementX: movement.x,
        movementY: movement.y,
        velocityX: movement.velocityX,
        velocityY: movement.velocityY,
        presence
      };
    },
    afterPaint(element, snapshot, override) {
      const resolved = transition(override);
      if (!resolved) {
        stopMovement(element);
        presenceAnimation?.cancel();
        presenceAnimation = void 0;
        presencePhase = void 0;
        element.style.removeProperty("opacity");
        element.style.removeProperty("transform");
        return;
      }
      const nextLeft = finiteStyleNumber(element.style.left);
      const nextTop = finiteStyleNumber(element.style.top);
      animateMovement(element, resolved, {
        x: snapshot.wasHidden || snapshot.previousLeft === void 0 || nextLeft === void 0 ? 0 : snapshot.previousLeft + snapshot.movementX - nextLeft,
        y: snapshot.wasHidden || snapshot.previousTop === void 0 || nextTop === void 0 ? 0 : snapshot.previousTop + snapshot.movementY - nextTop,
        velocityX: snapshot.velocityX,
        velocityY: snapshot.velocityY
      });
      if (!snapshot.showPresence) return;
      presenceAnimation?.cancel();
      presencePhase = "enter";
      const animation = animatePresence(
        element,
        resolved,
        snapshot.presence ?? { opacity: 0, scale: 0.96 },
        { opacity: 1, scale: 1 }
      );
      presenceAnimation = animation;
      if (!animation) {
        presencePhase = void 0;
        element.style.removeProperty("opacity");
        element.style.removeProperty("transform");
        return;
      }
      animation.onfinish = () => {
        if (presenceAnimation !== animation) return;
        element.style.removeProperty("opacity");
        element.style.removeProperty("transform");
        presenceAnimation = void 0;
        presencePhase = void 0;
      };
    },
    hide(element, override, complete) {
      const resolved = transition(override);
      if (!resolved) {
        stopMovement(element);
        presenceAnimation?.cancel();
        presenceAnimation = void 0;
        presencePhase = void 0;
        element.style.removeProperty("opacity");
        element.style.removeProperty("transform");
        return false;
      }
      const generation = ++hideGeneration;
      const from = readTooltipPresenceState(element);
      presenceAnimation?.cancel();
      presencePhase = "exit";
      const animation = animatePresence(element, resolved, from, {
        opacity: 0,
        scale: 0.96
      });
      presenceAnimation = animation;
      if (!animation) {
        presencePhase = void 0;
        stopMovement(element);
        return false;
      }
      animation.onfinish = () => {
        if (generation !== hideGeneration) return;
        complete();
        element.style.removeProperty("opacity");
        element.style.removeProperty("transform");
        presenceAnimation = void 0;
        presencePhase = void 0;
        stopMovement(element);
      };
      return true;
    },
    destroy(element) {
      hideGeneration += 1;
      presenceAnimation?.cancel();
      presenceAnimation = void 0;
      presencePhase = void 0;
      stopMovement(element);
      element?.style.removeProperty("opacity");
      element?.style.removeProperty("transform");
    }
  };
}
const emptyTooltipMovement = {
  x: 0,
  y: 0,
  velocityX: 0,
  velocityY: 0
};
function tooltipMotionSamples(transition) {
  if (transition.type === "tween") {
    const offsets2 = Array.from({ length: 31 }, (_, index) => index / 30);
    return {
      duration: transition.duration,
      offsets: offsets2,
      values: offsets2.map(transition.easing)
    };
  }
  const offsets = [];
  const values = [];
  let duration = 0;
  for (let elapsed = 0; elapsed <= 2e3; elapsed += 16) {
    const sample = transition.spring.sample(elapsed);
    duration = elapsed;
    offsets.push(elapsed);
    values.push(sample.value);
    if (sample.done && elapsed > 0) break;
  }
  if (duration === 0) {
    return { duration: 0, offsets: [0, 1], values: [0, 1] };
  }
  offsets[offsets.length - 1] = duration;
  values[values.length - 1] = 1;
  return {
    duration,
    offsets: offsets.map((elapsed) => elapsed / duration),
    values
  };
}
function interpolate(from, to, progress) {
  return from + (to - from) * progress;
}
function finiteStyleNumber(value) {
  const number = Number.parseFloat(value);
  return Number.isFinite(number) ? number : void 0;
}
function readTooltipPresenceState(element) {
  const style = element.ownerDocument.defaultView?.getComputedStyle(element);
  return {
    opacity: finiteStyleNumber(style?.opacity ?? "") ?? 1,
    scale: readTransformScale(style?.transform ?? "")
  };
}
function readTooltipTranslate(element) {
  const style = element.ownerDocument.defaultView?.getComputedStyle(element);
  const value = style?.translate || element.style.translate;
  if (!value || value === "none") return { x: 0, y: 0 };
  const values = value.match(/-?\d*\.?\d+(?:e[-+]?\d+)?/gi)?.map(Number);
  return {
    x: values?.[0] ?? 0,
    y: values?.[1] ?? 0
  };
}
function readTransformScale(value) {
  if (!value || value === "none") return 1;
  const values = value.match(/-?\d*\.?\d+(?:e[-+]?\d+)?/gi)?.map(Number);
  if (!values?.length) return 1;
  if (value.startsWith("matrix3d(")) {
    return Math.hypot(values[0] ?? 1, values[1] ?? 0, values[2] ?? 0);
  }
  if (value.startsWith("matrix(")) {
    return Math.hypot(values[0] ?? 1, values[1] ?? 0);
  }
  return value.startsWith("scale(") ? values[0] ?? 1 : 1;
}
function parseSvgFragment(current, markup) {
  const template = current.ownerDocument.createElement("template");
  template.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg">${markup}</svg>`;
  return template.content.firstElementChild?.firstElementChild ?? void 0;
}
function motion(options = {}) {
  const capabilityDriver = createSvgMotionDriver(options);
  const capabilities = {
    tooltipMotion: {
      protocol: 1,
      createController: capabilityDriver.createTooltip
    }
  };
  const renderer = {
    id: `svg:${capabilityDriver.id}`,
    capabilities,
    prerender: renderChartSvgWithResources,
    mount(container, requestRender) {
      return createMotionSvgChartRenderer(
        createSvgMotionDriver(options),
        renderChartSvgWithResources,
        renderer
      ).mount(container, requestRender);
    }
  };
  return renderer;
}
function createMotionSvgChartRenderer(motion2, renderSvg = renderChartSvgWithResources, ownerRenderer) {
  const renderer = {
    id: `svg:${motion2.id}`,
    capabilities: {
      tooltipMotion: {
        protocol: 1,
        createController: motion2.createTooltip
      }
    },
    prerender: renderSvg,
    mount(container) {
      const adoptedRoot = container.firstElementChild?.matches("svg.ts-chart") ?? false;
      let cancelAnimation = () => {
      };
      let cancelFocusAnimation = () => {
      };
      const visibleFocusGuides = /* @__PURE__ */ new Set();
      let scene;
      let presentationPoints;
      const presentationListeners = /* @__PURE__ */ new Set();
      let renderOptions;
      let stateTransition;
      let stateTransitions;
      let stateScene;
      let dataMotionRevision = 0;
      let dataMotionActive = false;
      let stateFlushQueued = false;
      let destroyed = false;
      let pendingStateFocus;
      let desiredStateFocus;
      const svgElement = () => {
        const svg = container.querySelector("svg.ts-chart");
        if (!svg) {
          throw new Error(
            "The motion SVG renderer must produce an svg.ts-chart root element."
          );
        }
        return svg;
      };
      const publishPresentationPoints = (points) => {
        presentationPoints = points;
        for (const listener of presentationListeners) listener(points);
      };
      const queuePendingStateFocus = () => {
        if (stateFlushQueued || !pendingStateFocus) return;
        stateFlushQueued = true;
        queueMicrotask(() => {
          stateFlushQueued = false;
          if (destroyed || dataMotionActive || !pendingStateFocus) return;
          const pending = pendingStateFocus;
          pendingStateFocus = void 0;
          applyStateFocus(pending.focus, pending.pointer, pending.cursor);
        });
      };
      const applyStateFocus = (focus, pointer, cursor, resolved = scene ? resolveMarkStateScene(scene, focus, pointer) : void 0) => {
        if (!scene || !renderOptions || !resolved) return;
        const presented = resolveFocusScene(resolved.scene, focus);
        cancelFocusAnimation();
        cancelFocusAnimation = () => {
        };
        const previousTransition = stateTransition;
        const previousTransitions = stateTransitions;
        if (presented.scene !== scene || stateScene || previousTransition) {
          const focusGuideLayers = detachSvgFocusGuideLayers(svgElement());
          cancelAnimation();
          if (presentationPoints !== scene.points) {
            publishPresentationPoints(scene.points);
          }
          const transition = resolved.transition ?? previousTransition;
          const markTransitions = resolved.transitions ?? previousTransitions;
          const reduced = motion2.respectReducedMotion && (transition?.respectReducedMotion ?? true) && (container.ownerDocument.defaultView?.matchMedia?.(
            "(prefers-reduced-motion: reduce)"
          ).matches ?? false);
          const markup = renderSvg(presented.scene, renderOptions);
          cancelAnimation = reduced ? reconcileChartSvg(container, markup) : motion2.animateSvg({
            container,
            scene: presented.scene,
            previousScene: stateScene ?? scene,
            presentationPoints: scene.points,
            markup,
            phase: "update",
            transition: markTransitions ? void 0 : transition,
            markTransitions
          });
          restoreSvgFocusGuideLayers(svgElement(), focusGuideLayers);
          stateScene = focus && presented.scene !== scene ? presented.scene : void 0;
        }
        stateTransition = focus ? resolved.transition ?? previousTransition : void 0;
        stateTransitions = focus ? resolved.transitions ?? previousTransitions : void 0;
        paintMotionSvgFocus(svgElement(), presented.scene, focus);
        cancelFocusAnimation = paintMotionSvgFocusGuides({
          container,
          svg: svgElement(),
          scene: presented.scene,
          focus,
          pointer,
          cursor,
          idPrefix: renderOptions.idPrefix,
          motion: motion2,
          visible: visibleFocusGuides
        });
        return presented.scene;
      };
      const surface = {
        renderer: ownerRenderer ?? renderer,
        get element() {
          return svgElement();
        },
        render(nextScene, options) {
          const previousScene = scene;
          const initial = previousScene === void 0;
          const resized = Boolean(
            previousScene && (previousScene.width !== nextScene.width || previousScene.height !== nextScene.height)
          );
          const reduced = motion2.respectReducedMotion && (container.ownerDocument.defaultView?.matchMedia?.(
            "(prefers-reduced-motion: reduce)"
          ).matches ?? false);
          const animate = !reduced && (initial ? motion2.initial && (!adoptedRoot || motion2.initial === "always") : motion2.resize || !resized);
          const viewportMoved = Boolean(
            previousScene && viewportTranslationChanged(previousScene, nextScene)
          );
          const markup = renderSvg(nextScene, options);
          cancelAnimation();
          const previousPresentation = presentationPoints ?? previousScene?.points ?? [];
          cancelFocusAnimation();
          cancelFocusAnimation = () => {
          };
          const retainsFocusGuideLayers = Boolean(
            previousScene?.focusGuides?.length
          );
          const focusGuideLayers = retainsFocusGuideLayers ? detachSvgFocusGuideLayers(svgElement()) : {};
          for (const placement of visibleFocusGuides) {
            if (!nextScene.focusGuides?.some(
              (guide) => guide.placement === placement
            )) {
              visibleFocusGuides.delete(placement);
            }
          }
          const revision = ++dataMotionRevision;
          stateScene = void 0;
          stateTransition = void 0;
          scene = nextScene;
          renderOptions = options;
          dataMotionActive = animate && !viewportMoved;
          pendingStateFocus = dataMotionActive ? desiredStateFocus : void 0;
          if (animate && !viewportMoved) {
            if (initial) reconcileChartSvg(container, markup);
            cancelAnimation = motion2.animateSvg({
              container,
              scene: nextScene,
              previousScene,
              presentationPoints: previousPresentation,
              markup,
              phase: initial ? "initial" : "update",
              setPresentationPoints(points) {
                publishPresentationPoints(
                  points
                );
                if (revision === dataMotionRevision && points === nextScene.points) {
                  dataMotionActive = false;
                  queuePendingStateFocus();
                }
              }
            });
          } else {
            reconcileChartSvg(container, markup);
            publishPresentationPoints(nextScene.points);
            dataMotionActive = false;
          }
          if (retainsFocusGuideLayers) {
            restoreSvgFocusGuideLayers(
              svgElement(),
              focusGuideLayers,
              (placement) => nextScene.focusGuides?.some(
                (guide) => guide.placement === placement
              ) === true
            );
          }
          scene = nextScene;
          stateScene = void 0;
          renderOptions = options;
          stateTransition = void 0;
          stateTransitions = void 0;
        },
        clientToScene(currentScene, clientX, clientY) {
          return svgClientToScene(svgElement(), currentScene, clientX, clientY);
        },
        getPresentationPoints() {
          if (!scene || !presentationPoints || presentationPoints === scene.points) {
            return void 0;
          }
          return presentationPoints;
        },
        subscribePresentationPoints(listener) {
          presentationListeners.add(listener);
          return () => presentationListeners.delete(listener);
        },
        paintFocus(focus, pointer, cursor) {
          if (!scene || !renderOptions) return;
          desiredStateFocus = {
            focus,
            pointer: pointer ?? null,
            cursor: cursor ?? null
          };
          const resolved = resolveMarkStateScene(scene, focus, pointer);
          if (dataMotionActive) {
            pendingStateFocus = desiredStateFocus;
            paintMotionSvgFocus(svgElement(), resolved.scene, focus);
            cancelFocusAnimation();
            cancelFocusAnimation = paintMotionSvgFocusGuides({
              container,
              svg: svgElement(),
              scene: resolved.scene,
              focus,
              pointer,
              cursor,
              idPrefix: renderOptions.idPrefix,
              motion: motion2,
              visible: visibleFocusGuides
            });
            return resolved.scene;
          }
          pendingStateFocus = void 0;
          return applyStateFocus(
            focus,
            pointer ?? null,
            cursor ?? null,
            resolved
          );
        },
        destroy() {
          destroyed = true;
          dataMotionRevision += 1;
          pendingStateFocus = void 0;
          desiredStateFocus = void 0;
          cancelAnimation();
          presentationListeners.clear();
          cancelFocusAnimation();
        }
      };
      return surface;
    }
  };
  return renderer;
}
function paintMotionSvgFocus(svg, scene, focus) {
  const sceneLayers = collectMotionFocusLayers(scene.nodes);
  const elements = svg.querySelectorAll(
    "[data-ts-focus-layer]:not([data-ts-focus-guide-layer])"
  );
  elements.forEach((element, index) => {
    const layer = sceneLayers[index];
    if (layer?.focus?.retarget) {
      const hasChildren = element.children.length > 0;
      element.setAttribute("visibility", hasChildren ? "visible" : "hidden");
      element.querySelectorAll("[data-ts-key]").forEach((child) => child.setAttribute("visibility", "visible"));
      return;
    }
    const visible = layer ? focusedNodeKeys(layer, focus) : /* @__PURE__ */ new Set();
    element.setAttribute(
      "visibility",
      focus && visible.size ? "visible" : "hidden"
    );
    element.querySelectorAll("[data-ts-key]").forEach((child) => {
      const key = child.dataset.tsKey;
      child.setAttribute(
        "visibility",
        key && visible.has(key) ? "visible" : "hidden"
      );
    });
  });
}
function paintMotionSvgFocusGuides(options) {
  const {
    container,
    svg,
    scene,
    focus,
    pointer,
    cursor,
    idPrefix = "",
    motion: motion2,
    visible
  } = options;
  const presentation = resolveFocusGuides(scene, focus, pointer, cursor);
  const reduced = motion2.respectReducedMotion && (container.ownerDocument.defaultView?.matchMedia?.(
    "(prefers-reduced-motion: reduce)"
  ).matches ?? false);
  const cancellations = [];
  for (const placement of ["under", "over"]) {
    if (!scene.focusGuides?.some((guide) => guide.placement === placement)) {
      removeSvgFocusGuideLayer(svg, placement);
      visible.delete(placement);
      continue;
    }
    const layer = ensureSvgFocusGuideLayer(svg, placement);
    const nodes = presentation[placement];
    if (!nodes.length) {
      layer.setAttribute("visibility", "hidden");
      visible.delete(placement);
      continue;
    }
    const markup = renderFocusGuideLayer(nodes, placement, idPrefix);
    if (reduced || !visible.has(placement)) {
      reconcileChartSvgFragment(layer, markup);
    } else {
      cancellations.push(
        motion2.animateSvgFragment({
          container,
          root: layer,
          scene,
          markup
        })
      );
    }
    visible.add(placement);
  }
  return () => cancellations.forEach((cancel) => cancel());
}
function collectMotionFocusLayers(nodes) {
  const layers = [];
  for (const node of nodes) {
    if (node.kind !== "group") continue;
    if (node.focus) layers.push(node);
    else layers.push(...collectMotionFocusLayers(node.children));
  }
  return layers;
}
function createBarTracks(root, scene, points, timingFor, runtime) {
  const groups = [
    ...root.querySelectorAll(
      "g.ts-chart__bar-y, g.ts-chart__bar-x"
    )
  ];
  const tracks = [];
  groups.forEach((group, seriesIndex) => {
    const horizontal = group.classList.contains("ts-chart__bar-x");
    const rectangles = [...group.children].filter(
      (element) => element.localName === "rect"
    );
    const seriesKey = group.getAttribute("data-ts-key") ?? `series:${seriesIndex}`;
    rectangles.forEach((rectangle, datumIndex) => {
      const key = rectangle.getAttribute("data-ts-key") ?? `${seriesKey}:${datumIndex}`;
      const point = points.get(key);
      const targetX = numberAttribute(rectangle, "x");
      const targetY = numberAttribute(rectangle, "y");
      const targetWidth = numberAttribute(rectangle, "width");
      const targetHeight = numberAttribute(rectangle, "height");
      const baseline = resolveBarBaseline(
        scene,
        point,
        horizontal,
        horizontal ? targetX : targetY + targetHeight
      );
      const timing = timingFor({
        phase: "enter",
        role: "bar",
        key,
        markId: point?.markId ?? motionMarkId(scene, seriesKey),
        seriesKey,
        seriesIndex,
        datumIndex,
        datumCount: rectangles.length,
        datum: point?.datum,
        point
      });
      rectangle.dataset.tsMotionRole = "bar";
      const names = horizontal ? ["x", "width"] : ["y", "height"];
      const from = [baseline, 0];
      const to = horizontal ? [targetX, targetWidth] : [targetY, targetHeight];
      const states = names.flatMap(
        (name, index) => elementValueStates(runtime, rectangle, name, [from[index] ?? 0])
      );
      const apply = (values) => {
        rectangle.setAttribute(names[0], formatNumber(values[0] ?? 0));
        rectangle.setAttribute(names[1], formatNumber(values[1] ?? 0));
      };
      const finish = () => {
        rectangle.setAttribute("x", formatNumber(targetX));
        rectangle.setAttribute("y", formatNumber(targetY));
        rectangle.setAttribute("width", formatNumber(targetWidth));
        rectangle.setAttribute("height", formatNumber(targetHeight));
        delete rectangle.dataset.tsMotionRole;
      };
      apply(from);
      tracks.push({
        ...timing,
        values: bindMotionValues(states, from, to),
        apply,
        finish,
        cancel: () => delete rectangle.dataset.tsMotionRole
      });
    });
  });
  return tracks;
}
function createCartesianPathTracks(root, scene, timingFor) {
  const groups = [
    ...root.querySelectorAll(
      "g.ts-chart__line:not(.ts-chart__radial-line), g.ts-chart__area:not(.ts-chart__radial-area)"
    )
  ];
  return groups.map((group, seriesIndex) => {
    const role = group.classList.contains("ts-chart__area") ? "area" : "line";
    const seriesKey = group.getAttribute("data-ts-key") ?? `${role}:${seriesIndex}`;
    const timing = timingFor({
      phase: "enter",
      role,
      key: seriesKey,
      markId: motionMarkId(scene, seriesKey),
      seriesKey,
      seriesIndex,
      datumIndex: 0,
      datumCount: 1,
      datum: void 0,
      point: void 0
    });
    const horizontal = scenePathAffinity(scene, seriesKey) === "y";
    const baseline = resolvePathBaseline(scene, horizontal);
    const previousTransform = group.getAttribute("transform");
    group.dataset.tsMotionRole = role;
    const apply = (values) => {
      const progress = values[0] ?? 0;
      const transform = horizontal ? `matrix(${formatNumber(progress)} 0 0 1 ${formatNumber(baseline * (1 - progress))} 0)` : `matrix(1 0 0 ${formatNumber(progress)} 0 ${formatNumber(baseline * (1 - progress))})`;
      group.setAttribute(
        "transform",
        previousTransform ? `${previousTransform} ${transform}` : transform
      );
    };
    const cleanup = () => {
      if (previousTransform === null) group.removeAttribute("transform");
      else group.setAttribute("transform", previousTransform);
      delete group.dataset.tsMotionRole;
    };
    apply([0]);
    return {
      ...timing,
      values: bindMotionValues(void 0, [0], [1]),
      apply,
      finish: cleanup,
      cancel: cleanup
    };
  });
}
function createRadialPathTracks(root, scene, timingFor) {
  const groups = [
    ...root.querySelectorAll(
      "g.ts-chart__radial-line, g.ts-chart__radial-area, g.ts-chart__radial-dot"
    )
  ];
  return groups.map((group, seriesIndex) => {
    const role = group.classList.contains(
      "ts-chart__radial-area"
    ) ? "area" : group.classList.contains("ts-chart__radial-dot") ? "dot" : "line";
    const seriesKey = group.getAttribute("data-ts-key") ?? `${role}:${seriesIndex}`;
    const timing = timingFor({
      phase: "enter",
      role,
      key: seriesKey,
      markId: motionMarkId(scene, seriesKey),
      seriesKey,
      seriesIndex,
      datumIndex: 0,
      datumCount: 1,
      datum: void 0,
      point: void 0
    });
    const previousTransform = group.getAttribute("transform");
    group.dataset.tsMotionRole = role;
    const apply = (values) => {
      const progress = values[0] ?? 0;
      const transform = `scale(${formatNumber(progress)})`;
      group.setAttribute(
        "transform",
        previousTransform ? `${previousTransform} ${transform}` : transform
      );
    };
    const cleanup = () => {
      if (previousTransform === null) group.removeAttribute("transform");
      else group.setAttribute("transform", previousTransform);
      delete group.dataset.tsMotionRole;
    };
    apply([0]);
    return {
      ...timing,
      values: bindMotionValues(void 0, [0], [1]),
      apply,
      finish: cleanup,
      cancel: cleanup
    };
  });
}
function createArcTracks(root, scene, timingFor) {
  const groups = [...root.querySelectorAll("g.ts-chart__arc")];
  return groups.flatMap((group, seriesIndex) => {
    const seriesKey = group.getAttribute("data-ts-key") ?? `arc:${seriesIndex}`;
    const geometry = sceneArcGeometry(scene, seriesKey);
    if (!geometry) return [];
    const role = group.classList.contains("ts-chart__bar") ? "bar" : "arc";
    const timing = timingFor({
      phase: "enter",
      role,
      key: seriesKey,
      markId: motionMarkId(scene, seriesKey),
      seriesKey,
      seriesIndex,
      datumIndex: 0,
      datumCount: 1,
      datum: void 0,
      point: void 0
    });
    const document = root.ownerDocument;
    let definitions = root.querySelector("defs");
    if (!definitions) {
      definitions = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "defs"
      );
      definitions.dataset.tsMotionDefs = "";
      root.prepend(definitions);
    }
    const clip = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "clipPath"
    );
    const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
    const id = `ts-chart-motion-clip-${++clipId}`;
    clip.id = id;
    clip.append(path);
    definitions.append(clip);
    const previousClip = group.getAttribute("clip-path");
    group.setAttribute("clip-path", `url(#${id})`);
    group.dataset.tsMotionRole = role;
    const apply = (values) => {
      const progress = Math.max(0, Math.min(1, values[0] ?? 0));
      path.setAttribute(
        "d",
        radialSweepClipPath(
          geometry.startAngle,
          geometry.sweep * progress,
          geometry.radius
        )
      );
    };
    const cleanup = () => {
      if (previousClip === null) group.removeAttribute("clip-path");
      else group.setAttribute("clip-path", previousClip);
      delete group.dataset.tsMotionRole;
      clip.remove();
      if (definitions?.dataset.tsMotionDefs !== void 0 && !definitions.children.length) {
        definitions.remove();
      }
    };
    apply([0]);
    return [
      {
        ...timing,
        values: bindMotionValues(void 0, [0], [1]),
        apply,
        finish: cleanup,
        cancel: cleanup
      }
    ];
  });
}
function resolvePathBaseline(scene, horizontal) {
  const chartStart = horizontal ? scene.chart.x : scene.chart.y;
  const chartSize = horizontal ? scene.chart.width : scene.chart.height;
  const fallback = horizontal ? chartStart : chartStart + chartSize;
  const scale = scene.scales[horizontal ? "x" : "y"];
  if (!scale) return fallback;
  const zero = scale.map(0);
  return Number.isFinite(zero) && zero >= chartStart && zero <= chartStart + chartSize ? zero : fallback;
}
function scenePathAffinity(scene, key) {
  const node = findSceneNodeContext(scene.nodes, key)?.node;
  if (!node) return void 0;
  const visit = (candidate) => {
    if (candidate.kind === "group") {
      for (const child of candidate.children) {
        const affinity = visit(child);
        if (affinity) return affinity;
      }
      return void 0;
    }
    return "interaction" in candidate ? candidate.interaction?.affinity : void 0;
  };
  return visit(node);
}
function sceneArcGeometry(scene, key) {
  const node = findSceneNodeContext(scene.nodes, key)?.node;
  if (!node) return void 0;
  const pointSets = [];
  const visit = (candidate) => {
    if (candidate.kind === "group") {
      candidate.children.forEach(visit);
      return;
    }
    if (candidate.kind === "area" && candidate.points.length) {
      pointSets.push(candidate.points);
    }
  };
  visit(node);
  const firstSet = pointSets.find((points) => points.length > 1);
  const firstPoint = firstSet?.[0];
  if (!firstSet || !firstPoint) return void 0;
  const startAngle = polarPointAngle(firstPoint);
  let direction = 0;
  for (let index = 1; index < firstSet.length; index += 1) {
    const point = firstSet[index];
    if (!point) continue;
    const delta = signedAngleDelta(startAngle, polarPointAngle(point));
    if (Math.abs(delta) > 1e-4) {
      direction = Math.sign(delta);
      break;
    }
  }
  if (!direction) return void 0;
  let sweep = 0;
  let radius = 0;
  for (const points of pointSets) {
    for (const point of points) {
      const angle = polarPointAngle(point);
      const distance = direction > 0 ? positiveAngle(angle - startAngle) : positiveAngle(startAngle - angle);
      sweep = Math.max(sweep, distance);
      radius = Math.max(radius, Math.hypot(point[0], point[1]));
    }
  }
  const tau = Math.PI * 2;
  if (sweep > tau - Math.PI / 12) sweep = tau;
  if (sweep <= 1e-4 || radius <= 0) return void 0;
  return { startAngle, sweep: sweep * direction, radius: radius + 2 };
}
function polarPointAngle(point) {
  return Math.atan2(point[0], -point[1]);
}
function signedAngleDelta(from, to) {
  const tau = Math.PI * 2;
  return ((to - from + Math.PI) % tau + tau) % tau - Math.PI;
}
function positiveAngle(angle) {
  const tau = Math.PI * 2;
  return (angle % tau + tau) % tau;
}
function radialSweepClipPath(startAngle, sweep, radius) {
  if (Math.abs(sweep) <= 1e-6) return "M0 0Z";
  const steps = Math.max(1, Math.ceil(Math.abs(sweep) / (Math.PI / 24)));
  let path = "M0 0";
  for (let index = 0; index <= steps; index += 1) {
    const angle = startAngle + sweep * index / steps;
    path += `L${formatNumber(Math.sin(angle) * radius)} ${formatNumber(-Math.cos(angle) * radius)}`;
  }
  return `${path}Z`;
}
function reconcileMotionSvg(context, options, timingFor, runtime) {
  const template = context.container.ownerDocument.createElement("template");
  template.innerHTML = context.markup ?? "";
  const nextRoot = template.content.firstElementChild;
  const currentRoot = context.container.firstElementChild;
  if (!nextRoot || !currentRoot || currentRoot.namespaceURI !== nextRoot.namespaceURI || currentRoot.localName !== nextRoot.localName) {
    if (nextRoot) context.container.replaceChildren(nextRoot);
    context.setPresentationPoints?.(context.scene.points);
    return () => {
    };
  }
  const tracks = [];
  const pathPlans = createRollingPathPlans(
    currentRoot,
    nextRoot,
    context.previousScene,
    context.scene,
    timingFor
  );
  reconcileMotionElement(currentRoot, nextRoot, tracks, {
    scene: context.scene,
    previousScene: context.previousScene,
    timingFor,
    options,
    runtime,
    pathPlans
  });
  const root = currentRoot;
  const presentation = createPresentationTracks(
    root,
    context.scene,
    context.presentationPoints ?? context.previousScene?.points ?? [],
    timingFor,
    context.setPresentationPoints,
    "update",
    runtime,
    pathPlans
  );
  return runTracks(root, [...tracks, ...presentation.tracks], {
    publish: presentation.publish,
    finish: () => context.setPresentationPoints?.(context.scene.points)
  });
}
function createRollingPathPlans(currentRoot, nextRoot, previousScene, scene, timingFor) {
  const elements = /* @__PURE__ */ new Map();
  const points = /* @__PURE__ */ new Map();
  if (!previousScene) return { elements, points };
  const currentPaths = keyedElementMap(
    currentRoot,
    "g.ts-chart__line path, g.ts-chart__area path"
  );
  const nextPaths = keyedElementMap(
    nextRoot,
    "g.ts-chart__line path, g.ts-chart__area path"
  );
  for (const [key] of nextPaths) {
    const currentPath = currentPaths.get(key);
    if (!currentPath) continue;
    const motionContext = elementTimingContext(currentPath, "update", scene);
    if (!motionContext) continue;
    const timing = timingFor(motionContext);
    if (!isRollingPathMotion(timing.path)) continue;
    const previous = scenePathSnapshot(previousScene, key);
    const next = scenePathSnapshot(scene, key);
    let outcome = previous && next ? resolveRollingPathPlan(previous, next, timing.path) : {
      kind: "fallback",
      fallback: timing.path.fallback ?? "snap",
      reason: "missing-semantic-points"
    };
    if (outcome.kind === "transform") {
      outcome = {
        ...outcome,
        transform: composeRollingTransform(
          parseRollingTransform(currentPath.getAttribute("transform")),
          outcome.transform
        )
      };
    }
    const planned = {
      key,
      outcome,
      points: next?.points ?? [],
      previousPoints: previous?.points ?? [],
      timing
    };
    elements.set(key, planned);
    for (const point of previous?.points ?? []) {
      points.set(pointIdentity(point), planned);
    }
    for (const point of next?.points ?? []) {
      points.set(pointIdentity(point), planned);
    }
  }
  return { elements, points };
}
function scenePathSnapshot(scene, key) {
  const context = findSceneNodeContext(scene.nodes, key);
  const node = context?.node;
  if (!node || node.kind !== "polyline" && node.kind !== "area") {
    return void 0;
  }
  const interaction = node.interaction;
  const points = interaction && "points" in interaction ? interaction.points ?? [] : [];
  if (!points.length) return void 0;
  const yScaleId = motionPointScaleId(scene, points[0], "y") ?? "y";
  return {
    kind: node.kind,
    points,
    geometry: node.points,
    chart: scene.chart,
    yScale: scene.scales[yScaleId],
    viewportTranslate: {
      x: context.translateX,
      y: context.translateY
    },
    clipped: context.clipped,
    customPath: node.path !== void 0
  };
}
function motionPointScaleId(scene, point, channel) {
  if (!point) return void 0;
  const initialized = motionSceneSource(scene)?.[1];
  const mark = initialized?.filter(
    (candidate) => point.markId === candidate.id || point.markId.startsWith(`${candidate.id}:`)
  ).sort((left, right) => right.id.length - left.id.length)[0];
  return mark?.channels[channel]?.scale;
}
function findSceneNodeContext(nodes, key, translateX = 0, translateY = 0, clipped = false) {
  for (const node of nodes) {
    if (node.key === key) return { node, translateX, translateY, clipped };
    if (node.kind === "group") {
      const nested = findSceneNodeContext(
        node.children,
        key,
        translateX + (node.translateX ?? 0),
        translateY + (node.translateY ?? 0),
        clipped || node.clip !== void 0
      );
      if (nested) return nested;
    }
  }
  return void 0;
}
function isRollingPathMotion(path) {
  return typeof path === "object" && path.update === "rolling";
}
function composeRollingTransform(current, next) {
  return {
    x: current.x + next.x,
    yScale: current.yScale * next.yScale,
    y: current.yScale * next.y + current.y
  };
}
function parseRollingTransform(value) {
  if (!value) return { x: 0, yScale: 1, y: 0 };
  const translated = translatedX(value);
  if (translated !== void 0) return { x: translated, yScale: 1, y: 0 };
  const match = /^matrix\(\s*1(?:\.0+)?\s+0(?:\.0+)?\s+0(?:\.0+)?\s+(-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)\s+(-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)\s+(-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)\s*\)$/i.exec(
    value
  );
  if (!match) return { x: 0, yScale: 1, y: 0 };
  const yScale = Number(match[1]);
  const x = Number(match[2]);
  const y = Number(match[3]);
  return Number.isFinite(x) && Number.isFinite(yScale) && Number.isFinite(y) ? { x, yScale, y } : { x: 0, yScale: 1, y: 0 };
}
const motionAttributes = /* @__PURE__ */ new Set([
  "cx",
  "cy",
  "d",
  "fill-opacity",
  "font-size",
  "font-weight",
  "height",
  "opacity",
  "r",
  "rx",
  "stroke-opacity",
  "stroke-width",
  "transform",
  "width",
  "x",
  "x1",
  "x2",
  "y",
  "y1",
  "y2"
]);
const rollingPointGeometryAttributes = /* @__PURE__ */ new Set([
  "cx",
  "cy",
  "height",
  "width",
  "x",
  "x1",
  "x2",
  "y",
  "y1",
  "y2"
]);
function reconcileMotionElement(current, next, tracks, context) {
  addUpdateTrack(current, next, tracks, context);
  if (!next.firstElementChild) {
    if (current.firstElementChild) {
      for (const child of [...current.children]) {
        addExitMotionTrack(child, tracks, context);
      }
    } else if (current.textContent !== next.textContent) {
      current.textContent = next.textContent;
    }
    return;
  }
  const currentChildren = [...current.children];
  const nextChildren = [...next.children];
  const currentByIdentity = indexMotionChildren(currentChildren);
  const nextIdentities = motionIdentities(nextChildren);
  const retained = /* @__PURE__ */ new Set();
  let cursor = current.firstElementChild;
  nextChildren.forEach((nextChild, index) => {
    const matched = currentByIdentity.get(nextIdentities[index]);
    let rendered;
    if (matched && matched.namespaceURI === nextChild.namespaceURI && matched.localName === nextChild.localName) {
      rendered = matched;
      retained.add(matched);
      if (rendered !== cursor) current.insertBefore(rendered, cursor);
      reconcileMotionElement(rendered, nextChild, tracks, context);
    } else {
      rendered = nextChild.cloneNode(true);
      current.insertBefore(rendered, cursor);
      addEnterMotionTrack(rendered, tracks, context);
    }
    cursor = rendered.nextElementSibling;
  });
  for (const child of currentChildren) {
    if (!retained.has(child) && child.parentElement === current) {
      addExitMotionTrack(child, tracks, context);
    }
  }
}
function addUpdateTrack(current, next, tracks, context) {
  let timingContext = elementTimingContext(current, "update", context.scene);
  let timing;
  const pathKey = current.getAttribute("data-ts-key");
  const rolling = pathKey ? context.pathPlans.elements.get(pathKey) : void 0;
  const rollingTransform = rolling?.outcome.kind === "transform" ? rolling.outcome.transform : void 0;
  const rollingSnap = rolling?.outcome.kind === "fallback" && rolling.outcome.fallback === "snap";
  const pointRolling = timingContext?.point ? context.pathPlans.points.get(pointIdentity(timingContext.point)) : void 0;
  const pointRollingSnap = pointRolling?.outcome.kind === "fallback" && pointRolling.outcome.fallback === "snap";
  const semanticPath = addSemanticPathUpdateTrack(
    current,
    next,
    tracks,
    context,
    timingContext
  );
  const nextNames = new Set(next.getAttributeNames());
  for (const name of current.getAttributeNames()) {
    if (!nextNames.has(name) && !(rollingTransform !== void 0 && name === "transform")) {
      current.removeAttribute(name);
    }
  }
  const attributes = [];
  for (const name of nextNames) {
    const target = next.getAttribute(name);
    const previous = current.getAttribute(name);
    if (target === previous) continue;
    if (semanticPath && name === "d") continue;
    if (pointRollingSnap && rollingPointGeometryAttributes.has(name) && target !== null) {
      current.setAttribute(name, target);
      continue;
    }
    if ((rollingTransform !== void 0 || rollingSnap) && name === "d" && target !== null) {
      current.setAttribute(name, target);
      continue;
    }
    const parsed = previous !== null && target !== null && motionAttributes.has(name) ? parseMotionAttribute(previous, target) : void 0;
    if (parsed) attributes.push({ name, ...parsed, target });
    else if (target !== null) current.setAttribute(name, target);
  }
  if (rollingTransform && timingContext && rolling) {
    current.setAttribute("data-ts-motion-role", timingContext.role);
    const apply = (values) => {
      current.setAttribute(
        "transform",
        `matrix(1 0 0 ${formatNumber(values[1] ?? 1)} ${formatNumber(values[0] ?? 0)} ${formatNumber(values[2] ?? 0)})`
      );
    };
    const from2 = [
      rollingTransform.x,
      rollingTransform.yScale,
      rollingTransform.y
    ];
    apply(from2);
    tracks.push({
      ...rolling.timing,
      values: bindMotionValues(void 0, from2, [0, 1, 0]),
      apply,
      finish() {
        current.removeAttribute("transform");
        current.removeAttribute("data-ts-motion-role");
      },
      cancel() {
        current.removeAttribute("data-ts-motion-role");
      }
    });
  }
  if (!attributes.length) return;
  timingContext ??= elementTimingContext(current, "update", context.scene);
  if (!timingContext) {
    finishMotionAttributes(current, attributes);
    return;
  }
  timing ??= pointRolling?.outcome.kind === "transform" ? pointRolling.timing : context.timingFor(timingContext);
  current.setAttribute("data-ts-motion-role", timingContext.role);
  const states = attributes.flatMap(
    (attribute) => elementValueStates(
      context.runtime,
      current,
      attribute.name,
      attribute.from
    )
  );
  const from = attributes.flatMap((attribute) => attribute.from);
  const to = attributes.flatMap((attribute) => attribute.to);
  tracks.push({
    ...timing,
    values: bindMotionValues(states, from, to),
    apply(values) {
      let offset = 0;
      for (const attribute of attributes) {
        const count = attribute.to.length;
        current.setAttribute(
          attribute.name,
          formatMotionAttribute(
            attribute.skeleton,
            values.slice(offset, offset + count)
          )
        );
        offset += count;
      }
    },
    finish() {
      finishMotionAttributes(current, attributes);
      current.removeAttribute("data-ts-motion-role");
    },
    cancel() {
      current.removeAttribute("data-ts-motion-role");
    }
  });
}
function addSemanticPathUpdateTrack(current, next, tracks, context, timingContext) {
  if (current.localName !== "path") return false;
  const key = current.getAttribute("data-ts-key");
  const targetPath = next.getAttribute("d");
  if (!key || !targetPath || !context.previousScene) return false;
  const previous = sceneMotionEntry(context.previousScene, key)?.metadata.path;
  const target = sceneMotionEntry(context.scene, key)?.metadata.path;
  const geometry = compatiblePathGeometry(previous, target);
  if (!geometry) return false;
  const resolvedContext = timingContext ?? elementTimingContext(current, "update", context.scene);
  if (!resolvedContext) return false;
  const sourceValues = livePathGeometryValues(
    current,
    geometry.source,
    context.runtime
  );
  current.setAttribute("data-ts-motion-role", resolvedContext.role);
  tracks.push(
    semanticPathTrack({
      element: current,
      sourceValues,
      target: geometry.target,
      targetPath,
      timing: context.timingFor(resolvedContext),
      runtime: context.runtime,
      finish() {
        current.removeAttribute("data-ts-motion-role");
      },
      cancel() {
        current.removeAttribute("data-ts-motion-role");
      }
    })
  );
  return true;
}
function semanticPathTrack(options) {
  const states = elementValueStates(
    options.runtime,
    options.element,
    "semantic-path",
    options.sourceValues
  );
  return {
    ...options.timing,
    values: bindMotionValues(
      states,
      options.sourceValues,
      options.target.values
    ),
    apply(values) {
      const path = options.target.project(values);
      if (path) options.element.setAttribute("d", path);
    },
    finish() {
      options.element.setAttribute("d", options.targetPath);
      options.finish();
    },
    cancel: options.cancel
  };
}
function translatedX(transform) {
  if (!transform) return void 0;
  const match = /^translate\(\s*(-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)\s*(?:[, ]\s*0(?:\.0+)?)?\s*\)$/i.exec(
    transform
  );
  if (!match) return void 0;
  const value = Number(match[1]);
  return Number.isFinite(value) ? value : void 0;
}
function addEnterMotionTrack(element, tracks, context) {
  const timingContext = elementTimingContext(element, "enter", context.scene);
  if (!timingContext) return;
  const timing = context.timingFor(timingContext);
  element.setAttribute("data-ts-motion-role", timingContext.role);
  const pointRolling = timingContext.point ? context.pathPlans.points.get(pointIdentity(timingContext.point)) : void 0;
  if (element.localName === "circle" && pointRolling) {
    if (pointRolling.outcome.kind === "fallback") {
      if (pointRolling.outcome.fallback === "snap") {
        element.removeAttribute("data-ts-motion-role");
        return;
      }
    } else {
      const targetX = numberAttribute(element, "cx");
      const targetY = numberAttribute(element, "cy");
      const { x, yScale, y } = pointRolling.outcome.transform;
      const from = [targetX + x, targetY * yScale + y];
      const to = [targetX, targetY];
      const apply = (values) => {
        element.setAttribute("cx", formatNumber(values[0] ?? targetX));
        element.setAttribute("cy", formatNumber(values[1] ?? targetY));
      };
      apply(from);
      tracks.push({
        ...pointRolling.timing,
        values: bindMotionValues(void 0, from, to),
        apply,
        finish() {
          apply(to);
          element.removeAttribute("data-ts-motion-role");
        },
        cancel() {
          element.removeAttribute("data-ts-motion-role");
        }
      });
      return;
    }
  }
  if (timingContext.role === "bar" && element.localName === "rect" && !element.closest("[data-ts-focus-retarget]")) {
    const horizontal = Boolean(element.closest("g.ts-chart__bar-x"));
    const targetX = numberAttribute(element, "x");
    const targetY = numberAttribute(element, "y");
    const targetWidth = numberAttribute(element, "width");
    const targetHeight = numberAttribute(element, "height");
    const baseline = resolveBarBaseline(
      context.scene,
      timingContext.point,
      horizontal,
      horizontal ? targetX : targetY + targetHeight
    );
    const names = horizontal ? ["x", "width"] : ["y", "height"];
    const from = [baseline, 0];
    const to = horizontal ? [targetX, targetWidth] : [targetY, targetHeight];
    const states2 = names.flatMap(
      (name, index) => elementValueStates(context.runtime, element, name, [from[index] ?? 0])
    );
    const apply = (values) => {
      element.setAttribute(names[0], formatNumber(values[0] ?? 0));
      element.setAttribute(names[1], formatNumber(values[1] ?? 0));
    };
    apply(from);
    tracks.push({
      ...timing,
      values: bindMotionValues(states2, from, to),
      apply,
      finish() {
        element.setAttribute("x", formatNumber(targetX));
        element.setAttribute("y", formatNumber(targetY));
        element.setAttribute("width", formatNumber(targetWidth));
        element.setAttribute("height", formatNumber(targetHeight));
        element.removeAttribute("data-ts-motion-role");
      },
      cancel() {
        element.removeAttribute("data-ts-motion-role");
      }
    });
    return;
  }
  const hierarchyGeometry = hierarchyRelatedGeometry(
    element,
    context.scene,
    context.previousScene,
    "enter"
  );
  if (hierarchyGeometry) {
    const sourceValues = livePathGeometryValues(
      hierarchyGeometry.relatedElement,
      hierarchyGeometry.source,
      context.runtime
    );
    const sourcePath = hierarchyGeometry.target.project(sourceValues);
    if (sourcePath) element.setAttribute("d", sourcePath);
    tracks.push(
      semanticPathTrack({
        element,
        sourceValues,
        target: hierarchyGeometry.target,
        targetPath: hierarchyGeometry.targetPath,
        timing,
        runtime: context.runtime,
        finish() {
        },
        cancel() {
        }
      })
    );
  }
  const targetOpacity = element.getAttribute("opacity");
  const opacity = finiteOpacity(targetOpacity);
  element.setAttribute("opacity", "0");
  const states = elementValueStates(context.runtime, element, "opacity", [0]);
  tracks.push({
    ...timing,
    values: bindMotionValues(states, [0], [opacity]),
    apply(values) {
      element.setAttribute("opacity", formatNumber(values[0] ?? 0));
    },
    finish() {
      if (targetOpacity === null) element.removeAttribute("opacity");
      else element.setAttribute("opacity", targetOpacity);
      element.removeAttribute("data-ts-motion-role");
    },
    cancel() {
      element.removeAttribute("data-ts-motion-role");
    }
  });
}
function addExitMotionTrack(element, tracks, context) {
  const retargetLayer = element.closest(
    "g[data-ts-focus-retarget]"
  );
  const cleanup = () => {
    element.remove();
    if (retargetLayer && !retargetLayer.children.length) {
      retargetLayer.setAttribute("visibility", "hidden");
    }
  };
  const timingContext = elementTimingContext(
    element,
    "exit",
    context.previousScene ?? context.scene
  );
  if (!timingContext) {
    cleanup();
    return;
  }
  const pointRolling = timingContext.point ? context.pathPlans.points.get(pointIdentity(timingContext.point)) : void 0;
  if (pointRolling?.outcome.kind === "fallback" && pointRolling.outcome.fallback === "snap") {
    cleanup();
    return;
  }
  if (element.localName === "circle" && pointRolling?.outcome.kind === "transform") {
    const startX = numberAttribute(element, "cx");
    const startY = numberAttribute(element, "cy");
    const { x, yScale, y } = pointRolling.outcome.transform;
    const targetX = startX - x;
    const targetY = (startY - y) / yScale;
    const apply = (values) => {
      element.setAttribute("cx", formatNumber(values[0] ?? targetX));
      element.setAttribute("cy", formatNumber(values[1] ?? targetY));
    };
    element.setAttribute("data-ts-motion-role", timingContext.role);
    tracks.push({
      ...pointRolling.timing,
      values: bindMotionValues(void 0, [startX, startY], [targetX, targetY]),
      apply,
      finish: cleanup,
      cancel: cleanup
    });
    return;
  }
  const hierarchyGeometry = hierarchyRelatedGeometry(
    element,
    context.previousScene,
    context.scene,
    "exit"
  );
  if (hierarchyGeometry) {
    const targetPath = hierarchyGeometry.target.project(
      hierarchyGeometry.target.values
    );
    if (targetPath) {
      const sourceValues = livePathGeometryValues(
        element,
        hierarchyGeometry.source,
        context.runtime
      );
      tracks.push(
        semanticPathTrack({
          element,
          sourceValues,
          target: hierarchyGeometry.target,
          targetPath,
          timing: context.timingFor(timingContext),
          runtime: context.runtime,
          finish() {
          },
          cancel() {
          }
        })
      );
    }
  }
  const target = Number(element.getAttribute("opacity") ?? 1);
  const opacity = Number.isFinite(target) ? target : 1;
  element.setAttribute("data-ts-motion-role", timingContext.role);
  const states = elementValueStates(context.runtime, element, "opacity", [
    opacity
  ]);
  tracks.push({
    ...context.timingFor(timingContext),
    values: bindMotionValues(states, [opacity], [0]),
    apply(values) {
      element.setAttribute("opacity", formatNumber(values[0] ?? 0));
    },
    finish: cleanup,
    cancel: cleanup
  });
}
function hierarchyRelatedGeometry(element, ownerScene, relatedScene, phase) {
  const relation = hierarchyMotionRelation(element, ownerScene, relatedScene);
  if (!relation) return void 0;
  const source = phase === "enter" ? relation.related.metadata.path : relation.owner.metadata.path;
  const target = phase === "enter" ? relation.owner.metadata.path : relation.related.metadata.path;
  const geometry = compatiblePathGeometry(source, target);
  if (!geometry) return void 0;
  return {
    ...geometry,
    relatedElement: relation.relatedElement,
    targetPath: phase === "enter" ? relation.ownerPath : relation.relatedPath
  };
}
function hierarchyMotionRelation(element, ownerScene, relatedScene) {
  if (element.localName !== "path" || !ownerScene || !relatedScene) {
    return void 0;
  }
  const key = element.getAttribute("data-ts-key");
  const ownerPath = element.getAttribute("d");
  if (!key || !ownerPath) return void 0;
  const owner = sceneMotionEntry(ownerScene, key);
  const hierarchy = owner?.metadata.hierarchy;
  if (!owner || !hierarchy) return void 0;
  const related = sceneMotionEntries(relatedScene);
  let ancestor;
  for (let index = hierarchy.ancestorIds.length - 1; index >= 0; index -= 1) {
    const ancestorId = hierarchy.ancestorIds[index];
    ancestor = related.find(
      (entry) => entry.metadata.hierarchy?.markId === hierarchy.markId && entry.metadata.hierarchy.id === ancestorId
    );
    if (ancestor) break;
  }
  if (!ancestor) return void 0;
  const root = element.closest("svg");
  const relatedElement = root ? [...root.querySelectorAll("path[data-ts-key]")].find(
    (candidate) => candidate.getAttribute("data-ts-key") === ancestor.node.key
  ) : void 0;
  const relatedPath = relatedElement?.getAttribute("d");
  if (!relatedElement || !relatedPath) return void 0;
  return {
    owner,
    related: ancestor,
    relatedElement,
    ownerPath,
    relatedPath
  };
}
const sceneMotionEntriesCache = /* @__PURE__ */ new WeakMap();
function sceneMotionEntries(scene) {
  const cached = sceneMotionEntriesCache.get(scene);
  if (cached) return cached;
  const entries = [];
  const visit = (nodes) => {
    for (const node of nodes) {
      const metadata = node[sceneMotionNode];
      if (metadata) entries.push({ node, metadata });
      if (node.kind === "group") visit(node.children);
    }
  };
  visit(scene.nodes);
  sceneMotionEntriesCache.set(scene, entries);
  return entries;
}
function sceneMotionEntry(scene, key) {
  return sceneMotionEntries(scene).find((entry) => entry.node.key === key);
}
function compatiblePathGeometry(source, target) {
  if (!source || !target || source.project !== target.project || source.values.length !== target.values.length || source.values.length === 0) {
    return void 0;
  }
  return { source, target };
}
function livePathGeometryValues(element, geometry, runtime) {
  const states = elementValueStates(
    runtime,
    element,
    "semantic-path",
    geometry.values
  );
  const staticPath = geometry.project(geometry.values);
  return staticPath && element.getAttribute("d") === staticPath ? geometry.values : states.map((state) => state.value);
}
function finiteOpacity(value) {
  const opacity = Number(value ?? 1);
  return Number.isFinite(opacity) ? opacity : 1;
}
function elementTimingContext(element, phase, scene) {
  if (element.closest("[data-ts-focus-retarget]")) {
    return guideOrMarkTimingContext(element, phase, scene);
  }
  const barGroup = element.closest(
    "g.ts-chart__bar-y, g.ts-chart__bar-x"
  );
  const lineGroup = element.closest("g.ts-chart__line");
  const group = barGroup ?? lineGroup;
  if (!group) return guideOrMarkTimingContext(element, phase, scene);
  const role = barGroup ? "bar" : "line";
  const root = element.closest("svg");
  const groups = root ? [
    ...root.querySelectorAll(
      role === "bar" ? "g.ts-chart__bar-y, g.ts-chart__bar-x" : "g.ts-chart__line"
    )
  ] : [group];
  const seriesIndex = Math.max(0, groups.indexOf(group));
  const seriesKey = group.getAttribute("data-ts-key") ?? `${role}:${seriesIndex}`;
  const key = element.getAttribute("data-ts-key") ?? (role === "line" ? seriesKey : `${seriesKey}:0`);
  const point = scene.points.find(
    (candidate) => candidate.key === key || key === `${candidate.key}:dot`
  );
  const rectangles = barGroup ? [...barGroup.children].filter((child) => child.localName === "rect") : [];
  const datumIndex = point?.datumIndex ?? Math.max(0, rectangles.indexOf(element));
  return {
    phase,
    role,
    key,
    markId: point?.markId ?? motionMarkId(scene, seriesKey),
    seriesKey,
    seriesIndex,
    datumIndex,
    datumCount: barGroup ? Math.max(1, rectangles.length) : 1,
    datum: point?.datum,
    point
  };
}
function guideOrMarkTimingContext(element, phase, scene) {
  const key = element.getAttribute("data-ts-key");
  if (!key) return void 0;
  const focusGuide = element.closest("g.ts-chart__crosshair");
  if (focusGuide) {
    const ownerKey2 = focusGuide.getAttribute("data-ts-key") ?? key;
    const markId2 = motionMarkId(scene, ownerKey2);
    return {
      phase,
      role: markMotionRole(focusGuide, element),
      key,
      markId: markId2,
      seriesKey: ownerKey2,
      seriesIndex: 0,
      datumIndex: 0,
      datumCount: 1,
      datum: void 0,
      point: void 0
    };
  }
  const presentationFocusLayer = element.closest(
    "g.ts-chart__focus-layer"
  );
  if (presentationFocusLayer && !presentationFocusLayer.hasAttribute("data-ts-focus-retarget")) {
    const focusPoints = collectMotionFocusLayers(scene.nodes).flatMap(
      (layer) => layer.focus?.points ?? []
    );
    const point2 = focusPoints.find(
      (candidate) => candidate.key === key || key === `${candidate.key}:dot`
    );
    if (!point2) return void 0;
    const markPoints2 = focusPoints.filter(
      (candidate) => candidate.markId === point2.markId
    );
    return {
      phase,
      role: markMotionRole(presentationFocusLayer, element),
      key,
      markId: point2.markId,
      seriesKey: `${point2.markId}:${motionGroupIdentity(point2)}`,
      seriesIndex: 0,
      datumIndex: point2.datumIndex,
      datumCount: Math.max(1, markPoints2.length),
      datum: point2.datum,
      point: point2
    };
  }
  const axes = element.closest("g.ts-chart__axes");
  const grid = element.closest("g.ts-chart__grid");
  const scaleId = guideScaleId(scene, key);
  const axis = scaleId ? guideScaleChannel(scene, scaleId) : void 0;
  if (scaleId && axis && (axes || grid)) {
    const role2 = grid ? "grid" : key === `${scaleId}-axis` ? "axis" : key.startsWith(`${scaleId}-tick-rule:`) ? "tick" : key.startsWith(`${scaleId}-tick-label:`) ? "tick-label" : key === `${scaleId}-label` ? "axis-label" : "axis";
    const parent = grid ?? axes;
    const prefix = role2 === "grid" ? `${scaleId}-grid:` : role2 === "tick" ? `${scaleId}-tick-rule:` : role2 === "tick-label" ? `${scaleId}-tick-label:` : key;
    const peers = parent ? [...parent.querySelectorAll("[data-ts-key]")].filter(
      (candidate) => candidate.getAttribute("data-ts-key")?.startsWith(prefix) ?? false
    ) : [element];
    return {
      phase,
      role: role2,
      key,
      axis,
      scaleId,
      seriesKey: `${role2}:${scaleId}`,
      seriesIndex: Math.max(0, Object.keys(scene.scales).indexOf(scaleId)),
      datumIndex: Math.max(0, peers.indexOf(element)),
      datumCount: Math.max(1, peers.length),
      datum: void 0,
      point: void 0
    };
  }
  const marks = element.closest("g.ts-chart__marks");
  if (!marks) return void 0;
  const focusLayer = element.closest("g.ts-chart__focus-layer");
  if (focusLayer && !focusLayer.hasAttribute("data-ts-focus-retarget")) {
    return void 0;
  }
  const focusContext = focusLayer ? retargetFocusContext(element, focusLayer, scene) : void 0;
  const point = focusContext?.point ?? motionPointForKey(scene.points, key);
  let owner = element;
  const ownerParent = focusLayer ?? marks;
  while (owner.parentElement && owner.parentNode !== ownerParent) {
    owner = owner.parentElement;
  }
  const ownerKey = owner.getAttribute("data-ts-key") ?? key;
  const markId = point?.markId ?? motionMarkId(scene, ownerKey);
  const role = markMotionRole(owner, element);
  const markPoints = markId ? (focusContext?.layer.focus?.points ?? scene.points).filter(
    (candidate) => candidate.markId === markId
  ) : [];
  const seriesKey = point ? `${point.markId}:${String(point.group ?? "")}` : ownerKey;
  return {
    phase,
    role,
    key,
    markId,
    seriesKey,
    seriesIndex: 0,
    datumIndex: point?.datumIndex ?? 0,
    datumCount: Math.max(1, markPoints.length),
    datum: point?.datum,
    point
  };
}
function guideScaleId(scene, key) {
  return Object.keys(scene.scales).filter(
    (id) => key === `${id}-axis` || key === `${id}-label` || key.startsWith(`${id}-tick-rule:`) || key.startsWith(`${id}-tick-label:`) || key.startsWith(`${id}-grid:`)
  ).sort((left, right) => right.length - left.length)[0];
}
function guideScaleChannel(scene, scaleId) {
  if (scaleId === "x" || scaleId === "y") return scaleId;
  const definition = motionSceneSource(scene)?.[0];
  return definition?.scales?.[scaleId]?.channel;
}
function retargetFocusContext(element, focusLayer, scene) {
  const layerKey = focusLayer.getAttribute("data-ts-key");
  if (!layerKey) return void 0;
  const layer = findSceneGroup(scene.nodes, layerKey);
  if (!layer?.focus?.retarget) return void 0;
  const prefix = `${layerKey}:selection:`;
  let current = element;
  let slot;
  while (current && current !== focusLayer) {
    const key = current.getAttribute("data-ts-key");
    if (key?.startsWith(prefix)) {
      const value = Number(key.slice(prefix.length).split(":")[0]);
      if (Number.isInteger(value) && value >= 0) slot = value;
      break;
    }
    current = current.parentElement;
  }
  return {
    layer,
    point: layer.focus.activePoints?.[slot ?? 0]
  };
}
function findSceneGroup(nodes, key) {
  for (const node of nodes) {
    if (node.kind !== "group") continue;
    if (node.key === key) return node;
    const nested = findSceneGroup(node.children, key);
    if (nested) return nested;
  }
  return void 0;
}
function motionPointForKey(points, key) {
  let match;
  for (const point of points) {
    if (key !== point.key && key !== `${point.key}:dot` && !key.startsWith(`${point.key}:`)) {
      continue;
    }
    if (!match || point.key.length > match.key.length) match = point;
  }
  return match;
}
function markMotionRole(owner, element) {
  let className = "";
  let current = element;
  while (current) {
    className += ` ${current.getAttribute("class") ?? ""}`;
    if (current === owner) break;
    current = current.parentElement;
  }
  if (className.includes("ts-chart__area") || className.includes("ts-chart__radial-area"))
    return "area";
  if (className.includes("ts-chart__bar")) return "bar";
  if (className.includes("ts-chart__arc")) return "arc";
  if (className.includes("ts-chart__arrow")) return "arrow";
  if (className.includes("ts-chart__band")) return "band";
  if (className.includes("ts-chart__dot")) return "dot";
  if (className.includes("ts-chart__facet")) return "facet";
  if (className.includes("ts-chart__frame")) return "frame";
  if (className.includes("ts-chart__geo")) return "geo";
  if (className.includes("ts-chart__hexagon")) return "hexagon";
  if (className.includes("ts-chart__line")) return "line";
  if (className.includes("ts-chart__link")) return "link";
  if (className.includes("ts-chart__text")) return "text";
  if (className.includes("ts-chart__rect")) return "rect";
  if (className.includes("ts-chart__rule")) return "rule";
  if (className.includes("ts-chart__tick")) return "tick";
  if (className.includes("ts-chart__vector")) return "vector";
  if (element.localName === "circle") return "dot";
  if (element.localName === "text") return "text";
  if (element.localName === "rect") return "rect";
  if (element.localName === "line") return "rule";
  if (element.localName === "path") return "area";
  return "mark";
}
function motionMarkId(scene, key) {
  const focusGuide = scene.focusGuides?.slice().sort((left, right) => right.key.length - left.key.length).find((guide) => key === guide.key || key.startsWith(`${guide.key}:`));
  if (focusGuide) return focusGuide.markId;
  const source = motionSceneSource(scene);
  const candidates = /* @__PURE__ */ new Set([
    ...scene.points.map((point) => point.markId),
    ...source?.[1].map((mark) => mark.id) ?? []
  ]);
  return [...candidates].sort((left, right) => right.length - left.length).find((candidate) => key === candidate || key.startsWith(`${candidate}:`));
}
function createPresentationTracks(root, scene, fromPoints, timingFor, setPresentationPoints, defaultPhase, runtime, rollingPlans) {
  const verticalBars = keyedElements(root, "g.ts-chart__bar-y > rect");
  const horizontalBars = keyedElements(root, "g.ts-chart__bar-x > rect");
  const pathGroups = keyedElementMap(
    root,
    "g.ts-chart__line, g.ts-chart__area, g.ts-chart__radial-area"
  );
  const elements = keyedElementMap(root, "[data-ts-key]");
  const targetByIdentity = new Map(
    scene.points.map((point) => [pointIdentity(point), point])
  );
  const fromByIdentity = new Map(
    fromPoints.map((point) => [pointIdentity(point), point])
  );
  const presented = new Map(
    fromPoints.map((point) => [pointIdentity(point), point])
  );
  const tracks = [];
  const series = [...new Set(scene.points.map((point) => point.markId))];
  const counts = /* @__PURE__ */ new Map();
  for (const point of scene.points) {
    counts.set(point.markId, (counts.get(point.markId) ?? 0) + 1);
  }
  for (const point of scene.points) {
    const identity = pointIdentity(point);
    const previous = fromByIdentity.get(identity);
    const vertical = verticalBars.has(point.key);
    const horizontal = horizontalBars.has(point.key);
    if (!vertical && !horizontal) {
      const rolling = rollingPlans?.points.get(identity);
      if (rolling?.outcome.kind === "transform") {
        const transform = rolling.outcome.transform;
        presented.set(identity, {
          ...point,
          x: point.x + transform.x,
          y: point.y * transform.yScale + transform.y
        });
        continue;
      }
      if (rolling?.outcome.kind === "fallback" && rolling.outcome.fallback === "snap") {
        presented.set(identity, point);
        continue;
      }
      if (seriesElementForPoint(point, pathGroups)) {
        presented.set(
          identity,
          previous ? { ...point, x: previous.x, y: previous.y } : point
        );
        continue;
      }
      if (!previous || previous.x === point.x && previous.y === point.y) {
        presented.set(identity, point);
        continue;
      }
      const element = elements.get(point.key) ?? elements.get(`${point.key}:dot`);
      const context = element ? elementTimingContext(element, "update", scene) : void 0;
      if (!context) {
        presented.set(identity, point);
        continue;
      }
      presented.set(identity, { ...point, x: previous.x, y: previous.y });
      const states2 = pointValueStates(runtime, identity, [
        previous.x,
        previous.y
      ]);
      tracks.push({
        ...timingFor(context),
        values: bindMotionValues(
          states2,
          [previous.x, previous.y],
          [point.x, point.y]
        ),
        apply(values) {
          presented.set(identity, {
            ...point,
            x: values[0] ?? point.x,
            y: values[1] ?? point.y
          });
        },
        finish() {
          presented.set(identity, point);
        }
      });
      continue;
    }
    const phase = previous ? "update" : "enter";
    const baseline = resolveBarBaseline(
      scene,
      point,
      horizontal,
      horizontal ? point.x : point.y
    );
    const start = previous ?? (horizontal ? { ...point, x: baseline } : { ...point, y: baseline });
    presented.set(identity, { ...point, x: start.x, y: start.y });
    const timing = timingFor({
      phase: defaultPhase === "enter" ? "enter" : phase,
      role: "bar",
      key: point.key,
      markId: point.markId,
      seriesKey: point.markId,
      seriesIndex: Math.max(0, series.indexOf(point.markId)),
      datumIndex: point.datumIndex,
      datumCount: counts.get(point.markId) ?? 1,
      datum: point.datum,
      point
    });
    const states = pointValueStates(runtime, identity, [start.x, start.y]);
    tracks.push({
      ...timing,
      values: bindMotionValues(states, [start.x, start.y], [point.x, point.y]),
      apply(values) {
        presented.set(identity, {
          ...point,
          x: values[0] ?? point.x,
          y: values[1] ?? point.y
        });
      },
      finish() {
        presented.set(identity, point);
      }
    });
  }
  if (rollingPlans) {
    for (const planned of rollingPlans.elements.values()) {
      if (planned.outcome.kind !== "transform") continue;
      const transform = planned.outcome.transform;
      const exiting = planned.previousPoints.flatMap((point) => {
        const identity = pointIdentity(point);
        if (targetByIdentity.has(identity)) return [];
        const start = fromByIdentity.get(identity) ?? point;
        return [
          {
            identity,
            point,
            state: runtime.points.get(identity),
            target: {
              x: start.x - transform.x,
              y: (start.y - transform.y) / transform.yScale
            }
          }
        ];
      });
      const from = [transform.x, transform.yScale, transform.y];
      const apply = (values) => {
        for (const point of planned.points) {
          const x = point.x + (values[0] ?? 0);
          const y = point.y * (values[1] ?? 1) + (values[2] ?? 0);
          presented.set(pointIdentity(point), { ...point, x, y });
        }
        for (const entry of exiting) {
          const x = entry.target.x + (values[0] ?? 0);
          const y = entry.target.y * (values[1] ?? 1) + (values[2] ?? 0);
          presented.set(entry.identity, { ...entry.point, x, y });
        }
      };
      apply(from);
      const cleanupExiting = () => {
        for (const entry of exiting) {
          presented.delete(entry.identity);
          if (runtime.points.get(entry.identity) === entry.state) {
            runtime.points.delete(entry.identity);
          }
        }
      };
      tracks.push({
        ...planned.timing,
        values: bindMotionValues(void 0, from, [0, 1, 0]),
        apply,
        finish() {
          for (const point of planned.points) {
            presented.set(pointIdentity(point), point);
          }
          cleanupExiting();
        },
        cancel: cleanupExiting
      });
    }
  }
  const pathSeries = /* @__PURE__ */ new Map();
  for (const point of scene.points) {
    if (verticalBars.has(point.key) || horizontalBars.has(point.key)) continue;
    const rolling = rollingPlans?.points.get(pointIdentity(point));
    if (rolling?.outcome.kind === "transform" || rolling?.outcome.kind === "fallback" && rolling.outcome.fallback === "snap") {
      continue;
    }
    const pathSeriesEntry = seriesElementForPoint(point, pathGroups);
    if (!pathSeriesEntry) continue;
    const seriesKey = pathSeriesEntry[0];
    const points = pathSeries.get(seriesKey);
    if (points) points.push(point);
    else pathSeries.set(seriesKey, [point]);
  }
  for (const [seriesKey, points] of pathSeries) {
    const previous = points.map(
      (point) => fromByIdentity.get(pointIdentity(point))
    );
    const group = pathGroups.get(seriesKey);
    const role = group ? markMotionRole(group, group) : "line";
    const timing = timingFor({
      phase: defaultPhase === "enter" ? "enter" : previous.some(Boolean) ? "update" : "enter",
      role,
      key: seriesKey,
      markId: points[0]?.markId ?? motionMarkId(scene, seriesKey),
      seriesKey,
      seriesIndex: Math.max(0, series.indexOf(seriesKey)),
      datumIndex: 0,
      datumCount: points.length,
      datum: void 0,
      point: void 0
    });
    const from = [];
    const to = [];
    const states = [];
    points.forEach((point, index) => {
      const start = previous[index] ?? point;
      from.push(start.x, start.y);
      to.push(point.x, point.y);
      states.push(
        ...pointValueStates(runtime, pointIdentity(point), [start.x, start.y])
      );
    });
    tracks.push({
      ...timing,
      values: bindMotionValues(states, from, to),
      apply(values) {
        points.forEach((point, index) => {
          presented.set(pointIdentity(point), {
            ...point,
            x: values[index * 2] ?? point.x,
            y: values[index * 2 + 1] ?? point.y
          });
        });
      },
      finish() {
        points.forEach((point) => presented.set(pointIdentity(point), point));
      }
    });
  }
  for (const point of fromPoints) {
    const identity = pointIdentity(point);
    if (targetByIdentity.has(identity)) continue;
    const rolling = rollingPlans?.points.get(identity);
    if (rolling?.outcome.kind === "transform") continue;
    if (rolling?.outcome.kind === "fallback" && rolling.outcome.fallback === "snap") {
      presented.delete(identity);
      runtime.points.delete(identity);
      continue;
    }
    const element = elements.get(point.key) ?? elements.get(`${point.key}:dot`);
    const pathSeriesEntry = seriesElementForPoint(point, pathGroups);
    const role = verticalBars.has(point.key) || horizontalBars.has(point.key) ? "bar" : pathSeriesEntry ? markMotionRole(pathSeriesEntry[1], pathSeriesEntry[1]) : element ? markMotionRole(element, element) : "mark";
    const state = runtime.points.get(identity);
    const cleanup = () => {
      presented.delete(identity);
      if (runtime.points.get(identity) === state) {
        runtime.points.delete(identity);
      }
    };
    tracks.push({
      ...timingFor({
        phase: "exit",
        role,
        key: point.key,
        markId: point.markId,
        seriesKey: point.markId,
        seriesIndex: Math.max(0, series.indexOf(point.markId)),
        datumIndex: point.datumIndex,
        datumCount: 1,
        datum: point.datum,
        point
      }),
      values: bindMotionValues(void 0, [0], [1]),
      apply() {
      },
      finish: cleanup,
      cancel: cleanup
    });
  }
  const publish = () => setPresentationPoints?.([...presented.values()]);
  publish();
  return { tracks, publish };
}
function keyedElements(root, selector) {
  return new Set(keyedElementMap(root, selector).keys());
}
function keyedElementMap(root, selector) {
  const result = /* @__PURE__ */ new Map();
  for (const element of root.querySelectorAll(selector)) {
    const key = element.getAttribute("data-ts-key");
    if (key && !result.has(key)) result.set(key, element);
  }
  return result;
}
function pointIdentity(point) {
  return `${point.markId}\0${point.key}`;
}
function motionGroupIdentity(point) {
  return valueKey(point.group);
}
function seriesElementForPoint(point, series) {
  const entries = [...series.entries()];
  const keyed = entries.filter(([key]) => point.key === key || point.key.startsWith(`${key}:`)).sort(([left], [right]) => right.length - left.length)[0];
  if (keyed) return keyed;
  return entries.filter(
    ([key]) => key === point.markId || key.startsWith(`${point.markId}:`)
  ).sort(([left], [right]) => right.length - left.length)[0];
}
function finishMotionAttributes(element, attributes) {
  for (const attribute of attributes) {
    if (attribute.target === null) element.removeAttribute(attribute.name);
    else element.setAttribute(attribute.name, attribute.target);
  }
}
function parseMotionAttribute(previous, next) {
  const from = extractMotionNumbers(previous);
  const to = extractMotionNumbers(next);
  if (from.skeleton !== to.skeleton || from.values.length !== to.values.length || !from.values.length) {
    return void 0;
  }
  return { skeleton: to.skeleton, from: from.values, to: to.values };
}
function formatMotionAttribute(skeleton, values) {
  let index = 0;
  return skeleton.replaceAll("#", () => formatNumber(values[index++] ?? 0));
}
function extractMotionNumbers(value) {
  const values = [];
  const skeleton = value.replace(
    /-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?/gi,
    (match) => {
      values.push(Number(match));
      return "#";
    }
  );
  return { skeleton, values };
}
function elementValueStates(runtime, element, name, values) {
  let attributes = runtime.elements.get(element);
  if (!attributes) {
    attributes = /* @__PURE__ */ new Map();
    runtime.elements.set(element, attributes);
  }
  let states = attributes.get(name);
  if (!states || states.length !== values.length) {
    states = values.map((value) => ({ value, velocity: 0 }));
    attributes.set(name, states);
  }
  return states;
}
function pointValueStates(runtime, key, values) {
  let states = runtime.points.get(key);
  if (!states || states.length !== values.length) {
    states = values.map((value) => ({ value, velocity: 0 }));
    runtime.points.set(key, states);
  }
  return states;
}
function bindMotionValues(states, from, to) {
  return to.map((target, index) => {
    const source = from[index] ?? target;
    const state = states?.[index] ?? { value: source, velocity: 0 };
    if (!Number.isFinite(state.value) || Math.abs(state.value - source) > 2e-3) {
      state.value = source;
      state.velocity = 0;
    }
    return {
      state,
      from: state.value,
      to: target,
      velocity: state.velocity
    };
  });
}
function indexMotionChildren(children) {
  const result = /* @__PURE__ */ new Map();
  motionIdentities(children).forEach((identity, index) => {
    const child = children[index];
    if (child) result.set(identity, child);
  });
  return result;
}
function motionIdentities(children) {
  const counts = /* @__PURE__ */ new Map();
  return children.map((child) => {
    const key = child.getAttribute("data-ts-key");
    if (key) return `key:${key}`;
    const count = counts.get(child.localName) ?? 0;
    counts.set(child.localName, count + 1);
    return `tag:${child.localName}:${count}`;
  });
}
function resolveTiming(options, context, definitions, overrides) {
  const baseDuration = options.transition.type === "tween" ? options.transition.duration : defaultDuration;
  const automaticDelay = context.role === "bar" && context.phase === "enter" ? baseDuration * defaultStaggerRatio * context.datumIndex / Math.max(1, context.datumCount) : 0;
  let delay = automaticDelay;
  let transition = options.transition;
  let path = "morph";
  let disabled = false;
  const apply = (definition) => {
    const authored = typeof definition === "function" ? definition(context) : definition;
    if (authored === void 0) return;
    if (authored === false) {
      delay = automaticDelay;
      transition = options.transition;
      path = "morph";
      disabled = true;
      return;
    }
    disabled = false;
    const authoredDelay = typeof authored.delay === "function" ? authored.delay(context) : authored.delay;
    if (authoredDelay !== void 0) {
      delay = nonNegative(authoredDelay, delay);
    }
    if (authored.path !== void 0) path = authored.path;
    transition = resolveTransition(
      authored.transition,
      transition.type === "tween" ? transition.duration : defaultDuration,
      void 0,
      transition
    );
  };
  apply(definitions?.default);
  if (context.markId && definitions?.marks) {
    const markId = Object.keys(definitions.marks).filter(
      (candidate) => context.markId === candidate || context.markId?.startsWith(`${candidate}:`)
    ).sort((left, right) => right.length - left.length)[0];
    if (markId) apply(definitions.marks[markId]);
  }
  const guideId = context.scaleId ?? context.axis;
  if (guideId) {
    apply(definitions?.guides?.[`axis:${guideId}`]);
    if (context.role !== "axis") {
      apply(definitions?.guides?.[`${context.role}:${guideId}`]);
    }
  }
  apply(overrides?.default);
  if (context.markId && overrides?.marks) {
    const markId = Object.keys(overrides.marks).filter(
      (candidate) => context.markId === candidate || context.markId?.startsWith(`${candidate}:`)
    ).sort((left, right) => right.length - left.length)[0];
    if (markId) apply(overrides.marks[markId]);
  }
  if (context.phase === "update" && transition.type === "spring") delay = 0;
  return { disabled, delay, transition, path };
}
function createTimingResolver(options, scene, overrides) {
  const definitions = motionDefinitions(scene);
  const cache = /* @__PURE__ */ new Map();
  return (context) => {
    const key = `${context.phase}\0${context.role}\0${context.key}`;
    const existing = cache.get(key);
    if (existing) return existing;
    const timing = resolveTiming(options, context, definitions, overrides);
    cache.set(key, timing);
    return timing;
  };
}
function motionDefinitions(scene) {
  const source = motionSceneSource(scene);
  const marks = {};
  let defaultDefinition;
  if (source) {
    const [definition, initialized] = source;
    defaultDefinition = definition.motion;
    initialized.forEach((mark, index) => {
      const authored = mark.motion ?? definition.marks[index]?.motion;
      if (authored !== void 0) marks[mark.id] = authored;
    });
  }
  for (const guide of scene.focusGuides ?? []) {
    if (guide.motion !== void 0) {
      marks[guide.markId] = guide.motion;
    }
  }
  const guides = {};
  if (source) {
    const [definition] = source;
    const configuredScales = definition.scales ?? {
      x: definition.x,
      y: definition.y
    };
    for (const [scaleId, configured] of Object.entries(configuredScales)) {
      const presentation = !configured || configured.axis === false ? void 0 : configured.axis ?? {};
      if (presentation?.motion !== void 0) {
        guides[`axis:${scaleId}`] = presentation.motion;
      }
      if (presentation?.ticks && presentation.ticks.motion !== void 0) {
        guides[`tick:${scaleId}`] = presentation.ticks.motion;
      }
      if (presentation?.tickLabels && presentation.tickLabels.motion !== void 0) {
        guides[`tick-label:${scaleId}`] = presentation.tickLabels.motion;
      }
      if (typeof presentation?.label === "object" && presentation.label.motion !== void 0) {
        guides[`axis-label:${scaleId}`] = presentation.label.motion;
      }
    }
  }
  const hasMarks = Object.keys(marks).length > 0;
  const hasGuides = Object.keys(guides).length > 0;
  if (defaultDefinition === void 0 && !hasMarks && !hasGuides) {
    return void 0;
  }
  return {
    ...defaultDefinition === void 0 ? {} : { default: defaultDefinition },
    ...hasMarks ? { marks } : {},
    ...hasGuides ? { guides } : {}
  };
}
function motionSceneSource(scene) {
  return scene[chartSceneSource];
}
function resolveBarBaseline(scene, point, horizontal, fallback) {
  const scale = scene.scales[horizontal ? "x" : "y"];
  const value = horizontal ? point?.x1Value : point?.y1Value;
  if (!scale || value === void 0) return fallback;
  const baseline = scale.map(value);
  return Number.isFinite(baseline) ? baseline : fallback;
}
function runTracks(root, tracks, lifecycle = {}) {
  const activeTracks = tracks.filter((track) => {
    if (!track.disabled) return true;
    completeMotionTrack(track);
    return false;
  });
  if (activeTracks.length !== tracks.length) lifecycle.publish?.();
  if (!activeTracks.length) {
    lifecycle.finish?.();
    return () => {
    };
  }
  const view = root.ownerDocument.defaultView;
  const requestFrame = view?.requestAnimationFrame?.bind(view);
  const cancelFrame = view?.cancelAnimationFrame?.bind(view);
  if (!requestFrame || !cancelFrame) {
    activeTracks.forEach(completeMotionTrack);
    lifecycle.finish?.();
    return () => {
    };
  }
  const safetyLimit = Math.max(
    ...activeTracks.map(
      (track) => track.delay + (track.transition.type === "tween" ? track.transition.duration : springSafetyLimit)
    )
  );
  if (safetyLimit <= 0) {
    activeTracks.forEach(completeMotionTrack);
    lifecycle.finish?.();
    return () => {
    };
  }
  let frame = 0;
  let start;
  let cancelled = false;
  const finished = /* @__PURE__ */ new Set();
  root.dataset.tsMotionState = "running";
  root.dataset.tsMotionProgress = "0";
  const tick = (time) => {
    if (cancelled) return;
    start ??= time;
    const elapsed = time - start;
    for (const track of activeTracks) {
      if (finished.has(track)) continue;
      if (sampleMotionTrack(track, elapsed)) {
        completeMotionTrack(track);
        finished.add(track);
      }
    }
    lifecycle.publish?.();
    root.dataset.tsMotionProgress = String(finished.size / activeTracks.length);
    if (finished.size < activeTracks.length && elapsed < safetyLimit) {
      frame = requestFrame(tick);
      return;
    }
    for (const track of activeTracks) {
      if (!finished.has(track)) completeMotionTrack(track);
    }
    lifecycle.finish?.();
    root.dataset.tsMotionState = "finished";
    root.dataset.tsMotionProgress = "1";
  };
  frame = requestFrame(tick);
  return () => {
    if (cancelled) return;
    cancelled = true;
    cancelFrame(frame);
    activeTracks.forEach((track) => {
      if (!finished.has(track)) track.cancel?.();
    });
    lifecycle.publish?.();
    root.dataset.tsMotionState = "cancelled";
  };
}
function sampleMotionTrack(track, elapsed) {
  const localElapsed = elapsed - track.delay;
  if (localElapsed < 0) {
    track.apply(track.values.map((binding) => binding.state.value));
    return false;
  }
  if (track.transition.type === "tween") {
    const duration = track.transition.duration;
    if (duration <= 0) return true;
    const progress = Math.max(0, Math.min(1, localElapsed / duration));
    const eased = track.transition.easing(progress);
    const slope = easingSlope(track.transition.easing, progress);
    const seconds = duration / 1e3;
    const values2 = track.values.map((binding) => {
      const delta = binding.to - binding.from;
      binding.state.value = binding.from + delta * eased;
      binding.state.velocity = progress >= 1 ? 0 : delta * slope / seconds;
      return binding.state.value;
    });
    track.apply(values2);
    return progress >= 1;
  }
  let done = true;
  const spring = track.transition.spring;
  const values = track.values.map((binding) => {
    const sample = spring.sample(localElapsed, {
      from: binding.from,
      to: binding.to,
      velocity: binding.velocity
    });
    binding.state.value = sample.value;
    binding.state.velocity = sample.velocity;
    done &&= sample.done;
    return sample.value;
  });
  track.apply(values);
  return done || localElapsed >= springSafetyLimit;
}
function completeMotionTrack(track) {
  const values = track.values.map((binding) => {
    binding.state.value = binding.to;
    binding.state.velocity = 0;
    return binding.to;
  });
  track.apply(values);
  track.finish();
}
function easingSlope(easing, progress) {
  const step = 1e-4;
  const before = Math.max(0, progress - step);
  const after = Math.min(1, progress + step);
  if (after === before) return 0;
  return (easing(after) - easing(before)) / (after - before);
}
function resolveTransition(transition, fallbackDuration, fallbackEasing, fallback) {
  if (!transition && fallback) return fallback;
  if (transition?.type === "spring") {
    const {
      type: _type,
      respectReducedMotion: _reduced,
      ...options
    } = transition;
    return {
      type: "spring",
      spring: createChartSpring({
        ...fallback?.type === "spring" ? fallback.spring.options : {},
        ...options
      })
    };
  }
  return {
    type: "tween",
    duration: nonNegative(
      transition?.duration,
      fallback?.type === "tween" ? fallback.duration : fallbackDuration
    ),
    easing: transition?.easing === void 0 && fallback?.type === "tween" ? fallback.easing : resolveEasing(transition?.easing ?? fallbackEasing)
  };
}
function resolveEasing(easing) {
  if (typeof easing === "function") return easing;
  switch (easing) {
    case "linear":
      return (progress) => progress;
    case "ease-in":
      return (progress) => progress * progress;
    case "ease-in-out":
      return (progress) => progress < 0.5 ? 2 * progress * progress : 1 - Math.pow(-2 * progress + 2, 2) / 2;
    case "ease":
      return cubicBezier(0.25, 0.1, 0.25, 1);
    case "ease-out":
      return (progress) => 1 - (1 - progress) * (1 - progress);
    default:
      return defaultEasing;
  }
}
function cubicBezier(x1, y1, x2, y2) {
  const sample = (time, first, second) => {
    const inverse = 1 - time;
    return 3 * inverse * inverse * time * first + 3 * inverse * time * time * second + time * time * time;
  };
  return (progress) => {
    let low = 0;
    let high = 1;
    let time = progress;
    for (let iteration = 0; iteration < 12; iteration++) {
      time = (low + high) / 2;
      if (sample(time, x1, x2) < progress) low = time;
      else high = time;
    }
    return sample(time, y1, y2);
  };
}
function numberAttribute(element, name) {
  const value = Number(element.getAttribute(name));
  return Number.isFinite(value) ? value : 0;
}
function nonNegative(value, fallback) {
  return Number.isFinite(value) ? Math.max(0, value) : fallback;
}
function formatNumber(value) {
  return String(Math.round(value * 1e3) / 1e3);
}
export {
  motion,
  stagger
};