diff --git a/.changeset/fix-series-state-effect-root.md b/.changeset/fix-series-state-effect-root.md new file mode 100644 index 000000000..74f51974a --- /dev/null +++ b/.changeset/fix-series-state-effect-root.md @@ -0,0 +1,7 @@ +--- +'layerchart': patch +--- + +fix(SeriesState): Avoid `derived_inert` crash when chart unmounts under a `` + +The `selectedKeys` sync effect was wrapped in `$effect.root`, creating an isolated scope that survived chart unmount. When the parent chart was destroyed (e.g. an example reloading inside the docs `` after an async `$derived` re-evaluated), the `#series` derived became inert while the orphaned effect kept reading it — producing `Reading a derived belonging to a now-destroyed effect may result in stale values` warnings followed by `TypeError: e.some is not a function`. The effect now lives in the constructor, scoped to the component that instantiated `SeriesState`, so it is torn down with the chart. diff --git a/.changeset/fix-tween-on-mount.md b/.changeset/fix-tween-on-mount.md new file mode 100644 index 000000000..631e956d9 --- /dev/null +++ b/.changeset/fix-tween-on-mount.md @@ -0,0 +1,11 @@ +--- +'layerchart': patch +--- + +fix(Arc, RectClipPath, ChartClipPath): Restore on-mount tween animations + +Two related regressions introduced in the layer-component split (#848) prevented `motion` + `initial*` props from animating on mount. + +**`Arc`** — `motion`, `value`, `initialValue` and the rest of Arc's geometry props (`domain`, `range`, `startAngle`, `endAngle`, `innerRadius`, `outerRadius`, `cornerRadius`, `padAngle`, `track*`, `offset`) were not destructured in `Arc.base.svelte`, so they leaked through `{...restProps}` onto the inner ``. The forwarded `motion` made `Path` *also* tween the path-string on top of the end-angle tween that `ArcState` already drives, producing visibly wrong arcs (NaN coordinates, runaway radii). They are now extracted and passed explicitly to `ArcState`. + +**`RectClipPath` / `ChartClipPath`** — `motion`, `initialX`, `initialY`, `initialWidth`, `initialHeight` were declared on the type but never consumed: the path was a plain `$derived` of the static `x`/`y`/`width`/`height` props, so passing `` rendered the final width on mount with no animation. Each dimension now flows through its own `createMotion` (using the corresponding `initial*` value as the animation start), and the path is built from the animated values. diff --git a/.changeset/path-lazy-feature-allocation.md b/.changeset/path-lazy-feature-allocation.md new file mode 100644 index 000000000..e90076e9c --- /dev/null +++ b/.changeset/path-lazy-feature-allocation.md @@ -0,0 +1,25 @@ +--- +'layerchart': patch +--- + +perf: Reduce per-tick reactive overhead in `Path` / `Link` (force-simulation graphs) + +In mark-heavy scenes (force simulations with hundreds of links flowing through `Link → Path`) several reactive structures unconditionally subscribed every `` template updater to props that don't change on a tick, causing per-frame work to scale with the number of props × the number of marks. Each fix below is independent; together they take the lattice (n=20, 760 links) example from ~5–6 fps to ~9 fps during simulation. + +**`PathState.tweenedPathData` now reads only `pathData`, not all Path props.** +Pre-fix, the getter resolved `pathData` via `getProps()`, a function that constructs an object literal of every reactive Path prop. Each read of `tweenedPathData` (i.e. each per-tick `` update) therefore subscribed the updater to every Path prop and re-read all of them. `PathState` now takes a dedicated `getPathData` getter alongside `getProps`, and the hot-path tween / DOM read only touches `pathData`. `Path.svg.svelte` and `Path.canvas.svelte` pass them as separate getters. + +**`Link.base.svelte` passes a stable `getPathData` function rather than `motionPath.current` directly.** +Reading `motionPath.current` from `Link.base.svelte`'s template subscribed the entire `` block to every tick, forcing the parent's prop spread (`{...restProps}`) and `cls(...)` evaluation to re-run on every change. Passing a stable function reference moves the per-tick read inside ``'s own template, keeping `Link.base.svelte` stable. Requires the new `pathData?: string | (() => string)` form on `Path`. + +**`Path.svg.svelte` allocates `draw`-related state lazily.** + +- `endPoint = createControlledMotion(..., { type: 'none' })` was created for every `Path`, even when no `draw` transition was configured. Now only created when `draw` is set. +- The `$effect` that tracked `tweenedPathData` for `startContent` / `endContent` positioning ran on every `Path`, even when neither prop was provided. Now only registered when at least one is set. +- `drawKey` is only ever set when `draw` is configured, so the `{#key c.drawKey}` block is a no-op for paths without a draw transition. The block stays unconditional — splitting it behind `{#if draw}` showed no measurable benefit over leaving the inert subscription in place. + +**`Path.svg.svelte` extracts styling props out of `...rest`.** +`pathData`, `class`, `fill` / `fillOpacity` / `stroke` / `strokeOpacity` / `strokeWidth` / `opacity` and `motion` are now destructured out of `$props()` rather than left in `...rest`, so the `` element's `{...rest}` spread doesn't re-evaluate every frame when those props change (`pathData` changes on every force-sim tick; `class` is typically a fresh `cls(...)` string per parent render). + +**`Link.base.svelte` drops a redundant prop spread.** +Removed `{...extractLayerProps(restProps, 'lc-link')}` before `{...restProps}` — the call's only contribution (`class`) was being immediately overridden by the explicit `class={cls('lc-link', …)}` that follows, making the spread pure overhead. diff --git a/.changeset/skip-empty-markinfo-effect.md b/.changeset/skip-empty-markinfo-effect.md new file mode 100644 index 000000000..7859c4c34 --- /dev/null +++ b/.changeset/skip-empty-markinfo-effect.md @@ -0,0 +1,9 @@ +--- +'layerchart': patch +--- + +perf: Skip mark-info `$effect` for pixel-mode primitives + +`registerComponent` now probes `markInfo()` once at construction; if the result is initially empty (pixel-mode primitives where `cx`/`cy`/`r`/etc. are numbers rather than string/function accessors), it skips creating the tracking `$effect` entirely. Saves one effect frame per primitive — adds up in mark-heavy scenes (force simulations, scatter plots with hundreds of nodes). + +Trade-off: a primitive that starts in pixel mode and later flips to data mode at runtime (e.g. `cx` mutates from a number to a string) will not register a mark. Mark mode is typically static; if a chart needs runtime data-mode marks, define an explicit `series` on the chart instead. diff --git a/bundle-analyzer/bundle-scenarios.ts b/bundle-analyzer/bundle-scenarios.ts index 43e032454..da93cfcd8 100644 --- a/bundle-analyzer/bundle-scenarios.ts +++ b/bundle-analyzer/bundle-scenarios.ts @@ -1749,28 +1749,30 @@ const INDIVIDUAL_COMPONENTS: string[] = [ 'AnnotationPoint', 'AnnotationRange', 'Arc', + 'ArcChart', 'ArcLabel', 'Area', + 'AreaChart', 'Axis', 'Bar', + 'BarChart', 'Bars', 'Blur', - 'BoxPlot', 'Bounds', + 'BoxPlot', 'BrushContext', 'Calendar', 'Canvas', 'Cell', 'Chart', + 'ChartClipPath', 'ChartCore', 'Chord', - 'ChartClipPath', 'Circle', 'CircleClipPath', 'CircleLegend', 'ClipPath', 'ColorRamp', - 'Connector', 'Contour', 'Dagre', 'Density', @@ -1778,6 +1780,7 @@ const INDIVIDUAL_COMPONENTS: string[] = [ 'ForceSimulation', 'Frame', 'GeoCircle', + 'GeoClipPath', 'GeoEdgeFade', 'GeoLegend', 'GeoPath', @@ -1799,6 +1802,7 @@ const INDIVIDUAL_COMPONENTS: string[] = [ 'Legend', 'Line', 'LinearGradient', + 'LineChart', 'Link', 'Month', 'MotionPath', @@ -1807,6 +1811,7 @@ const INDIVIDUAL_COMPONENTS: string[] = [ 'Path', 'Pattern', 'Pie', + 'PieChart', 'Point', 'Points', 'Polygon', @@ -1817,14 +1822,15 @@ const INDIVIDUAL_COMPONENTS: string[] = [ 'Ribbon', 'Rule', 'Sankey', + 'ScatterChart', 'Spline', 'Svg', 'Text', 'Threshold', 'TileImage', 'Tooltip', - 'TransformContext', 'Trail', + 'TransformContext', 'Tree', 'Treemap', 'Vector', diff --git a/bundle-analyzer/generate-pr-comment.js b/bundle-analyzer/generate-pr-comment.js index 2de8c4a08..e48bf9794 100644 --- a/bundle-analyzer/generate-pr-comment.js +++ b/bundle-analyzer/generate-pr-comment.js @@ -50,13 +50,30 @@ function formatPercent(percent) { return `${sign}${percent.toFixed(1)}%`; } -function getStatusIcon(status, sizeDiff) { +// Percent threshold above which a change is considered "large" (red/green). +// Smaller changes still count as significant (per the size/percent gates in +// analyzeChanges) but render as yellow to signal they're within tolerance. +const LARGE_CHANGE_THRESHOLD_PERCENT = 1; + +function getStatusIcon(status, sizeDiff, sizePercent) { if (status === "changed") { - return sizeDiff > 0 ? "\u{1F534}" : sizeDiff < 0 ? "\u{1F7E2}" : "\u27A1\uFE0F"; + if (sizeDiff === 0) return "\u27A1\uFE0F"; + if (Math.abs(sizePercent) < LARGE_CHANGE_THRESHOLD_PERCENT) { + return "\u{1F7E1}"; + } + return sizeDiff > 0 ? "\u{1F534}" : "\u{1F7E2}"; } return "\u27A1\uFE0F"; } +function isWarning(c) { + return ( + c.status === "changed" && + c.sizeDiff > 0 && + Math.abs(c.sizePercent) >= LARGE_CHANGE_THRESHOLD_PERCENT + ); +} + function analyzeChanges(prReport, targetReport) { /** @type {ScenarioDiff[]} */ const changes = []; @@ -162,6 +179,9 @@ function generateComment(changes, hasBaseline = true) { if (!byGroup.has(g)) byGroup.set(g, []); byGroup.get(g).push(s); } + for (const rows of byGroup.values()) { + rows.sort((a, b) => b.currentSize - a.currentSize); + } const expandedGroups = new Set(["Base (agnostic)", "Base (layer-specific)", "Core"]); @@ -179,10 +199,13 @@ function generateComment(changes, hasBaseline = true) { } if (components.length > 0) { + const sortedComponents = [...components].sort( + (a, b) => b.currentSize - a.currentSize + ); comment += "
\nIndividual Components\n\n"; comment += "| Component | Size | Gzipped |\n"; comment += "|-----------|-----:|--------:|\n"; - for (const c of components) { + for (const c of sortedComponents) { const name = c.scenario.replace("component:", ""); comment += `| \`${name}\` | ${formatKB(c.currentSize)} KB | ${formatKB(c.currentGzipSize)} KB |\n`; } @@ -207,11 +230,35 @@ function generateComment(changes, hasBaseline = true) { c.scenario.startsWith("component:") ); + // Warnings — significant size increases shown at the top, uncollapsed, so + // regressions are visible without expanding any sections. Items still appear + // in their normal group/components section below. + const warnings = changedItems + .filter(isWarning) + .sort((a, b) => b.sizeDiff - a.sizeDiff); + if (warnings.length > 0) { + comment += `### ⚠️ Warnings (${warnings.length} significant size increase${warnings.length === 1 ? "" : "s"})\n\n`; + comment += "| Item | Current | New | Change |\n"; + comment += "|------|--------:|----:|-------:|\n"; + for (const w of warnings) { + const icon = getStatusIcon(w.status, w.sizeDiff, w.sizePercent); + const name = w.scenario.startsWith("component:") + ? w.scenario.replace("component:", "") + : w.scenario; + const current = `${formatKB(w.targetSize)} KB
${formatKB(w.targetGzipSize)} gz`; + const newSize = `${formatKB(w.currentSize)} KB
${formatKB(w.currentGzipSize)} gz`; + const change = `${formatDiff(w.sizeDiff)} KB (${formatPercent(w.sizePercent)})
${formatDiff(w.gzipSizeDiff)} gz (${formatPercent(w.gzipSizePercent)})`; + comment += `| ${icon} \`${name}\` | ${current} | ${newSize} | ${change} |\n`; + } + comment += "\n"; + } + if (changedScenarios.length > 0) { comment += "### Use-Case Scenarios\n\n"; // Group rows by `group` field, preserving insertion order. Scenarios - // without a `group` end up under "Other". + // without a `group` end up under "Other". Rows within each group are + // sorted by current size desc. /** @type {Map} */ const byGroup = new Map(); for (const s of changedScenarios) { @@ -219,22 +266,22 @@ function generateComment(changes, hasBaseline = true) { if (!byGroup.has(g)) byGroup.set(g, []); byGroup.get(g).push(s); } + for (const rows of byGroup.values()) { + rows.sort((a, b) => b.currentSize - a.currentSize); + } const renderRow = (s) => { - const icon = getStatusIcon(s.status, s.sizeDiff); + const icon = getStatusIcon(s.status, s.sizeDiff, s.sizePercent); const current = `${formatKB(s.targetSize)} KB
${formatKB(s.targetGzipSize)} gz`; const newSize = `${formatKB(s.currentSize)} KB
${formatKB(s.currentGzipSize)} gz`; const change = `${formatDiff(s.sizeDiff)} KB (${formatPercent(s.sizePercent)})
${formatDiff(s.gzipSizeDiff)} gz (${formatPercent(s.gzipSizePercent)})`; return `| ${icon} \`${s.scenario}\` | ${current} | ${newSize} | ${change} |\n`; }; - // Base + Core groups expanded by default; other groups collapsed to keep - // the comment scannable. Each group shows the count of changed scenarios. - const expandedGroups = new Set(["Base (agnostic)", "Base (layer-specific)", "Core"]); - + // All groups collapsed by default — Warnings section above surfaces any + // notable regressions, so the detail tables don't need to be open. for (const [groupName, rows] of byGroup) { - const open = expandedGroups.has(groupName) ? " open" : ""; - comment += `\n`; + comment += `
\n`; comment += `${groupName} (${rows.length} changed)\n\n`; comment += "| Scenario | Current | New | Change |\n"; comment += "|----------|--------:|----:|-------:|\n"; @@ -244,6 +291,7 @@ function generateComment(changes, hasBaseline = true) { } if (changedComponents.length > 0) { + changedComponents.sort((a, b) => b.currentSize - a.currentSize); comment += "
\nIndividual Components"; comment += ` (${changedComponents.length} changed)\n\n`; comment += "| Component | Current | New | Change |\n"; @@ -251,7 +299,7 @@ function generateComment(changes, hasBaseline = true) { for (const c of changedComponents) { const name = c.scenario.replace("component:", ""); - const icon = getStatusIcon(c.status, c.sizeDiff); + const icon = getStatusIcon(c.status, c.sizeDiff, c.sizePercent); const current = `${formatKB(c.targetSize)} KB`; const newSize = `${formatKB(c.currentSize)} KB`; const change = `${formatDiff(c.sizeDiff)} KB (${formatPercent(c.sizePercent)})`; diff --git a/packages/layerchart/src/lib/components/Arc/Arc.base.svelte b/packages/layerchart/src/lib/components/Arc/Arc.base.svelte index 8fd7d1d0b..adca12847 100644 --- a/packages/layerchart/src/lib/components/Arc/Arc.base.svelte +++ b/packages/layerchart/src/lib/components/Arc/Arc.base.svelte @@ -20,24 +20,66 @@ Path, ref: refProp = $bindable(), trackRef: trackRefProp = $bindable(), - fill, - fillOpacity, + // Override the `.lc-path` CSS stroke default so arcs don't get a visible outline stroke = 'none', - strokeWidth, - opacity, + // Arc-specific config — extracted out of `...restProps` so it doesn't + // leak onto ``. `motion` in particular would otherwise make Path + // also tween the path-string on top of the end-angle tween that + // `ArcState` already drives, producing visibly wrong arcs. + motion, + value, + initialValue, + domain, + range, + startAngle, + endAngle, + innerRadius, + outerRadius, + cornerRadius, + padAngle, + trackStartAngle, + trackEndAngle, + trackInnerRadius, + trackOuterRadius, + trackCornerRadius, + trackPadAngle, + offset, + // Pointer / tooltip wiring data, + tooltip, + track = false, onpointerenter = () => {}, onpointermove = () => {}, onpointerleave = () => {}, ontouchmove = () => {}, - tooltip, - track = false, children, class: className, ...restProps }: ArcBaseProps = $props(); - const c = new ArcState(() => ({ ...restProps, fill, fillOpacity, stroke, strokeWidth, opacity, data, tooltip, track }) as ArcProps); + const c = new ArcState( + () => + ({ + motion, + value, + initialValue, + domain, + range, + startAngle, + endAngle, + innerRadius, + outerRadius, + cornerRadius, + padAngle, + trackStartAngle, + trackEndAngle, + trackInnerRadius, + trackOuterRadius, + trackCornerRadius, + trackPadAngle, + offset, + }) as ArcProps + ); let ref = $state(); @@ -78,11 +120,7 @@ bind:pathRef={ref} pathData={c.arc()} transform="translate({c.xOffset}, {c.yOffset})" - {fill} - {fillOpacity} {stroke} - {strokeWidth} - {opacity} {...restProps} class={cls('lc-arc-line', className)} onpointerenter={onPointerEnter} diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-full-circle--360-degree-range--1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-full-circle--360-degree-range--1.png new file mode 100644 index 000000000..411366e7c Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-full-circle--360-degree-range--1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-full-circle--360-degree-range--2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-full-circle--360-degree-range--2.png new file mode 100644 index 000000000..411366e7c Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-full-circle--360-degree-range--2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-innerRadius-of-0--pie-slice--1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-innerRadius-of-0--pie-slice--1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-innerRadius-of-0--pie-slice--1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-innerRadius-of-0--pie-slice--2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-innerRadius-of-0--pie-slice--2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-innerRadius-of-0--pie-slice--2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-negative-domain-values-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-negative-domain-values-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-negative-domain-values-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-negative-domain-values-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-negative-domain-values-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-negative-domain-values-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-partial-arc--e-g---180-degrees--1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-partial-arc--e-g---180-degrees--1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-partial-arc--e-g---180-degrees--1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-partial-arc--e-g---180-degrees--2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-partial-arc--e-g---180-degrees--2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-partial-arc--e-g---180-degrees--2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-at-max-domain-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-at-max-domain-1.png new file mode 100644 index 000000000..411366e7c Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-at-max-domain-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-at-max-domain-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-at-max-domain-2.png new file mode 100644 index 000000000..411366e7c Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-at-max-domain-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-below-domain-min-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-below-domain-min-1.png new file mode 100644 index 000000000..11ae1ff75 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-below-domain-min-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-below-domain-min-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-below-domain-min-2.png new file mode 100644 index 000000000..11ae1ff75 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-below-domain-min-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-exceeding-domain-max-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-exceeding-domain-max-1.png new file mode 100644 index 000000000..411366e7c Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-exceeding-domain-max-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-exceeding-domain-max-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-exceeding-domain-max-2.png new file mode 100644 index 000000000..411366e7c Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-exceeding-domain-max-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-of-0-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-of-0-1.png new file mode 100644 index 000000000..47767d2f3 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-of-0-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-of-0-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-of-0-2.png new file mode 100644 index 000000000..47767d2f3 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-of-0-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-enter-events-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-enter-events-1.png new file mode 100644 index 000000000..5bb8fa9e2 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-enter-events-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-enter-events-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-enter-events-2.png new file mode 100644 index 000000000..5bb8fa9e2 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-enter-events-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-move-events-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-move-events-1.png new file mode 100644 index 000000000..5bb8fa9e2 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-move-events-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-move-events-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-move-events-2.png new file mode 100644 index 000000000..5bb8fa9e2 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-move-events-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-touch-move-events-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-touch-move-events-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-touch-move-events-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-touch-move-events-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-touch-move-events-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-touch-move-events-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-custom-class-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-custom-class-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-custom-class-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-custom-class-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-custom-class-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-custom-class-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fill-color-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fill-color-1.png new file mode 100644 index 000000000..e44f57ef4 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fill-color-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fill-color-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fill-color-2.png new file mode 100644 index 000000000..e44f57ef4 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fill-color-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fillOpacity-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fillOpacity-1.png new file mode 100644 index 000000000..652271678 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fillOpacity-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fillOpacity-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fillOpacity-2.png new file mode 100644 index 000000000..652271678 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fillOpacity-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-offset-to-arc-position-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-offset-to-arc-position-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-offset-to-arc-position-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-offset-to-arc-position-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-offset-to-arc-position-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-offset-to-arc-position-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-opacity-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-opacity-1.png new file mode 100644 index 000000000..a91127acc Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-opacity-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-opacity-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-opacity-2.png new file mode 100644 index 000000000..a91127acc Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-opacity-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-stroke-color-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-stroke-color-1.png new file mode 100644 index 000000000..62b4ce581 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-stroke-color-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-stroke-color-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-stroke-color-2.png new file mode 100644 index 000000000..62b4ce581 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-stroke-color-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-strokeWidth-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-strokeWidth-1.png new file mode 100644 index 000000000..5d2cfa5a2 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-strokeWidth-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-strokeWidth-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-strokeWidth-2.png new file mode 100644 index 000000000..5d2cfa5a2 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-strokeWidth-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-zero-offset-by-default-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-zero-offset-by-default-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-zero-offset-by-default-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-zero-offset-by-default-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-zero-offset-by-default-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-zero-offset-by-default-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-hide-on-pointer-leave-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-hide-on-pointer-leave-1.png new file mode 100644 index 000000000..5bb8fa9e2 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-hide-on-pointer-leave-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-hide-on-pointer-leave-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-hide-on-pointer-leave-2.png new file mode 100644 index 000000000..5bb8fa9e2 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-hide-on-pointer-leave-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-show-on-pointer-enter-with-data-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-show-on-pointer-enter-with-data-1.png new file mode 100644 index 000000000..5bb8fa9e2 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-show-on-pointer-enter-with-data-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-show-on-pointer-enter-with-data-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-show-on-pointer-enter-with-data-2.png new file mode 100644 index 000000000..5bb8fa9e2 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-show-on-pointer-enter-with-data-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-handle-custom-start-angle-in-range-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-handle-custom-start-angle-in-range-1.png new file mode 100644 index 000000000..99f913aa1 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-handle-custom-start-angle-in-range-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-handle-custom-start-angle-in-range-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-handle-custom-start-angle-in-range-2.png new file mode 100644 index 000000000..99f913aa1 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-handle-custom-start-angle-in-range-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-have-stroke--none--by-default-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-have-stroke--none--by-default-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-have-stroke--none--by-default-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-have-stroke--none--by-default-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-have-stroke--none--by-default-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-have-stroke--none--by-default-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-an-arc-path-with-value-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-an-arc-path-with-value-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-an-arc-path-with-value-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-an-arc-path-with-value-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-an-arc-path-with-value-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-an-arc-path-with-value-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-when-track-prop-is-provided-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-when-track-prop-is-provided-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-when-track-prop-is-provided-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-when-track-prop-is-provided-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-when-track-prop-is-provided-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-when-track-prop-is-provided-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-custom-class-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-custom-class-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-custom-class-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-custom-class-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-custom-class-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-custom-class-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackCornerRadius-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackCornerRadius-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackCornerRadius-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackCornerRadius-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackCornerRadius-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackCornerRadius-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackEndAngle-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackEndAngle-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackEndAngle-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackEndAngle-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackEndAngle-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackEndAngle-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-and-trackOuterRadius-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-and-trackOuterRadius-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-and-trackOuterRadius-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-and-trackOuterRadius-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-and-trackOuterRadius-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-and-trackOuterRadius-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackOuterRadius-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackOuterRadius-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackOuterRadius-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackOuterRadius-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackOuterRadius-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackOuterRadius-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackPadAngle-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackPadAngle-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackPadAngle-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackPadAngle-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackPadAngle-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackPadAngle-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-and-trackEndAngle-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-and-trackEndAngle-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-and-trackEndAngle-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-and-trackEndAngle-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-and-trackEndAngle-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-and-trackEndAngle-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-both-startAngle-and-endAngle-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-both-startAngle-and-endAngle-1.png new file mode 100644 index 000000000..def6f8f41 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-both-startAngle-and-endAngle-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-both-startAngle-and-endAngle-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-both-startAngle-and-endAngle-2.png new file mode 100644 index 000000000..def6f8f41 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-both-startAngle-and-endAngle-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-cornerRadius-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-cornerRadius-1.png new file mode 100644 index 000000000..22888d83c Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-cornerRadius-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-cornerRadius-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-cornerRadius-2.png new file mode 100644 index 000000000..22888d83c Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-cornerRadius-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-1.png new file mode 100644 index 000000000..def6f8f41 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-2.png new file mode 100644 index 000000000..def6f8f41 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-and-range-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-and-range-1.png new file mode 100644 index 000000000..def6f8f41 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-and-range-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-and-range-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-and-range-2.png new file mode 100644 index 000000000..def6f8f41 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-and-range-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-range-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-range-1.png new file mode 100644 index 000000000..def6f8f41 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-range-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-range-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-range-2.png new file mode 100644 index 000000000..def6f8f41 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-range-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-endAngle-in-radians-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-endAngle-in-radians-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-endAngle-in-radians-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-endAngle-in-radians-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-endAngle-in-radians-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-endAngle-in-radians-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-1.png new file mode 100644 index 000000000..08177c51a Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-2.png new file mode 100644 index 000000000..08177c51a Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-and-outerRadius-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-and-outerRadius-1.png new file mode 100644 index 000000000..bf11432e9 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-and-outerRadius-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-and-outerRadius-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-and-outerRadius-2.png new file mode 100644 index 000000000..bf11432e9 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-and-outerRadius-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-outerRadius-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-outerRadius-1.png new file mode 100644 index 000000000..2e63e6a7e Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-outerRadius-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-outerRadius-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-outerRadius-2.png new file mode 100644 index 000000000..2e63e6a7e Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-outerRadius-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-padAngle-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-padAngle-1.png new file mode 100644 index 000000000..28b284611 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-padAngle-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-padAngle-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-padAngle-2.png new file mode 100644 index 000000000..28b284611 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-padAngle-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-startAngle-in-radians-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-startAngle-in-radians-1.png new file mode 100644 index 000000000..99f913aa1 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-startAngle-in-radians-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-startAngle-in-radians-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-startAngle-in-radians-2.png new file mode 100644 index 000000000..99f913aa1 Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-startAngle-in-radians-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-Arc-element-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-Arc-element-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-Arc-element-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-Arc-element-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-Arc-element-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-Arc-element-2.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-track-1.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-track-1.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-track-1.png differ diff --git a/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-track-2.png b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-track-2.png new file mode 100644 index 000000000..c39ea627b Binary files /dev/null and b/packages/layerchart/src/lib/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-track-2.png differ diff --git a/packages/layerchart/src/lib/components/ArcLabel/__screenshots__/ArcLabel.svelte.test.ts/ArcLabel-renders-a-text-element-with-the-supplied-value-at-the-centroid-1.png b/packages/layerchart/src/lib/components/ArcLabel/__screenshots__/ArcLabel.svelte.test.ts/ArcLabel-renders-a-text-element-with-the-supplied-value-at-the-centroid-1.png new file mode 100644 index 000000000..a658d2515 Binary files /dev/null and b/packages/layerchart/src/lib/components/ArcLabel/__screenshots__/ArcLabel.svelte.test.ts/ArcLabel-renders-a-text-element-with-the-supplied-value-at-the-centroid-1.png differ diff --git a/packages/layerchart/src/lib/components/ArcLabel/__screenshots__/ArcLabel.svelte.test.ts/ArcLabel-renders-a-text-element-with-the-supplied-value-at-the-centroid-2.png b/packages/layerchart/src/lib/components/ArcLabel/__screenshots__/ArcLabel.svelte.test.ts/ArcLabel-renders-a-text-element-with-the-supplied-value-at-the-centroid-2.png new file mode 100644 index 000000000..a658d2515 Binary files /dev/null and b/packages/layerchart/src/lib/components/ArcLabel/__screenshots__/ArcLabel.svelte.test.ts/ArcLabel-renders-a-text-element-with-the-supplied-value-at-the-centroid-2.png differ diff --git a/packages/layerchart/src/lib/components/Link/Link.base.svelte b/packages/layerchart/src/lib/components/Link/Link.base.svelte index ff21c4012..9953704da 100644 --- a/packages/layerchart/src/lib/components/Link/Link.base.svelte +++ b/packages/layerchart/src/lib/components/Link/Link.base.svelte @@ -18,7 +18,6 @@ getLinkRadialPresetPath, } from '$lib/utils/linkUtils.js'; import { getChartContext } from '$lib/contexts/chart.js'; - import { extractLayerProps } from '$lib/utils/attributes.js'; import { accessor, type Accessor } from '$lib/utils/common.js'; import { cls } from '@layerstack/tailwind'; import { @@ -203,11 +202,20 @@ } : undefined; - const motionPath = createMotion( - '', - () => singlePathData, - tweenOptions ? tweenOptions : { type: 'none' } - ); + // Pass `tweenOptions` (possibly undefined) so `createMotion` takes its + // fast-path passthrough when no tween is configured — avoids allocating + // a MotionNone container + per-instance `$effect` that fires on every + // x1/y1/x2/y2 change. Critical for force-simulation graphs which can + // have hundreds of links updating on every tick. + const motionPath = createMotion('', () => singlePathData, tweenOptions); + + // Stable getter handed to `` instead of `motionPath.current`. + // Reading `motionPath.current` directly in the template would subscribe + // *this* component's template to per-tick updates, forcing the entire + // `` block to re-evaluate (and re-spread props) on every change. + // By passing a function reference, the per-tick `current` read happens + // inside ``'s own template — the parent stays stable. + const getPathData = () => motionPath.current; const arrayRows = $derived(isArrayMode ? data ?? ctx.data ?? [] : []); @@ -237,7 +245,6 @@ {markerStart} {markerMid} {markerEnd} - {...extractLayerProps(restProps, 'lc-link')} {...restProps} stroke={resolvedStroke} fill={resolvePerDatum(fillProp, d)} @@ -247,13 +254,12 @@ {/each} {:else} diff --git a/packages/layerchart/src/lib/components/Path/Path.canvas.svelte b/packages/layerchart/src/lib/components/Path/Path.canvas.svelte index d958a6e50..65454c1a0 100644 --- a/packages/layerchart/src/lib/components/Path/Path.canvas.svelte +++ b/packages/layerchart/src/lib/components/Path/Path.canvas.svelte @@ -9,9 +9,12 @@ import { createKey } from '$lib/utils/key.svelte.js'; import { PathState, type PathProps } from './Path.shared.svelte.js'; - let { ...rest }: PathProps = $props(); + let { pathData, ...rest }: PathProps = $props(); - const c = new PathState(() => rest as PathProps); + const c = new PathState( + () => pathData, + () => rest as PathProps + ); function render( ctx: CanvasRenderingContext2D, diff --git a/packages/layerchart/src/lib/components/Path/Path.shared.svelte.ts b/packages/layerchart/src/lib/components/Path/Path.shared.svelte.ts index 7e1113978..c47d85626 100644 --- a/packages/layerchart/src/lib/components/Path/Path.shared.svelte.ts +++ b/packages/layerchart/src/lib/components/Path/Path.shared.svelte.ts @@ -18,10 +18,15 @@ import type { draw as _drawTransition } from 'svelte/transition'; export type PathPropsWithoutHTML = { /** - * Pass `` explicitly instead of calculating - * from data / context + * The `d` attribute of the rendered ``. + * + * Accepts either a value (resolved at call site) or a function that + * returns the current value. Passing a function lets the parent avoid + * re-rendering its own template on every change to the path data — + * useful when a parent like `Link` / `Spline` / `Area` updates the + * path on every animation tick across hundreds of instances. */ - pathData?: string | undefined | null; + pathData?: string | undefined | null | (() => string | undefined | null); /** * Whether to animate the drawing of the path over time. @@ -69,11 +74,21 @@ export type PathPropsWithoutHTML = { export type PathProps = PathPropsWithoutHTML & Without, PathPropsWithoutHTML>; +/** Resolve `pathData` whether it was passed as a value or a getter function. */ +function resolvePathData(v: PathProps['pathData']): string | null | undefined { + return typeof v === 'function' ? v() : v; +} + /** * Reactive state shared by every per-layer Path variant. */ export class PathState { - #getProps: () => PathProps = () => ({}) as PathProps; + // Hot-path getter: reads only `pathData` (or invokes the function-getter form). + // Kept separate from the full-props getter so that the `` template + // updater does not subscribe to every Path prop on every read — critical for + // mark-heavy scenes (force-simulation graphs with hundreds of links updating + // per tick) where pre-fix each tween read re-evaluated all 15+ props. + #getPathData: () => string | null | undefined; // Contexts chartCtx: ChartState = getChartContext(); @@ -88,8 +103,19 @@ export class PathState { // Re-key trigger for draw transitions drawKey = $state(Symbol()); - constructor(getProps: () => PathProps) { - this.#getProps = getProps; + /** + * @param getPathData Hot-path getter — reads only `pathData`. Kept separate from + * `getProps` so the `` updater (and the canvas + * `tweenedPathData` consumer) does not subscribe to every + * Path prop on every tick. + * @param getProps Full-props getter — used for one-time / cold-path config + * (motion, draw). + */ + constructor( + getPathData: () => PathProps['pathData'], + getProps: () => PathProps = () => ({}) as PathProps + ) { + this.#getPathData = () => resolvePathData(getPathData()); const initial = getProps(); const extractedTween = extractTweenConfig(initial.motion); @@ -105,22 +131,24 @@ export class PathState { if (!tweenedOptions) { // Fast initial render when not tweened return ''; - } else if (initial.pathData) { + } + const resolved = resolvePathData(getPathData()); + if (resolved) { return flattenPathData( - initial.pathData, + resolved, Math.min(this.chartCtx.yScale(0) ?? this.chartCtx.yRange[0], this.chartCtx.yRange[0]) ); } return ''; })(); - this.#tweenedState = createMotion(defaultPathData, () => getProps().pathData, tweenedOptions); + this.#tweenedState = createMotion(defaultPathData, this.#getPathData, tweenedOptions); // Re-trigger draw transition when path data changes $effect(() => { if (!getProps().draw) return; - // Touch dependency - void getProps().pathData; + // Touch dependency (resolves getter form too) + void this.#getPathData(); this.drawKey = Symbol(); }); } diff --git a/packages/layerchart/src/lib/components/Path/Path.svg.svelte b/packages/layerchart/src/lib/components/Path/Path.svg.svelte index 2f0cbb233..1cd062fa0 100644 --- a/packages/layerchart/src/lib/components/Path/Path.svg.svelte +++ b/packages/layerchart/src/lib/components/Path/Path.svg.svelte @@ -16,7 +16,7 @@ const uid = $props.id(); let { - pathRef: pathRefProp = $bindable(), + pathRef = $bindable(), marker, markerStart: markerStartProp, markerMid: markerMidProp, @@ -24,29 +24,35 @@ startContent, endContent, draw, + motion, + // Extracted out of `rest` so the `` element's `{...rest}` + // spread doesn't re-evaluate on every frame in mark-heavy scenes + // (force-simulation graphs with hundreds of links updating per tick). + // - `pathData`: changes every frame + // - `class`: parents typically pass `cls(...)` which produces a new + // string reference per parent render + // - styling props: explicit on the element below, no need to + // leak them through the spread + pathData: _pathData, + class: classProp, + fill: fillProp, + fillOpacity: fillOpacityProp, + stroke: strokeProp, + strokeOpacity: strokeOpacityProp, + strokeWidth: strokeWidthProp, + opacity: opacityProp, ...rest }: PathProps = $props(); + // Pass `pathData` as its own getter so the hot-path tween read only subscribes + // to `pathData` (which changes per tick on force sims) and not to every other + // Path prop. Pre-fix the per-tick `` updater re-read all 15+ props + // through `getProps()` on each force-sim tick × hundreds of paths. const c = new PathState( - () => - ({ - marker, - markerStart: markerStartProp, - markerMid: markerMidProp, - markerEnd: markerEndProp, - startContent, - endContent, - draw, - ...rest, - }) as PathProps + () => _pathData, + () => ({ draw, motion }) as PathProps ); - let pathRef = $state(); - - $effect.pre(() => { - pathRefProp = pathRef; - }); - const markerStart = $derived(markerStartProp ?? marker); const markerMid = $derived(markerMidProp ?? marker); const markerEnd = $derived(markerEndProp ?? marker); @@ -56,7 +62,6 @@ const markerEndId = $derived(markerEnd ? createId('marker-end', uid) : ''); const drawTransition = $derived(draw ? _drawTransition : () => ({})); - let startPoint = $state(); const endPointDuration = $derived.by(() => { @@ -70,60 +75,70 @@ return 800; }); - const endPoint = createControlledMotion( - undefined, - draw - ? { - type: 'tween', - duration: () => endPointDuration, - easing: typeof draw === 'object' && draw.easing ? draw.easing : cubicInOut, - interpolate() { - return (t: number) => { - const totalLength = pathRef?.getTotalLength() ?? 0; - const point = pathRef?.getPointAtLength(totalLength * t); - return point; - }; - }, - } - : { type: 'none' } - ); - - $effect(() => { - if (!startContent && !endContent) return; - // Track path data changes - void c.tweenedPathData; - if (!pathRef) return; - - tick().then(() => { + // Only allocate the controlled motion container when `draw` is configured; + // otherwise the per-Path `MotionNone` × hundreds of paths was a measurable + // mount-time cost in mark-heavy scenes. + const endPoint = draw + ? createControlledMotion(undefined, { + type: 'tween', + duration: () => endPointDuration, + easing: typeof draw === 'object' && draw.easing ? draw.easing : cubicInOut, + interpolate() { + return (t: number) => { + const totalLength = pathRef?.getTotalLength() ?? 0; + const point = pathRef?.getPointAtLength(totalLength * t); + return point; + }; + }, + }) + : null; + + // Only set up path-end tracking when startContent/endContent require it. + if (startContent || endContent) { + $effect(() => { + // Track path data changes + void c.tweenedPathData; if (!pathRef) return; - const totalLength = pathRef.getTotalLength(); - if (!totalLength) return; - startPoint = pathRef.getPointAtLength(0); - endPoint.target = pathRef.getPointAtLength(totalLength); + + tick().then(() => { + if (!pathRef) return; + const totalLength = pathRef.getTotalLength(); + if (!totalLength) return; + startPoint = pathRef.getPointAtLength(0); + if (endPoint) { + endPoint.target = pathRef.getPointAtLength(totalLength); + } + }); }); - }); + } {#key c.drawKey} - - - + {#if markerStart} + + {/if} + {#if markerMid} + + {/if} + {#if markerEnd} + + {/if} {#if startContent && startPoint} @@ -137,7 +152,7 @@ {/if} - {#if endContent && endPoint.current} + {#if endContent && endPoint?.current} {@render endContent({ point: endPoint.current, diff --git a/packages/layerchart/src/lib/components/RectClipPath/RectClipPath.base.svelte b/packages/layerchart/src/lib/components/RectClipPath/RectClipPath.base.svelte index 91e6b39b2..93396a976 100644 --- a/packages/layerchart/src/lib/components/RectClipPath/RectClipPath.base.svelte +++ b/packages/layerchart/src/lib/components/RectClipPath/RectClipPath.base.svelte @@ -11,6 +11,7 @@ diff --git a/packages/layerchart/src/lib/components/RectClipPath/RectClipPath.shared.svelte.ts b/packages/layerchart/src/lib/components/RectClipPath/RectClipPath.shared.svelte.ts index 9c95db231..6f5763034 100644 --- a/packages/layerchart/src/lib/components/RectClipPath/RectClipPath.shared.svelte.ts +++ b/packages/layerchart/src/lib/components/RectClipPath/RectClipPath.shared.svelte.ts @@ -9,12 +9,20 @@ export type BaseRectClipPathPropsWithoutHTML = { id?: string; /** The x position of the clipPath. @default 0 */ x?: number; + /** The initial x position (used as the animation start when `motion` is set). @default x */ + initialX?: number; /** The y position of the clipPath. @default 0 */ y?: number; + /** The initial y position (used as the animation start when `motion` is set). @default y */ + initialY?: number; /** The width of the clipPath. @required */ width: number; + /** The initial width (used as the animation start when `motion` is set). @default width */ + initialWidth?: number; /** The height of the clipPath. @required */ height: number; + /** The initial height (used as the animation start when `motion` is set). @default height */ + initialHeight?: number; /** Whether to disable clipping (show all). @default false */ disabled?: boolean; /** Invert the clip — content renders *outside* the rect. @default false */ diff --git a/packages/layerchart/src/lib/components/Trail/Trail.base.svelte b/packages/layerchart/src/lib/components/Trail/Trail.base.svelte index db58b6b35..a000af1fd 100644 --- a/packages/layerchart/src/lib/components/Trail/Trail.base.svelte +++ b/packages/layerchart/src/lib/components/Trail/Trail.base.svelte @@ -122,12 +122,15 @@ return ''; } - const tweenState = createMotion(defaultPathData(), () => trailPath, { - type: 'tween', - interpolate: interpolatePath, - }); - - const isTweened = $derived(extractTweenConfig(motion) != null); + // Only allocate the tween container when the user opts into a tween via + // `motion`; otherwise the template reads `trailPath` directly. + const tweenState = + extractTweenConfig(motion) != null + ? createMotion(defaultPathData(), () => trailPath, { + type: 'tween', + interpolate: interpolatePath, + }) + : null; ctx.registerComponent({ name: 'Trail', @@ -137,7 +140,7 @@ { /** Svelte context key for tracking the nearest parent ComponentNode. */ const _ParentNodeContext = new Context('ComponentTreeParent'); +/** Mark info is "empty" when none of the fields the chart uses for series / + * domain inference are populated. Pixel-mode primitives produce empty info + * since they have no string/function accessors and no own data. */ +function isEmptyMarkInfo(info: MarkInfo): boolean { + return !info.x && !info.y && !info.data && !info.color && !info.seriesKey && !info.label; +} + export class ChartState< TData = any, XScale extends AnyScale = AnyScale, @@ -222,14 +229,25 @@ export class ChartState< }); if (markInfo && !insideCompositeMark) { - $effect(() => { - const info = markInfo(); - // Skip registration for empty mark info (e.g. pixel-mode marks) - // to avoid unnecessary array push/splice and version bumps - if (!info.x && !info.y && !info.data && !info.color && !info.seriesKey && !info.label) - return; - return untrack(() => this.registerMark(info)); - }); + // Probe once at construction: if mark info is initially empty + // (pixel-mode primitives where cx/cy/r are numbers), skip the + // tracking $effect entirely. This is the common case for + // mark-heavy scenes (force simulations, scatter plots with + // pixel coordinates) and avoids one effect frame per primitive. + // + // Trade-off: a primitive that starts in pixel mode and later + // flips to data mode (e.g. cx changes from number to string at + // runtime) won't register a mark. This is uncommon — modes are + // typically static — but if needed, use explicit `series` on the + // chart instead of relying on implicit mark-derived series. + const initial = untrack(markInfo); + if (!isEmptyMarkInfo(initial)) { + $effect(() => { + const info = markInfo(); + if (isEmptyMarkInfo(info)) return; + return untrack(() => this.registerMark(info)); + }); + } } return node; diff --git a/packages/layerchart/src/lib/states/series.svelte.test.ts b/packages/layerchart/src/lib/states/series.svelte.test.ts index 16efcecf9..6ea76e123 100644 --- a/packages/layerchart/src/lib/states/series.svelte.test.ts +++ b/packages/layerchart/src/lib/states/series.svelte.test.ts @@ -17,10 +17,14 @@ const series = [ ]; function createSeriesState(seriesData = series as any[], stackConfig: any = null) { - return new SeriesState( - () => seriesData, - () => stackConfig - ); + let state: SeriesState; + $effect.root(() => { + state = new SeriesState( + () => seriesData, + () => stackConfig + ); + }); + return state!; } describe('SeriesState', () => { diff --git a/packages/layerchart/src/lib/states/series.svelte.ts b/packages/layerchart/src/lib/states/series.svelte.ts index 46866b031..32e563df6 100644 --- a/packages/layerchart/src/lib/states/series.svelte.ts +++ b/packages/layerchart/src/lib/states/series.svelte.ts @@ -24,20 +24,6 @@ export class SeriesState { selectedKeys: SelectionState; - /** - * Reactively syncs selectedKeys when series `selected` props change. - * When any series explicitly sets `selected: false`, the remaining series - * (with `selected` undefined or true) are pre-selected. - */ - #_syncSelectedFromProps = $effect.root(() => { - $effect(() => { - const keys = SeriesState.#selectedKeysFromSeries(this.#series); - if (keys) { - this.selectedKeys.current = keys; - } - }); - }); - /** * The current highlight series key for the chart. */ @@ -53,6 +39,16 @@ export class SeriesState { // Compute initial selectedKeys synchronously from series `selected` props const initialKeys = SeriesState.#selectedKeysFromSeries(getSeries()); this.selectedKeys = new SelectionState({ initial: initialKeys ?? undefined }); + + // Reactively sync selectedKeys when series `selected` props change. + // When any series explicitly sets `selected: false`, the remaining series + // (with `selected` undefined or true) are pre-selected. + $effect(() => { + const keys = SeriesState.#selectedKeysFromSeries(this.#series); + if (keys) { + this.selectedKeys.current = keys; + } + }); } /**