diff --git a/.changeset/smooth-shadcn-charts.md b/.changeset/smooth-shadcn-charts.md new file mode 100644 index 00000000..87801a13 --- /dev/null +++ b/.changeset/smooth-shadcn-charts.md @@ -0,0 +1,7 @@ +--- +'@tanstack/charts': minor +--- + +Add ShadCN-compatible chart catalog support with renderer-owned tooltip and +entrance motion, composable stagger timing, motion cascade opt-outs, and +expanded polar presentation controls. diff --git a/API-FRICTION.md b/API-FRICTION.md index f3c75724..65b3bfcc 100644 --- a/API-FRICTION.md +++ b/API-FRICTION.md @@ -309,6 +309,18 @@ Each entry records: | F-270 | Catalog migration left generated release evidence stale | Tooling | resolved | | F-271 | Radial focus collapsed angular cross-sections to centroids | API | resolved | | F-272 | Pointer probes armed between transient inactive frames | Tooling | resolved | +| F-273 | Catalog cases could not declare an application viewport height | Tooling | resolved | +| F-274 | Upstream example clones had no drift boundary | Tooling | resolved | +| F-275 | Preview transparency validation rejected semantic IDs | Tooling | resolved | +| F-276 | Definition coverage assumed a combined renderer module | Tooling | resolved | +| F-277 | Preview errors omitted the failing catalog case | Tooling | resolved | +| F-278 | Renderer checks could validate matching approximations | Tooling | resolved | +| F-279 | Radial grids could not render authored fills | API | resolved | +| F-280 | Chart motion did not reach HTML tooltips | API | resolved | +| F-281 | Bars could not express an authored outline | API | resolved | +| F-282 | Collection actions followed the viewport instead of the card | Application | resolved | +| F-283 | Interactive chart shells rendered inert controls | Application | resolved | +| F-284 | Stagger timing required repeated callback arithmetic | API | resolved | ## Findings @@ -8005,3 +8017,217 @@ Each entry records: - Verification: the exact Chart.js grouped-pointer cell and the complete quick partition 3 pass with trusted activation, exact grouped series values, and zero recovered retries. + +### F-273 — Catalog cases could not declare an application viewport height + +- Status: resolved +- Severity: low +- Owner: Tooling +- Observed in: reproducing shadcn/ui's dashboard as a renderer-comparison case +- Friction: the catalog fixed every case at 480 pixels tall, which clipped a + full application shell and made its Recharts and TanStack renderers impossible + to inspect as one shared viewport. +- Decision: allow validated case metadata to declare an optional height and use + it for catalog cards, mounts, updates, and embeds while retaining 480 pixels + as the default. +- Verification: metadata tests accept the dashboard's 860-pixel height, the + conformance example build passes, and Chromium renders both comparison panels + at 960 by 860 pixels without root overflow. + +### F-274 — Upstream example clones had no drift boundary + +- Status: resolved +- Severity: medium +- Owner: Tooling +- Observed in: spiking a maintained TanStack clone of the shadcn chart catalog +- Friction: an example's source URL recorded where it came from, but did not + identify the exact upstream revision or detect changed, added, and removed + examples. Visual clones could silently diverge from shadcn's 70-file catalog. +- Decision: pin the upstream commit, registry blob, and each example's Git blob + SHA in one manifest. Keep normal validation offline and add an explicit remote + drift command that compares the manifest with shadcn's current Git tree. +- Verification: the inventory test requires all 70 files, exact family counts, + unique local mappings, and reports synthetic source changes, additions, and + removals. The offline command verifies all five spike mappings. + +### F-275 — Preview transparency validation rejected semantic IDs + +- Status: resolved +- Severity: low +- Owner: Tooling +- Observed in: generating the shadcn radial-text catalog preview +- Friction: the preview validator searched the complete SVG string for + `background:`. A valid semantic key such as `radial-background:object` + therefore failed the transparent-background gate. +- Decision: detect an actual CSS `background` declaration after a rule or + declaration boundary, without reserving ordinary chart IDs. +- Verification: the preview regression accepts the semantic background key and + continues to reject an authored CSS background declaration. + +### F-276 — Definition-shape coverage assumed a combined renderer module + +- Status: resolved +- Severity: low +- Owner: Tooling +- Observed in: isolating TanStack and Recharts bundle graphs for the shadcn + catalog spike +- Friction: the definition-shape gate scanned `tanstack.ts`, `view.tsx`, and + `chart.ts`, but ignored a renderer-specific `tanstack-view.tsx`. Splitting the + two implementations for honest bundle measurement made five valid static + definitions disappear from coverage. +- Decision: include renderer-specific TanStack view modules in the definition + scanner while retaining the existing entry and shared-view conventions. +- Verification: the shape gate finds all 122 definitions again: 117 static and + five responsive. + +### F-277 — Preview errors omitted the failing catalog case + +- Status: resolved +- Severity: low +- Owner: Tooling +- Observed in: generating static previews for the 70-case shadcn collection +- Friction: the renderer's clipped-label diagnostic described the labels and + bounds but omitted the case ID and theme. A full-catalog run therefore could + not identify which new example needed a margin correction. +- Decision: wrap presentation-validation failures with the current case ID and + theme while retaining the original error as the cause. +- Verification: the next full preview run reports any failed case directly. + +### F-278 — Renderer conformance could validate two matching approximations + +- Status: resolved +- Severity: high +- Owner: Tooling +- Observed in: expanding the shadcn chart collection from five measured cases + to all 70 upstream examples +- Friction: geometry and paint checks compared TanStack Charts with a local + Recharts implementation built from the same simplified family spec. Both + renderers could agree while their card, data, layout, and variant behavior + visibly differed from the official shadcn output. The later whole-card gate + still allowed a wrong chart type to pass because white card pixels dominated + the score. +- Decision: capture every official shadcn card at a fixed 640-pixel viewport + and compare the complete TanStack card screenshot against that committed + baseline. Require at least 90% pixel similarity and 70% similarity over the + union of non-white pixels in a fixed chart region for every case; retain + failed local and diff images for review. +- Verification: the full gate passes all 70 whole-card and chart-region checks. + Whole-card similarity averages above 97%, and every chart-region score is + above 75%. The earlier wrong radial-stacked implementation would fail at + 20.9%. The pinned inventory check also requires one baseline image per + catalog entry and an ordered 70-case reference manifest. + +### F-279 — Radial grids could not render authored fills + +- Status: resolved +- Severity: medium +- Owner: API +- Observed in: reproducing shadcn's filled polygon and circle radar grids +- Friction: `RadialGridOptions` exposed only stroke styling and the renderer + hard-coded every ring to `fill: none`. Shadcn's filled-grid variants therefore + required a separate approximation instead of expressing their source grid. +- Decision: expose `fill` and `fillOpacity` on `RadialGridOptions` and forward + both values to every generated ring. +- Verification: the polar scene test asserts an authored radial-grid fill and + opacity, and the two filled shadcn radar variants pass the chart-foreground + visual gate above 97%. + +### F-280 — Chart motion did not reach HTML tooltips + +- Status: resolved +- Severity: medium +- Owner: API +- Observed in: applying the shadcn spring preset to the complete chart catalog +- Friction: chart and mark motion animated renderer geometry, but the HTML + tooltip was owned by a separate extension and appeared, moved, and vanished + immediately. The first tooltip motion pass also restarted every move from the + prior target instead of the spring's live position and velocity, so crossing + points quickly produced visible jumps. Applying one motion policy therefore + produced a visibly split interaction. +- Decision: let the optional `motion()` renderer attach an internal tooltip + motion controller to the host. The tooltip inherits that renderer's fallback + or a static chart-level transition, while `tooltip.motion` can override or + disable it. Keep translation independent from presence motion, and retarget + springs from their current sampled position and velocity. The public tooltip + extension contract stays renderer-neutral. +- Verification: the renderer test asserts that a chart spring produces sampled + tooltip keyframes, keeps the tooltip mounted until its exit completes, and + preserves both visual position and travel direction across a mid-flight + retarget. The tooltip-only bundle retains neither `motion.ts` nor `spring.ts` + and measures 4.37 KiB gzip. + +### F-281 — Bars could not express an authored outline + +- Status: resolved +- Severity: medium +- Owner: API +- Observed in: reproducing shadcn's active-bar example +- Friction: `barX` and `barY` exposed fill paint but not base stroke paint. The + selected Firefox bar therefore could not reproduce the upstream dashed + outline without replacing the mark or abusing focus-state styling. +- Decision: expose visual `stroke` and `strokeDasharray` channels plus + `strokeOpacity` and `strokeWidth` on both bar orientations. +- Verification: the bar scene test resolves per-datum outlines for horizontal + and vertical bars, and the active-bar screenshot passes at 98% whole-card and + 97% chart-region similarity. + +### F-282 — Collection actions followed the viewport instead of the card + +- Status: resolved +- Severity: medium +- Owner: Application +- Observed in: browsing the two-column ShadCN collection +- Friction: every collection preview reserved the case's fixed conformance + viewport height, while the rendered ShadCN card was often much shorter. The + Code, Preview, and Original actions followed the reserved viewport rather + than the visible card, leaving roughly 280 pixels of empty space and making + the actions appear to belong to the next row. +- Decision: retain the full height as the chart's rendering input, then size + the collection preview shell to the rendered card. Observe the card so the + shell follows responsive height changes without coupling the gallery to + per-case dimensions. +- Verification: the live Radar collection now measures each preview shell to + its visible card and places the action row 14 pixels below it at both columns; + resizing continues to refit through the card observer. + +### F-283 — Interactive chart shells rendered inert controls + +- Status: resolved +- Severity: high +- Owner: Application +- Observed in: the generated ShadCN area, bar, line, and pie interactive cases +- Friction: the catalog reproduced the controls visually with static `div` + elements, but they had no input semantics or state. The pie month picker, + area range picker, and desktop/mobile metric panels could not change their + charts even though each case was explicitly named interactive. +- Decision: keep interaction state in the shared React example shell and + rebuild the selected chart definition from that state. Use native select and + button semantics, preserve the official labels and active treatment, and + keep the four behaviors centralized rather than adding state code to every + generated case. +- Verification: DOM regressions change the area range, both metric-series + charts, and the active pie month and assert the plotted geometry or paint + changes. Browser checks confirm the 7-day area path and ticks, mobile bar and + line geometry and colors, and May pie slice, swatch, and center value. + +### F-284 — Stagger timing required repeated callback arithmetic + +- Status: resolved +- Severity: medium +- Owner: API +- Observed in: evaluating optional entrance staggering for stacked radial and + pie compositions +- Friction: every definition had to repeat phase filters, role filters, index + selection, and delay arithmetic. Combining that callback with an existing + transition or rolling-path definition required another handwritten merge. + An initial `composeMotion()` helper recovered composition but made ordinary + timing objects unnecessarily indirect. +- Decision: allow `ChartMotionTiming.delay` to resolve from motion context and + make `stagger()` return that single partial timing field. It composes through + native object spread, whose order also defines conflict precedence. Export it + from `/motion` for convenience and `/motion/definition` as an isolated + policy-only entry. +- Verification: unit tests cover datum and series staggering, phase and role + filters, offsets, invalid inputs, native spread, and field precedence. The + isolated entry is 0.26 KiB gzip and retains neither the SVG motion renderer + nor spring physics. diff --git a/benchmarks/bundle-size/universal-baseline.json b/benchmarks/bundle-size/universal-baseline.json index fd9f6438..02d2cd6b 100644 --- a/benchmarks/bundle-size/universal-baseline.json +++ b/benchmarks/bundle-size/universal-baseline.json @@ -11,28 +11,28 @@ "gzip": 19616 }, "Representative marks": { - "bytes": 71389, - "gzip": 26201 + "bytes": 71536, + "gzip": 26220 }, "TanStack DOM host": { - "bytes": 70570, - "gzip": 24758 + "bytes": 70878, + "gzip": 24859 }, "React adapter": { - "bytes": 72729, - "gzip": 25524 + "bytes": 73037, + "gzip": 25621 }, "React line consumer": { - "bytes": 95923, - "gzip": 34754 + "bytes": 96231, + "gzip": 34868 }, "Compact-scale line scene": { "bytes": 29572, "gzip": 10769 }, "React compact-scale line consumer": { - "bytes": 78424, - "gzip": 27684 + "bytes": 78732, + "gzip": 27803 }, "Custom-scale line scene": { "bytes": 27756, diff --git a/benchmarks/comparison/bundle-baseline.json b/benchmarks/comparison/bundle-baseline.json index 2173d00c..25ca8b2b 100644 --- a/benchmarks/comparison/bundle-baseline.json +++ b/benchmarks/comparison/bundle-baseline.json @@ -1,8 +1,8 @@ { "schemaVersion": 4, - "generatedAt": "2026-08-12T23:00:29.524Z", + "generatedAt": "2026-08-14T05:55:21.257Z", "packageVersions": { - "tanstack": "0.11.2", + "tanstack": "0.12.0", "chartjs": "4.5.1", "echarts": "6.1.0", "recharts": "3.10.1", @@ -11,8 +11,8 @@ "sources": { "tanstack": { "kind": "workspace", - "revision": "b9d8c541ef6a94afdc1812555ee0e4459c81d140", - "inputDigest": "sha256:52b55a904317f891ec664992193770fd9468bddddeef1e8f9fe10ec113051b05" + "revision": "56567ec51902d8af0d0d730f3a31206999cc8438", + "inputDigest": "sha256:6755913ae5f9c34a2bbb1ccb7cd2144274bc37d74051c091fc279ac3d1d7a4cd" }, "chartjs": { "kind": "package", @@ -45,88 +45,88 @@ }, "bundles": { "tanstack-line-basic": { - "minifiedBytes": 105444, - "gzipBytes": 38355, - "brotliBytes": 33936, - "incrementalGzipBytes": 38355, - "incrementalBrotliBytes": 33936 + "minifiedBytes": 105944, + "gzipBytes": 38533, + "brotliBytes": 34109, + "incrementalGzipBytes": 38533, + "incrementalBrotliBytes": 34109 }, "tanstack-line-interactive": { - "minifiedBytes": 110885, - "gzipBytes": 40139, - "brotliBytes": 35457, - "incrementalGzipBytes": 40139, - "incrementalBrotliBytes": 35457 + "minifiedBytes": 111385, + "gzipBytes": 40302, + "brotliBytes": 35551, + "incrementalGzipBytes": 40302, + "incrementalBrotliBytes": 35551 }, "tanstack-line-advanced": { - "minifiedBytes": 118073, - "gzipBytes": 42438, - "brotliBytes": 37478, - "incrementalGzipBytes": 42438, - "incrementalBrotliBytes": 37478 + "minifiedBytes": 118575, + "gzipBytes": 42629, + "brotliBytes": 37541, + "incrementalGzipBytes": 42629, + "incrementalBrotliBytes": 37541 }, "tanstack-bar-basic": { - "minifiedBytes": 113973, - "gzipBytes": 41522, - "brotliBytes": 36650, - "incrementalGzipBytes": 41522, - "incrementalBrotliBytes": 36650 + "minifiedBytes": 114612, + "gzipBytes": 41793, + "brotliBytes": 36837, + "incrementalGzipBytes": 41793, + "incrementalBrotliBytes": 36837 }, "tanstack-bar-interactive": { - "minifiedBytes": 118265, - "gzipBytes": 42870, - "brotliBytes": 37747, - "incrementalGzipBytes": 42870, - "incrementalBrotliBytes": 37747 + "minifiedBytes": 118904, + "gzipBytes": 43127, + "brotliBytes": 37954, + "incrementalGzipBytes": 43127, + "incrementalBrotliBytes": 37954 }, "tanstack-bar-advanced": { - "minifiedBytes": 118604, - "gzipBytes": 43012, - "brotliBytes": 37919, - "incrementalGzipBytes": 43012, - "incrementalBrotliBytes": 37919 + "minifiedBytes": 119243, + "gzipBytes": 43268, + "brotliBytes": 38025, + "incrementalGzipBytes": 43268, + "incrementalBrotliBytes": 38025 }, "tanstack-area-basic": { - "minifiedBytes": 110531, - "gzipBytes": 40265, - "brotliBytes": 35653, - "incrementalGzipBytes": 40265, - "incrementalBrotliBytes": 35653 + "minifiedBytes": 111028, + "gzipBytes": 40457, + "brotliBytes": 35802, + "incrementalGzipBytes": 40457, + "incrementalBrotliBytes": 35802 }, "tanstack-area-interactive": { - "minifiedBytes": 115972, - "gzipBytes": 42001, - "brotliBytes": 37115, - "incrementalGzipBytes": 42001, - "incrementalBrotliBytes": 37115 + "minifiedBytes": 116469, + "gzipBytes": 42189, + "brotliBytes": 37272, + "incrementalGzipBytes": 42189, + "incrementalBrotliBytes": 37272 }, "tanstack-area-advanced": { - "minifiedBytes": 123342, - "gzipBytes": 44453, - "brotliBytes": 39193, - "incrementalGzipBytes": 44453, - "incrementalBrotliBytes": 39193 + "minifiedBytes": 123841, + "gzipBytes": 44639, + "brotliBytes": 39311, + "incrementalGzipBytes": 44639, + "incrementalBrotliBytes": 39311 }, "tanstack-scatter-basic": { - "minifiedBytes": 106483, - "gzipBytes": 38737, - "brotliBytes": 34248, - "incrementalGzipBytes": 38737, - "incrementalBrotliBytes": 34248 + "minifiedBytes": 106983, + "gzipBytes": 38919, + "brotliBytes": 34369, + "incrementalGzipBytes": 38919, + "incrementalBrotliBytes": 34369 }, "tanstack-scatter-interactive": { - "minifiedBytes": 111924, - "gzipBytes": 40487, - "brotliBytes": 35678, - "incrementalGzipBytes": 40487, - "incrementalBrotliBytes": 35678 + "minifiedBytes": 112424, + "gzipBytes": 40678, + "brotliBytes": 35859, + "incrementalGzipBytes": 40678, + "incrementalBrotliBytes": 35859 }, "tanstack-scatter-advanced": { - "minifiedBytes": 111940, - "gzipBytes": 40494, - "brotliBytes": 35700, - "incrementalGzipBytes": 40494, - "incrementalBrotliBytes": 35700 + "minifiedBytes": 112440, + "gzipBytes": 40683, + "brotliBytes": 35866, + "incrementalGzipBytes": 40683, + "incrementalBrotliBytes": 35866 }, "chartjs-line-basic": { "minifiedBytes": 137909, diff --git a/benchmarks/conformance/DEFINITION-COVERAGE-AUDIT.md b/benchmarks/conformance/DEFINITION-COVERAGE-AUDIT.md index 2aebf161..5308ba7d 100644 --- a/benchmarks/conformance/DEFINITION-COVERAGE-AUDIT.md +++ b/benchmarks/conformance/DEFINITION-COVERAGE-AUDIT.md @@ -2,7 +2,7 @@ Date: 2026-08-10 -Scope: all 117 catalog directories. The 67 cases previously classified as +Scope: all 188 catalog directories. The 67 cases previously classified as strict custom authoring, preparation review, or shell-only are reviewed beside the former 42-case definition-native control group from [the custom authoring audit](./CUSTOM-AUTHORING-AUDIT.md). That audit remains @@ -31,12 +31,12 @@ work: | Disposition | Cases | Meaning | | --------------------- | ------: | ------------------------------------------------------------------------ | -| Definition now | 64 | Current marks and eager transforms are sufficient | +| Definition now | 135 | Current marks and eager transforms are sufficient | | First-party primitive | 35 | Add a reusable mark, transform, layout, guide, or controlled signal | | Optional primitive | 15 | Keep a heavy dependency granular, but hide its layout DTOs and lifecycle | | Application boundary | 2 | The remaining work is product state, DOM layout, or data arrival | | Inline custom mark | 1 | The geometry is intentionally case-specific | -| **Total** | **117** | | +| **Total** | **188** | | A normal definition does not require every implementation algorithm to live in Charts core. D3 may implement an optional `sankeyDiagram`, `densityContour`, @@ -222,35 +222,106 @@ ownership boundary. ## Interaction and shell cases -| Case | Disposition | Target definition or application boundary | -| -------------------------------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [80 — Axis pointer](./cases/80-echarts-axis-pointer/tanstack.ts) | Definition now | Resolved with `focusGuideX`, grouped x focus, native grouped tooltip, and the native color legend; only conformance observation remains outside. | -| [81 — Interactive legend](./cases/81-recharts-interactive-legend/tanstack.ts) | First-party primitive | Resolved with a controlled accessible color legend: full domains resolve before series geometry and focus points are filtered. | -| [82 — Chart/table selection](./cases/82-chart-table-selection/view.tsx) | First-party primitive | Resolved with `keyedSelection` and `whenSelected`; the semantic HTML table, status, clear action, representative-row policy, and persistence remain application or case UI. | -| [83 — Focus/context](./cases/83-focus-context-window/view.tsx) | Optional primitive | `brushX`, controlled signals, and `keyedSelection` own the semantic window and detail selection. Two chart hosts are warranted rather than `viewGrid`: detail and overview have independent rows, domains, axes, margins, sizes, and behavior. The range input and four-month policy remain application UI. | -| [84 — Pinned nested tooltip](./cases/84-pinned-nested-chart-tooltip/view.tsx) | Definition now | Resolved with sticky pin-only tooltip visibility, portal placement, dismissal, inline pinned state, and adapter body composition; only cohort policy and the nested child remain outside. | -| [85 — Resource lanes](./cases/85-scrollable-resource-lanes/tanstack.ts) | Application boundary | Native interval marks, inferred ID identity, focus, and tooltip own chart semantics. DOM scrolling, fixed rail/legend, auto-scroll, persistent details, and schedule semantics remain application-owned. | -| [86 — Streaming window](./cases/86-streaming-window-preservation/view.tsx) | Application boundary | Resolved: one exported definition bounds rows before scale inference, paints a decorative line plus interactive observations, and owns focus and tooltip. Arrival, viewport policy, controls, and announcements remain application-owned. | -| [87 — Synchronized cursors](./cases/87-echarts-synchronized-cursors/tanstack.ts) | Definition now | Resolved with one `viewGrid`, shared x semantics, grouped focus, child `focusGuideX` marks, decorative lines, and native sticky pinning. Only the live value summary remains application UI. | -| [88 — Free cursor](./cases/88-echarts-free-cursor/tanstack.ts) | First-party primitive | Resolved with `continuousCursor`: final-scale inversion, unsnapped rules, marker and labels, transient pointer/touch previews, controlled pinning and Escape clearing are definition-owned. Semantic sliders and status remain application UI. | -| [89 — Brush selection](./cases/89-brush-range-selection/tanstack.ts) | Optional primitive | Resolved with controlled `brushX`: final-scale candidate mapping, observed-date snapping, reverse normalization, painting, keyboard handles, cancellation, and teardown are definition-owned. The monthly cohort and live financial summary remain application-owned. | -| [90 — Zoomable window](./cases/90-zoomable-time-window/tanstack.ts) | Optional primitive | Resolved with controlled `zoomX`: final-scale inversion, pointer-anchored wheel zoom, drag and horizontal-wheel pan, touch, keyboard, clamping, cancellation, focus-gated wheel capture, and teardown are definition-owned. Visible-row and y-domain policy, status, reset, and persistence remain application-owned. | -| [91 — Playback scrubber](./cases/91-timeline-playback-scrubber/tanstack.ts) | First-party primitive | Resolved with controlled `handleX`: final-scale track, rule and playhead painting, observed-date snapping, pointer and touch capture, cancellation, keyboard slider semantics, and teardown are definition-owned. Playback timing, transport controls, status, and announcements remain application-owned. | -| [92 — Editable range](./cases/92-editable-event-range/tanstack.ts) | First-party primitive | Resolved with the same controlled `handleX`: final-scale end-handle painting, constrained date snapping, semantic lane placement, pointer and touch capture, cancellation, keyboard slider semantics, and teardown are definition-owned. The date input, validation, event constraint, commit state, status, and exact-value alternative remain application-owned. | -| [112 — Entrance motion](./cases/112-motion-entrance/tanstack.ts) | Definition now | Motion is already definition-local; replay and demo settings are shell controls. | -| [113 — Spring updates](./cases/113-motion-updates/tanstack.ts) | Definition now | Keyed enter/update/exit, interruption, and per-datum policy are already declarative; stage controls remain outside. | -| [114 — Spring line](./cases/114-spring-line-motion/tanstack.ts) | Definition now | Line morph and per-series spring overrides are already declarative; demo controls remain outside. | -| [115 — Definition motion](./cases/115-definition-motion/tanstack.ts) | Definition now | Chart, mark, datum, axis, tick, and label motion are already definition-owned. | -| [117 — Focus cursor motion](./cases/117-focus-cursor-motion/tanstack.ts) | First-party primitive | Add a stable-key focus guide with x/y rules, marker, labels, and normal motion; remove the second SVG and spring loop. | -| [118 — Token calendar](./cases/118-token-usage-calendar/shell.tsx) | First-party primitive | Add per-tick text style, anchor, and offset accessors so the first month label needs no post-render DOM mutation. | -| [119 — Stacked bar cursor](./cases/119-stacked-bar-band-cursor/chart.ts) | First-party primitive | Use renderer-native `crosshair` axes for the categorical band, endpoint rule, labels, grouped focus, and ordinary definition motion; only conformance observation remains outside. | -| [120 — Themed area card](./cases/120-themed-interactive-area/chart.ts) | Definition now | Native area, line, points, gradients, sparse guides, tooltip, and keyed spring policy own the chart; the range selector and card remain application UI. | -| [121 — Active bar dashboard](./cases/121-active-bar-dashboard/chart.ts) | Definition now | Native gradient bars, focus states, band cursor, tooltip, and spring updates own the chart; KPI totals double as application-owned series controls. | -| [122 — KPI sparklines](./cases/122-premium-kpi-sparklines/chart.ts) | Definition now | Three guide-free definitions use ordinary area and line marks, gradients, CSS-variable paint, and keyed spring updates; metric copy and responsive card layout remain application content. | -| [123 — Active donut](./cases/123-active-donut-metric/chart.ts) | Definition now | Native pie allocation, rounded arcs, selected wedge and ring layers, center text, tooltip, and spring motion own the visualization; legend buttons own persistent selection. | -| [124 — Theme matrix](./cases/124-theme-palette-matrix/chart.ts) | Definition now | The same area-and-line definition is rendered through three scoped CSS-variable themes with identical geometry; only the three-card matrix and labels are application composition. | -| [125 — Sales funnel](./cases/125-sales-funnel/tanstack.ts) | Definition now | Derive centered stage boundaries from the ordered raw values, then compose one native `areaX` group per stage with direct labels and stable semantic keys. | -| [126 — Drillable sunburst](./cases/126-drillable-sunburst/tanstack.ts) | Optional primitive | The optional sunburst mark owns focused-root partitioning, bounded visible depth, hierarchy-aware polar motion, and stable node geometry; the application owns selected-root state and the center back control. | +| Case | Disposition | Target definition or application boundary | +| ---------------------------------------------------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| [80 — Axis pointer](./cases/80-echarts-axis-pointer/tanstack.ts) | Definition now | Resolved with `focusGuideX`, grouped x focus, native grouped tooltip, and the native color legend; only conformance observation remains outside. | +| [81 — Interactive legend](./cases/81-recharts-interactive-legend/tanstack.ts) | First-party primitive | Resolved with a controlled accessible color legend: full domains resolve before series geometry and focus points are filtered. | +| [82 — Chart/table selection](./cases/82-chart-table-selection/view.tsx) | First-party primitive | Resolved with `keyedSelection` and `whenSelected`; the semantic HTML table, status, clear action, representative-row policy, and persistence remain application or case UI. | +| [83 — Focus/context](./cases/83-focus-context-window/view.tsx) | Optional primitive | `brushX`, controlled signals, and `keyedSelection` own the semantic window and detail selection. Two chart hosts are warranted rather than `viewGrid`: detail and overview have independent rows, domains, axes, margins, sizes, and behavior. The range input and four-month policy remain application UI. | +| [84 — Pinned nested tooltip](./cases/84-pinned-nested-chart-tooltip/view.tsx) | Definition now | Resolved with sticky pin-only tooltip visibility, portal placement, dismissal, inline pinned state, and adapter body composition; only cohort policy and the nested child remain outside. | +| [85 — Resource lanes](./cases/85-scrollable-resource-lanes/tanstack.ts) | Application boundary | Native interval marks, inferred ID identity, focus, and tooltip own chart semantics. DOM scrolling, fixed rail/legend, auto-scroll, persistent details, and schedule semantics remain application-owned. | +| [86 — Streaming window](./cases/86-streaming-window-preservation/view.tsx) | Application boundary | Resolved: one exported definition bounds rows before scale inference, paints a decorative line plus interactive observations, and owns focus and tooltip. Arrival, viewport policy, controls, and announcements remain application-owned. | +| [87 — Synchronized cursors](./cases/87-echarts-synchronized-cursors/tanstack.ts) | Definition now | Resolved with one `viewGrid`, shared x semantics, grouped focus, child `focusGuideX` marks, decorative lines, and native sticky pinning. Only the live value summary remains application UI. | +| [88 — Free cursor](./cases/88-echarts-free-cursor/tanstack.ts) | First-party primitive | Resolved with `continuousCursor`: final-scale inversion, unsnapped rules, marker and labels, transient pointer/touch previews, controlled pinning and Escape clearing are definition-owned. Semantic sliders and status remain application UI. | +| [89 — Brush selection](./cases/89-brush-range-selection/tanstack.ts) | Optional primitive | Resolved with controlled `brushX`: final-scale candidate mapping, observed-date snapping, reverse normalization, painting, keyboard handles, cancellation, and teardown are definition-owned. The monthly cohort and live financial summary remain application-owned. | +| [90 — Zoomable window](./cases/90-zoomable-time-window/tanstack.ts) | Optional primitive | Resolved with controlled `zoomX`: final-scale inversion, pointer-anchored wheel zoom, drag and horizontal-wheel pan, touch, keyboard, clamping, cancellation, focus-gated wheel capture, and teardown are definition-owned. Visible-row and y-domain policy, status, reset, and persistence remain application-owned. | +| [91 — Playback scrubber](./cases/91-timeline-playback-scrubber/tanstack.ts) | First-party primitive | Resolved with controlled `handleX`: final-scale track, rule and playhead painting, observed-date snapping, pointer and touch capture, cancellation, keyboard slider semantics, and teardown are definition-owned. Playback timing, transport controls, status, and announcements remain application-owned. | +| [92 — Editable range](./cases/92-editable-event-range/tanstack.ts) | First-party primitive | Resolved with the same controlled `handleX`: final-scale end-handle painting, constrained date snapping, semantic lane placement, pointer and touch capture, cancellation, keyboard slider semantics, and teardown are definition-owned. The date input, validation, event constraint, commit state, status, and exact-value alternative remain application-owned. | +| [112 — Entrance motion](./cases/112-motion-entrance/tanstack.ts) | Definition now | Motion is already definition-local; replay and demo settings are shell controls. | +| [113 — Spring updates](./cases/113-motion-updates/tanstack.ts) | Definition now | Keyed enter/update/exit, interruption, and per-datum policy are already declarative; stage controls remain outside. | +| [114 — Spring line](./cases/114-spring-line-motion/tanstack.ts) | Definition now | Line morph and per-series spring overrides are already declarative; demo controls remain outside. | +| [115 — Definition motion](./cases/115-definition-motion/tanstack.ts) | Definition now | Chart, mark, datum, axis, tick, and label motion are already definition-owned. | +| [117 — Focus cursor motion](./cases/117-focus-cursor-motion/tanstack.ts) | First-party primitive | Add a stable-key focus guide with x/y rules, marker, labels, and normal motion; remove the second SVG and spring loop. | +| [118 — Token calendar](./cases/118-token-usage-calendar/shell.tsx) | First-party primitive | Add per-tick text style, anchor, and offset accessors so the first month label needs no post-render DOM mutation. | +| [119 — Stacked bar cursor](./cases/119-stacked-bar-band-cursor/chart.ts) | First-party primitive | Use renderer-native `crosshair` axes for the categorical band, endpoint rule, labels, grouped focus, and ordinary definition motion; only conformance observation remains outside. | +| [120 — Themed area card](./cases/120-themed-interactive-area/chart.ts) | Definition now | Native area, line, points, gradients, sparse guides, tooltip, and keyed spring policy own the chart; the range selector and card remain application UI. | +| [121 — Active bar dashboard](./cases/121-active-bar-dashboard/chart.ts) | Definition now | Native gradient bars, focus states, band cursor, tooltip, and spring updates own the chart; KPI totals double as application-owned series controls. | +| [122 — KPI sparklines](./cases/122-premium-kpi-sparklines/chart.ts) | Definition now | Three guide-free definitions use ordinary area and line marks, gradients, CSS-variable paint, and keyed spring updates; metric copy and responsive card layout remain application content. | +| [123 — Active donut](./cases/123-active-donut-metric/chart.ts) | Definition now | Native pie allocation, rounded arcs, selected wedge and ring layers, center text, tooltip, and spring motion own the visualization; legend buttons own persistent selection. | +| [124 — Theme matrix](./cases/124-theme-palette-matrix/chart.ts) | Definition now | The same area-and-line definition is rendered through three scoped CSS-variable themes with identical geometry; only the three-card matrix and labels are application composition. | +| [125 — Sales funnel](./cases/125-sales-funnel/tanstack.ts) | Definition now | Derive centered stage boundaries from the ordered raw values, then compose one native `areaX` group per stage with direct labels and stable semantic keys. | +| [126 — Drillable sunburst](./cases/126-drillable-sunburst/tanstack.ts) | Optional primitive | The optional sunburst mark owns focused-root partitioning, bounded visible depth, hierarchy-aware polar motion, and stable node geometry; the application owns selected-root state and the center back control. | +| [127 — shadcn dashboard](./cases/127-shadcn-dashboard/tanstack.ts) | Definition now | Shared native TanStack Charts dashboard definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [133 — shadcn area-axes](./cases/133-shadcn-area-axes/tanstack.ts) | Definition now | Shared native TanStack Charts area definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [134 — shadcn area-default](./cases/134-shadcn-area-default/tanstack.ts) | Definition now | Shared native TanStack Charts area definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [135 — shadcn area-gradient](./cases/135-shadcn-area-gradient/tanstack.ts) | Definition now | Shared native TanStack Charts area definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [136 — shadcn area-icons](./cases/136-shadcn-area-icons/tanstack.ts) | Definition now | Shared native TanStack Charts area definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [137 — shadcn area-interactive](./cases/137-shadcn-area-interactive/tanstack.ts) | Definition now | Shared native TanStack Charts area definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [138 — shadcn area-legend](./cases/138-shadcn-area-legend/tanstack.ts) | Definition now | Shared native TanStack Charts area definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [139 — shadcn area-linear](./cases/139-shadcn-area-linear/tanstack.ts) | Definition now | Shared native TanStack Charts area definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [140 — shadcn area-stacked-expand](./cases/140-shadcn-area-stacked-expand/tanstack.ts) | Definition now | Shared native TanStack Charts area definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [141 — shadcn area-stacked](./cases/141-shadcn-area-stacked/tanstack.ts) | Definition now | Shared native TanStack Charts area definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [142 — shadcn area-step](./cases/142-shadcn-area-step/tanstack.ts) | Definition now | Shared native TanStack Charts area definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [143 — shadcn bar-active](./cases/143-shadcn-bar-active/tanstack.ts) | Definition now | Shared native TanStack Charts bar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [144 — shadcn bar-default](./cases/144-shadcn-bar-default/tanstack.ts) | Definition now | Shared native TanStack Charts bar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [145 — shadcn bar-horizontal](./cases/145-shadcn-bar-horizontal/tanstack.ts) | Definition now | Shared native TanStack Charts bar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [146 — shadcn bar-interactive](./cases/146-shadcn-bar-interactive/tanstack.ts) | Definition now | Shared native TanStack Charts bar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [147 — shadcn bar-label-custom](./cases/147-shadcn-bar-label-custom/tanstack.ts) | Definition now | Shared native TanStack Charts bar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [148 — shadcn bar-label](./cases/148-shadcn-bar-label/tanstack.ts) | Definition now | Shared native TanStack Charts bar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [149 — shadcn bar-mixed](./cases/149-shadcn-bar-mixed/tanstack.ts) | Definition now | Shared native TanStack Charts bar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [128 — shadcn bar-multiple](./cases/128-shadcn-bar-multiple/tanstack.ts) | Definition now | Shared native TanStack Charts bar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [150 — shadcn bar-negative](./cases/150-shadcn-bar-negative/tanstack.ts) | Definition now | Shared native TanStack Charts bar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [151 — shadcn bar-stacked](./cases/151-shadcn-bar-stacked/tanstack.ts) | Definition now | Shared native TanStack Charts bar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [152 — shadcn line-default](./cases/152-shadcn-line-default/tanstack.ts) | Definition now | Shared native TanStack Charts line definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [153 — shadcn line-dots-colors](./cases/153-shadcn-line-dots-colors/tanstack.ts) | Definition now | Shared native TanStack Charts line definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [154 — shadcn line-dots-custom](./cases/154-shadcn-line-dots-custom/tanstack.ts) | Definition now | Shared native TanStack Charts line definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [155 — shadcn line-dots](./cases/155-shadcn-line-dots/tanstack.ts) | Definition now | Shared native TanStack Charts line definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [156 — shadcn line-interactive](./cases/156-shadcn-line-interactive/tanstack.ts) | Definition now | Shared native TanStack Charts line definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [157 — shadcn line-label-custom](./cases/157-shadcn-line-label-custom/tanstack.ts) | Definition now | Shared native TanStack Charts line definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [158 — shadcn line-label](./cases/158-shadcn-line-label/tanstack.ts) | Definition now | Shared native TanStack Charts line definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [159 — shadcn line-linear](./cases/159-shadcn-line-linear/tanstack.ts) | Definition now | Shared native TanStack Charts line definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [160 — shadcn line-multiple](./cases/160-shadcn-line-multiple/tanstack.ts) | Definition now | Shared native TanStack Charts line definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [161 — shadcn line-step](./cases/161-shadcn-line-step/tanstack.ts) | Definition now | Shared native TanStack Charts line definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [162 — shadcn pie-donut-active](./cases/162-shadcn-pie-donut-active/tanstack.ts) | Definition now | Shared native TanStack Charts pie definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [129 — shadcn pie-donut-text](./cases/129-shadcn-pie-donut-text/tanstack.ts) | Definition now | Shared native TanStack Charts pie definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [163 — shadcn pie-donut](./cases/163-shadcn-pie-donut/tanstack.ts) | Definition now | Shared native TanStack Charts pie definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [164 — shadcn pie-interactive](./cases/164-shadcn-pie-interactive/tanstack.ts) | Definition now | Shared native TanStack Charts pie definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [165 — shadcn pie-label-custom](./cases/165-shadcn-pie-label-custom/tanstack.ts) | Definition now | Shared native TanStack Charts pie definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [166 — shadcn pie-label-list](./cases/166-shadcn-pie-label-list/tanstack.ts) | Definition now | Shared native TanStack Charts pie definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [167 — shadcn pie-label](./cases/167-shadcn-pie-label/tanstack.ts) | Definition now | Shared native TanStack Charts pie definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [168 — shadcn pie-legend](./cases/168-shadcn-pie-legend/tanstack.ts) | Definition now | Shared native TanStack Charts pie definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [169 — shadcn pie-separator-none](./cases/169-shadcn-pie-separator-none/tanstack.ts) | Definition now | Shared native TanStack Charts pie definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [170 — shadcn pie-simple](./cases/170-shadcn-pie-simple/tanstack.ts) | Definition now | Shared native TanStack Charts pie definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [171 — shadcn pie-stacked](./cases/171-shadcn-pie-stacked/tanstack.ts) | Definition now | Shared native TanStack Charts pie definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [172 — shadcn radar-default](./cases/172-shadcn-radar-default/tanstack.ts) | Definition now | Shared native TanStack Charts radar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [173 — shadcn radar-dots](./cases/173-shadcn-radar-dots/tanstack.ts) | Definition now | Shared native TanStack Charts radar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [174 — shadcn radar-grid-circle-fill](./cases/174-shadcn-radar-grid-circle-fill/tanstack.ts) | Definition now | Shared native TanStack Charts radar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [175 — shadcn radar-grid-circle-no-lines](./cases/175-shadcn-radar-grid-circle-no-lines/tanstack.ts) | Definition now | Shared native TanStack Charts radar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [176 — shadcn radar-grid-circle](./cases/176-shadcn-radar-grid-circle/tanstack.ts) | Definition now | Shared native TanStack Charts radar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [177 — shadcn radar-grid-custom](./cases/177-shadcn-radar-grid-custom/tanstack.ts) | Definition now | Shared native TanStack Charts radar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [178 — shadcn radar-grid-fill](./cases/178-shadcn-radar-grid-fill/tanstack.ts) | Definition now | Shared native TanStack Charts radar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [179 — shadcn radar-grid-none](./cases/179-shadcn-radar-grid-none/tanstack.ts) | Definition now | Shared native TanStack Charts radar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [180 — shadcn radar-icons](./cases/180-shadcn-radar-icons/tanstack.ts) | Definition now | Shared native TanStack Charts radar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [181 — shadcn radar-label-custom](./cases/181-shadcn-radar-label-custom/tanstack.ts) | Definition now | Shared native TanStack Charts radar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [182 — shadcn radar-legend](./cases/182-shadcn-radar-legend/tanstack.ts) | Definition now | Shared native TanStack Charts radar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [183 — shadcn radar-lines-only](./cases/183-shadcn-radar-lines-only/tanstack.ts) | Definition now | Shared native TanStack Charts radar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [130 — shadcn radar-multiple](./cases/130-shadcn-radar-multiple/tanstack.ts) | Definition now | Shared native TanStack Charts radar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [184 — shadcn radar-radius](./cases/184-shadcn-radar-radius/tanstack.ts) | Definition now | Shared native TanStack Charts radar definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [185 — shadcn radial-grid](./cases/185-shadcn-radial-grid/tanstack.ts) | Definition now | Shared native TanStack Charts radial definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [186 — shadcn radial-label](./cases/186-shadcn-radial-label/tanstack.ts) | Definition now | Shared native TanStack Charts radial definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [187 — shadcn radial-shape](./cases/187-shadcn-radial-shape/tanstack.ts) | Definition now | Shared native TanStack Charts radial definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [188 — shadcn radial-simple](./cases/188-shadcn-radial-simple/tanstack.ts) | Definition now | Shared native TanStack Charts radial definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [189 — shadcn radial-stacked](./cases/189-shadcn-radial-stacked/tanstack.ts) | Definition now | Shared native TanStack Charts radial definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [131 — shadcn radial-text](./cases/131-shadcn-radial-text/tanstack.ts) | Definition now | Shared native TanStack Charts radial definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [132 — shadcn tooltip-advanced](./cases/132-shadcn-tooltip-advanced/tanstack.ts) | Definition now | Shared native TanStack Charts tooltip definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [190 — shadcn tooltip-default](./cases/190-shadcn-tooltip-default/tanstack.ts) | Definition now | Shared native TanStack Charts tooltip definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [191 — shadcn tooltip-formatter](./cases/191-shadcn-tooltip-formatter/tanstack.ts) | Definition now | Shared native TanStack Charts tooltip definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [192 — shadcn tooltip-icons](./cases/192-shadcn-tooltip-icons/tanstack.ts) | Definition now | Shared native TanStack Charts tooltip definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [193 — shadcn tooltip-indicator-line](./cases/193-shadcn-tooltip-indicator-line/tanstack.ts) | Definition now | Shared native TanStack Charts tooltip definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [194 — shadcn tooltip-indicator-none](./cases/194-shadcn-tooltip-indicator-none/tanstack.ts) | Definition now | Shared native TanStack Charts tooltip definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [195 — shadcn tooltip-label-custom](./cases/195-shadcn-tooltip-label-custom/tanstack.ts) | Definition now | Shared native TanStack Charts tooltip definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [196 — shadcn tooltip-label-formatter](./cases/196-shadcn-tooltip-label-formatter/tanstack.ts) | Definition now | Shared native TanStack Charts tooltip definitions reproduce the pinned shadcn variant; the card remains application presentation. | +| [197 — shadcn tooltip-label-none](./cases/197-shadcn-tooltip-label-none/tanstack.ts) | Definition now | Shared native TanStack Charts tooltip definitions reproduce the pinned shadcn variant; the card remains application presentation. | ## Reference evidence @@ -282,8 +353,8 @@ bundle. ## Delivery result -All 117 catalog directories now have one roadmap record and case-local -evidence. All 114 normal-definition cases are verified against their current +All 188 catalog directories now have one roadmap record and case-local +evidence. All 185 normal-definition cases are verified against their current boundary; only cases 85, 86, and 116 retain accepted application or bespoke geometry work. The roadmap validator compares its IDs with the live catalog directories so a new case cannot silently remain outside this review. diff --git a/benchmarks/conformance/DEFINITION-COVERAGE-OVERVIEW.md b/benchmarks/conformance/DEFINITION-COVERAGE-OVERVIEW.md index e1f3ea0d..45f712e3 100644 --- a/benchmarks/conformance/DEFINITION-COVERAGE-OVERVIEW.md +++ b/benchmarks/conformance/DEFINITION-COVERAGE-OVERVIEW.md @@ -2,19 +2,19 @@ Date: 2026-08-10 -The catalog has 117 case directories. One hundred fourteen visualizations now use +The catalog has 188 case directories. 185 visualizations now use normal chart definitions. Cases 85 and 86 retain application shells, and Case 116 retains one deliberately bespoke inline mark. No other case-owned layout or renderer is an accepted endpoint. | Disposition | Cases | Result | | --------------------- | ------: | ------------------------------------------------- | -| Definition now | 64 | Normal definition | +| Definition now | 135 | Normal definition | | First-party primitive | 35 | Normal definition | | Optional primitive | 15 | Normal definition | | Application boundary | 2 | Accepted boundary | | Inline custom mark | 1 | Accepted boundary | -| **Total** | **117** | **114 normal definitions; 3 accepted boundaries** | +| **Total** | **188** | **185 normal definitions; 3 accepted boundaries** | ## Shared decisions @@ -63,7 +63,7 @@ presentation policy. ## Verification -- The roadmap validator compares all 117 roadmap and audit IDs with the live +- The roadmap validator compares all 188 roadmap and audit IDs with the live catalog directories, requires unique IDs, validates the capability DAG, and requires every verified or accepted case to cite evidence under its own directory. @@ -78,122 +78,193 @@ presentation policy. ## Case catalog -| Case | Disposition | Current boundary | Case-local evidence | -| --------------------------------------------------------------------------------------------------------------------------------- | --------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------- | -| [01-line-gaps — Apple stock line with seasonal gaps](./cases/01-line-gaps/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [02-multi-line-end-labels — Industry unemployment with end labels](./cases/02-multi-line-end-labels/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [03-temperature-range-band — San Francisco temperature range band](./cases/03-temperature-range-band/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [04-stacked-time-area — Industry unemployment stacked area](./cases/04-stacked-time-area/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [13-interval-timeline — Open-to-close price intervals](./cases/13-interval-timeline/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [14-error-bars — Point estimates with error bars](./cases/14-error-bars/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [15-boxplot — Grouped boxplot](./cases/15-boxplot/tanstack.ts) | First-party primitive | `composite-mark`, `box-mark` | `tanstack.ts`, `tanstack.test.ts` | -| [16-lollipop — Ranked lollipop chart](./cases/16-lollipop/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [17-dumbbell — Dumbbell comparison](./cases/17-dumbbell/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [18-cumulative-histogram — Cumulative histogram](./cases/18-cumulative-histogram/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [19-moving-average-line — San Francisco temperature moving averages](./cases/19-moving-average-line/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [20-normalized-stacked-area — Industry share of unemployment](./cases/20-normalized-stacked-area/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [21-streamgraph — Streamgraph](./cases/21-streamgraph/tanstack.ts) | First-party primitive | `inside-out-wiggle-stack` | `tanstack.ts`, `tanstack.test.ts` | -| [22-bollinger-band — Bollinger band](./cases/22-bollinger-band/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [24-quantitative-binned-heatmap — Quantitative binned heatmap](./cases/24-quantitative-binned-heatmap/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [25-calendar-heatmap — Precipitation calendar heatmap](./cases/25-calendar-heatmap/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [26-diverging-likert — Diverging Likert responses](./cases/26-diverging-likert/tanstack.ts) | First-party primitive | `anchored-stack` | `tanstack.ts`, `tanstack.test.ts` | -| [27-parallel-coordinates — Parallel coordinates](./cases/27-parallel-coordinates/tanstack.test.ts) | First-party primitive | `fold-transform` | `tanstack.test.ts` | -| [28-candlestick — Candlestick chart](./cases/28-candlestick/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [29-waterfall — Annual gasoline-price waterfall](./cases/29-waterfall/tanstack.ts) | First-party primitive | `waterfall-layout` | `tanstack.ts`, `tanstack.test.ts` | -| [30-slopegraph — Two-period slopegraph](./cases/30-slopegraph/tanstack.test.ts) | First-party primitive | `fold-transform` | `tanstack.test.ts` | -| [31-linear-regression — Scatterplot with linear regression](./cases/31-linear-regression/tanstack.ts) | First-party primitive | `composite-mark`, `line-x-mark`, `regression-mark` | `tanstack.ts`, `tanstack.test.ts` | -| [32-change-arrows — Metro inequality change arrows](./cases/32-change-arrows/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [33-difference-chart — Apple close versus moving average](./cases/33-difference-chart/tanstack.ts) | First-party primitive | `composite-mark`, `line-x-mark`, `resolved-mark-layout`, `difference-mark` | `tanstack.ts`, `tanstack.test.ts` | -| [34-pointer-tooltip — Pointer-selected tooltip](./cases/34-pointer-tooltip/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [35-grouped-tooltip — Grouped X tooltip](./cases/35-grouped-tooltip/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [36-hierarchy-tree — Tidy hierarchy tree](./cases/36-hierarchy-tree/tanstack.ts) | Optional primitive | `hierarchy-tree` | `tanstack.ts` | -| [37-delaunay-network — Delaunay spatial network](./cases/37-delaunay-network/tanstack.ts) | Optional primitive | `resolved-mark-layout`, `spatial-delaunay` | `tanstack.ts` | -| [38-contour-topography — Filled wind-speed contours](./cases/38-contour-topography/tanstack.ts) | Optional primitive | `spatial-contour` | `tanstack.ts` | -| [39-density-contours — Point density contours](./cases/39-density-contours/tanstack.ts) | Optional primitive | `resolved-mark-layout`, `spatial-density` | `tanstack.ts` | -| [40-force-directed-network — Les Misérables character network](./cases/40-force-directed-network/tanstack.ts) | Optional primitive | `network-force` | `tanstack.ts` | -| [40-geojson-map — Westport House floor plan](./cases/40-geojson-map/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [41-waffle-unit-chart — English letter frequency waffle](./cases/41-waffle-unit-chart/tanstack.ts) | First-party primitive | `resolved-mark-layout`, `waffle-mark` | `tanstack.ts` | -| [42-vector-field — Two-dimensional vector field](./cases/42-vector-field/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [43-hexbin-density — Hexagonally binned density](./cases/43-hexbin-density/tanstack.ts) | Optional primitive | `resolved-mark-layout`, `spatial-hexbin` | `tanstack.ts` | -| [44-framed-scatter — Framed guide-free scatterplot](./cases/44-framed-scatter/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [50-empirical-cdf — Empirical cumulative distribution](./cases/50-empirical-cdf/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [51-faceted-distributions — Faceted distributions](./cases/51-faceted-distributions/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [52-beeswarm-dodge — Beeswarm distribution](./cases/52-beeswarm-dodge/tanstack.ts) | First-party primitive | `resolved-mark-layout`, `dodge-layout` | `tanstack.ts` | -| [53-log-scale-scatter — Log-scale Flare class sizes](./cases/53-log-scale-scatter/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [54-bump-ranking — Industry unemployment bump chart](./cases/54-bump-ranking/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [55-indexed-multi-line — Indexed industry unemployment](./cases/55-indexed-multi-line/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [56-connected-scatter — Directed connected scatterplot](./cases/56-connected-scatter/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [57-scatter-marginal-histograms — Scatterplot with marginal histograms](./cases/57-scatter-marginal-histograms/tanstack.ts) | First-party primitive | `view-composition` | `tanstack.ts`, `tanstack.test.ts` | -| [58-select-extrema — Annotated AAPL extrema](./cases/58-select-extrema/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [59-grouped-reducer-bars — Mean penguin mass reducer bars](./cases/59-grouped-reducer-bars/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [60-lag-autocorrelation — AAPL lag-one autocorrelation](./cases/60-lag-autocorrelation/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [61-quantile-ribbon — Industry unemployment percentile ribbon](./cases/61-quantile-ribbon/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [62-ridgeline-density — Ridgeline density comparison](./cases/62-ridgeline-density/tanstack.ts) | First-party primitive | `ridgeline-mark` | `tanstack.ts`, `tanstack.test.ts` | -| [63-violin-distributions — Violin distribution comparison](./cases/63-violin-distributions/tanstack.ts) | First-party primitive | `violin-mark` | `tanstack.ts`, `tanstack.test.ts` | -| [64-marimekko-mosaic — Marimekko survey composition](./cases/64-marimekko-mosaic/tanstack.ts) | First-party primitive | `mosaic-layout` | `tanstack.ts`, `tanstack.test.ts` | -| [65-voronoi-nearest-tooltip — Voronoi nearest-point interaction](./cases/65-voronoi-nearest-tooltip/tanstack.ts) | Optional primitive | `spatial-voronoi` | `tanstack.ts` | -| [70-composed-chart — Layered Seattle weather chart](./cases/70-composed-chart/tanstack.ts) | First-party primitive | `bar-max-thickness` | `tanstack.ts`, `tanstack.test.ts` | -| [71-recharts-population-pyramid — Palmer penguins by species and sex](./cases/71-recharts-population-pyramid/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [72-recharts-mixed-bars — Stacked and adjacent Seattle weather bars](./cases/72-recharts-mixed-bars/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [73-many-point-scatter — Automobile specifications scatter](./cases/73-many-point-scatter/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [74-recharts-treemap — Flare analytics treemap](./cases/74-recharts-treemap/tanstack.ts) | Optional primitive | `resolved-mark-layout`, `hierarchy-treemap` | `tanstack.ts` | -| [75-radar — Simple radar chart](./cases/75-radar/tanstack.test.ts) | First-party primitive | `fold-transform` | `tanstack.test.ts` | -| [76-pie — English letter frequency pie](./cases/76-pie/tanstack.test.ts) | First-party primitive | `polar-value-allocation` | `tanstack.test.ts` | -| [77-donut — English letter frequency donut](./cases/77-donut/tanstack.test.ts) | First-party primitive | `polar-value-allocation` | `tanstack.test.ts` | -| [78-gauge — Survey agreement share gauge](./cases/78-gauge/tanstack.test.ts) | First-party primitive | `polar-value-allocation` | `tanstack.test.ts` | -| [80-echarts-axis-pointer — Snapped axis pointer with grouped tooltip](./cases/80-echarts-axis-pointer/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [81-recharts-interactive-legend — Interactive series legend](./cases/81-recharts-interactive-legend/tanstack.ts) | First-party primitive | `controlled-signals`, `interactive-legend` | `tanstack.ts`, `tanstack.test.ts`, `view.tsx` | -| [82-chart-table-selection — Linked chart and data table selection](./cases/82-chart-table-selection/tanstack.ts) | First-party primitive | `controlled-signals`, `keyed-selection` | `tanstack.ts`, `tanstack.test.ts`, `view.tsx` | -| [83-focus-context-window — Focus and context time window](./cases/83-focus-context-window/tanstack.ts) | Optional primitive | `controlled-signals`, `keyed-selection`, `configured-scale-inversion`, `brush-x` | `tanstack.ts`, `tanstack.test.ts`, `view.tsx` | -| [84-pinned-nested-chart-tooltip — Pinned penguin tooltip with a nested chart](./cases/84-pinned-nested-chart-tooltip/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `model.test.ts`, `view.tsx` | -| [85-scrollable-resource-lanes — Scrollable resource timeline lanes](./cases/85-scrollable-resource-lanes/tanstack.ts) | Application boundary | `application-boundary` | `tanstack.ts`, `tanstack.test.ts`, `shell.ts` | -| [86-streaming-window-preservation — Streaming window preservation](./cases/86-streaming-window-preservation/data.test.ts) | Application boundary | `application-boundary`, `decorative-mark` | `data.test.ts`, `tanstack.test.ts` | -| [87-echarts-synchronized-cursors — Synchronized cursors across views](./cases/87-echarts-synchronized-cursors/tanstack.ts) | Definition now | `view-composition`, `focus-guide`, `decorative-mark` | `tanstack.ts`, `tanstack.test.ts` | -| [88-echarts-free-cursor — Free cursor over car measurements](./cases/88-echarts-free-cursor/tanstack.ts) | First-party primitive | `controlled-signals`, `configured-scale-inversion`, `continuous-cursor` | `tanstack.ts`, `tanstack.test.ts` | -| [89-brush-range-selection — Observed monthly AAPL brush selection](./cases/89-brush-range-selection/tanstack.ts) | Optional primitive | `controlled-signals`, `configured-scale-inversion`, `brush-x` | `tanstack.ts`, `tanstack.test.ts` | -| [90-zoomable-time-window — Wheel zoom and pan over AAPL closes](./cases/90-zoomable-time-window/case.json) | Optional primitive | `controlled-signals`, `configured-scale-inversion`, `zoom-x` | `case.json`, `tanstack.ts`, `tanstack.test.ts` | -| [91-timeline-playback-scrubber — AAPL close playback scrubber](./cases/91-timeline-playback-scrubber/tanstack.ts) | First-party primitive | `controlled-signals`, `scale-handle` | `tanstack.ts`, `tanstack.test.ts` | -| [92-editable-event-range — Editable event range](./cases/92-editable-event-range/tanstack.ts) | First-party primitive | `controlled-signals`, `scale-handle` | `tanstack.ts`, `tanstack.test.ts` | -| [93-labeled-pie — Letter frequency pie with labels](./cases/93-labeled-pie/tanstack.test.ts) | First-party primitive | `polar-value-allocation`, `radial-labels` | `tanstack.test.ts` | -| [94-center-donut — Letter frequency donut with total](./cases/94-center-donut/tanstack.test.ts) | First-party primitive | `polar-value-allocation` | `tanstack.test.ts` | -| [95-rounded-donut — Rounded letter frequency donut](./cases/95-rounded-donut/tanstack.test.ts) | First-party primitive | `polar-value-allocation` | `tanstack.test.ts` | -| [96-nested-donut — Nested Flare package sizes](./cases/96-nested-donut/tanstack.test.ts) | First-party primitive | `polar-value-allocation` | `tanstack.test.ts` | -| [97-rose — English letter frequency rose](./cases/97-rose/tanstack.ts) | First-party primitive | `radial-bars` | `tanstack.ts`, `tanstack.test.ts` | -| [98-needle-gauge — County unemployment gauge](./cases/98-needle-gauge/tanstack.test.ts) | First-party primitive | `polar-value-allocation` | `tanstack.test.ts` | -| [99-comparative-radar — Comparative radar chart](./cases/99-comparative-radar/tanstack.test.ts) | First-party primitive | `fold-transform` | `tanstack.test.ts` | -| [100-radial-bars — Concentric letter frequency bars](./cases/100-radial-bars/tanstack.test.ts) | First-party primitive | `radial-bars` | `tanstack.test.ts` | -| [101-sunburst — Flare analytics sunburst](./cases/101-sunburst/tanstack.ts) | Optional primitive | `hierarchy-sunburst` | `tanstack.ts`, `tanstack.test.ts` | -| [102-world-choropleth — World learning-poverty choropleth](./cases/102-world-choropleth/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [103-bubble-map — World population bubble map](./cases/103-bubble-map/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [104-orthographic-globe — Orthographic globe with graticule](./cases/104-orthographic-globe/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [105-route-map — HMS Beagle voyage](./cases/105-route-map/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [106-polar-line — Seattle daily high-temperature polar line](./cases/106-polar-line/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [107-polar-scatter — Surface wind polar scatter](./cases/107-polar-scatter/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [108-country-choropleth — World population-density choropleth](./cases/108-country-choropleth/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [109-us-state-choropleth — United States county unemployment choropleth](./cases/109-us-state-choropleth/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [110-projection-gallery — Standard world projection gallery](./cases/110-projection-gallery/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [111-basic-sankey — Basic Sankey](./cases/111-basic-sankey/tanstack.ts) | Optional primitive | `resolved-mark-layout`, `network-sankey` | `tanstack.ts`, `tanstack.test.ts` | -| [111-sankey-flow — Sankey](./cases/111-sankey-flow/tanstack.ts) | Optional primitive | `resolved-mark-layout`, `network-sankey` | `tanstack.ts`, `tanstack.test.ts` | -| [112-motion-entrance — Staggered entrance motion](./cases/112-motion-entrance/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [113-motion-updates — Interrupted spring updates](./cases/113-motion-updates/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [114-spring-line-motion — Spring line updates](./cases/114-spring-line-motion/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [115-definition-motion — Definition-owned motion](./cases/115-definition-motion/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | -| [116-geometry-morph — Cross-chart geometry morph](./cases/116-geometry-morph/tanstack.ts) | Inline custom mark | `inline-custom-boundary` | `tanstack.ts`, `tanstack.test.ts` | -| [117-focus-cursor-motion — Focus and crosshair motion](./cases/117-focus-cursor-motion/tanstack.ts) | First-party primitive | `focus-guide` | `tanstack.ts`, `tanstack.test.ts` | -| [118-token-usage-calendar — Token use calendar heatmap](./cases/118-token-usage-calendar/tanstack.ts) | First-party primitive | `axis-tick-label-accessors` | `tanstack.ts`, `tanstack.test.ts`, `shell.test.ts` | -| [119-stacked-bar-band-cursor — Stacked bars with band and rule cursors](./cases/119-stacked-bar-band-cursor/chart.ts) | First-party primitive | `crosshair` | `chart.ts`, `model.test.ts`, `case.json` | -| [120-themed-interactive-area — Themed interactive area card](./cases/120-themed-interactive-area/chart.ts) | Definition now | `current-definition-api` | `chart.ts`, `tanstack.ts`, `tanstack.test.ts` | -| [121-active-bar-dashboard — Active bar dashboard](./cases/121-active-bar-dashboard/chart.ts) | Definition now | `current-definition-api` | `chart.ts`, `tanstack.test.ts`, `view.tsx` | -| [122-premium-kpi-sparklines — Premium KPI sparklines](./cases/122-premium-kpi-sparklines/chart.ts) | Definition now | `current-definition-api` | `chart.ts`, `tanstack.test.ts`, `view.tsx` | -| [123-active-donut-metric — Active donut metric](./cases/123-active-donut-metric/chart.ts) | Definition now | `current-definition-api` | `chart.ts`, `tanstack.test.ts`, `view.tsx` | -| [124-theme-palette-matrix — Theme palette matrix](./cases/124-theme-palette-matrix/chart.ts) | Definition now | `current-definition-api` | `chart.ts`, `tanstack.test.ts`, `view.tsx` | -| [125-sales-funnel — Sales conversion funnel](./cases/125-sales-funnel/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `model.test.ts`, `case.json` | -| [126-drillable-sunburst — Drillable Flare sunburst](./cases/126-drillable-sunburst/tanstack.ts) | Optional primitive | `hierarchy-sunburst` | `tanstack.ts`, `tanstack.test.ts`, `model.test.ts` | -| [bar-grouped — Grouped bars](./cases/bar-grouped/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [bar-horizontal-ranking — Horizontal ranking with long labels](./cases/bar-horizontal-ranking/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [bar-stacked — Stacked bars](./cases/bar-stacked/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [bar-vertical-sorted — Sorted vertical bars](./cases/bar-vertical-sorted/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [facets-anscombe — Anscombe quartet small multiples](./cases/facets-anscombe/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [heatmap-labeled — Labeled ordinal heatmap](./cases/heatmap-labeled/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [histogram — Histogram](./cases/histogram/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | -| [scatter-bubble — Bubble scatterplot](./cases/scatter-bubble/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| Case | Disposition | Current boundary | Case-local evidence | +| ----------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------- | +| [01-line-gaps — Apple stock line with seasonal gaps](./cases/01-line-gaps/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [02-multi-line-end-labels — Industry unemployment with end labels](./cases/02-multi-line-end-labels/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [03-temperature-range-band — San Francisco temperature range band](./cases/03-temperature-range-band/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [04-stacked-time-area — Industry unemployment stacked area](./cases/04-stacked-time-area/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [13-interval-timeline — Open-to-close price intervals](./cases/13-interval-timeline/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [14-error-bars — Point estimates with error bars](./cases/14-error-bars/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [15-boxplot — Grouped boxplot](./cases/15-boxplot/tanstack.ts) | First-party primitive | `composite-mark`, `box-mark` | `tanstack.ts`, `tanstack.test.ts` | +| [16-lollipop — Ranked lollipop chart](./cases/16-lollipop/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [17-dumbbell — Dumbbell comparison](./cases/17-dumbbell/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [18-cumulative-histogram — Cumulative histogram](./cases/18-cumulative-histogram/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [19-moving-average-line — San Francisco temperature moving averages](./cases/19-moving-average-line/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [20-normalized-stacked-area — Industry share of unemployment](./cases/20-normalized-stacked-area/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [21-streamgraph — Streamgraph](./cases/21-streamgraph/tanstack.ts) | First-party primitive | `inside-out-wiggle-stack` | `tanstack.ts`, `tanstack.test.ts` | +| [22-bollinger-band — Bollinger band](./cases/22-bollinger-band/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [24-quantitative-binned-heatmap — Quantitative binned heatmap](./cases/24-quantitative-binned-heatmap/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [25-calendar-heatmap — Precipitation calendar heatmap](./cases/25-calendar-heatmap/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [26-diverging-likert — Diverging Likert responses](./cases/26-diverging-likert/tanstack.ts) | First-party primitive | `anchored-stack` | `tanstack.ts`, `tanstack.test.ts` | +| [27-parallel-coordinates — Parallel coordinates](./cases/27-parallel-coordinates/tanstack.test.ts) | First-party primitive | `fold-transform` | `tanstack.test.ts` | +| [28-candlestick — Candlestick chart](./cases/28-candlestick/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [29-waterfall — Annual gasoline-price waterfall](./cases/29-waterfall/tanstack.ts) | First-party primitive | `waterfall-layout` | `tanstack.ts`, `tanstack.test.ts` | +| [30-slopegraph — Two-period slopegraph](./cases/30-slopegraph/tanstack.test.ts) | First-party primitive | `fold-transform` | `tanstack.test.ts` | +| [31-linear-regression — Scatterplot with linear regression](./cases/31-linear-regression/tanstack.ts) | First-party primitive | `composite-mark`, `line-x-mark`, `regression-mark` | `tanstack.ts`, `tanstack.test.ts` | +| [32-change-arrows — Metro inequality change arrows](./cases/32-change-arrows/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [33-difference-chart — Apple close versus moving average](./cases/33-difference-chart/tanstack.ts) | First-party primitive | `composite-mark`, `line-x-mark`, `resolved-mark-layout`, `difference-mark` | `tanstack.ts`, `tanstack.test.ts` | +| [34-pointer-tooltip — Pointer-selected tooltip](./cases/34-pointer-tooltip/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [35-grouped-tooltip — Grouped X tooltip](./cases/35-grouped-tooltip/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [36-hierarchy-tree — Tidy hierarchy tree](./cases/36-hierarchy-tree/tanstack.ts) | Optional primitive | `hierarchy-tree` | `tanstack.ts` | +| [37-delaunay-network — Delaunay spatial network](./cases/37-delaunay-network/tanstack.ts) | Optional primitive | `resolved-mark-layout`, `spatial-delaunay` | `tanstack.ts` | +| [38-contour-topography — Filled wind-speed contours](./cases/38-contour-topography/tanstack.ts) | Optional primitive | `spatial-contour` | `tanstack.ts` | +| [39-density-contours — Point density contours](./cases/39-density-contours/tanstack.ts) | Optional primitive | `resolved-mark-layout`, `spatial-density` | `tanstack.ts` | +| [40-force-directed-network — Les Misérables character network](./cases/40-force-directed-network/tanstack.ts) | Optional primitive | `network-force` | `tanstack.ts` | +| [40-geojson-map — Westport House floor plan](./cases/40-geojson-map/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [41-waffle-unit-chart — English letter frequency waffle](./cases/41-waffle-unit-chart/tanstack.ts) | First-party primitive | `resolved-mark-layout`, `waffle-mark` | `tanstack.ts` | +| [42-vector-field — Two-dimensional vector field](./cases/42-vector-field/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [43-hexbin-density — Hexagonally binned density](./cases/43-hexbin-density/tanstack.ts) | Optional primitive | `resolved-mark-layout`, `spatial-hexbin` | `tanstack.ts` | +| [44-framed-scatter — Framed guide-free scatterplot](./cases/44-framed-scatter/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [50-empirical-cdf — Empirical cumulative distribution](./cases/50-empirical-cdf/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [51-faceted-distributions — Faceted distributions](./cases/51-faceted-distributions/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [52-beeswarm-dodge — Beeswarm distribution](./cases/52-beeswarm-dodge/tanstack.ts) | First-party primitive | `resolved-mark-layout`, `dodge-layout` | `tanstack.ts` | +| [53-log-scale-scatter — Log-scale Flare class sizes](./cases/53-log-scale-scatter/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [54-bump-ranking — Industry unemployment bump chart](./cases/54-bump-ranking/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [55-indexed-multi-line — Indexed industry unemployment](./cases/55-indexed-multi-line/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [56-connected-scatter — Directed connected scatterplot](./cases/56-connected-scatter/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [57-scatter-marginal-histograms — Scatterplot with marginal histograms](./cases/57-scatter-marginal-histograms/tanstack.ts) | First-party primitive | `view-composition` | `tanstack.ts`, `tanstack.test.ts` | +| [58-select-extrema — Annotated AAPL extrema](./cases/58-select-extrema/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [59-grouped-reducer-bars — Mean penguin mass reducer bars](./cases/59-grouped-reducer-bars/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [60-lag-autocorrelation — AAPL lag-one autocorrelation](./cases/60-lag-autocorrelation/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [61-quantile-ribbon — Industry unemployment percentile ribbon](./cases/61-quantile-ribbon/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [62-ridgeline-density — Ridgeline density comparison](./cases/62-ridgeline-density/tanstack.ts) | First-party primitive | `ridgeline-mark` | `tanstack.ts`, `tanstack.test.ts` | +| [63-violin-distributions — Violin distribution comparison](./cases/63-violin-distributions/tanstack.ts) | First-party primitive | `violin-mark` | `tanstack.ts`, `tanstack.test.ts` | +| [64-marimekko-mosaic — Marimekko survey composition](./cases/64-marimekko-mosaic/tanstack.ts) | First-party primitive | `mosaic-layout` | `tanstack.ts`, `tanstack.test.ts` | +| [65-voronoi-nearest-tooltip — Voronoi nearest-point interaction](./cases/65-voronoi-nearest-tooltip/tanstack.ts) | Optional primitive | `spatial-voronoi` | `tanstack.ts` | +| [70-composed-chart — Layered Seattle weather chart](./cases/70-composed-chart/tanstack.ts) | First-party primitive | `bar-max-thickness` | `tanstack.ts`, `tanstack.test.ts` | +| [71-recharts-population-pyramid — Palmer penguins by species and sex](./cases/71-recharts-population-pyramid/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [72-recharts-mixed-bars — Stacked and adjacent Seattle weather bars](./cases/72-recharts-mixed-bars/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [73-many-point-scatter — Automobile specifications scatter](./cases/73-many-point-scatter/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [74-recharts-treemap — Flare analytics treemap](./cases/74-recharts-treemap/tanstack.ts) | Optional primitive | `resolved-mark-layout`, `hierarchy-treemap` | `tanstack.ts` | +| [75-radar — Simple radar chart](./cases/75-radar/tanstack.test.ts) | First-party primitive | `fold-transform` | `tanstack.test.ts` | +| [76-pie — English letter frequency pie](./cases/76-pie/tanstack.test.ts) | First-party primitive | `polar-value-allocation` | `tanstack.test.ts` | +| [77-donut — English letter frequency donut](./cases/77-donut/tanstack.test.ts) | First-party primitive | `polar-value-allocation` | `tanstack.test.ts` | +| [78-gauge — Survey agreement share gauge](./cases/78-gauge/tanstack.test.ts) | First-party primitive | `polar-value-allocation` | `tanstack.test.ts` | +| [80-echarts-axis-pointer — Snapped axis pointer with grouped tooltip](./cases/80-echarts-axis-pointer/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [81-recharts-interactive-legend — Interactive series legend](./cases/81-recharts-interactive-legend/tanstack.ts) | First-party primitive | `controlled-signals`, `interactive-legend` | `tanstack.ts`, `tanstack.test.ts`, `view.tsx` | +| [82-chart-table-selection — Linked chart and data table selection](./cases/82-chart-table-selection/tanstack.ts) | First-party primitive | `controlled-signals`, `keyed-selection` | `tanstack.ts`, `tanstack.test.ts`, `view.tsx` | +| [83-focus-context-window — Focus and context time window](./cases/83-focus-context-window/tanstack.ts) | Optional primitive | `controlled-signals`, `keyed-selection`, `configured-scale-inversion`, `brush-x` | `tanstack.ts`, `tanstack.test.ts`, `view.tsx` | +| [84-pinned-nested-chart-tooltip — Pinned penguin tooltip with a nested chart](./cases/84-pinned-nested-chart-tooltip/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `model.test.ts`, `view.tsx` | +| [85-scrollable-resource-lanes — Scrollable resource timeline lanes](./cases/85-scrollable-resource-lanes/tanstack.ts) | Application boundary | `application-boundary` | `tanstack.ts`, `tanstack.test.ts`, `shell.ts` | +| [86-streaming-window-preservation — Streaming window preservation](./cases/86-streaming-window-preservation/data.test.ts) | Application boundary | `application-boundary`, `decorative-mark` | `data.test.ts`, `tanstack.test.ts` | +| [87-echarts-synchronized-cursors — Synchronized cursors across views](./cases/87-echarts-synchronized-cursors/tanstack.ts) | Definition now | `view-composition`, `focus-guide`, `decorative-mark` | `tanstack.ts`, `tanstack.test.ts` | +| [88-echarts-free-cursor — Free cursor over car measurements](./cases/88-echarts-free-cursor/tanstack.ts) | First-party primitive | `controlled-signals`, `configured-scale-inversion`, `continuous-cursor` | `tanstack.ts`, `tanstack.test.ts` | +| [89-brush-range-selection — Observed monthly AAPL brush selection](./cases/89-brush-range-selection/tanstack.ts) | Optional primitive | `controlled-signals`, `configured-scale-inversion`, `brush-x` | `tanstack.ts`, `tanstack.test.ts` | +| [90-zoomable-time-window — Wheel zoom and pan over AAPL closes](./cases/90-zoomable-time-window/case.json) | Optional primitive | `controlled-signals`, `configured-scale-inversion`, `zoom-x` | `case.json`, `tanstack.ts`, `tanstack.test.ts` | +| [91-timeline-playback-scrubber — AAPL close playback scrubber](./cases/91-timeline-playback-scrubber/tanstack.ts) | First-party primitive | `controlled-signals`, `scale-handle` | `tanstack.ts`, `tanstack.test.ts` | +| [92-editable-event-range — Editable event range](./cases/92-editable-event-range/tanstack.ts) | First-party primitive | `controlled-signals`, `scale-handle` | `tanstack.ts`, `tanstack.test.ts` | +| [93-labeled-pie — Letter frequency pie with labels](./cases/93-labeled-pie/tanstack.test.ts) | First-party primitive | `polar-value-allocation`, `radial-labels` | `tanstack.test.ts` | +| [94-center-donut — Letter frequency donut with total](./cases/94-center-donut/tanstack.test.ts) | First-party primitive | `polar-value-allocation` | `tanstack.test.ts` | +| [95-rounded-donut — Rounded letter frequency donut](./cases/95-rounded-donut/tanstack.test.ts) | First-party primitive | `polar-value-allocation` | `tanstack.test.ts` | +| [96-nested-donut — Nested Flare package sizes](./cases/96-nested-donut/tanstack.test.ts) | First-party primitive | `polar-value-allocation` | `tanstack.test.ts` | +| [97-rose — English letter frequency rose](./cases/97-rose/tanstack.ts) | First-party primitive | `radial-bars` | `tanstack.ts`, `tanstack.test.ts` | +| [98-needle-gauge — County unemployment gauge](./cases/98-needle-gauge/tanstack.test.ts) | First-party primitive | `polar-value-allocation` | `tanstack.test.ts` | +| [99-comparative-radar — Comparative radar chart](./cases/99-comparative-radar/tanstack.test.ts) | First-party primitive | `fold-transform` | `tanstack.test.ts` | +| [100-radial-bars — Concentric letter frequency bars](./cases/100-radial-bars/tanstack.test.ts) | First-party primitive | `radial-bars` | `tanstack.test.ts` | +| [101-sunburst — Flare analytics sunburst](./cases/101-sunburst/tanstack.ts) | Optional primitive | `hierarchy-sunburst` | `tanstack.ts`, `tanstack.test.ts` | +| [102-world-choropleth — World learning-poverty choropleth](./cases/102-world-choropleth/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [103-bubble-map — World population bubble map](./cases/103-bubble-map/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [104-orthographic-globe — Orthographic globe with graticule](./cases/104-orthographic-globe/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [105-route-map — HMS Beagle voyage](./cases/105-route-map/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [106-polar-line — Seattle daily high-temperature polar line](./cases/106-polar-line/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [107-polar-scatter — Surface wind polar scatter](./cases/107-polar-scatter/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [108-country-choropleth — World population-density choropleth](./cases/108-country-choropleth/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [109-us-state-choropleth — United States county unemployment choropleth](./cases/109-us-state-choropleth/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [110-projection-gallery — Standard world projection gallery](./cases/110-projection-gallery/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [111-basic-sankey — Basic Sankey](./cases/111-basic-sankey/tanstack.ts) | Optional primitive | `resolved-mark-layout`, `network-sankey` | `tanstack.ts`, `tanstack.test.ts` | +| [111-sankey-flow — Sankey](./cases/111-sankey-flow/tanstack.ts) | Optional primitive | `resolved-mark-layout`, `network-sankey` | `tanstack.ts`, `tanstack.test.ts` | +| [112-motion-entrance — Staggered entrance motion](./cases/112-motion-entrance/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [113-motion-updates — Interrupted spring updates](./cases/113-motion-updates/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [114-spring-line-motion — Spring line updates](./cases/114-spring-line-motion/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [115-definition-motion — Definition-owned motion](./cases/115-definition-motion/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `tanstack.test.ts` | +| [116-geometry-morph — Cross-chart geometry morph](./cases/116-geometry-morph/tanstack.ts) | Inline custom mark | `inline-custom-boundary` | `tanstack.ts`, `tanstack.test.ts` | +| [117-focus-cursor-motion — Focus and crosshair motion](./cases/117-focus-cursor-motion/tanstack.ts) | First-party primitive | `focus-guide` | `tanstack.ts`, `tanstack.test.ts` | +| [118-token-usage-calendar — Token use calendar heatmap](./cases/118-token-usage-calendar/tanstack.ts) | First-party primitive | `axis-tick-label-accessors` | `tanstack.ts`, `tanstack.test.ts`, `shell.test.ts` | +| [119-stacked-bar-band-cursor — Stacked bars with band and rule cursors](./cases/119-stacked-bar-band-cursor/chart.ts) | First-party primitive | `crosshair` | `chart.ts`, `model.test.ts`, `case.json` | +| [120-themed-interactive-area — Themed interactive area card](./cases/120-themed-interactive-area/chart.ts) | Definition now | `current-definition-api` | `chart.ts`, `tanstack.ts`, `tanstack.test.ts` | +| [121-active-bar-dashboard — Active bar dashboard](./cases/121-active-bar-dashboard/chart.ts) | Definition now | `current-definition-api` | `chart.ts`, `tanstack.test.ts`, `view.tsx` | +| [122-premium-kpi-sparklines — Premium KPI sparklines](./cases/122-premium-kpi-sparklines/chart.ts) | Definition now | `current-definition-api` | `chart.ts`, `tanstack.test.ts`, `view.tsx` | +| [123-active-donut-metric — Active donut metric](./cases/123-active-donut-metric/chart.ts) | Definition now | `current-definition-api` | `chart.ts`, `tanstack.test.ts`, `view.tsx` | +| [124-theme-palette-matrix — Theme palette matrix](./cases/124-theme-palette-matrix/chart.ts) | Definition now | `current-definition-api` | `chart.ts`, `tanstack.test.ts`, `view.tsx` | +| [125-sales-funnel — Sales conversion funnel](./cases/125-sales-funnel/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `model.test.ts`, `case.json` | +| [126-drillable-sunburst — Drillable Flare sunburst](./cases/126-drillable-sunburst/tanstack.ts) | Optional primitive | `hierarchy-sunburst` | `tanstack.ts`, `tanstack.test.ts`, `model.test.ts` | +| [127-shadcn-dashboard — Dashboard](./cases/127-shadcn-dashboard/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [133-shadcn-area-axes — Area Chart - Axes](./cases/133-shadcn-area-axes/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [134-shadcn-area-default — Area Chart](./cases/134-shadcn-area-default/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [135-shadcn-area-gradient — Area Chart - Gradient](./cases/135-shadcn-area-gradient/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [136-shadcn-area-icons — Area Chart - Icons](./cases/136-shadcn-area-icons/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [137-shadcn-area-interactive — Area Chart - Interactive](./cases/137-shadcn-area-interactive/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [138-shadcn-area-legend — Area Chart - Legend](./cases/138-shadcn-area-legend/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [139-shadcn-area-linear — Area Chart - Linear](./cases/139-shadcn-area-linear/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [140-shadcn-area-stacked-expand — Area Chart - Stacked Expanded](./cases/140-shadcn-area-stacked-expand/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [141-shadcn-area-stacked — Area Chart - Stacked](./cases/141-shadcn-area-stacked/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [142-shadcn-area-step — Area Chart - Step](./cases/142-shadcn-area-step/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [143-shadcn-bar-active — Bar Chart - Active](./cases/143-shadcn-bar-active/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [144-shadcn-bar-default — Bar Chart](./cases/144-shadcn-bar-default/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [145-shadcn-bar-horizontal — Bar Chart - Horizontal](./cases/145-shadcn-bar-horizontal/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [146-shadcn-bar-interactive — Bar Chart - Interactive](./cases/146-shadcn-bar-interactive/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [147-shadcn-bar-label-custom — Bar Chart - Custom Label](./cases/147-shadcn-bar-label-custom/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [148-shadcn-bar-label — Bar Chart - Label](./cases/148-shadcn-bar-label/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [149-shadcn-bar-mixed — Bar Chart - Mixed](./cases/149-shadcn-bar-mixed/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [128-shadcn-bar-multiple — Bar Chart - Multiple](./cases/128-shadcn-bar-multiple/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [150-shadcn-bar-negative — Bar Chart - Negative](./cases/150-shadcn-bar-negative/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [151-shadcn-bar-stacked — Bar Chart - Stacked + Legend](./cases/151-shadcn-bar-stacked/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [152-shadcn-line-default — Line Chart](./cases/152-shadcn-line-default/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [153-shadcn-line-dots-colors — Line Chart - Dots Colors](./cases/153-shadcn-line-dots-colors/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [154-shadcn-line-dots-custom — Line Chart - Custom Dots](./cases/154-shadcn-line-dots-custom/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [155-shadcn-line-dots — Line Chart - Dots](./cases/155-shadcn-line-dots/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [156-shadcn-line-interactive — Line Chart - Interactive](./cases/156-shadcn-line-interactive/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [157-shadcn-line-label-custom — Line Chart - Custom Label](./cases/157-shadcn-line-label-custom/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [158-shadcn-line-label — Line Chart - Label](./cases/158-shadcn-line-label/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [159-shadcn-line-linear — Line Chart - Linear](./cases/159-shadcn-line-linear/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [160-shadcn-line-multiple — Line Chart - Multiple](./cases/160-shadcn-line-multiple/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [161-shadcn-line-step — Line Chart - Step](./cases/161-shadcn-line-step/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [162-shadcn-pie-donut-active — Pie Chart - Donut Active](./cases/162-shadcn-pie-donut-active/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [129-shadcn-pie-donut-text — Pie Chart - Donut with Text](./cases/129-shadcn-pie-donut-text/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [163-shadcn-pie-donut — Pie Chart - Donut](./cases/163-shadcn-pie-donut/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [164-shadcn-pie-interactive — Pie Chart - Interactive](./cases/164-shadcn-pie-interactive/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [165-shadcn-pie-label-custom — Pie Chart - Custom Label](./cases/165-shadcn-pie-label-custom/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [166-shadcn-pie-label-list — Pie Chart - Label List](./cases/166-shadcn-pie-label-list/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [167-shadcn-pie-label — Pie Chart - Label](./cases/167-shadcn-pie-label/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [168-shadcn-pie-legend — Pie Chart - Legend](./cases/168-shadcn-pie-legend/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [169-shadcn-pie-separator-none — Pie Chart - Separator None](./cases/169-shadcn-pie-separator-none/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [170-shadcn-pie-simple — Pie Chart](./cases/170-shadcn-pie-simple/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [171-shadcn-pie-stacked — Pie Chart - Stacked](./cases/171-shadcn-pie-stacked/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [172-shadcn-radar-default — Radar Chart](./cases/172-shadcn-radar-default/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [173-shadcn-radar-dots — Radar Chart - Dots](./cases/173-shadcn-radar-dots/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [174-shadcn-radar-grid-circle-fill — Radar Chart - Grid Circle Filled](./cases/174-shadcn-radar-grid-circle-fill/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [175-shadcn-radar-grid-circle-no-lines — Radar Chart - Grid Circle - No lines](./cases/175-shadcn-radar-grid-circle-no-lines/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [176-shadcn-radar-grid-circle — Radar Chart - Grid Circle](./cases/176-shadcn-radar-grid-circle/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [177-shadcn-radar-grid-custom — Radar Chart - Grid Custom](./cases/177-shadcn-radar-grid-custom/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [178-shadcn-radar-grid-fill — Radar Chart - Grid Filled](./cases/178-shadcn-radar-grid-fill/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [179-shadcn-radar-grid-none — Radar Chart - Grid None](./cases/179-shadcn-radar-grid-none/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [180-shadcn-radar-icons — Radar Chart - Icons](./cases/180-shadcn-radar-icons/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [181-shadcn-radar-label-custom — Radar Chart - Custom Label](./cases/181-shadcn-radar-label-custom/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [182-shadcn-radar-legend — Radar Chart - Legend](./cases/182-shadcn-radar-legend/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [183-shadcn-radar-lines-only — Radar Chart - Lines Only](./cases/183-shadcn-radar-lines-only/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [130-shadcn-radar-multiple — Radar Chart - Multiple](./cases/130-shadcn-radar-multiple/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [184-shadcn-radar-radius — Radar Chart - Radius Axis](./cases/184-shadcn-radar-radius/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [185-shadcn-radial-grid — Radial Chart - Grid](./cases/185-shadcn-radial-grid/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [186-shadcn-radial-label — Radial Chart - Label](./cases/186-shadcn-radial-label/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [187-shadcn-radial-shape — Radial Chart - Shape](./cases/187-shadcn-radial-shape/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [188-shadcn-radial-simple — Radial Chart](./cases/188-shadcn-radial-simple/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [189-shadcn-radial-stacked — Radial Chart - Stacked](./cases/189-shadcn-radial-stacked/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [131-shadcn-radial-text — Radial Chart - Text](./cases/131-shadcn-radial-text/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [132-shadcn-tooltip-advanced — Tooltip - Advanced](./cases/132-shadcn-tooltip-advanced/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [190-shadcn-tooltip-default — Tooltip - Default](./cases/190-shadcn-tooltip-default/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [191-shadcn-tooltip-formatter — Tooltip - Formatter](./cases/191-shadcn-tooltip-formatter/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [192-shadcn-tooltip-icons — Tooltip - Icons](./cases/192-shadcn-tooltip-icons/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [193-shadcn-tooltip-indicator-line — Tooltip - Line Indicator](./cases/193-shadcn-tooltip-indicator-line/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [194-shadcn-tooltip-indicator-none — Tooltip - No Indicator](./cases/194-shadcn-tooltip-indicator-none/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [195-shadcn-tooltip-label-custom — Tooltip - Custom label](./cases/195-shadcn-tooltip-label-custom/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [196-shadcn-tooltip-label-formatter — Tooltip - Label Formatter](./cases/196-shadcn-tooltip-label-formatter/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [197-shadcn-tooltip-label-none — Tooltip - No Label](./cases/197-shadcn-tooltip-label-none/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts`, `case.json` | +| [bar-grouped — Grouped bars](./cases/bar-grouped/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [bar-horizontal-ranking — Horizontal ranking with long labels](./cases/bar-horizontal-ranking/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [bar-stacked — Stacked bars](./cases/bar-stacked/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [bar-vertical-sorted — Sorted vertical bars](./cases/bar-vertical-sorted/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [facets-anscombe — Anscombe quartet small multiples](./cases/facets-anscombe/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [heatmap-labeled — Labeled ordinal heatmap](./cases/heatmap-labeled/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [histogram — Histogram](./cases/histogram/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | +| [scatter-bubble — Bubble scatterplot](./cases/scatter-bubble/tanstack.ts) | Definition now | `current-definition-api` | `tanstack.ts` | diff --git a/benchmarks/conformance/cases/113-motion-updates/tanstack.test.ts b/benchmarks/conformance/cases/113-motion-updates/tanstack.test.ts index 91f26567..af7022ee 100644 --- a/benchmarks/conformance/cases/113-motion-updates/tanstack.test.ts +++ b/benchmarks/conformance/cases/113-motion-updates/tanstack.test.ts @@ -275,6 +275,6 @@ function motionContext( function resolveMotion( definition: ChartMotionDefinition | undefined, context: ChartMotionContext, -): ChartMotionTiming | undefined { +): false | ChartMotionTiming | undefined { return typeof definition === 'function' ? definition(context) : definition } diff --git a/benchmarks/conformance/cases/114-spring-line-motion/tanstack.test.ts b/benchmarks/conformance/cases/114-spring-line-motion/tanstack.test.ts index bbd968ae..ed6fdb87 100644 --- a/benchmarks/conformance/cases/114-spring-line-motion/tanstack.test.ts +++ b/benchmarks/conformance/cases/114-spring-line-motion/tanstack.test.ts @@ -246,7 +246,7 @@ function motionContext( function resolveMotion( definition: ChartMotionDefinition | undefined, context: ChartMotionContext, -): ChartMotionTiming | undefined { +): false | ChartMotionTiming | undefined { return typeof definition === 'function' ? definition(context) : definition } diff --git a/benchmarks/conformance/cases/115-definition-motion/tanstack.test.ts b/benchmarks/conformance/cases/115-definition-motion/tanstack.test.ts index f2a38436..9f6c451c 100644 --- a/benchmarks/conformance/cases/115-definition-motion/tanstack.test.ts +++ b/benchmarks/conformance/cases/115-definition-motion/tanstack.test.ts @@ -283,7 +283,7 @@ function guideMotionContext( function resolveMotion( definition: ChartMotionDefinition | undefined, context: ChartMotionContext, -): ChartMotionTiming | undefined { +): false | ChartMotionTiming | undefined { return typeof definition === 'function' ? definition(context) : definition } diff --git a/benchmarks/conformance/cases/116-geometry-morph/tanstack.test.ts b/benchmarks/conformance/cases/116-geometry-morph/tanstack.test.ts index 3b3162af..7f40ce6d 100644 --- a/benchmarks/conformance/cases/116-geometry-morph/tanstack.test.ts +++ b/benchmarks/conformance/cases/116-geometry-morph/tanstack.test.ts @@ -258,7 +258,7 @@ function motionContext( function resolveMotion( definition: ChartMotionDefinition | undefined, context: ChartMotionContext, -): ChartMotionTiming | undefined { +): false | ChartMotionTiming | undefined { return typeof definition === 'function' ? definition(context) : definition } diff --git a/benchmarks/conformance/cases/127-shadcn-dashboard/case.json b/benchmarks/conformance/cases/127-shadcn-dashboard/case.json new file mode 100644 index 00000000..0ff67710 --- /dev/null +++ b/benchmarks/conformance/cases/127-shadcn-dashboard/case.json @@ -0,0 +1,30 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "height": 860, + "order": 1280, + "id": "127-shadcn-dashboard", + "title": "shadcn dashboard", + "family": "application", + "intent": "Reproduce shadcn/ui's canonical dashboard shell and interactive stacked-area card while replacing only Recharts with TanStack Charts.", + "support": "native", + "features": [ + "responsive dashboard shell", + "stacked area chart", + "natural curves", + "linear gradient fills", + "grouped tooltip", + "responsive time-range controls", + "CSS-variable theming", + "application-level catalog viewport" + ], + "geometry": [{ "role": "area", "count": 2 }], + "source": { + "title": "shadcn/ui dashboard-01", + "url": "https://ui.shadcn.com/examples/dashboard" + }, + "ai": { + "create": "Reproduce the official shadcn/ui dashboard-01 layout and data. Keep the dashboard shell independent of the chart renderer, then implement its filtered, stacked, gradient area chart with TanStack Charts.", + "maintain": "Preserve the official dashboard data, responsive sidebar, metric cards, time-range behavior, table shell, natural stacked-area geometry, gradients, axis labels, tooltip content, light and dark themes, and a renderer-only compact catalog preview." + } +} diff --git a/benchmarks/conformance/cases/127-shadcn-dashboard/dashboard.tsx b/benchmarks/conformance/cases/127-shadcn-dashboard/dashboard.tsx new file mode 100644 index 00000000..f1bfa373 --- /dev/null +++ b/benchmarks/conformance/cases/127-shadcn-dashboard/dashboard.tsx @@ -0,0 +1,686 @@ +import { + forwardRef, + useEffect, + useImperativeHandle, + useMemo, + useRef, + useState, +} from 'react' +import { + dashboardTableRows, + filterDashboardData, + formatDashboardDate, + type DashboardDatum, + type DashboardRange, +} from './data' +import { shadcnDashboardStyles } from './styles' +import type { ComponentType, ReactNode, SVGProps } from 'react' +import type { ConformanceInput, ConformanceTestDriver } from '../../types' + +export interface DashboardChartProps { + data: readonly DashboardDatum[] + input: ConformanceInput +} + +export function dashboardChartWidth(width: number): number { + if (width <= 480) return Math.max(1, width - 50) + if (width < 768) return Math.max(1, width - 82) + return Math.max(1, width - 354) +} + +export function dashboardTickValues( + data: readonly DashboardDatum[], + width: number, +): readonly string[] { + if (data.length < 2) return data.map((datum) => datum.date) + + const labels = data.map((datum) => formatDashboardDate(datum.date)) + const context = + typeof document === 'undefined' || + typeof CanvasRenderingContext2D === 'undefined' + ? null + : document.createElement('canvas').getContext('2d') + if (context) { + context.font = + '12px Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif' + } + const measure = (label: string) => + context?.measureText(label).width ?? label.length * 6.1 + const start = 5 + const axisLength = Math.max(1, width - 10) + let end = start + axisLength + const visible: string[] = [] + + for (let index = data.length - 1; index >= 0; index -= 1) { + const size = measure(labels[index]!) + const coordinate = start + (axisLength * index) / (data.length - 1) + const tickCoordinate = + index === data.length - 1 + ? Math.min(coordinate, end - size / 2) + : coordinate + if ( + tickCoordinate - size / 2 >= start && + tickCoordinate + size / 2 <= end + ) { + visible.push(data[index]!.date) + end = tickCoordinate - size / 2 - 32 + } + } + + return visible.reverse() +} + +interface DashboardProps { + ChartRenderer: ComponentType + input: ConformanceInput +} + +const ranges: readonly { value: DashboardRange; label: string }[] = [ + { value: '90d', label: 'Last 3 months' }, + { value: '30d', label: 'Last 30 days' }, + { value: '7d', label: 'Last 7 days' }, +] + +const statCards = [ + { + label: 'Total Revenue', + value: '$1,250.00', + change: '+12.5%', + trend: 'Trending up this month', + detail: 'Visitors for the last 6 months', + direction: 'up' as const, + }, + { + label: 'New Customers', + value: '1,234', + change: '-20%', + trend: 'Down 20% this period', + detail: 'Acquisition needs attention', + direction: 'down' as const, + }, + { + label: 'Active Accounts', + value: '45,678', + change: '+12.5%', + trend: 'Strong user retention', + detail: 'Engagement exceed targets', + direction: 'up' as const, + }, + { + label: 'Growth Rate', + value: '4.5%', + change: '+4.5%', + trend: 'Steady performance increase', + detail: 'Meets growth projections', + direction: 'up' as const, + }, +] + +const mainNavigation = [ + ['Dashboard', 'dashboard'], + ['Lifecycle', 'list'], + ['Analytics', 'analytics'], + ['Projects', 'folder'], + ['Team', 'users'], +] as const + +const documentNavigation = [ + ['Data Library', 'database'], + ['Reports', 'report'], + ['Word Assistant', 'word'], +] as const + +const secondaryNavigation = [ + ['Settings', 'settings'], + ['Get Help', 'help'], + ['Search', 'search'], +] as const + +export const ShadcnDashboard = forwardRef< + ConformanceTestDriver, + DashboardProps +>(function ShadcnDashboard({ ChartRenderer, input }, ref) { + const rootRef = useRef(null) + const [range, setRange] = useState( + input.width < 768 ? '7d' : '90d', + ) + const chartData = useMemo(() => filterDashboardData(range), [range]) + + useEffect(() => { + if (input.width < 768) setRange('7d') + }, [input.width]) + + useImperativeHandle( + ref, + () => ({ + resolveTarget(target) { + if (target.view && target.view !== 'main') return null + const value = target.anchor.startsWith('range:') + ? target.anchor.slice('range:'.length) + : null + const element = rootRef.current?.querySelector( + `[data-range-value="${value}"]`, + ) + return element ? center(element) : null + }, + readState() { + return { + range, + chartRows: chartData.length, + tableRows: dashboardTableRows.length, + } + }, + viewBounds(view) { + if (view && view !== 'main') return null + const bounds = rootRef.current?.getBoundingClientRect() + return bounds + ? { + x: bounds.left, + y: bounds.top, + width: bounds.width, + height: bounds.height, + } + : null + }, + }), + [chartData.length, range], + ) + + return ( +
+ +
+ +
+
+ +
+
+ {statCards.map((card) => ( + + ))} +
+
+
+

Total Visitors

+

+ Total for the last 3 months +

+
+ {ranges.map((option) => ( + + ))} +
+ +
+
+ +
+
+ +
+
+
+
+
+ ) +}) + +function DashboardSidebar() { + return ( + + ) +} + +function DashboardHeader() { + return ( +
+ +
+ ) +} + +function NavItem({ + label, + icon, + active = false, + more = false, +}: { + label: string + icon: IconName + active?: boolean + more?: boolean +}) { + return ( + + ) +} + +function StatCard({ + label, + value, + change, + trend, + detail, + direction, +}: (typeof statCards)[number]) { + return ( +
+
+ {label} + + + {change} + +

{value}

+
+
+ + {trend} + + + {detail} +
+
+ ) +} + +function DashboardTable() { + return ( +
+
+
+ + + + +
+
+ + +
+
+
+
+ + + + + + + + + + + + + + {dashboardTableRows.map((row) => ( + + + + + + + + + + + + ))} + +
+ + + HeaderSection TypeStatusTargetLimitReviewer +
+ + + + + + {row.header} + + + {row.type} + + + + {row.status} + + + + + + {row.reviewer} + +
+
+
+
+ 0 of 68 row(s) selected. + + Rows per page + + Page 1 of 7 + + + +
+
+ ) +} + +type IconName = + | 'analytics' + | 'brand' + | 'chevron-down' + | 'chevron-left' + | 'chevron-right' + | 'chevrons' + | 'columns' + | 'dashboard' + | 'database' + | 'folder' + | 'help' + | 'list' + | 'menu' + | 'more' + | 'more-vertical' + | 'plus' + | 'report' + | 'search' + | 'settings' + | 'trend-down' + | 'trend-up' + | 'users' + | 'word' + +function Icon({ + name, + ...props +}: SVGProps & { name: IconName }) { + return ( + + ) +} + +const iconPaths: Record = { + brand: ( + <> + + + + ), + menu: ( + <> + + + + ), + dashboard: ( + <> + + + + + + ), + list: ( + <> + + + + ), + analytics: ( + <> + + + ), + folder: , + users: ( + <> + + + + ), + database: ( + <> + + + + ), + report: ( + <> + + + ), + word: ( + <> + + + + ), + settings: ( + <> + + + + ), + help: ( + <> + + + + ), + search: ( + <> + + + + ), + more: ( + <> + + + + + ), + 'more-vertical': ( + <> + + + + + ), + chevrons: , + 'trend-up': , + 'trend-down': , + columns: ( + <> + + + + ), + plus: , + 'chevron-down': , + 'chevron-left': , + 'chevron-right': , +} + +function center(element: HTMLElement | SVGElement) { + const bounds = element.getBoundingClientRect() + return { + x: bounds.left + bounds.width / 2, + y: bounds.top + bounds.height / 2, + focusElement: element, + } +} diff --git a/benchmarks/conformance/cases/127-shadcn-dashboard/data.test.ts b/benchmarks/conformance/cases/127-shadcn-dashboard/data.test.ts new file mode 100644 index 00000000..87ac4c29 --- /dev/null +++ b/benchmarks/conformance/cases/127-shadcn-dashboard/data.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from 'vitest' +import { + dashboardAreaRows, + filterDashboardData, + formatDashboardDate, +} from './data' + +describe('shadcn dashboard data', () => { + it('matches the inclusive ranges used by dashboard-01', () => { + expect(filterDashboardData('90d')).toHaveLength(91) + expect(filterDashboardData('30d')).toHaveLength(31) + expect(filterDashboardData('7d')).toHaveLength(8) + expect(filterDashboardData('7d')[0]?.date).toBe('2024-06-23') + }) + + it('folds each date into the official mobile-first stack order', () => { + const source = filterDashboardData('7d') + const rows = dashboardAreaRows(source) + + expect(rows).toHaveLength(source.length * 2) + expect(rows.slice(0, 2).map((row) => row.series)).toEqual([ + 'mobile', + 'desktop', + ]) + expect(rows[0]?.visitors).toBe(source[0]?.mobile) + expect(rows[1]?.visitors).toBe(source[0]?.desktop) + }) + + it('formats dates independently of the local timezone', () => { + expect(formatDashboardDate(new Date('2024-06-30T00:00:00Z'))).toBe('Jun 30') + }) +}) diff --git a/benchmarks/conformance/cases/127-shadcn-dashboard/data.ts b/benchmarks/conformance/cases/127-shadcn-dashboard/data.ts new file mode 100644 index 00000000..2ff5ae27 --- /dev/null +++ b/benchmarks/conformance/cases/127-shadcn-dashboard/data.ts @@ -0,0 +1,256 @@ +export type DashboardRange = '90d' | '30d' | '7d' +export type DashboardSeries = 'mobile' | 'desktop' + +export interface DashboardDatum { + date: string + desktop: number + mobile: number +} + +export interface DashboardAreaDatum { + date: Date + dateKey: string + series: DashboardSeries + visitors: number +} + +export interface DashboardTableRow { + id: number + header: string + type: string + status: 'Done' | 'In Process' + target: string + limit: string + reviewer: string +} + +export const dashboardChartData: readonly DashboardDatum[] = [ + { date: '2024-04-01', desktop: 222, mobile: 150 }, + { date: '2024-04-02', desktop: 97, mobile: 180 }, + { date: '2024-04-03', desktop: 167, mobile: 120 }, + { date: '2024-04-04', desktop: 242, mobile: 260 }, + { date: '2024-04-05', desktop: 373, mobile: 290 }, + { date: '2024-04-06', desktop: 301, mobile: 340 }, + { date: '2024-04-07', desktop: 245, mobile: 180 }, + { date: '2024-04-08', desktop: 409, mobile: 320 }, + { date: '2024-04-09', desktop: 59, mobile: 110 }, + { date: '2024-04-10', desktop: 261, mobile: 190 }, + { date: '2024-04-11', desktop: 327, mobile: 350 }, + { date: '2024-04-12', desktop: 292, mobile: 210 }, + { date: '2024-04-13', desktop: 342, mobile: 380 }, + { date: '2024-04-14', desktop: 137, mobile: 220 }, + { date: '2024-04-15', desktop: 120, mobile: 170 }, + { date: '2024-04-16', desktop: 138, mobile: 190 }, + { date: '2024-04-17', desktop: 446, mobile: 360 }, + { date: '2024-04-18', desktop: 364, mobile: 410 }, + { date: '2024-04-19', desktop: 243, mobile: 180 }, + { date: '2024-04-20', desktop: 89, mobile: 150 }, + { date: '2024-04-21', desktop: 137, mobile: 200 }, + { date: '2024-04-22', desktop: 224, mobile: 170 }, + { date: '2024-04-23', desktop: 138, mobile: 230 }, + { date: '2024-04-24', desktop: 387, mobile: 290 }, + { date: '2024-04-25', desktop: 215, mobile: 250 }, + { date: '2024-04-26', desktop: 75, mobile: 130 }, + { date: '2024-04-27', desktop: 383, mobile: 420 }, + { date: '2024-04-28', desktop: 122, mobile: 180 }, + { date: '2024-04-29', desktop: 315, mobile: 240 }, + { date: '2024-04-30', desktop: 454, mobile: 380 }, + { date: '2024-05-01', desktop: 165, mobile: 220 }, + { date: '2024-05-02', desktop: 293, mobile: 310 }, + { date: '2024-05-03', desktop: 247, mobile: 190 }, + { date: '2024-05-04', desktop: 385, mobile: 420 }, + { date: '2024-05-05', desktop: 481, mobile: 390 }, + { date: '2024-05-06', desktop: 498, mobile: 520 }, + { date: '2024-05-07', desktop: 388, mobile: 300 }, + { date: '2024-05-08', desktop: 149, mobile: 210 }, + { date: '2024-05-09', desktop: 227, mobile: 180 }, + { date: '2024-05-10', desktop: 293, mobile: 330 }, + { date: '2024-05-11', desktop: 335, mobile: 270 }, + { date: '2024-05-12', desktop: 197, mobile: 240 }, + { date: '2024-05-13', desktop: 197, mobile: 160 }, + { date: '2024-05-14', desktop: 448, mobile: 490 }, + { date: '2024-05-15', desktop: 473, mobile: 380 }, + { date: '2024-05-16', desktop: 338, mobile: 400 }, + { date: '2024-05-17', desktop: 499, mobile: 420 }, + { date: '2024-05-18', desktop: 315, mobile: 350 }, + { date: '2024-05-19', desktop: 235, mobile: 180 }, + { date: '2024-05-20', desktop: 177, mobile: 230 }, + { date: '2024-05-21', desktop: 82, mobile: 140 }, + { date: '2024-05-22', desktop: 81, mobile: 120 }, + { date: '2024-05-23', desktop: 252, mobile: 290 }, + { date: '2024-05-24', desktop: 294, mobile: 220 }, + { date: '2024-05-25', desktop: 201, mobile: 250 }, + { date: '2024-05-26', desktop: 213, mobile: 170 }, + { date: '2024-05-27', desktop: 420, mobile: 460 }, + { date: '2024-05-28', desktop: 233, mobile: 190 }, + { date: '2024-05-29', desktop: 78, mobile: 130 }, + { date: '2024-05-30', desktop: 340, mobile: 280 }, + { date: '2024-05-31', desktop: 178, mobile: 230 }, + { date: '2024-06-01', desktop: 178, mobile: 200 }, + { date: '2024-06-02', desktop: 470, mobile: 410 }, + { date: '2024-06-03', desktop: 103, mobile: 160 }, + { date: '2024-06-04', desktop: 439, mobile: 380 }, + { date: '2024-06-05', desktop: 88, mobile: 140 }, + { date: '2024-06-06', desktop: 294, mobile: 250 }, + { date: '2024-06-07', desktop: 323, mobile: 370 }, + { date: '2024-06-08', desktop: 385, mobile: 320 }, + { date: '2024-06-09', desktop: 438, mobile: 480 }, + { date: '2024-06-10', desktop: 155, mobile: 200 }, + { date: '2024-06-11', desktop: 92, mobile: 150 }, + { date: '2024-06-12', desktop: 492, mobile: 420 }, + { date: '2024-06-13', desktop: 81, mobile: 130 }, + { date: '2024-06-14', desktop: 426, mobile: 380 }, + { date: '2024-06-15', desktop: 307, mobile: 350 }, + { date: '2024-06-16', desktop: 371, mobile: 310 }, + { date: '2024-06-17', desktop: 475, mobile: 520 }, + { date: '2024-06-18', desktop: 107, mobile: 170 }, + { date: '2024-06-19', desktop: 341, mobile: 290 }, + { date: '2024-06-20', desktop: 408, mobile: 450 }, + { date: '2024-06-21', desktop: 169, mobile: 210 }, + { date: '2024-06-22', desktop: 317, mobile: 270 }, + { date: '2024-06-23', desktop: 480, mobile: 530 }, + { date: '2024-06-24', desktop: 132, mobile: 180 }, + { date: '2024-06-25', desktop: 141, mobile: 190 }, + { date: '2024-06-26', desktop: 434, mobile: 380 }, + { date: '2024-06-27', desktop: 448, mobile: 490 }, + { date: '2024-06-28', desktop: 149, mobile: 200 }, + { date: '2024-06-29', desktop: 103, mobile: 160 }, + { date: '2024-06-30', desktop: 446, mobile: 400 }, +] + +export const dashboardTableRows: readonly DashboardTableRow[] = [ + { + id: 1, + header: 'Cover page', + type: 'Cover page', + status: 'In Process', + target: '18', + limit: '5', + reviewer: 'Eddie Lake', + }, + { + id: 2, + header: 'Table of contents', + type: 'Table of contents', + status: 'Done', + target: '29', + limit: '24', + reviewer: 'Eddie Lake', + }, + { + id: 3, + header: 'Executive summary', + type: 'Narrative', + status: 'Done', + target: '10', + limit: '13', + reviewer: 'Eddie Lake', + }, + { + id: 4, + header: 'Technical approach', + type: 'Narrative', + status: 'Done', + target: '27', + limit: '23', + reviewer: 'Jamik Tashpulatov', + }, + { + id: 5, + header: 'Design', + type: 'Narrative', + status: 'In Process', + target: '2', + limit: '16', + reviewer: 'Jamik Tashpulatov', + }, + { + id: 6, + header: 'Capabilities', + type: 'Narrative', + status: 'In Process', + target: '20', + limit: '8', + reviewer: 'Jamik Tashpulatov', + }, + { + id: 7, + header: 'Integration with existing systems', + type: 'Narrative', + status: 'In Process', + target: '19', + limit: '21', + reviewer: 'Jamik Tashpulatov', + }, + { + id: 8, + header: 'Innovation and Advantages', + type: 'Narrative', + status: 'Done', + target: '25', + limit: '26', + reviewer: 'Assign reviewer', + }, + { + id: 9, + header: "Overview of EMR's Innovative Solutions", + type: 'Technical content', + status: 'Done', + target: '7', + limit: '23', + reviewer: 'Assign reviewer', + }, + { + id: 10, + header: 'Advanced Algorithms and Machine Learning', + type: 'Narrative', + status: 'Done', + target: '30', + limit: '28', + reviewer: 'Assign reviewer', + }, +] + +const referenceDate = Date.parse('2024-06-30T00:00:00Z') +const day = 24 * 60 * 60 * 1_000 + +export function filterDashboardData( + range: DashboardRange, +): readonly DashboardDatum[] { + const days = range === '7d' ? 7 : range === '30d' ? 30 : 90 + const start = referenceDate - days * day + return dashboardChartData.filter( + (datum) => Date.parse(`${datum.date}T00:00:00Z`) >= start, + ) +} + +export function dashboardAreaRows( + data: readonly DashboardDatum[], +): readonly DashboardAreaDatum[] { + return data.flatMap((datum) => { + const date = new Date(`${datum.date}T00:00:00Z`) + return [ + { + date, + dateKey: datum.date, + series: 'mobile' as const, + visitors: datum.mobile, + }, + { + date, + dateKey: datum.date, + series: 'desktop' as const, + visitors: datum.desktop, + }, + ] + }) +} + +export function formatDashboardDate(value: string | number | Date): string { + const date = value instanceof Date ? value : new Date(value) + return date.toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + timeZone: 'UTC', + }) +} diff --git a/benchmarks/conformance/cases/127-shadcn-dashboard/recharts-view.tsx b/benchmarks/conformance/cases/127-shadcn-dashboard/recharts-view.tsx new file mode 100644 index 00000000..97527403 --- /dev/null +++ b/benchmarks/conformance/cases/127-shadcn-dashboard/recharts-view.tsx @@ -0,0 +1,129 @@ +import { forwardRef } from 'react' +import { + Area, + AreaChart, + CartesianGrid, + ResponsiveContainer, + Tooltip, + XAxis, +} from 'recharts' +import { + dashboardChartWidth, + dashboardTickValues, + ShadcnDashboard, + type DashboardChartProps, +} from './dashboard' +import { formatDashboardDate } from './data' +import { reactMount } from '../../shared/react-mount' +import type { ConformanceTestDriver } from '../../types' +import type { ReactConformanceProps } from '../../shared/react-mount' + +function RechartsDashboardChart({ data, input }: DashboardChartProps) { + const width = dashboardChartWidth(input.width) + const tickValues = dashboardTickValues(data, width) + + return ( + + + + + + + + + + + + + + + + typeof value === 'string' || typeof value === 'number' + ? formatDashboardDate(value) + : '' + } + formatter={(value, name) => [ + Number(value).toLocaleString('en-US'), + name === 'desktop' ? 'Desktop' : 'Mobile', + ]} + contentStyle={{ + minWidth: 138, + padding: '8px 10px', + border: '1px solid var(--sd-border)', + borderRadius: 8, + color: 'var(--sd-card-foreground)', + background: 'var(--sd-card)', + boxShadow: '0 2px 6px rgb(0 0 0 / 8%)', + fontSize: 12, + lineHeight: 1.45, + }} + labelStyle={{ color: 'var(--sd-card-foreground)', fontWeight: 500 }} + itemStyle={{ color: 'var(--sd-card-foreground)', padding: 0 }} + /> + + + + + ) +} + +const RechartsDashboardView = forwardRef< + ConformanceTestDriver, + ReactConformanceProps +>(function RechartsDashboardView({ input }, ref) { + return ( + + ) +}) + +export const mount = reactMount(RechartsDashboardView) diff --git a/benchmarks/conformance/cases/127-shadcn-dashboard/recharts.ts b/benchmarks/conformance/cases/127-shadcn-dashboard/recharts.ts new file mode 100644 index 00000000..cdcca9a7 --- /dev/null +++ b/benchmarks/conformance/cases/127-shadcn-dashboard/recharts.ts @@ -0,0 +1 @@ +export { mount } from './recharts-view' diff --git a/benchmarks/conformance/cases/127-shadcn-dashboard/styles.ts b/benchmarks/conformance/cases/127-shadcn-dashboard/styles.ts new file mode 100644 index 00000000..8f396b08 --- /dev/null +++ b/benchmarks/conformance/cases/127-shadcn-dashboard/styles.ts @@ -0,0 +1,807 @@ +export const shadcnDashboardStyles = ` + .shadcn-dashboard { + --sd-background: oklch(1 0 0); + --sd-foreground: oklch(0.145 0 0); + --sd-card: oklch(1 0 0); + --sd-card-foreground: oklch(0.145 0 0); + --sd-primary: oklch(0.205 0 0); + --sd-primary-foreground: oklch(0.985 0 0); + --sd-secondary: oklch(0.97 0 0); + --sd-muted: oklch(0.97 0 0); + --sd-muted-foreground: oklch(0.556 0 0); + --sd-accent: oklch(0.97 0 0); + --sd-border: oklch(0.922 0 0); + --sd-input: oklch(0.922 0 0); + --sd-sidebar: oklch(0.985 0 0); + --sd-sidebar-accent: oklch(0.97 0 0); + --sd-radius: 0.625rem; + width: 100%; + height: 100%; + overflow: hidden; + color: var(--sd-foreground); + background: var(--sd-background); + font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + font-size: 14px; + line-height: 1.45; + text-rendering: geometricPrecision; + } + + :root[data-theme='dark'] .shadcn-dashboard { + --sd-background: oklch(0.145 0 0); + --sd-foreground: oklch(0.985 0 0); + --sd-card: oklch(0.205 0 0); + --sd-card-foreground: oklch(0.985 0 0); + --sd-primary: oklch(0.922 0 0); + --sd-primary-foreground: oklch(0.205 0 0); + --sd-secondary: oklch(0.269 0 0); + --sd-muted: oklch(0.269 0 0); + --sd-muted-foreground: oklch(0.708 0 0); + --sd-accent: oklch(0.269 0 0); + --sd-border: oklch(1 0 0 / 10%); + --sd-input: oklch(1 0 0 / 15%); + --sd-sidebar: oklch(0.205 0 0); + --sd-sidebar-accent: oklch(0.269 0 0); + } + + .shadcn-dashboard, + .shadcn-dashboard * { + box-sizing: border-box; + } + + .shadcn-dashboard :where(button, select, input) { + min-height: 0; + color: inherit; + font: inherit; + } + + .sd-viewport { + display: flex; + width: 100%; + height: 100%; + overflow: auto; + background: var(--sd-sidebar); + } + + .sd-sidebar { + position: sticky; + top: 0; + display: flex; + flex: 0 0 256px; + height: 100%; + flex-direction: column; + border-right: 1px solid var(--sd-border); + background: var(--sd-sidebar); + } + + .sd-sidebar-inner { + display: flex; + min-height: 0; + flex: 1; + flex-direction: column; + overflow: hidden; + border: 0; + border-radius: 0; + background: var(--sd-sidebar); + } + + .sd-brand, + .sd-user { + display: flex; + min-height: 48px; + align-items: center; + gap: 10px; + padding: 8px; + } + + .sd-brand { + min-height: 49px; + border-bottom: 1px solid var(--sd-border); + } + + .sd-brand-mark { + width: 20px; + height: 20px; + color: var(--sd-foreground); + } + + .sd-brand-name { + overflow: hidden; + font-size: 16px; + font-weight: 600; + letter-spacing: -0.015em; + text-overflow: ellipsis; + white-space: nowrap; + } + + .sd-nav { + display: flex; + min-height: 0; + flex: 1; + flex-direction: column; + gap: 18px; + overflow: auto; + padding: 4px 8px 12px; + } + + .sd-nav-group { + display: grid; + gap: 2px; + } + + .sd-nav-label { + padding: 6px 8px 3px; + color: var(--sd-muted-foreground); + font-size: 12px; + font-weight: 500; + } + + .sd-nav-bottom { + margin-top: auto; + } + + .sd-nav-item { + display: flex; + width: 100%; + height: 32px; + align-items: center; + gap: 9px; + padding: 0 8px; + border: 0; + border-radius: 6px; + color: var(--sd-foreground); + background: transparent; + cursor: default; + text-align: left; + } + + .sd-nav-item:hover, + .sd-nav-item[data-active='true'] { + background: var(--sd-sidebar-accent); + } + + .sd-nav-item svg, + .sd-user svg, + .sd-header svg, + .sd-toolbar svg, + .sd-badge svg { + width: 16px; + height: 16px; + flex: none; + } + + .sd-nav-more { + margin-left: auto; + color: var(--sd-muted-foreground); + } + + .sd-user { + border-top: 1px solid transparent; + } + + .sd-avatar { + display: grid; + width: 32px; + height: 32px; + flex: none; + place-items: center; + border-radius: 8px; + color: var(--sd-primary-foreground); + background: var(--sd-primary); + font-size: 11px; + font-weight: 650; + } + + .sd-user-copy { + min-width: 0; + } + + .sd-user-copy strong, + .sd-user-copy span { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .sd-user-copy strong { + font-size: 13px; + font-weight: 600; + } + + .sd-user-copy span { + color: var(--sd-muted-foreground); + font-size: 11px; + } + + .sd-main { + min-width: 0; + flex: 1; + margin: 0; + overflow: hidden; + border: 0; + border-radius: 0; + background: var(--sd-background); + } + + .sd-main-scroll { + width: 100%; + height: 100%; + overflow: auto; + } + + .sd-header { + position: sticky; + top: 0; + z-index: 4; + display: flex; + height: 49px; + align-items: center; + gap: 10px; + padding: 0 24px; + border-bottom: 1px solid var(--sd-border); + background: color-mix(in srgb, var(--sd-background) 94%, transparent); + backdrop-filter: blur(8px); + } + + .sd-icon-button { + display: inline-grid; + width: 32px; + height: 32px; + padding: 0; + place-items: center; + border: 0; + border-radius: 6px; + background: transparent; + } + + .sd-separator { + display: none; + width: 1px; + height: 16px; + margin: 0 6px; + background: var(--sd-border); + } + + .sd-menu-button { + display: none; + } + + .sd-header h1 { + margin: 0; + font-size: 16px; + font-weight: 500; + letter-spacing: -0.01em; + } + + .sd-quick-create { + display: inline-flex; + height: 32px; + align-items: center; + gap: 6px; + margin-left: auto; + padding: 0 11px; + border: 0; + border-radius: 6px; + color: var(--sd-primary-foreground); + background: var(--sd-primary); + font-size: 13px; + font-weight: 600; + } + + .shadcn-dashboard .sd-quick-create { + color: var(--sd-primary-foreground); + } + + .sd-content { + container-type: inline-size; + display: flex; + flex-direction: column; + gap: 24px; + padding: 24px; + } + + .sd-cards { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 16px; + } + + .sd-card { + position: relative; + overflow: hidden; + border: 1px solid var(--sd-border); + border-radius: var(--sd-radius); + color: var(--sd-card-foreground); + background: + linear-gradient(to top, color-mix(in srgb, var(--sd-primary) 5%, transparent), transparent 45%), + var(--sd-card); + box-shadow: 0 1px 2px rgb(0 0 0 / 3%); + } + + :root[data-theme='dark'] .sd-card { + background: var(--sd-card); + } + + .sd-stat-card { + min-height: 200px; + padding: 23px 23px 20px; + } + + .sd-stat-head { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 3px 12px; + } + + .sd-stat-label, + .sd-chart-description { + color: var(--sd-muted-foreground); + font-size: 14px; + } + + .sd-stat-value { + grid-column: 1; + margin: 0; + font-size: clamp(24px, 2.35cqw, 30px); + font-weight: 600; + letter-spacing: -0.035em; + line-height: 1.2; + font-variant-numeric: tabular-nums; + } + + .sd-badge { + display: inline-flex; + height: 23px; + align-items: center; + gap: 4px; + padding: 0 7px; + border: 1px solid var(--sd-border); + border-radius: 7px; + color: var(--sd-foreground); + background: transparent; + font-size: 12px; + font-weight: 500; + line-height: 1; + white-space: nowrap; + } + + .sd-stat-foot { + display: grid; + gap: 3px; + margin-top: 30px; + font-size: 14px; + } + + .sd-stat-trend { + display: flex; + min-width: 0; + align-items: center; + gap: 7px; + overflow: hidden; + font-weight: 500; + text-overflow: ellipsis; + white-space: nowrap; + } + + .sd-stat-trend svg { + width: 16px; + height: 16px; + flex: none; + } + + .sd-stat-detail { + overflow: hidden; + color: var(--sd-muted-foreground); + text-overflow: ellipsis; + white-space: nowrap; + } + + .sd-chart-card { + height: 392px; + background: var(--sd-card); + } + + .sd-chart-header { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 4px 16px; + align-items: start; + padding: 24px 24px 0; + } + + .sd-chart-title { + margin: 0; + font-size: 16px; + font-weight: 600; + letter-spacing: -0.015em; + } + + .sd-chart-description { + grid-column: 1; + } + + .sd-range-buttons { + grid-column: 2; + grid-row: 1 / span 2; + display: inline-flex; + height: 32px; + overflow: hidden; + border: 1px solid var(--sd-border); + border-radius: 8px; + } + + .sd-range-button { + height: 30px; + padding: 0 16px; + border: 0; + border-left: 1px solid var(--sd-border); + background: transparent; + cursor: pointer; + font-size: 13px; + font-weight: 500; + white-space: nowrap; + } + + .sd-range-button:first-child { + border-left: 0; + } + + .sd-range-button:hover, + .sd-range-button[data-active='true'] { + background: var(--sd-accent); + } + + .sd-range-select { + display: none; + grid-column: 2; + grid-row: 1 / span 2; + width: 160px; + height: 32px; + padding: 0 30px 0 10px; + border: 1px solid var(--sd-border); + border-radius: 8px; + background: var(--sd-background); + font-size: 13px; + } + + .sd-chart-stage { + position: relative; + width: 100%; + height: 274px; + margin-top: 24px; + padding: 8px 24px 0; + } + + .sd-chart-stage > * { + width: 100%; + height: 100%; + } + + .sd-chart-stage .ts-chart-host { + color: var(--sd-muted-foreground); + font-size: 12px; + } + + .sd-chart-stage .ts-chart__grid line { + stroke: var(--sd-border); + } + + .sd-chart-stage .ts-chart__axis text { + fill: var(--sd-muted-foreground); + } + + .ts-chart-tooltip.sd-chart-tooltip { + min-width: 138px !important; + padding: 8px 10px !important; + border: 1px solid var(--sd-border) !important; + border-radius: 8px !important; + color: var(--sd-card-foreground) !important; + background: var(--sd-card) !important; + box-shadow: 0 2px 6px rgb(0 0 0 / 8%) !important; + font: 12px/1.45 Inter, ui-sans-serif, system-ui, sans-serif !important; + } + + .ts-chart-tooltip.sd-chart-tooltip .ts-chart-tooltip__title { + margin-bottom: 5px !important; + font-weight: 500 !important; + } + + .ts-chart-tooltip.sd-chart-tooltip .ts-chart-tooltip__row { + grid-template-columns: 8px minmax(0, 1fr) auto !important; + column-gap: 7px !important; + } + + .ts-chart-tooltip.sd-chart-tooltip .ts-chart-tooltip__swatch { + width: 8px !important; + height: 8px !important; + border-radius: 999px !important; + } + + .sd-recharts-tooltip { + min-width: 138px; + padding: 8px 10px; + border: 1px solid var(--sd-border); + border-radius: 8px; + color: var(--sd-card-foreground); + background: var(--sd-card); + box-shadow: 0 2px 6px rgb(0 0 0 / 8%); + font-size: 12px; + line-height: 1.45; + } + + .sd-recharts-tooltip-date { + margin-bottom: 5px; + font-weight: 500; + } + + .sd-recharts-tooltip-row { + display: grid; + grid-template-columns: 8px minmax(0, 1fr) auto; + align-items: center; + gap: 7px; + } + + .sd-recharts-tooltip-dot { + width: 8px; + height: 8px; + border-radius: 999px; + background: var(--sd-primary); + } + + .sd-table-section { + display: grid; + gap: 24px; + } + + .sd-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + } + + .sd-tabs { + display: inline-flex; + height: 36px; + align-items: center; + padding: 3px; + border-radius: 9px; + background: var(--sd-muted); + } + + .sd-tab { + height: 30px; + padding: 0 11px; + border: 0; + border-radius: 7px; + background: transparent; + font-size: 13px; + } + + .sd-tab[data-active='true'] { + border: 1px solid var(--sd-border); + background: var(--sd-background); + box-shadow: 0 1px 2px rgb(0 0 0 / 5%); + } + + .sd-toolbar-actions { + display: flex; + gap: 8px; + } + + .sd-button { + display: inline-flex; + height: 32px; + align-items: center; + justify-content: center; + gap: 7px; + padding: 0 11px; + border: 1px solid var(--sd-border); + border-radius: 8px; + background: var(--sd-background); + font-size: 13px; + font-weight: 500; + white-space: nowrap; + } + + .sd-table-wrap { + overflow: hidden; + border: 1px solid var(--sd-border); + border-radius: 9px; + } + + .sd-table-scroll { + overflow-x: auto; + } + + .sd-table { + width: 100%; + min-width: 860px; + border-collapse: collapse; + font-size: 13px; + } + + .sd-table th, + .sd-table td { + height: 48px; + padding: 0 12px; + border-bottom: 1px solid var(--sd-border); + text-align: left; + white-space: nowrap; + } + + .sd-table th { + height: 40px; + color: var(--sd-muted-foreground); + background: var(--sd-muted); + font-weight: 500; + } + + .sd-table tbody tr:last-child td { + border-bottom: 0; + } + + .sd-grip { + color: var(--sd-muted-foreground); + letter-spacing: -3px; + } + + .sd-checkbox { + width: 16px; + height: 16px; + accent-color: var(--sd-primary); + } + + .sd-table-link { + color: var(--sd-foreground); + font-weight: 500; + text-decoration: underline; + text-decoration-color: color-mix(in srgb, var(--sd-muted-foreground) 45%, transparent); + text-underline-offset: 4px; + } + + .sd-status { + display: inline-flex; + align-items: center; + gap: 5px; + } + + .sd-status-dot { + width: 8px; + height: 8px; + border: 1.5px solid currentColor; + border-radius: 999px; + } + + .sd-status[data-done='true'] .sd-status-dot { + border-color: #22c55e; + background: #22c55e; + box-shadow: inset 0 0 0 1.5px var(--sd-card); + } + + .sd-number-input { + width: 64px; + height: 32px; + padding: 0 8px; + border: 1px solid transparent; + border-radius: 6px; + background: transparent; + text-align: right; + } + + .sd-number-input:hover { + background: color-mix(in srgb, var(--sd-input) 30%, transparent); + } + + .sd-table-footer { + display: flex; + min-height: 36px; + align-items: center; + gap: 24px; + color: var(--sd-muted-foreground); + font-size: 13px; + } + + .sd-pagination { + display: flex; + margin-left: auto; + align-items: center; + gap: 8px; + color: var(--sd-foreground); + font-weight: 500; + } + + @container (min-width: 1000px) { + .sd-cards { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + } + + @container (max-width: 540px) { + .sd-cards { + grid-template-columns: 1fr; + } + + .sd-stat-card { + min-height: 132px; + } + + .sd-tabs { + display: none; + } + + .sd-toolbar::before { + content: 'Outline'; + display: inline-flex; + height: 32px; + align-items: center; + padding: 0 10px; + border: 1px solid var(--sd-border); + border-radius: 8px; + font-size: 13px; + } + } + + @container (max-width: 767px) { + .sd-range-buttons { + display: none; + } + + .sd-range-select { + display: block; + } + } + + @media (max-width: 767px) { + .sd-sidebar { + display: none; + } + + .sd-main { + margin: 0; + border: 0; + border-radius: 0; + } + + .sd-header, + .sd-content { + padding-inline: 16px; + } + + .sd-menu-button { + display: inline-grid; + } + + .sd-separator { + display: block; + } + + .sd-quick-create { + display: none; + } + } + + @media (max-width: 480px) { + .sd-chart-card { + height: 414px; + } + + .sd-chart-header { + grid-template-columns: 1fr; + } + + .sd-range-select { + grid-column: 1; + grid-row: 3; + margin-top: 8px; + } + + .sd-chart-stage { + padding-inline: 8px; + } + + .sd-button span { + display: none; + } + } +` diff --git a/benchmarks/conformance/cases/127-shadcn-dashboard/tanstack-view.tsx b/benchmarks/conformance/cases/127-shadcn-dashboard/tanstack-view.tsx new file mode 100644 index 00000000..d094de21 --- /dev/null +++ b/benchmarks/conformance/cases/127-shadcn-dashboard/tanstack-view.tsx @@ -0,0 +1,194 @@ +import { forwardRef, useMemo } from 'react' +import { areaY, d3Curve, defineChart, stack } from '@tanstack/charts' +import { tooltip } from '@tanstack/charts/tooltip' +import { Chart } from '@tanstack/charts/react/core' +import { scaleLinear, scalePoint } from 'd3-scale' +import { curveNatural } from 'd3-shape' +import { + dashboardChartWidth, + dashboardTickValues, + ShadcnDashboard, + type DashboardChartProps, +} from './dashboard' +import { + dashboardAreaRows, + filterDashboardData, + formatDashboardDate, + type DashboardAreaDatum, + type DashboardDatum, + type DashboardSeries, +} from './data' +import { reactMount } from '../../shared/react-mount' +import { tanstackCase } from '../../shared/mount' +import { createShadcnSpringRenderer } from '../../shared/shadcn-motion' +import type { ChartTooltipContent, ChartTooltipOptions } from '@tanstack/charts' +import type { ConformanceTestDriver } from '../../types' +import type { ReactConformanceProps } from '../../shared/react-mount' + +const seriesOrder: readonly DashboardSeries[] = ['mobile', 'desktop'] +const primaryColor = 'var(--sd-primary, var(--ts-chart-1, #171717))' +const mutedColor = 'var(--sd-muted-foreground, #737373)' +const borderColor = 'var(--sd-border, #e5e5e5)' + +export function shadcnDashboardChartDefinition( + data: readonly DashboardDatum[], + width = 288, +) { + const rows = dashboardAreaRows(data) + const tickValues = dashboardTickValues(data, width) + + return defineChart({ + marks: [ + areaY(rows, { + id: 'visitor-areas', + x: 'dateKey', + y: 'visitors', + z: 'series', + color: 'series', + key: (row) => `${row.dateKey}:${row.series}`, + layout: stack({ order: seriesOrder }), + curve: d3Curve(curveNatural), + fill: (row) => `url(#fill-${row.series})`, + fillOpacity: 0.6, + stroke: primaryColor, + strokeWidth: 1, + }), + ], + x: { + scale: scalePoint, + axis: { + line: false, + ticks: { + values: tickValues, + size: 0, + padding: 8, + format: formatDashboardDate, + }, + tickLabels: { + fontSize: 12, + opacity: 0.72, + dy: 5, + anchor: ({ value }) => + value === data.at(-1)?.date ? 'end' : 'middle', + dx: ({ value }) => (value === data.at(-1)?.date ? 5 : 0), + thin: { minGap: 32, priority: 'ends' }, + }, + }, + }, + y: { + scale: scaleLinear().domain([0, 1_200]), + grid: true, + axis: { + line: false, + ticks: { values: [0, 300, 600, 900, 1_200], size: 0 }, + tickLabels: false, + }, + }, + color: { + domain: seriesOrder, + range: [primaryColor, primaryColor], + }, + gradients: [ + { + id: 'fill-mobile', + x1: 0, + y1: 1, + x2: 0, + y2: 0, + stops: [ + { offset: 0.05, color: primaryColor, opacity: 0.1 }, + { offset: 0.95, color: primaryColor, opacity: 0.8 }, + ], + }, + { + id: 'fill-desktop', + x1: 0, + y1: 1, + x2: 0, + y2: 0, + stops: [ + { offset: 0.05, color: primaryColor, opacity: 0.1 }, + { offset: 0.95, color: primaryColor, opacity: 1 }, + ], + }, + ], + margin: { top: 5, right: 5, bottom: 35, left: 5 }, + theme: { + foreground: mutedColor, + grid: borderColor, + background: 'transparent', + palette: [primaryColor], + }, + clip: true, + }) +} + +const dashboardTooltip: ChartTooltipOptions = { + className: 'sd-chart-tooltip', + anchor: 'group-center', + placement: ['top', 'right', 'left', 'bottom'], + sticky: false, + sort: 'color-domain', + content(points): ChartTooltipContent { + return { + title: formatDashboardDate(points[0]?.xValue ?? ''), + rows: points.map((point) => ({ + label: point.datum.series === 'desktop' ? 'Desktop' : 'Mobile', + value: point.datum.visitors.toLocaleString('en-US'), + color: 'var(--sd-primary)', + })), + } + }, +} + +function TanStackDashboardChart({ data, input }: DashboardChartProps) { + const width = dashboardChartWidth(input.width) + const renderer = useMemo( + () => createShadcnSpringRenderer(), + [], + ) + const definition = useMemo( + () => + defineChart(shadcnDashboardChartDefinition(data, width), { + svgAnimation: false, + focus: 'group-x', + focusRing: true, + maxFocusDistance: Number.POSITIVE_INFINITY, + keyboard: true, + tooltip: { use: tooltip, ...dashboardTooltip }, + }), + [data, width], + ) + + return ( + + ) +} + +const TanStackDashboardView = forwardRef< + ConformanceTestDriver, + ReactConformanceProps +>(function TanStackDashboardView({ input }, ref) { + return ( + + ) +}) + +export const catalogCase = tanstackCase( + () => shadcnDashboardChartDefinition(filterDashboardData('90d')), + 'Total visitors for the last three months', + dashboardTooltip, + { margin: true }, +) + +export const mount = reactMount(TanStackDashboardView) diff --git a/benchmarks/conformance/cases/127-shadcn-dashboard/tanstack.test.ts b/benchmarks/conformance/cases/127-shadcn-dashboard/tanstack.test.ts new file mode 100644 index 00000000..ae335638 --- /dev/null +++ b/benchmarks/conformance/cases/127-shadcn-dashboard/tanstack.test.ts @@ -0,0 +1,35 @@ +import { createChartRuntime } from '@tanstack/charts' +import { describe, expect, it } from 'vitest' +import { filterDashboardData } from './data' +import { shadcnDashboardChartDefinition } from './tanstack' +import type { SceneNode } from '@tanstack/charts' + +describe('shadcn dashboard TanStack chart', () => { + it('renders two natural stacked areas with scoped gradients', () => { + const runtime = createChartRuntime() + const scene = runtime.render( + shadcnDashboardChartDefinition(filterDashboardData('90d')), + { width: 900, height: 250 }, + ) + const areas = flatten(scene.nodes).filter((node) => node.kind === 'area') + + expect(areas).toHaveLength(2) + expect(scene.points).toHaveLength(182) + expect(scene.gradients.map((gradient) => gradient.id)).toEqual([ + 'fill-mobile', + 'fill-desktop', + ]) + expect(areas.map((area) => area.style?.fill)).toEqual([ + 'url(#fill-mobile)', + 'url(#fill-desktop)', + ]) + + runtime.destroy() + }) +}) + +function flatten(nodes: readonly SceneNode[]): SceneNode[] { + return nodes.flatMap((node) => + node.kind === 'group' ? [node, ...flatten(node.children)] : [node], + ) +} diff --git a/benchmarks/conformance/cases/127-shadcn-dashboard/tanstack.ts b/benchmarks/conformance/cases/127-shadcn-dashboard/tanstack.ts new file mode 100644 index 00000000..2048d598 --- /dev/null +++ b/benchmarks/conformance/cases/127-shadcn-dashboard/tanstack.ts @@ -0,0 +1,5 @@ +export { + catalogCase, + mount, + shadcnDashboardChartDefinition, +} from './tanstack-view' diff --git a/benchmarks/conformance/cases/128-shadcn-bar-multiple/case.json b/benchmarks/conformance/cases/128-shadcn-bar-multiple/case.json new file mode 100644 index 00000000..83ef3d45 --- /dev/null +++ b/benchmarks/conformance/cases/128-shadcn-bar-multiple/case.json @@ -0,0 +1,28 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1290, + "id": "128-shadcn-bar-multiple", + "title": "shadcn bar chart – multiple", + "family": "bar", + "intent": "Reproduce shadcn/ui's grouped bar example while replacing Recharts with TanStack Charts.", + "support": "native", + "features": [ + "grouped bars", + "rounded corners", + "horizontal grid", + "grouped tooltip", + "shadcn card shell" + ], + "geometry": [{ "role": "bar", "count": 12 }], + "source": { + "title": "shadcn/ui chart-bar-multiple", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-multiple" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-multiple card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, grouped layout, axis treatment, tooltip labels, card dimensions, footer, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/128-shadcn-bar-multiple/data.ts b/benchmarks/conformance/cases/128-shadcn-bar-multiple/data.ts new file mode 100644 index 00000000..d3c06bf5 --- /dev/null +++ b/benchmarks/conformance/cases/128-shadcn-bar-multiple/data.ts @@ -0,0 +1,14 @@ +export interface VisitorMonth { + month: string + desktop: number + mobile: number +} + +export const visitorMonths: readonly VisitorMonth[] = [ + { month: 'January', desktop: 186, mobile: 80 }, + { month: 'February', desktop: 305, mobile: 200 }, + { month: 'March', desktop: 237, mobile: 120 }, + { month: 'April', desktop: 73, mobile: 190 }, + { month: 'May', desktop: 209, mobile: 130 }, + { month: 'June', desktop: 214, mobile: 140 }, +] diff --git a/benchmarks/conformance/cases/128-shadcn-bar-multiple/recharts-view.tsx b/benchmarks/conformance/cases/128-shadcn-bar-multiple/recharts-view.tsx new file mode 100644 index 00000000..4767d5a9 --- /dev/null +++ b/benchmarks/conformance/cases/128-shadcn-bar-multiple/recharts-view.tsx @@ -0,0 +1,45 @@ +import { Bar, BarChart, CartesianGrid, Tooltip, XAxis } from 'recharts' +import { visitorMonths } from './data' +import { BarMultipleCard } from './view' +import { shadcnChartMount } from '../../shared/shadcn-chart-card' +import type { ConformanceInput } from '../../types' + +function RechartsView({ input }: { input: ConformanceInput }) { + return ( + + {({ width, height }) => ( + + + value.slice(0, 3)} + /> + + + + + )} + + ) +} + +export const rechartsMount = shadcnChartMount(RechartsView) diff --git a/benchmarks/conformance/cases/128-shadcn-bar-multiple/recharts.ts b/benchmarks/conformance/cases/128-shadcn-bar-multiple/recharts.ts new file mode 100644 index 00000000..93097581 --- /dev/null +++ b/benchmarks/conformance/cases/128-shadcn-bar-multiple/recharts.ts @@ -0,0 +1 @@ +export { rechartsMount as mount } from './recharts-view' diff --git a/benchmarks/conformance/cases/128-shadcn-bar-multiple/tanstack-view.tsx b/benchmarks/conformance/cases/128-shadcn-bar-multiple/tanstack-view.tsx new file mode 100644 index 00000000..58ea93a0 --- /dev/null +++ b/benchmarks/conformance/cases/128-shadcn-bar-multiple/tanstack-view.tsx @@ -0,0 +1,115 @@ +import { useMemo } from 'react' +import { barY, defineChart, group } from '@tanstack/charts' +import { Chart } from '@tanstack/charts/react/core' +import { tooltip } from '@tanstack/charts/tooltip' +import { scaleBand, scaleLinear } from 'd3-scale' +import { visitorMonths, type VisitorMonth } from './data' +import { BarMultipleCard } from './view' +import { shadcnChartMount } from '../../shared/shadcn-chart-card' +import { tanstackCase } from '../../shared/mount' +import { createShadcnSpringRenderer } from '../../shared/shadcn-motion' +import type { ChartTooltipOptions } from '@tanstack/charts' +import type { ConformanceInput } from '../../types' + +const series = ['desktop', 'mobile'] as const +const colors = [ + 'var(--chart-1, var(--ts-chart-1))', + 'var(--chart-2, var(--ts-chart-2))', +] +const groupScale = scaleBand() + .domain(series) + .paddingInner(0.08) + .paddingOuter(0) + +export const barMultipleDefinition = defineChart({ + marks: [ + barY(visitorMonths, { + id: 'desktop-bars', + x: 'month', + y: 'desktop', + z: () => 'desktop', + color: () => 'desktop', + layout: group({ scale: groupScale }), + radius: 4, + }), + barY(visitorMonths, { + id: 'mobile-bars', + x: 'month', + y: 'mobile', + z: () => 'mobile', + color: () => 'mobile', + layout: group({ scale: groupScale }), + radius: 4, + }), + ], + x: { + scale: () => scaleBand().paddingInner(0.2).paddingOuter(0.1), + axis: { + line: false, + ticks: { size: 0, padding: 10, format: (value) => value.slice(0, 3) }, + }, + }, + y: { + scale: scaleLinear().domain([0, 320]), + grid: true, + axis: { line: false, ticks: false, tickLabels: false }, + }, + color: { domain: series, range: colors }, + margin: { top: 5, right: 5, bottom: 35, left: 5 }, + theme: { + foreground: 'var(--muted-foreground, var(--muted))', + grid: 'var(--border)', + }, +}) + +const barTooltip: ChartTooltipOptions = { + className: 'sc-chart-tooltip', + anchor: 'group-center', + sort: 'color-domain', + content: (points) => ({ + title: points[0]?.datum.month ?? '', + rows: points.map((point) => ({ + label: point.markId === 'desktop-bars' ? 'Desktop' : 'Mobile', + value: Number(point.yValue).toLocaleString('en-US'), + color: point.markId === 'desktop-bars' ? colors[0] : colors[1], + })), + }), +} + +function TanStackView({ input }: { input: ConformanceInput }) { + const renderer = useMemo( + () => createShadcnSpringRenderer(), + [], + ) + const definition = useMemo( + () => + defineChart(barMultipleDefinition, { + svgAnimation: false, + focus: 'group-x', + keyboard: true, + tooltip: { use: tooltip, ...barTooltip }, + }), + [], + ) + return ( + + {({ width, height }) => ( + + )} + + ) +} + +export const catalogCase = tanstackCase( + () => barMultipleDefinition, + 'Monthly desktop and mobile visitors', + barTooltip, + { margin: true }, +) +export const tanstackMount = shadcnChartMount(TanStackView) diff --git a/benchmarks/conformance/cases/128-shadcn-bar-multiple/tanstack.ts b/benchmarks/conformance/cases/128-shadcn-bar-multiple/tanstack.ts new file mode 100644 index 00000000..d61b9f66 --- /dev/null +++ b/benchmarks/conformance/cases/128-shadcn-bar-multiple/tanstack.ts @@ -0,0 +1,5 @@ +export { + barMultipleDefinition, + catalogCase, + tanstackMount as mount, +} from './tanstack-view' diff --git a/benchmarks/conformance/cases/128-shadcn-bar-multiple/view.tsx b/benchmarks/conformance/cases/128-shadcn-bar-multiple/view.tsx new file mode 100644 index 00000000..576c29e9 --- /dev/null +++ b/benchmarks/conformance/cases/128-shadcn-bar-multiple/view.tsx @@ -0,0 +1,24 @@ +import { + ShadcnChartCard, + ShadcnTrendFooter, +} from '../../shared/shadcn-chart-card' +import type { ConformanceInput } from '../../types' + +export function BarMultipleCard({ + input, + children, +}: { + input: ConformanceInput + children: Parameters[0]['children'] +}) { + return ( + } + > + {children} + + ) +} diff --git a/benchmarks/conformance/cases/129-shadcn-pie-donut-text/case.json b/benchmarks/conformance/cases/129-shadcn-pie-donut-text/case.json new file mode 100644 index 00000000..0f23420a --- /dev/null +++ b/benchmarks/conformance/cases/129-shadcn-pie-donut-text/case.json @@ -0,0 +1,31 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1300, + "id": "129-shadcn-pie-donut-text", + "title": "shadcn pie chart – donut with text", + "family": "pie", + "intent": "Reproduce shadcn/ui's donut-with-center-text example while replacing Recharts with TanStack Charts.", + "support": "native", + "features": [ + "donut chart", + "center text", + "sector separators", + "tooltip", + "shadcn card shell" + ], + "geometry": [ + { "role": "arc", "count": 5 }, + { "role": "text", "count": 2 } + ], + "source": { + "title": "shadcn/ui chart-pie-donut-text", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-donut-text" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-donut-text card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream browser data, donut radii, separators, center total and label, tooltip, card dimensions, footer, and themes." + } +} diff --git a/benchmarks/conformance/cases/129-shadcn-pie-donut-text/data.ts b/benchmarks/conformance/cases/129-shadcn-pie-donut-text/data.ts new file mode 100644 index 00000000..a4dfca5b --- /dev/null +++ b/benchmarks/conformance/cases/129-shadcn-pie-donut-text/data.ts @@ -0,0 +1,42 @@ +export interface BrowserDatum { + browser: string + visitors: number +} + +export interface CenterLabel { + id: string + angle: number + radius: number + text: string +} + +export const browserData: readonly BrowserDatum[] = [ + { browser: 'chrome', visitors: 275 }, + { browser: 'safari', visitors: 200 }, + { browser: 'firefox', visitors: 287 }, + { browser: 'edge', visitors: 173 }, + { browser: 'other', visitors: 190 }, +] + +export const browserColors = [ + 'var(--chart-1, var(--ts-chart-1))', + 'var(--chart-2, var(--ts-chart-2))', + 'var(--chart-3, var(--ts-chart-3))', + 'var(--chart-4, var(--ts-chart-4))', + 'var(--chart-5, var(--ts-chart-5))', +] + +export const totalVisitors = browserData.reduce( + (sum, row) => sum + row.visitors, + 0, +) + +export const centerLabels: readonly CenterLabel[] = [ + { + id: 'total', + angle: 0, + radius: 0, + text: totalVisitors.toLocaleString('en-US'), + }, + { id: 'label', angle: 0, radius: 0, text: 'Visitors' }, +] diff --git a/benchmarks/conformance/cases/129-shadcn-pie-donut-text/recharts-view.tsx b/benchmarks/conformance/cases/129-shadcn-pie-donut-text/recharts-view.tsx new file mode 100644 index 00000000..ef9d87d7 --- /dev/null +++ b/benchmarks/conformance/cases/129-shadcn-pie-donut-text/recharts-view.tsx @@ -0,0 +1,60 @@ +import { Cell, Pie, PieChart, Tooltip } from 'recharts' +import { browserColors, browserData, totalVisitors } from './data' +import { PieDonutTextCard } from './view' +import { shadcnChartMount } from '../../shared/shadcn-chart-card' +import type { ConformanceInput } from '../../types' + +function RechartsView({ input }: { input: ConformanceInput }) { + return ( + + {({ width, height }) => ( + + + + {browserData.map((row, index) => ( + + ))} + + + {totalVisitors.toLocaleString('en-US')} + + + Visitors + + + )} + + ) +} + +export const rechartsMount = shadcnChartMount(RechartsView) diff --git a/benchmarks/conformance/cases/129-shadcn-pie-donut-text/recharts.ts b/benchmarks/conformance/cases/129-shadcn-pie-donut-text/recharts.ts new file mode 100644 index 00000000..93097581 --- /dev/null +++ b/benchmarks/conformance/cases/129-shadcn-pie-donut-text/recharts.ts @@ -0,0 +1 @@ +export { rechartsMount as mount } from './recharts-view' diff --git a/benchmarks/conformance/cases/129-shadcn-pie-donut-text/tanstack-view.tsx b/benchmarks/conformance/cases/129-shadcn-pie-donut-text/tanstack-view.tsx new file mode 100644 index 00000000..c4f8c350 --- /dev/null +++ b/benchmarks/conformance/cases/129-shadcn-pie-donut-text/tanstack-view.tsx @@ -0,0 +1,131 @@ +import { useMemo } from 'react' +import { defineChart } from '@tanstack/charts' +import { pie, polar, radialArc, radialText } from '@tanstack/charts/polar' +import { Chart } from '@tanstack/charts/react/core' +import { tooltip } from '@tanstack/charts/tooltip' +import { scaleLinear } from 'd3-scale' +import { + browserColors, + browserData, + centerLabels, + type BrowserDatum, + type CenterLabel, +} from './data' +import { PieDonutTextCard } from './view' +import { shadcnChartMount } from '../../shared/shadcn-chart-card' +import { tanstackCase } from '../../shared/mount' +import { createShadcnSpringRenderer } from '../../shared/shadcn-motion' +import type { ChartTooltipOptions } from '@tanstack/charts' +import type { PieDatum } from '@tanstack/charts/polar' +import type { ConformanceInput } from '../../types' + +const browsers = browserData.map((row) => row.browser) +const arcs = pie(browserData, { + value: 'visitors', + startAngle: Math.PI / 2, + endAngle: (-Math.PI * 3) / 2, +}) + +export const pieDonutTextDefinition = defineChart({ + marks: [ + polar({ + radiusRatio: 0.768, + angle: { scale: scaleLinear().domain([0, Math.PI * 2]) }, + radius: { scale: scaleLinear().domain([0, 1]) }, + marks: [ + radialArc(arcs, { + id: 'browser-slices', + key: 'browser', + innerRadius: ({ radius }) => radius * 0.625, + color: 'browser', + stroke: 'var(--background, var(--panel))', + strokeWidth: 5, + }), + radialText(centerLabels.slice(0, 1), { + id: 'visitor-total', + angle: 'angle', + radius: 'radius', + key: 'id', + text: 'text', + dy: -5, + fill: 'var(--foreground, currentColor)', + fontSize: 30, + fontWeight: 700, + }), + radialText(centerLabels.slice(1), { + id: 'visitor-label', + angle: 'angle', + radius: 'radius', + key: 'id', + text: 'text', + dy: 19, + fill: 'var(--muted-foreground, var(--muted))', + fontSize: 14, + }), + ], + }), + ], + color: { domain: browsers, range: browserColors }, + margin: 0, +}) + +const donutTooltip: ChartTooltipOptions | CenterLabel> = + { + className: 'sc-chart-tooltip', + content: (points) => ({ + rows: points.flatMap((point) => + 'browser' in point.datum + ? [ + { + label: + point.datum.browser[0]!.toUpperCase() + + point.datum.browser.slice(1), + value: point.datum.visitors.toLocaleString('en-US'), + color: point.color, + }, + ] + : [], + ), + }), + } + +function TanStackView({ input }: { input: ConformanceInput }) { + const renderer = useMemo( + () => + createShadcnSpringRenderer< + PieDatum | CenterLabel, + number, + number + >(), + [], + ) + const definition = useMemo( + () => + defineChart(pieDonutTextDefinition, { + svgAnimation: false, + keyboard: true, + tooltip: { use: tooltip, ...donutTooltip }, + }), + [], + ) + return ( + + {({ width, height }) => ( + + )} + + ) +} + +export const catalogCase = tanstackCase( + () => pieDonutTextDefinition, + 'Browser visitors donut', + donutTooltip, +) +export const tanstackMount = shadcnChartMount(TanStackView) diff --git a/benchmarks/conformance/cases/129-shadcn-pie-donut-text/tanstack.ts b/benchmarks/conformance/cases/129-shadcn-pie-donut-text/tanstack.ts new file mode 100644 index 00000000..07b01682 --- /dev/null +++ b/benchmarks/conformance/cases/129-shadcn-pie-donut-text/tanstack.ts @@ -0,0 +1,5 @@ +export { + catalogCase, + pieDonutTextDefinition, + tanstackMount as mount, +} from './tanstack-view' diff --git a/benchmarks/conformance/cases/129-shadcn-pie-donut-text/view.tsx b/benchmarks/conformance/cases/129-shadcn-pie-donut-text/view.tsx new file mode 100644 index 00000000..a999fcf7 --- /dev/null +++ b/benchmarks/conformance/cases/129-shadcn-pie-donut-text/view.tsx @@ -0,0 +1,26 @@ +import { + ShadcnChartCard, + ShadcnTrendFooter, +} from '../../shared/shadcn-chart-card' +import type { ConformanceInput } from '../../types' + +export function PieDonutTextCard({ + input, + children, +}: { + input: ConformanceInput + children: Parameters[0]['children'] +}) { + return ( + } + > + {children} + + ) +} diff --git a/benchmarks/conformance/cases/130-shadcn-radar-multiple/case.json b/benchmarks/conformance/cases/130-shadcn-radar-multiple/case.json new file mode 100644 index 00000000..38236c2b --- /dev/null +++ b/benchmarks/conformance/cases/130-shadcn-radar-multiple/case.json @@ -0,0 +1,29 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1310, + "id": "130-shadcn-radar-multiple", + "title": "shadcn radar chart – multiple", + "family": "radar", + "intent": "Reproduce shadcn/ui's multi-series radar example while replacing Recharts with TanStack Charts.", + "support": "native", + "features": [ + "radar chart", + "multiple series", + "polygon grid", + "angle labels", + "grouped tooltip", + "shadcn card shell" + ], + "geometry": [{ "role": "radar", "count": 2 }], + "source": { + "title": "shadcn/ui chart-radar-multiple", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-multiple" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-multiple card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, polygon guide, angle labels, two filled profiles, tooltip, card dimensions, footer, and themes." + } +} diff --git a/benchmarks/conformance/cases/130-shadcn-radar-multiple/data.ts b/benchmarks/conformance/cases/130-shadcn-radar-multiple/data.ts new file mode 100644 index 00000000..709505d3 --- /dev/null +++ b/benchmarks/conformance/cases/130-shadcn-radar-multiple/data.ts @@ -0,0 +1,31 @@ +export interface RadarDatum { + month: string + desktop: number + mobile: number +} + +export interface RadarPoint { + month: string + series: 'desktop' | 'mobile' + visitors: number +} + +export const radarData: readonly RadarDatum[] = [ + { month: 'January', desktop: 186, mobile: 80 }, + { month: 'February', desktop: 305, mobile: 200 }, + { month: 'March', desktop: 237, mobile: 120 }, + { month: 'April', desktop: 73, mobile: 190 }, + { month: 'May', desktop: 209, mobile: 130 }, + { month: 'June', desktop: 214, mobile: 140 }, +] + +export const radarMonths = radarData.map((row) => row.month) +export const radarSeries = ['desktop', 'mobile'] as const +export const radarColors = [ + 'var(--chart-1, var(--ts-chart-1))', + 'var(--chart-2, var(--ts-chart-2))', +] as const +export const radarPoints: readonly RadarPoint[] = radarData.flatMap((row) => [ + { month: row.month, series: 'desktop', visitors: row.desktop }, + { month: row.month, series: 'mobile', visitors: row.mobile }, +]) diff --git a/benchmarks/conformance/cases/130-shadcn-radar-multiple/recharts-view.tsx b/benchmarks/conformance/cases/130-shadcn-radar-multiple/recharts-view.tsx new file mode 100644 index 00000000..29305f17 --- /dev/null +++ b/benchmarks/conformance/cases/130-shadcn-radar-multiple/recharts-view.tsx @@ -0,0 +1,44 @@ +import { PolarAngleAxis, PolarGrid, Radar, RadarChart, Tooltip } from 'recharts' +import { radarData } from './data' +import { RadarMultipleCard } from './view' +import { shadcnChartMount } from '../../shared/shadcn-chart-card' +import type { ConformanceInput } from '../../types' + +function RechartsView({ input }: { input: ConformanceInput }) { + const compactLabels = input.width < 350 + return ( + + {({ width, height }) => ( + + + value.slice(0, 3) : undefined + } + /> + + + + + )} + + ) +} + +export const rechartsMount = shadcnChartMount(RechartsView) diff --git a/benchmarks/conformance/cases/130-shadcn-radar-multiple/recharts.ts b/benchmarks/conformance/cases/130-shadcn-radar-multiple/recharts.ts new file mode 100644 index 00000000..93097581 --- /dev/null +++ b/benchmarks/conformance/cases/130-shadcn-radar-multiple/recharts.ts @@ -0,0 +1 @@ +export { rechartsMount as mount } from './recharts-view' diff --git a/benchmarks/conformance/cases/130-shadcn-radar-multiple/tanstack-view.tsx b/benchmarks/conformance/cases/130-shadcn-radar-multiple/tanstack-view.tsx new file mode 100644 index 00000000..8c492aab --- /dev/null +++ b/benchmarks/conformance/cases/130-shadcn-radar-multiple/tanstack-view.tsx @@ -0,0 +1,138 @@ +import { useMemo } from 'react' +import { defineChart } from '@tanstack/charts' +import { + angleGrid, + focusGroupAngle, + polar, + radialArea, + radialGrid, +} from '@tanstack/charts/polar' +import { Chart } from '@tanstack/charts/react/core' +import { tooltip } from '@tanstack/charts/tooltip' +import { scaleLinear, scalePoint } from 'd3-scale' +import { curveLinearClosed } from 'd3-shape' +import { + radarColors, + radarMonths, + radarPoints, + radarSeries, + type RadarPoint, +} from './data' +import { RadarMultipleCard } from './view' +import { shadcnChartMount } from '../../shared/shadcn-chart-card' +import { tanstackCase } from '../../shared/mount' +import { createShadcnSpringRenderer } from '../../shared/shadcn-motion' +import type { ChartTooltipOptions } from '@tanstack/charts' +import type { ConformanceInput } from '../../types' + +export const radarMultipleDefinition = defineChart({ + marks: [ + polar({ + radiusRatio: 0.76, + angle: { scale: scalePoint().domain(radarMonths), wrap: true }, + radius: { scale: scaleLinear().domain([0, 320]) }, + guides: [ + radialGrid({ + values: [80, 160, 240, 320], + shape: 'polygon', + stroke: 'var(--border)', + }), + angleGrid({ + values: radarMonths, + labels: true, + labelOffset: 10, + labelFill: 'var(--muted-foreground, var(--muted))', + labelFontSize: 12, + stroke: 'var(--border)', + }), + ], + marks: [ + radialArea( + radarPoints.filter((row) => row.series === 'desktop'), + { + id: 'desktop-radar', + className: 'ts-chart__radar', + angle: 'month', + radius: 'visitors', + key: 'month', + z: 'series', + curve: curveLinearClosed, + fill: radarColors[0], + fillOpacity: 0.6, + stroke: radarColors[0], + strokeWidth: 1, + }, + ), + radialArea( + radarPoints.filter((row) => row.series === 'mobile'), + { + id: 'mobile-radar', + className: 'ts-chart__radar', + angle: 'month', + radius: 'visitors', + key: 'month', + z: 'series', + curve: curveLinearClosed, + fill: radarColors[1], + fillOpacity: 1, + stroke: radarColors[1], + strokeWidth: 1, + }, + ), + ], + }), + ], + color: { domain: radarSeries, range: radarColors }, + margin: 0, +}) + +const radarTooltip: ChartTooltipOptions = { + className: 'sc-chart-tooltip', + anchor: 'group-center', + content: (focused) => ({ + title: focused[0]?.datum.month ?? '', + rows: focused.map((point) => ({ + label: point.datum.series === 'desktop' ? 'Desktop' : 'Mobile', + value: point.datum.visitors.toLocaleString('en-US'), + color: point.datum.series === 'desktop' ? radarColors[0] : radarColors[1], + })), + }), +} + +function TanStackView({ input }: { input: ConformanceInput }) { + const renderer = useMemo( + () => createShadcnSpringRenderer(), + [], + ) + const definition = useMemo( + () => + defineChart(radarMultipleDefinition, { + svgAnimation: false, + focus: focusGroupAngle, + keyboard: true, + tooltip: { use: tooltip, ...radarTooltip }, + }), + [], + ) + return ( + + {({ width, height }) => ( + + )} + + ) +} + +export const catalogCase = tanstackCase( + () => radarMultipleDefinition, + 'Desktop and mobile visitor radar', + radarTooltip, + { guides: true }, +) +export const tanstackMount = shadcnChartMount(TanStackView) diff --git a/benchmarks/conformance/cases/130-shadcn-radar-multiple/tanstack.ts b/benchmarks/conformance/cases/130-shadcn-radar-multiple/tanstack.ts new file mode 100644 index 00000000..b786131e --- /dev/null +++ b/benchmarks/conformance/cases/130-shadcn-radar-multiple/tanstack.ts @@ -0,0 +1,5 @@ +export { + catalogCase, + radarMultipleDefinition, + tanstackMount as mount, +} from './tanstack-view' diff --git a/benchmarks/conformance/cases/130-shadcn-radar-multiple/view.tsx b/benchmarks/conformance/cases/130-shadcn-radar-multiple/view.tsx new file mode 100644 index 00000000..86b793fd --- /dev/null +++ b/benchmarks/conformance/cases/130-shadcn-radar-multiple/view.tsx @@ -0,0 +1,27 @@ +import { + ShadcnChartCard, + ShadcnTrendFooter, +} from '../../shared/shadcn-chart-card' +import type { ConformanceInput } from '../../types' + +export function RadarMultipleCard({ + input, + children, +}: { + input: ConformanceInput + children: Parameters[0]['children'] +}) { + return ( + = 400 ? 16 : 0} + footer={} + > + {children} + + ) +} diff --git a/benchmarks/conformance/cases/131-shadcn-radial-text/case.json b/benchmarks/conformance/cases/131-shadcn-radial-text/case.json new file mode 100644 index 00000000..6c5986a1 --- /dev/null +++ b/benchmarks/conformance/cases/131-shadcn-radial-text/case.json @@ -0,0 +1,31 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1320, + "id": "131-shadcn-radial-text", + "title": "shadcn radial chart – text", + "family": "radial", + "intent": "Reproduce shadcn/ui's partial radial chart with center text while replacing Recharts with TanStack Charts.", + "support": "native", + "features": [ + "partial radial bar", + "rounded caps", + "background track", + "center text", + "shadcn card shell" + ], + "geometry": [ + { "role": "arc", "count": 2 }, + { "role": "text", "count": 2 } + ], + "source": { + "title": "shadcn/ui chart-radial-text", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radial-text" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radial-text card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream value, 250-degree sweep, 80/90 radii, rounded track, center text, card dimensions, footer, and themes." + } +} diff --git a/benchmarks/conformance/cases/131-shadcn-radial-text/data.ts b/benchmarks/conformance/cases/131-shadcn-radial-text/data.ts new file mode 100644 index 00000000..b387bee9 --- /dev/null +++ b/benchmarks/conformance/cases/131-shadcn-radial-text/data.ts @@ -0,0 +1,14 @@ +export interface RadialDatum { + browser: 'safari' + visitors: number + ring: 'visitors' +} + +export const radialData: readonly RadialDatum[] = [ + { browser: 'safari', visitors: 200, ring: 'visitors' }, +] + +export const radialCenterLabels = [ + { id: 'value', angle: 0, radius: 0, text: '200' }, + { id: 'label', angle: 0, radius: 0, text: 'Visitors' }, +] diff --git a/benchmarks/conformance/cases/131-shadcn-radial-text/recharts-view.tsx b/benchmarks/conformance/cases/131-shadcn-radial-text/recharts-view.tsx new file mode 100644 index 00000000..dcda5380 --- /dev/null +++ b/benchmarks/conformance/cases/131-shadcn-radial-text/recharts-view.tsx @@ -0,0 +1,65 @@ +import { PolarGrid, RadialBar, RadialBarChart } from 'recharts' +import { radialData } from './data' +import { RadialTextCard } from './view' +import { shadcnChartMount } from '../../shared/shadcn-chart-card' +import type { ConformanceInput } from '../../types' + +function RechartsView({ input }: { input: ConformanceInput }) { + return ( + + {({ width, height }) => ( + + + + + 200 + + + Visitors + + + )} + + ) +} + +export const rechartsMount = shadcnChartMount(RechartsView) diff --git a/benchmarks/conformance/cases/131-shadcn-radial-text/recharts.ts b/benchmarks/conformance/cases/131-shadcn-radial-text/recharts.ts new file mode 100644 index 00000000..93097581 --- /dev/null +++ b/benchmarks/conformance/cases/131-shadcn-radial-text/recharts.ts @@ -0,0 +1 @@ +export { rechartsMount as mount } from './recharts-view' diff --git a/benchmarks/conformance/cases/131-shadcn-radial-text/tanstack-view.tsx b/benchmarks/conformance/cases/131-shadcn-radial-text/tanstack-view.tsx new file mode 100644 index 00000000..02448aea --- /dev/null +++ b/benchmarks/conformance/cases/131-shadcn-radial-text/tanstack-view.tsx @@ -0,0 +1,128 @@ +import { useMemo } from 'react' +import { defineChart } from '@tanstack/charts' +import { + pie, + polar, + radialArc, + radialBarAngle, + radialText, +} from '@tanstack/charts/polar' +import { Chart } from '@tanstack/charts/react/core' +import { scaleBand, scaleLinear } from 'd3-scale' +import { radialCenterLabels, radialData, type RadialDatum } from './data' +import { RadialTextCard } from './view' +import { shadcnChartMount } from '../../shared/shadcn-chart-card' +import { tanstackCase } from '../../shared/mount' +import { createShadcnSpringRenderer } from '../../shared/shadcn-motion' +import type { ConformanceInput } from '../../types' +import type { PieDatum } from '@tanstack/charts/polar' + +const startAngle = Math.PI / 2 +const endAngle = (-Math.PI * 160) / 180 +const radiusScale = scaleBand().domain(['visitors']) +const backgroundArc = pie([{ id: 'background', value: 1 }], { value: 'value' }) +type RadialTextDatum = + | PieDatum<{ id: string; value: number }> + | RadialDatum + | (typeof radialCenterLabels)[number] + +export const radialTextDefinition = defineChart({ + marks: [ + polar({ + angle: { scale: scaleLinear().domain([0, Math.PI * 2]) }, + radius: { scale: scaleLinear().domain([0, 1]) }, + marks: [ + radialArc(backgroundArc, { + id: 'radial-background', + key: 'id', + innerRadius: 80, + outerRadius: 90, + fill: 'var(--muted, var(--panel-muted))', + }), + ], + }), + polar({ + radiusRatio: 0.72, + startAngle, + endAngle, + angle: { scale: scaleLinear().domain([0, 200]) }, + radius: { + scale: radiusScale, + range: [80, 90], + }, + marks: [ + radialBarAngle(radialData, { + id: 'radial-value', + angle: 'visitors', + radius: 'ring', + key: 'browser', + fill: 'var(--chart-2, var(--ts-chart-2))', + cornerRadius: 10, + }), + ], + }), + polar({ + angle: { scale: scaleLinear().domain([0, Math.PI * 2]) }, + radius: { scale: scaleLinear().domain([0, 1]) }, + marks: [ + radialText(radialCenterLabels.slice(0, 1), { + id: 'radial-total', + angle: 'angle', + radius: 'radius', + key: 'id', + text: 'text', + dy: 0, + fill: 'var(--foreground, currentColor)', + fontSize: 36, + fontWeight: 700, + }), + radialText(radialCenterLabels.slice(1), { + id: 'radial-label', + angle: 'angle', + radius: 'radius', + key: 'id', + text: 'text', + dy: 24, + fill: 'var(--muted-foreground, var(--muted))', + fontSize: 14, + }), + ], + }), + ], + margin: 0, +}) + +function TanStackView({ input }: { input: ConformanceInput }) { + const renderer = useMemo( + () => + createShadcnSpringRenderer(), + [], + ) + const definition = useMemo( + () => + defineChart(radialTextDefinition, { + svgAnimation: false, + focus: 'nearest', + }), + [], + ) + return ( + + {({ width, height }) => ( + + )} + + ) +} + +export const catalogCase = tanstackCase( + () => radialTextDefinition, + 'Safari visitors radial chart', +) +export const tanstackMount = shadcnChartMount(TanStackView) diff --git a/benchmarks/conformance/cases/131-shadcn-radial-text/tanstack.ts b/benchmarks/conformance/cases/131-shadcn-radial-text/tanstack.ts new file mode 100644 index 00000000..452af0bf --- /dev/null +++ b/benchmarks/conformance/cases/131-shadcn-radial-text/tanstack.ts @@ -0,0 +1,5 @@ +export { + catalogCase, + radialTextDefinition, + tanstackMount as mount, +} from './tanstack-view' diff --git a/benchmarks/conformance/cases/131-shadcn-radial-text/view.tsx b/benchmarks/conformance/cases/131-shadcn-radial-text/view.tsx new file mode 100644 index 00000000..ac23e919 --- /dev/null +++ b/benchmarks/conformance/cases/131-shadcn-radial-text/view.tsx @@ -0,0 +1,26 @@ +import { + ShadcnChartCard, + ShadcnTrendFooter, +} from '../../shared/shadcn-chart-card' +import type { ConformanceInput } from '../../types' + +export function RadialTextCard({ + input, + children, +}: { + input: ConformanceInput + children: Parameters[0]['children'] +}) { + return ( + } + > + {children} + + ) +} diff --git a/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/case.json b/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/case.json new file mode 100644 index 00000000..78fae304 --- /dev/null +++ b/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/case.json @@ -0,0 +1,29 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1330, + "id": "132-shadcn-tooltip-advanced", + "title": "shadcn tooltip – advanced", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's stacked-bar card and custom total tooltip while replacing Recharts with TanStack Charts.", + "support": "native", + "features": [ + "stacked bars", + "rounded corners", + "rich tooltip body", + "computed total", + "default focus", + "shadcn card shell" + ], + "geometry": [{ "role": "bar", "count": 12 }], + "source": { + "title": "shadcn/ui chart-tooltip-advanced", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-advanced" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-advanced card and data with TanStack Charts and its React tooltip composition entry.", + "maintain": "Preserve the pinned upstream data, stack order, weekday axis, initial focused day, 180-pixel rich tooltip, row labels, kcal units, total, card dimensions, and themes." + } +} diff --git a/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/data.ts b/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/data.ts new file mode 100644 index 00000000..297cb656 --- /dev/null +++ b/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/data.ts @@ -0,0 +1,33 @@ +export interface ActivityDay { + date: string + running: number + swimming: number +} + +export interface ActivityPoint { + date: string + activity: 'running' | 'swimming' + calories: number +} + +export const activityData: readonly ActivityDay[] = [ + { date: '2024-07-15', running: 450, swimming: 300 }, + { date: '2024-07-16', running: 380, swimming: 420 }, + { date: '2024-07-17', running: 520, swimming: 120 }, + { date: '2024-07-18', running: 140, swimming: 550 }, + { date: '2024-07-19', running: 600, swimming: 350 }, + { date: '2024-07-20', running: 480, swimming: 400 }, +] + +export const activities = ['running', 'swimming'] as const +export const activityColors = [ + 'var(--chart-1, var(--ts-chart-1))', + 'var(--chart-2, var(--ts-chart-2))', +] as const +export const activityPoints: readonly ActivityPoint[] = activityData.flatMap( + (row) => [ + { date: row.date, activity: 'running', calories: row.running }, + { date: row.date, activity: 'swimming', calories: row.swimming }, + ], +) +export const weekday = new Intl.DateTimeFormat('en-US', { weekday: 'short' }) diff --git a/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/recharts-view.tsx b/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/recharts-view.tsx new file mode 100644 index 00000000..65d38f80 --- /dev/null +++ b/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/recharts-view.tsx @@ -0,0 +1,100 @@ +import { Bar, BarChart, Tooltip, XAxis } from 'recharts' +import { activityData, weekday, type ActivityDay } from './data' +import { AdvancedTooltipCard } from './view' +import { shadcnChartMount } from '../../shared/shadcn-chart-card' +import type { ConformanceInput } from '../../types' + +function RechartsTooltipBody({ + active, + payload, +}: { + active?: boolean + payload?: readonly { + dataKey?: string + value?: number + payload?: ActivityDay + }[] +}) { + if (!active || !payload?.length) return null + const rows = payload.filter( + (entry) => entry.dataKey === 'running' || entry.dataKey === 'swimming', + ) + const total = rows.reduce((sum, entry) => sum + Number(entry.value ?? 0), 0) + return ( +
+ {rows.map((entry) => ( +
+ + {entry.dataKey === 'running' ? 'Running' : 'Swimming'} + + {entry.value} + kcal + +
+ ))} +
+ Total + + {total} + kcal + +
+
+ ) +} + +function RechartsView({ input }: { input: ConformanceInput }) { + return ( + + {({ width, height }) => ( + + weekday.format(new Date(value))} + /> + + + } + /> + + )} + + ) +} + +export const rechartsMount = shadcnChartMount(RechartsView) diff --git a/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/recharts.ts b/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/recharts.ts new file mode 100644 index 00000000..93097581 --- /dev/null +++ b/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/recharts.ts @@ -0,0 +1 @@ +export { rechartsMount as mount } from './recharts-view' diff --git a/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/tanstack-view.tsx b/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/tanstack-view.tsx new file mode 100644 index 00000000..7b8fc400 --- /dev/null +++ b/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/tanstack-view.tsx @@ -0,0 +1,155 @@ +import { useMemo, useRef } from 'react' +import { barY, defineChart, stack } from '@tanstack/charts' +import { RendererChart as TooltipChart } from '@tanstack/charts/react/tooltip' +import { tooltip } from '@tanstack/charts/tooltip' +import { scaleBand, scaleLinear } from 'd3-scale' +import { + activities, + activityColors, + activityPoints, + weekday, + type ActivityPoint, +} from './data' +import { AdvancedTooltipCard } from './view' +import { shadcnChartMount } from '../../shared/shadcn-chart-card' +import { tanstackCase } from '../../shared/mount' +import { createShadcnSpringRenderer } from '../../shared/shadcn-motion' +import type { ChartPoint, ChartTooltipOptions } from '@tanstack/charts' +import type { ConformanceInput } from '../../types' + +export const advancedTooltipDefinition = defineChart({ + marks: [ + barY(activityPoints, { + id: 'activity-bars', + x: 'date', + y: 'calories', + z: 'activity', + color: 'activity', + key: (row) => `${row.date}:${row.activity}`, + layout: stack({ order: activities }), + radius: 4, + }), + ], + x: { + scale: () => scaleBand().paddingInner(0.2).paddingOuter(0.1), + axis: { + line: false, + ticks: { + size: 0, + padding: 10, + format: (value) => weekday.format(new Date(value)), + }, + }, + }, + y: { scale: scaleLinear().domain([0, 1_000]), axis: false }, + color: { domain: activities, range: activityColors }, + margin: { top: 0, right: 7, bottom: 32, left: 7 }, + theme: { foreground: 'var(--muted-foreground, var(--muted))' }, +}) + +const activityTooltip: ChartTooltipOptions = { + className: 'sc-chart-tooltip', + anchor: (_points, context) => ({ + x: context.surface.width * 0.271, + y: context.surface.height * 0.554, + }), + placement: 'bottom-right', + offset: 0, + sort: 'color-domain', + content: () => ({ rows: [] }), +} + +function AdvancedTooltipBody({ + points, +}: { + points: readonly ChartPoint[] +}) { + const ordered = [...points].sort( + (left, right) => + activities.indexOf(left.datum.activity) - + activities.indexOf(right.datum.activity), + ) + const total = ordered.reduce((sum, point) => sum + point.datum.calories, 0) + return ( +
+ {ordered.map((point) => ( +
+ + + {point.datum.activity === 'running' ? 'Running' : 'Swimming'} + + + {point.datum.calories} + kcal + +
+ ))} +
+ Total + + {total} + kcal + +
+
+ ) +} + +function TanStackView({ input }: { input: ConformanceInput }) { + const focused = useRef(false) + const renderer = useMemo( + () => createShadcnSpringRenderer(), + [], + ) + const definition = useMemo( + () => + defineChart(advancedTooltipDefinition, { + svgAnimation: false, + focus: 'group-x', + keyboard: true, + tooltip: { use: tooltip, ...activityTooltip }, + }), + [], + ) + return ( + + {({ width, height }) => ( + { + if (focused.current) return + const point = scene.points.find( + (candidate) => candidate.datum.date === '2024-07-16', + ) + if (!point) return + focused.current = true + interaction.setControlledFocus(point, { source: 'programmatic' }) + }} + renderTooltipBody={({ points }) => ( + + )} + /> + )} + + ) +} + +export const catalogCase = tanstackCase( + () => advancedTooltipDefinition, + 'Daily running and swimming calories', + activityTooltip, + { margin: true }, +) +export const tanstackMount = shadcnChartMount(TanStackView) diff --git a/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/tanstack.ts b/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/tanstack.ts new file mode 100644 index 00000000..ebb5b8c5 --- /dev/null +++ b/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/tanstack.ts @@ -0,0 +1,5 @@ +export { + advancedTooltipDefinition, + catalogCase, + tanstackMount as mount, +} from './tanstack-view' diff --git a/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/view.tsx b/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/view.tsx new file mode 100644 index 00000000..d6dd3c09 --- /dev/null +++ b/benchmarks/conformance/cases/132-shadcn-tooltip-advanced/view.tsx @@ -0,0 +1,20 @@ +import { ShadcnChartCard } from '../../shared/shadcn-chart-card' +import type { ConformanceInput } from '../../types' + +export function AdvancedTooltipCard({ + input, + children, +}: { + input: ConformanceInput + children: Parameters[0]['children'] +}) { + return ( + + {children} + + ) +} diff --git a/benchmarks/conformance/cases/133-shadcn-area-axes/case.json b/benchmarks/conformance/cases/133-shadcn-area-axes/case.json new file mode 100644 index 00000000..3f5d228f --- /dev/null +++ b/benchmarks/conformance/cases/133-shadcn-area-axes/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1340, + "id": "133-shadcn-area-axes", + "title": "shadcn Area Chart - Axes", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-axes example with TanStack Charts.", + "support": "native", + "features": ["area chart", "axes", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-area-axes", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-axes" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-axes card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area axes treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/133-shadcn-area-axes/recharts.ts b/benchmarks/conformance/cases/133-shadcn-area-axes/recharts.ts new file mode 100644 index 00000000..1e7c29fd --- /dev/null +++ b/benchmarks/conformance/cases/133-shadcn-area-axes/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-area-axes') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/133-shadcn-area-axes/tanstack.ts b/benchmarks/conformance/cases/133-shadcn-area-axes/tanstack.ts new file mode 100644 index 00000000..c31d786c --- /dev/null +++ b/benchmarks/conformance/cases/133-shadcn-area-axes/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-area-axes') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/134-shadcn-area-default/case.json b/benchmarks/conformance/cases/134-shadcn-area-default/case.json new file mode 100644 index 00000000..5134724a --- /dev/null +++ b/benchmarks/conformance/cases/134-shadcn-area-default/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1350, + "id": "134-shadcn-area-default", + "title": "shadcn Area Chart", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-default example with TanStack Charts.", + "support": "native", + "features": ["area chart", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-area-default", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-default" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-default card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area default treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/134-shadcn-area-default/recharts.ts b/benchmarks/conformance/cases/134-shadcn-area-default/recharts.ts new file mode 100644 index 00000000..34f380bb --- /dev/null +++ b/benchmarks/conformance/cases/134-shadcn-area-default/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-area-default') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/134-shadcn-area-default/tanstack.ts b/benchmarks/conformance/cases/134-shadcn-area-default/tanstack.ts new file mode 100644 index 00000000..dcccf27c --- /dev/null +++ b/benchmarks/conformance/cases/134-shadcn-area-default/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-area-default') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/135-shadcn-area-gradient/case.json b/benchmarks/conformance/cases/135-shadcn-area-gradient/case.json new file mode 100644 index 00000000..5532ce8b --- /dev/null +++ b/benchmarks/conformance/cases/135-shadcn-area-gradient/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1360, + "id": "135-shadcn-area-gradient", + "title": "shadcn Area Chart - Gradient", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-gradient example with TanStack Charts.", + "support": "native", + "features": ["area chart", "gradient", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-area-gradient", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-gradient" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-gradient card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area gradient treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/135-shadcn-area-gradient/recharts.ts b/benchmarks/conformance/cases/135-shadcn-area-gradient/recharts.ts new file mode 100644 index 00000000..bf121d42 --- /dev/null +++ b/benchmarks/conformance/cases/135-shadcn-area-gradient/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-area-gradient') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/135-shadcn-area-gradient/tanstack.ts b/benchmarks/conformance/cases/135-shadcn-area-gradient/tanstack.ts new file mode 100644 index 00000000..ef61cdf2 --- /dev/null +++ b/benchmarks/conformance/cases/135-shadcn-area-gradient/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-area-gradient') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/136-shadcn-area-icons/case.json b/benchmarks/conformance/cases/136-shadcn-area-icons/case.json new file mode 100644 index 00000000..1fb01632 --- /dev/null +++ b/benchmarks/conformance/cases/136-shadcn-area-icons/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1370, + "id": "136-shadcn-area-icons", + "title": "shadcn Area Chart - Icons", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-icons example with TanStack Charts.", + "support": "native", + "features": ["area chart", "icons", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-area-icons", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-icons" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-icons card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area icons treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/136-shadcn-area-icons/recharts.ts b/benchmarks/conformance/cases/136-shadcn-area-icons/recharts.ts new file mode 100644 index 00000000..7dc0140f --- /dev/null +++ b/benchmarks/conformance/cases/136-shadcn-area-icons/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-area-icons') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/136-shadcn-area-icons/tanstack.ts b/benchmarks/conformance/cases/136-shadcn-area-icons/tanstack.ts new file mode 100644 index 00000000..9abb14da --- /dev/null +++ b/benchmarks/conformance/cases/136-shadcn-area-icons/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-area-icons') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/137-shadcn-area-interactive/case.json b/benchmarks/conformance/cases/137-shadcn-area-interactive/case.json new file mode 100644 index 00000000..38280aa7 --- /dev/null +++ b/benchmarks/conformance/cases/137-shadcn-area-interactive/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1380, + "id": "137-shadcn-area-interactive", + "title": "shadcn Area Chart - Interactive", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-interactive example with TanStack Charts.", + "support": "native", + "features": ["area chart", "interactive", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-area-interactive", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-interactive" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-interactive card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area interactive treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/137-shadcn-area-interactive/recharts.ts b/benchmarks/conformance/cases/137-shadcn-area-interactive/recharts.ts new file mode 100644 index 00000000..1ee3cfd0 --- /dev/null +++ b/benchmarks/conformance/cases/137-shadcn-area-interactive/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-area-interactive') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/137-shadcn-area-interactive/tanstack.ts b/benchmarks/conformance/cases/137-shadcn-area-interactive/tanstack.ts new file mode 100644 index 00000000..818e8025 --- /dev/null +++ b/benchmarks/conformance/cases/137-shadcn-area-interactive/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-area-interactive') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/138-shadcn-area-legend/case.json b/benchmarks/conformance/cases/138-shadcn-area-legend/case.json new file mode 100644 index 00000000..81666b26 --- /dev/null +++ b/benchmarks/conformance/cases/138-shadcn-area-legend/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1390, + "id": "138-shadcn-area-legend", + "title": "shadcn Area Chart - Legend", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-legend example with TanStack Charts.", + "support": "native", + "features": ["area chart", "legend", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-area-legend", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-legend" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-legend card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area legend treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/138-shadcn-area-legend/recharts.ts b/benchmarks/conformance/cases/138-shadcn-area-legend/recharts.ts new file mode 100644 index 00000000..4851fb45 --- /dev/null +++ b/benchmarks/conformance/cases/138-shadcn-area-legend/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-area-legend') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/138-shadcn-area-legend/tanstack.ts b/benchmarks/conformance/cases/138-shadcn-area-legend/tanstack.ts new file mode 100644 index 00000000..eb2d937c --- /dev/null +++ b/benchmarks/conformance/cases/138-shadcn-area-legend/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-area-legend') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/139-shadcn-area-linear/case.json b/benchmarks/conformance/cases/139-shadcn-area-linear/case.json new file mode 100644 index 00000000..3d2e98c2 --- /dev/null +++ b/benchmarks/conformance/cases/139-shadcn-area-linear/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1400, + "id": "139-shadcn-area-linear", + "title": "shadcn Area Chart - Linear", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-linear example with TanStack Charts.", + "support": "native", + "features": ["area chart", "linear", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-area-linear", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-linear" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-linear card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area linear treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/139-shadcn-area-linear/recharts.ts b/benchmarks/conformance/cases/139-shadcn-area-linear/recharts.ts new file mode 100644 index 00000000..8da61afb --- /dev/null +++ b/benchmarks/conformance/cases/139-shadcn-area-linear/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-area-linear') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/139-shadcn-area-linear/tanstack.ts b/benchmarks/conformance/cases/139-shadcn-area-linear/tanstack.ts new file mode 100644 index 00000000..d236175c --- /dev/null +++ b/benchmarks/conformance/cases/139-shadcn-area-linear/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-area-linear') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/140-shadcn-area-stacked-expand/case.json b/benchmarks/conformance/cases/140-shadcn-area-stacked-expand/case.json new file mode 100644 index 00000000..e83b5f4a --- /dev/null +++ b/benchmarks/conformance/cases/140-shadcn-area-stacked-expand/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1410, + "id": "140-shadcn-area-stacked-expand", + "title": "shadcn Area Chart - Stacked Expanded", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-stacked-expand example with TanStack Charts.", + "support": "native", + "features": ["area chart", "stacked expand", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 3 + } + ], + "source": { + "title": "shadcn/ui chart-area-stacked-expand", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-stacked-expand" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-stacked-expand card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area stacked expand treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/140-shadcn-area-stacked-expand/recharts.ts b/benchmarks/conformance/cases/140-shadcn-area-stacked-expand/recharts.ts new file mode 100644 index 00000000..519f2bbd --- /dev/null +++ b/benchmarks/conformance/cases/140-shadcn-area-stacked-expand/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-area-stacked-expand') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/140-shadcn-area-stacked-expand/tanstack.ts b/benchmarks/conformance/cases/140-shadcn-area-stacked-expand/tanstack.ts new file mode 100644 index 00000000..d5fbb277 --- /dev/null +++ b/benchmarks/conformance/cases/140-shadcn-area-stacked-expand/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-area-stacked-expand') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/141-shadcn-area-stacked/case.json b/benchmarks/conformance/cases/141-shadcn-area-stacked/case.json new file mode 100644 index 00000000..384fbba1 --- /dev/null +++ b/benchmarks/conformance/cases/141-shadcn-area-stacked/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1420, + "id": "141-shadcn-area-stacked", + "title": "shadcn Area Chart - Stacked", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-stacked example with TanStack Charts.", + "support": "native", + "features": ["area chart", "stacked", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-area-stacked", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-stacked" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-stacked card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area stacked treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/141-shadcn-area-stacked/recharts.ts b/benchmarks/conformance/cases/141-shadcn-area-stacked/recharts.ts new file mode 100644 index 00000000..791a069f --- /dev/null +++ b/benchmarks/conformance/cases/141-shadcn-area-stacked/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-area-stacked') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/141-shadcn-area-stacked/tanstack.ts b/benchmarks/conformance/cases/141-shadcn-area-stacked/tanstack.ts new file mode 100644 index 00000000..3dcf9595 --- /dev/null +++ b/benchmarks/conformance/cases/141-shadcn-area-stacked/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-area-stacked') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/142-shadcn-area-step/case.json b/benchmarks/conformance/cases/142-shadcn-area-step/case.json new file mode 100644 index 00000000..7e87b66f --- /dev/null +++ b/benchmarks/conformance/cases/142-shadcn-area-step/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1430, + "id": "142-shadcn-area-step", + "title": "shadcn Area Chart - Step", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-step example with TanStack Charts.", + "support": "native", + "features": ["area chart", "step", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-area-step", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-step" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-step card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area step treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/142-shadcn-area-step/recharts.ts b/benchmarks/conformance/cases/142-shadcn-area-step/recharts.ts new file mode 100644 index 00000000..af77995f --- /dev/null +++ b/benchmarks/conformance/cases/142-shadcn-area-step/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-area-step') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/142-shadcn-area-step/tanstack.ts b/benchmarks/conformance/cases/142-shadcn-area-step/tanstack.ts new file mode 100644 index 00000000..8c9f95bc --- /dev/null +++ b/benchmarks/conformance/cases/142-shadcn-area-step/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-area-step') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/143-shadcn-bar-active/case.json b/benchmarks/conformance/cases/143-shadcn-bar-active/case.json new file mode 100644 index 00000000..83b28ee8 --- /dev/null +++ b/benchmarks/conformance/cases/143-shadcn-bar-active/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1440, + "id": "143-shadcn-bar-active", + "title": "shadcn Bar Chart - Active", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-active example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "active", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 6 + } + ], + "source": { + "title": "shadcn/ui chart-bar-active", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-active" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-active card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar active treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/143-shadcn-bar-active/recharts.ts b/benchmarks/conformance/cases/143-shadcn-bar-active/recharts.ts new file mode 100644 index 00000000..6e01e32b --- /dev/null +++ b/benchmarks/conformance/cases/143-shadcn-bar-active/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-bar-active') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/143-shadcn-bar-active/tanstack.ts b/benchmarks/conformance/cases/143-shadcn-bar-active/tanstack.ts new file mode 100644 index 00000000..eec46eee --- /dev/null +++ b/benchmarks/conformance/cases/143-shadcn-bar-active/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-bar-active') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/144-shadcn-bar-default/case.json b/benchmarks/conformance/cases/144-shadcn-bar-default/case.json new file mode 100644 index 00000000..c81903bb --- /dev/null +++ b/benchmarks/conformance/cases/144-shadcn-bar-default/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1450, + "id": "144-shadcn-bar-default", + "title": "shadcn Bar Chart", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-default example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 6 + } + ], + "source": { + "title": "shadcn/ui chart-bar-default", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-default" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-default card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar default treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/144-shadcn-bar-default/recharts.ts b/benchmarks/conformance/cases/144-shadcn-bar-default/recharts.ts new file mode 100644 index 00000000..14702a47 --- /dev/null +++ b/benchmarks/conformance/cases/144-shadcn-bar-default/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-bar-default') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/144-shadcn-bar-default/tanstack.ts b/benchmarks/conformance/cases/144-shadcn-bar-default/tanstack.ts new file mode 100644 index 00000000..10bef265 --- /dev/null +++ b/benchmarks/conformance/cases/144-shadcn-bar-default/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-bar-default') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/145-shadcn-bar-horizontal/case.json b/benchmarks/conformance/cases/145-shadcn-bar-horizontal/case.json new file mode 100644 index 00000000..e91baae4 --- /dev/null +++ b/benchmarks/conformance/cases/145-shadcn-bar-horizontal/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1460, + "id": "145-shadcn-bar-horizontal", + "title": "shadcn Bar Chart - Horizontal", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-horizontal example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "horizontal", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 6 + } + ], + "source": { + "title": "shadcn/ui chart-bar-horizontal", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-horizontal" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-horizontal card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar horizontal treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/145-shadcn-bar-horizontal/recharts.ts b/benchmarks/conformance/cases/145-shadcn-bar-horizontal/recharts.ts new file mode 100644 index 00000000..cadf3928 --- /dev/null +++ b/benchmarks/conformance/cases/145-shadcn-bar-horizontal/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-bar-horizontal') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/145-shadcn-bar-horizontal/tanstack.ts b/benchmarks/conformance/cases/145-shadcn-bar-horizontal/tanstack.ts new file mode 100644 index 00000000..ee3573ba --- /dev/null +++ b/benchmarks/conformance/cases/145-shadcn-bar-horizontal/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-bar-horizontal') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/146-shadcn-bar-interactive/case.json b/benchmarks/conformance/cases/146-shadcn-bar-interactive/case.json new file mode 100644 index 00000000..c09cb37a --- /dev/null +++ b/benchmarks/conformance/cases/146-shadcn-bar-interactive/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1470, + "id": "146-shadcn-bar-interactive", + "title": "shadcn Bar Chart - Interactive", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-interactive example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "interactive", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 6 + } + ], + "source": { + "title": "shadcn/ui chart-bar-interactive", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-interactive" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-interactive card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar interactive treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/146-shadcn-bar-interactive/recharts.ts b/benchmarks/conformance/cases/146-shadcn-bar-interactive/recharts.ts new file mode 100644 index 00000000..67076812 --- /dev/null +++ b/benchmarks/conformance/cases/146-shadcn-bar-interactive/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-bar-interactive') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/146-shadcn-bar-interactive/tanstack.ts b/benchmarks/conformance/cases/146-shadcn-bar-interactive/tanstack.ts new file mode 100644 index 00000000..10495e83 --- /dev/null +++ b/benchmarks/conformance/cases/146-shadcn-bar-interactive/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-bar-interactive') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/147-shadcn-bar-label-custom/case.json b/benchmarks/conformance/cases/147-shadcn-bar-label-custom/case.json new file mode 100644 index 00000000..49170bba --- /dev/null +++ b/benchmarks/conformance/cases/147-shadcn-bar-label-custom/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1480, + "id": "147-shadcn-bar-label-custom", + "title": "shadcn Bar Chart - Custom Label", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-label-custom example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "label custom", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 6 + } + ], + "source": { + "title": "shadcn/ui chart-bar-label-custom", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-label-custom" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-label-custom card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar label custom treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/147-shadcn-bar-label-custom/recharts.ts b/benchmarks/conformance/cases/147-shadcn-bar-label-custom/recharts.ts new file mode 100644 index 00000000..bf89ab65 --- /dev/null +++ b/benchmarks/conformance/cases/147-shadcn-bar-label-custom/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-bar-label-custom') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/147-shadcn-bar-label-custom/tanstack.ts b/benchmarks/conformance/cases/147-shadcn-bar-label-custom/tanstack.ts new file mode 100644 index 00000000..ddb3cd9d --- /dev/null +++ b/benchmarks/conformance/cases/147-shadcn-bar-label-custom/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-bar-label-custom') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/148-shadcn-bar-label/case.json b/benchmarks/conformance/cases/148-shadcn-bar-label/case.json new file mode 100644 index 00000000..73c7b2d8 --- /dev/null +++ b/benchmarks/conformance/cases/148-shadcn-bar-label/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1490, + "id": "148-shadcn-bar-label", + "title": "shadcn Bar Chart - Label", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-label example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "label", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 6 + } + ], + "source": { + "title": "shadcn/ui chart-bar-label", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-label" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-label card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar label treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/148-shadcn-bar-label/recharts.ts b/benchmarks/conformance/cases/148-shadcn-bar-label/recharts.ts new file mode 100644 index 00000000..0faf1a6e --- /dev/null +++ b/benchmarks/conformance/cases/148-shadcn-bar-label/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-bar-label') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/148-shadcn-bar-label/tanstack.ts b/benchmarks/conformance/cases/148-shadcn-bar-label/tanstack.ts new file mode 100644 index 00000000..24b06bb8 --- /dev/null +++ b/benchmarks/conformance/cases/148-shadcn-bar-label/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-bar-label') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/149-shadcn-bar-mixed/case.json b/benchmarks/conformance/cases/149-shadcn-bar-mixed/case.json new file mode 100644 index 00000000..09816de3 --- /dev/null +++ b/benchmarks/conformance/cases/149-shadcn-bar-mixed/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1500, + "id": "149-shadcn-bar-mixed", + "title": "shadcn Bar Chart - Mixed", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-mixed example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "mixed", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 6 + } + ], + "source": { + "title": "shadcn/ui chart-bar-mixed", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-mixed" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-mixed card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar mixed treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/149-shadcn-bar-mixed/recharts.ts b/benchmarks/conformance/cases/149-shadcn-bar-mixed/recharts.ts new file mode 100644 index 00000000..8dc5286c --- /dev/null +++ b/benchmarks/conformance/cases/149-shadcn-bar-mixed/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-bar-mixed') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/149-shadcn-bar-mixed/tanstack.ts b/benchmarks/conformance/cases/149-shadcn-bar-mixed/tanstack.ts new file mode 100644 index 00000000..2613aba7 --- /dev/null +++ b/benchmarks/conformance/cases/149-shadcn-bar-mixed/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-bar-mixed') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/150-shadcn-bar-negative/case.json b/benchmarks/conformance/cases/150-shadcn-bar-negative/case.json new file mode 100644 index 00000000..4ca5758c --- /dev/null +++ b/benchmarks/conformance/cases/150-shadcn-bar-negative/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1510, + "id": "150-shadcn-bar-negative", + "title": "shadcn Bar Chart - Negative", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-negative example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "negative", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 6 + } + ], + "source": { + "title": "shadcn/ui chart-bar-negative", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-negative" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-negative card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar negative treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/150-shadcn-bar-negative/recharts.ts b/benchmarks/conformance/cases/150-shadcn-bar-negative/recharts.ts new file mode 100644 index 00000000..70d40360 --- /dev/null +++ b/benchmarks/conformance/cases/150-shadcn-bar-negative/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-bar-negative') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/150-shadcn-bar-negative/tanstack.ts b/benchmarks/conformance/cases/150-shadcn-bar-negative/tanstack.ts new file mode 100644 index 00000000..1e5fa74b --- /dev/null +++ b/benchmarks/conformance/cases/150-shadcn-bar-negative/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-bar-negative') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/151-shadcn-bar-stacked/case.json b/benchmarks/conformance/cases/151-shadcn-bar-stacked/case.json new file mode 100644 index 00000000..e8aec797 --- /dev/null +++ b/benchmarks/conformance/cases/151-shadcn-bar-stacked/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1520, + "id": "151-shadcn-bar-stacked", + "title": "shadcn Bar Chart - Stacked + Legend", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-stacked example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "stacked", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 12 + } + ], + "source": { + "title": "shadcn/ui chart-bar-stacked", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-stacked" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-stacked card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar stacked treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/151-shadcn-bar-stacked/recharts.ts b/benchmarks/conformance/cases/151-shadcn-bar-stacked/recharts.ts new file mode 100644 index 00000000..0b2dfe03 --- /dev/null +++ b/benchmarks/conformance/cases/151-shadcn-bar-stacked/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-bar-stacked') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/151-shadcn-bar-stacked/tanstack.ts b/benchmarks/conformance/cases/151-shadcn-bar-stacked/tanstack.ts new file mode 100644 index 00000000..5e899b85 --- /dev/null +++ b/benchmarks/conformance/cases/151-shadcn-bar-stacked/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-bar-stacked') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/152-shadcn-line-default/case.json b/benchmarks/conformance/cases/152-shadcn-line-default/case.json new file mode 100644 index 00000000..43a71061 --- /dev/null +++ b/benchmarks/conformance/cases/152-shadcn-line-default/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1530, + "id": "152-shadcn-line-default", + "title": "shadcn Line Chart", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-default example with TanStack Charts.", + "support": "native", + "features": ["line chart", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-line-default", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-default" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-default card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line default treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/152-shadcn-line-default/recharts.ts b/benchmarks/conformance/cases/152-shadcn-line-default/recharts.ts new file mode 100644 index 00000000..7525e0c9 --- /dev/null +++ b/benchmarks/conformance/cases/152-shadcn-line-default/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-line-default') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/152-shadcn-line-default/tanstack.ts b/benchmarks/conformance/cases/152-shadcn-line-default/tanstack.ts new file mode 100644 index 00000000..96824eac --- /dev/null +++ b/benchmarks/conformance/cases/152-shadcn-line-default/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-line-default') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/153-shadcn-line-dots-colors/case.json b/benchmarks/conformance/cases/153-shadcn-line-dots-colors/case.json new file mode 100644 index 00000000..d3a182b4 --- /dev/null +++ b/benchmarks/conformance/cases/153-shadcn-line-dots-colors/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1540, + "id": "153-shadcn-line-dots-colors", + "title": "shadcn Line Chart - Dots Colors", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-dots-colors example with TanStack Charts.", + "support": "native", + "features": ["line chart", "dots colors", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-line-dots-colors", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-dots-colors" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-dots-colors card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line dots colors treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/153-shadcn-line-dots-colors/recharts.ts b/benchmarks/conformance/cases/153-shadcn-line-dots-colors/recharts.ts new file mode 100644 index 00000000..67daf1e9 --- /dev/null +++ b/benchmarks/conformance/cases/153-shadcn-line-dots-colors/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-line-dots-colors') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/153-shadcn-line-dots-colors/tanstack.ts b/benchmarks/conformance/cases/153-shadcn-line-dots-colors/tanstack.ts new file mode 100644 index 00000000..9dc257d9 --- /dev/null +++ b/benchmarks/conformance/cases/153-shadcn-line-dots-colors/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-line-dots-colors') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/154-shadcn-line-dots-custom/case.json b/benchmarks/conformance/cases/154-shadcn-line-dots-custom/case.json new file mode 100644 index 00000000..dd334e10 --- /dev/null +++ b/benchmarks/conformance/cases/154-shadcn-line-dots-custom/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1550, + "id": "154-shadcn-line-dots-custom", + "title": "shadcn Line Chart - Custom Dots", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-dots-custom example with TanStack Charts.", + "support": "native", + "features": ["line chart", "dots custom", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-line-dots-custom", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-dots-custom" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-dots-custom card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line dots custom treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/154-shadcn-line-dots-custom/recharts.ts b/benchmarks/conformance/cases/154-shadcn-line-dots-custom/recharts.ts new file mode 100644 index 00000000..227a82bd --- /dev/null +++ b/benchmarks/conformance/cases/154-shadcn-line-dots-custom/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-line-dots-custom') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/154-shadcn-line-dots-custom/tanstack.ts b/benchmarks/conformance/cases/154-shadcn-line-dots-custom/tanstack.ts new file mode 100644 index 00000000..2540373e --- /dev/null +++ b/benchmarks/conformance/cases/154-shadcn-line-dots-custom/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-line-dots-custom') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/155-shadcn-line-dots/case.json b/benchmarks/conformance/cases/155-shadcn-line-dots/case.json new file mode 100644 index 00000000..508620f3 --- /dev/null +++ b/benchmarks/conformance/cases/155-shadcn-line-dots/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1560, + "id": "155-shadcn-line-dots", + "title": "shadcn Line Chart - Dots", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-dots example with TanStack Charts.", + "support": "native", + "features": ["line chart", "dots", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-line-dots", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-dots" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-dots card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line dots treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/155-shadcn-line-dots/recharts.ts b/benchmarks/conformance/cases/155-shadcn-line-dots/recharts.ts new file mode 100644 index 00000000..1ad0f470 --- /dev/null +++ b/benchmarks/conformance/cases/155-shadcn-line-dots/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-line-dots') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/155-shadcn-line-dots/tanstack.ts b/benchmarks/conformance/cases/155-shadcn-line-dots/tanstack.ts new file mode 100644 index 00000000..bc15ac6b --- /dev/null +++ b/benchmarks/conformance/cases/155-shadcn-line-dots/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-line-dots') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/156-shadcn-line-interactive/case.json b/benchmarks/conformance/cases/156-shadcn-line-interactive/case.json new file mode 100644 index 00000000..2ad25717 --- /dev/null +++ b/benchmarks/conformance/cases/156-shadcn-line-interactive/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1570, + "id": "156-shadcn-line-interactive", + "title": "shadcn Line Chart - Interactive", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-interactive example with TanStack Charts.", + "support": "native", + "features": ["line chart", "interactive", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-line-interactive", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-interactive" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-interactive card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line interactive treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/156-shadcn-line-interactive/recharts.ts b/benchmarks/conformance/cases/156-shadcn-line-interactive/recharts.ts new file mode 100644 index 00000000..d7f4ace1 --- /dev/null +++ b/benchmarks/conformance/cases/156-shadcn-line-interactive/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-line-interactive') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/156-shadcn-line-interactive/tanstack.ts b/benchmarks/conformance/cases/156-shadcn-line-interactive/tanstack.ts new file mode 100644 index 00000000..16e6285b --- /dev/null +++ b/benchmarks/conformance/cases/156-shadcn-line-interactive/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-line-interactive') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/157-shadcn-line-label-custom/case.json b/benchmarks/conformance/cases/157-shadcn-line-label-custom/case.json new file mode 100644 index 00000000..f2cff6f4 --- /dev/null +++ b/benchmarks/conformance/cases/157-shadcn-line-label-custom/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1580, + "id": "157-shadcn-line-label-custom", + "title": "shadcn Line Chart - Custom Label", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-label-custom example with TanStack Charts.", + "support": "native", + "features": ["line chart", "label custom", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-line-label-custom", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-label-custom" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-label-custom card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line label custom treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/157-shadcn-line-label-custom/recharts.ts b/benchmarks/conformance/cases/157-shadcn-line-label-custom/recharts.ts new file mode 100644 index 00000000..12e393f4 --- /dev/null +++ b/benchmarks/conformance/cases/157-shadcn-line-label-custom/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-line-label-custom') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/157-shadcn-line-label-custom/tanstack.ts b/benchmarks/conformance/cases/157-shadcn-line-label-custom/tanstack.ts new file mode 100644 index 00000000..49625516 --- /dev/null +++ b/benchmarks/conformance/cases/157-shadcn-line-label-custom/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-line-label-custom') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/158-shadcn-line-label/case.json b/benchmarks/conformance/cases/158-shadcn-line-label/case.json new file mode 100644 index 00000000..04ac8ba5 --- /dev/null +++ b/benchmarks/conformance/cases/158-shadcn-line-label/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1590, + "id": "158-shadcn-line-label", + "title": "shadcn Line Chart - Label", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-label example with TanStack Charts.", + "support": "native", + "features": ["line chart", "label", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-line-label", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-label" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-label card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line label treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/158-shadcn-line-label/recharts.ts b/benchmarks/conformance/cases/158-shadcn-line-label/recharts.ts new file mode 100644 index 00000000..85cfabe6 --- /dev/null +++ b/benchmarks/conformance/cases/158-shadcn-line-label/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-line-label') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/158-shadcn-line-label/tanstack.ts b/benchmarks/conformance/cases/158-shadcn-line-label/tanstack.ts new file mode 100644 index 00000000..430e54b4 --- /dev/null +++ b/benchmarks/conformance/cases/158-shadcn-line-label/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-line-label') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/159-shadcn-line-linear/case.json b/benchmarks/conformance/cases/159-shadcn-line-linear/case.json new file mode 100644 index 00000000..caf49f59 --- /dev/null +++ b/benchmarks/conformance/cases/159-shadcn-line-linear/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1600, + "id": "159-shadcn-line-linear", + "title": "shadcn Line Chart - Linear", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-linear example with TanStack Charts.", + "support": "native", + "features": ["line chart", "linear", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-line-linear", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-linear" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-linear card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line linear treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/159-shadcn-line-linear/recharts.ts b/benchmarks/conformance/cases/159-shadcn-line-linear/recharts.ts new file mode 100644 index 00000000..5ebba957 --- /dev/null +++ b/benchmarks/conformance/cases/159-shadcn-line-linear/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-line-linear') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/159-shadcn-line-linear/tanstack.ts b/benchmarks/conformance/cases/159-shadcn-line-linear/tanstack.ts new file mode 100644 index 00000000..9e3151ce --- /dev/null +++ b/benchmarks/conformance/cases/159-shadcn-line-linear/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-line-linear') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/160-shadcn-line-multiple/case.json b/benchmarks/conformance/cases/160-shadcn-line-multiple/case.json new file mode 100644 index 00000000..5d07fd79 --- /dev/null +++ b/benchmarks/conformance/cases/160-shadcn-line-multiple/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1610, + "id": "160-shadcn-line-multiple", + "title": "shadcn Line Chart - Multiple", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-multiple example with TanStack Charts.", + "support": "native", + "features": ["line chart", "multiple", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-line-multiple", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-multiple" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-multiple card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line multiple treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/160-shadcn-line-multiple/recharts.ts b/benchmarks/conformance/cases/160-shadcn-line-multiple/recharts.ts new file mode 100644 index 00000000..6549c16c --- /dev/null +++ b/benchmarks/conformance/cases/160-shadcn-line-multiple/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-line-multiple') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/160-shadcn-line-multiple/tanstack.ts b/benchmarks/conformance/cases/160-shadcn-line-multiple/tanstack.ts new file mode 100644 index 00000000..2e6653d6 --- /dev/null +++ b/benchmarks/conformance/cases/160-shadcn-line-multiple/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-line-multiple') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/161-shadcn-line-step/case.json b/benchmarks/conformance/cases/161-shadcn-line-step/case.json new file mode 100644 index 00000000..201e33bb --- /dev/null +++ b/benchmarks/conformance/cases/161-shadcn-line-step/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1620, + "id": "161-shadcn-line-step", + "title": "shadcn Line Chart - Step", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-step example with TanStack Charts.", + "support": "native", + "features": ["line chart", "step", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-line-step", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-step" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-step card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line step treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/161-shadcn-line-step/recharts.ts b/benchmarks/conformance/cases/161-shadcn-line-step/recharts.ts new file mode 100644 index 00000000..53dac7bd --- /dev/null +++ b/benchmarks/conformance/cases/161-shadcn-line-step/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-line-step') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/161-shadcn-line-step/tanstack.ts b/benchmarks/conformance/cases/161-shadcn-line-step/tanstack.ts new file mode 100644 index 00000000..a2459ec3 --- /dev/null +++ b/benchmarks/conformance/cases/161-shadcn-line-step/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-line-step') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/162-shadcn-pie-donut-active/case.json b/benchmarks/conformance/cases/162-shadcn-pie-donut-active/case.json new file mode 100644 index 00000000..5c51f0ba --- /dev/null +++ b/benchmarks/conformance/cases/162-shadcn-pie-donut-active/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1630, + "id": "162-shadcn-pie-donut-active", + "title": "shadcn Pie Chart - Donut Active", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-donut-active example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "donut active", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-donut-active", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-donut-active" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-donut-active card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie donut active treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/162-shadcn-pie-donut-active/recharts.ts b/benchmarks/conformance/cases/162-shadcn-pie-donut-active/recharts.ts new file mode 100644 index 00000000..f7441264 --- /dev/null +++ b/benchmarks/conformance/cases/162-shadcn-pie-donut-active/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-pie-donut-active') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/162-shadcn-pie-donut-active/tanstack.ts b/benchmarks/conformance/cases/162-shadcn-pie-donut-active/tanstack.ts new file mode 100644 index 00000000..aa254872 --- /dev/null +++ b/benchmarks/conformance/cases/162-shadcn-pie-donut-active/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-pie-donut-active') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/163-shadcn-pie-donut/case.json b/benchmarks/conformance/cases/163-shadcn-pie-donut/case.json new file mode 100644 index 00000000..6b974818 --- /dev/null +++ b/benchmarks/conformance/cases/163-shadcn-pie-donut/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1640, + "id": "163-shadcn-pie-donut", + "title": "shadcn Pie Chart - Donut", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-donut example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "donut", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-donut", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-donut" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-donut card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie donut treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/163-shadcn-pie-donut/recharts.ts b/benchmarks/conformance/cases/163-shadcn-pie-donut/recharts.ts new file mode 100644 index 00000000..807c95c2 --- /dev/null +++ b/benchmarks/conformance/cases/163-shadcn-pie-donut/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-pie-donut') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/163-shadcn-pie-donut/tanstack.ts b/benchmarks/conformance/cases/163-shadcn-pie-donut/tanstack.ts new file mode 100644 index 00000000..1841429f --- /dev/null +++ b/benchmarks/conformance/cases/163-shadcn-pie-donut/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-pie-donut') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/164-shadcn-pie-interactive/case.json b/benchmarks/conformance/cases/164-shadcn-pie-interactive/case.json new file mode 100644 index 00000000..a3122b05 --- /dev/null +++ b/benchmarks/conformance/cases/164-shadcn-pie-interactive/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1650, + "id": "164-shadcn-pie-interactive", + "title": "shadcn Pie Chart - Interactive", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-interactive example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "interactive", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-interactive", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-interactive" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-interactive card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie interactive treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/164-shadcn-pie-interactive/recharts.ts b/benchmarks/conformance/cases/164-shadcn-pie-interactive/recharts.ts new file mode 100644 index 00000000..dee36507 --- /dev/null +++ b/benchmarks/conformance/cases/164-shadcn-pie-interactive/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-pie-interactive') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/164-shadcn-pie-interactive/tanstack.ts b/benchmarks/conformance/cases/164-shadcn-pie-interactive/tanstack.ts new file mode 100644 index 00000000..2a4b6e69 --- /dev/null +++ b/benchmarks/conformance/cases/164-shadcn-pie-interactive/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-pie-interactive') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/165-shadcn-pie-label-custom/case.json b/benchmarks/conformance/cases/165-shadcn-pie-label-custom/case.json new file mode 100644 index 00000000..e8a56f83 --- /dev/null +++ b/benchmarks/conformance/cases/165-shadcn-pie-label-custom/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1660, + "id": "165-shadcn-pie-label-custom", + "title": "shadcn Pie Chart - Custom Label", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-label-custom example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "label custom", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-label-custom", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-label-custom" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-label-custom card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie label custom treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/165-shadcn-pie-label-custom/recharts.ts b/benchmarks/conformance/cases/165-shadcn-pie-label-custom/recharts.ts new file mode 100644 index 00000000..8b1b2a97 --- /dev/null +++ b/benchmarks/conformance/cases/165-shadcn-pie-label-custom/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-pie-label-custom') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/165-shadcn-pie-label-custom/tanstack.ts b/benchmarks/conformance/cases/165-shadcn-pie-label-custom/tanstack.ts new file mode 100644 index 00000000..9cc97793 --- /dev/null +++ b/benchmarks/conformance/cases/165-shadcn-pie-label-custom/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-pie-label-custom') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/166-shadcn-pie-label-list/case.json b/benchmarks/conformance/cases/166-shadcn-pie-label-list/case.json new file mode 100644 index 00000000..e974a32a --- /dev/null +++ b/benchmarks/conformance/cases/166-shadcn-pie-label-list/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1670, + "id": "166-shadcn-pie-label-list", + "title": "shadcn Pie Chart - Label List", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-label-list example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "label list", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-label-list", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-label-list" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-label-list card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie label list treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/166-shadcn-pie-label-list/recharts.ts b/benchmarks/conformance/cases/166-shadcn-pie-label-list/recharts.ts new file mode 100644 index 00000000..4e1accee --- /dev/null +++ b/benchmarks/conformance/cases/166-shadcn-pie-label-list/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-pie-label-list') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/166-shadcn-pie-label-list/tanstack.ts b/benchmarks/conformance/cases/166-shadcn-pie-label-list/tanstack.ts new file mode 100644 index 00000000..98ef087b --- /dev/null +++ b/benchmarks/conformance/cases/166-shadcn-pie-label-list/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-pie-label-list') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/167-shadcn-pie-label/case.json b/benchmarks/conformance/cases/167-shadcn-pie-label/case.json new file mode 100644 index 00000000..ecd1ea57 --- /dev/null +++ b/benchmarks/conformance/cases/167-shadcn-pie-label/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1680, + "id": "167-shadcn-pie-label", + "title": "shadcn Pie Chart - Label", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-label example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "label", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-label", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-label" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-label card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie label treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/167-shadcn-pie-label/recharts.ts b/benchmarks/conformance/cases/167-shadcn-pie-label/recharts.ts new file mode 100644 index 00000000..b340b79a --- /dev/null +++ b/benchmarks/conformance/cases/167-shadcn-pie-label/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-pie-label') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/167-shadcn-pie-label/tanstack.ts b/benchmarks/conformance/cases/167-shadcn-pie-label/tanstack.ts new file mode 100644 index 00000000..0ac2dbe4 --- /dev/null +++ b/benchmarks/conformance/cases/167-shadcn-pie-label/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-pie-label') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/168-shadcn-pie-legend/case.json b/benchmarks/conformance/cases/168-shadcn-pie-legend/case.json new file mode 100644 index 00000000..f44f1d70 --- /dev/null +++ b/benchmarks/conformance/cases/168-shadcn-pie-legend/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1690, + "id": "168-shadcn-pie-legend", + "title": "shadcn Pie Chart - Legend", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-legend example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "legend", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-legend", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-legend" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-legend card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie legend treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/168-shadcn-pie-legend/recharts.ts b/benchmarks/conformance/cases/168-shadcn-pie-legend/recharts.ts new file mode 100644 index 00000000..6b2b77fc --- /dev/null +++ b/benchmarks/conformance/cases/168-shadcn-pie-legend/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-pie-legend') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/168-shadcn-pie-legend/tanstack.ts b/benchmarks/conformance/cases/168-shadcn-pie-legend/tanstack.ts new file mode 100644 index 00000000..dcfeb277 --- /dev/null +++ b/benchmarks/conformance/cases/168-shadcn-pie-legend/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-pie-legend') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/169-shadcn-pie-separator-none/case.json b/benchmarks/conformance/cases/169-shadcn-pie-separator-none/case.json new file mode 100644 index 00000000..d7e2065a --- /dev/null +++ b/benchmarks/conformance/cases/169-shadcn-pie-separator-none/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1700, + "id": "169-shadcn-pie-separator-none", + "title": "shadcn Pie Chart - Separator None", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-separator-none example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "separator none", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-separator-none", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-separator-none" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-separator-none card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie separator none treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/169-shadcn-pie-separator-none/recharts.ts b/benchmarks/conformance/cases/169-shadcn-pie-separator-none/recharts.ts new file mode 100644 index 00000000..00b3d915 --- /dev/null +++ b/benchmarks/conformance/cases/169-shadcn-pie-separator-none/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-pie-separator-none') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/169-shadcn-pie-separator-none/tanstack.ts b/benchmarks/conformance/cases/169-shadcn-pie-separator-none/tanstack.ts new file mode 100644 index 00000000..f11323b7 --- /dev/null +++ b/benchmarks/conformance/cases/169-shadcn-pie-separator-none/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-pie-separator-none') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/170-shadcn-pie-simple/case.json b/benchmarks/conformance/cases/170-shadcn-pie-simple/case.json new file mode 100644 index 00000000..24348823 --- /dev/null +++ b/benchmarks/conformance/cases/170-shadcn-pie-simple/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1710, + "id": "170-shadcn-pie-simple", + "title": "shadcn Pie Chart", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-simple example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-simple", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-simple" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-simple card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie simple treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/170-shadcn-pie-simple/recharts.ts b/benchmarks/conformance/cases/170-shadcn-pie-simple/recharts.ts new file mode 100644 index 00000000..01cdb179 --- /dev/null +++ b/benchmarks/conformance/cases/170-shadcn-pie-simple/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-pie-simple') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/170-shadcn-pie-simple/tanstack.ts b/benchmarks/conformance/cases/170-shadcn-pie-simple/tanstack.ts new file mode 100644 index 00000000..7316ddbc --- /dev/null +++ b/benchmarks/conformance/cases/170-shadcn-pie-simple/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-pie-simple') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/171-shadcn-pie-stacked/case.json b/benchmarks/conformance/cases/171-shadcn-pie-stacked/case.json new file mode 100644 index 00000000..fdca76ef --- /dev/null +++ b/benchmarks/conformance/cases/171-shadcn-pie-stacked/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1720, + "id": "171-shadcn-pie-stacked", + "title": "shadcn Pie Chart - Stacked", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-stacked example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "stacked", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-stacked", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-stacked" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-stacked card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie stacked treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/171-shadcn-pie-stacked/recharts.ts b/benchmarks/conformance/cases/171-shadcn-pie-stacked/recharts.ts new file mode 100644 index 00000000..cabe6ab8 --- /dev/null +++ b/benchmarks/conformance/cases/171-shadcn-pie-stacked/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-pie-stacked') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/171-shadcn-pie-stacked/tanstack.ts b/benchmarks/conformance/cases/171-shadcn-pie-stacked/tanstack.ts new file mode 100644 index 00000000..af81601e --- /dev/null +++ b/benchmarks/conformance/cases/171-shadcn-pie-stacked/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-pie-stacked') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/172-shadcn-radar-default/case.json b/benchmarks/conformance/cases/172-shadcn-radar-default/case.json new file mode 100644 index 00000000..0d18a8e5 --- /dev/null +++ b/benchmarks/conformance/cases/172-shadcn-radar-default/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1730, + "id": "172-shadcn-radar-default", + "title": "shadcn Radar Chart", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-default example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-default", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-default" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-default card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar default treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/172-shadcn-radar-default/recharts.ts b/benchmarks/conformance/cases/172-shadcn-radar-default/recharts.ts new file mode 100644 index 00000000..17472b0a --- /dev/null +++ b/benchmarks/conformance/cases/172-shadcn-radar-default/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radar-default') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/172-shadcn-radar-default/tanstack.ts b/benchmarks/conformance/cases/172-shadcn-radar-default/tanstack.ts new file mode 100644 index 00000000..b618d1b6 --- /dev/null +++ b/benchmarks/conformance/cases/172-shadcn-radar-default/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radar-default') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/173-shadcn-radar-dots/case.json b/benchmarks/conformance/cases/173-shadcn-radar-dots/case.json new file mode 100644 index 00000000..8277193c --- /dev/null +++ b/benchmarks/conformance/cases/173-shadcn-radar-dots/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1740, + "id": "173-shadcn-radar-dots", + "title": "shadcn Radar Chart - Dots", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-dots example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "dots", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-dots", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-dots" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-dots card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar dots treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/173-shadcn-radar-dots/recharts.ts b/benchmarks/conformance/cases/173-shadcn-radar-dots/recharts.ts new file mode 100644 index 00000000..d6dcca02 --- /dev/null +++ b/benchmarks/conformance/cases/173-shadcn-radar-dots/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radar-dots') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/173-shadcn-radar-dots/tanstack.ts b/benchmarks/conformance/cases/173-shadcn-radar-dots/tanstack.ts new file mode 100644 index 00000000..77b53490 --- /dev/null +++ b/benchmarks/conformance/cases/173-shadcn-radar-dots/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radar-dots') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/174-shadcn-radar-grid-circle-fill/case.json b/benchmarks/conformance/cases/174-shadcn-radar-grid-circle-fill/case.json new file mode 100644 index 00000000..8bbc0231 --- /dev/null +++ b/benchmarks/conformance/cases/174-shadcn-radar-grid-circle-fill/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1750, + "id": "174-shadcn-radar-grid-circle-fill", + "title": "shadcn Radar Chart - Grid Circle Filled", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-grid-circle-fill example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "grid circle fill", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-grid-circle-fill", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-grid-circle-fill" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-grid-circle-fill card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar grid circle fill treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/174-shadcn-radar-grid-circle-fill/recharts.ts b/benchmarks/conformance/cases/174-shadcn-radar-grid-circle-fill/recharts.ts new file mode 100644 index 00000000..3ccb366d --- /dev/null +++ b/benchmarks/conformance/cases/174-shadcn-radar-grid-circle-fill/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radar-grid-circle-fill') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/174-shadcn-radar-grid-circle-fill/tanstack.ts b/benchmarks/conformance/cases/174-shadcn-radar-grid-circle-fill/tanstack.ts new file mode 100644 index 00000000..c2958d4e --- /dev/null +++ b/benchmarks/conformance/cases/174-shadcn-radar-grid-circle-fill/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radar-grid-circle-fill') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/175-shadcn-radar-grid-circle-no-lines/case.json b/benchmarks/conformance/cases/175-shadcn-radar-grid-circle-no-lines/case.json new file mode 100644 index 00000000..9c9e1a68 --- /dev/null +++ b/benchmarks/conformance/cases/175-shadcn-radar-grid-circle-no-lines/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1760, + "id": "175-shadcn-radar-grid-circle-no-lines", + "title": "shadcn Radar Chart - Grid Circle - No lines", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-grid-circle-no-lines example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "grid circle no lines", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-grid-circle-no-lines", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-grid-circle-no-lines" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-grid-circle-no-lines card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar grid circle no lines treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/175-shadcn-radar-grid-circle-no-lines/recharts.ts b/benchmarks/conformance/cases/175-shadcn-radar-grid-circle-no-lines/recharts.ts new file mode 100644 index 00000000..a8c71457 --- /dev/null +++ b/benchmarks/conformance/cases/175-shadcn-radar-grid-circle-no-lines/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radar-grid-circle-no-lines') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/175-shadcn-radar-grid-circle-no-lines/tanstack.ts b/benchmarks/conformance/cases/175-shadcn-radar-grid-circle-no-lines/tanstack.ts new file mode 100644 index 00000000..20b63ad7 --- /dev/null +++ b/benchmarks/conformance/cases/175-shadcn-radar-grid-circle-no-lines/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radar-grid-circle-no-lines') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/176-shadcn-radar-grid-circle/case.json b/benchmarks/conformance/cases/176-shadcn-radar-grid-circle/case.json new file mode 100644 index 00000000..9781bc55 --- /dev/null +++ b/benchmarks/conformance/cases/176-shadcn-radar-grid-circle/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1770, + "id": "176-shadcn-radar-grid-circle", + "title": "shadcn Radar Chart - Grid Circle", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-grid-circle example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "grid circle", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-grid-circle", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-grid-circle" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-grid-circle card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar grid circle treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/176-shadcn-radar-grid-circle/recharts.ts b/benchmarks/conformance/cases/176-shadcn-radar-grid-circle/recharts.ts new file mode 100644 index 00000000..7226a166 --- /dev/null +++ b/benchmarks/conformance/cases/176-shadcn-radar-grid-circle/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radar-grid-circle') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/176-shadcn-radar-grid-circle/tanstack.ts b/benchmarks/conformance/cases/176-shadcn-radar-grid-circle/tanstack.ts new file mode 100644 index 00000000..273d3146 --- /dev/null +++ b/benchmarks/conformance/cases/176-shadcn-radar-grid-circle/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radar-grid-circle') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/177-shadcn-radar-grid-custom/case.json b/benchmarks/conformance/cases/177-shadcn-radar-grid-custom/case.json new file mode 100644 index 00000000..052e8c16 --- /dev/null +++ b/benchmarks/conformance/cases/177-shadcn-radar-grid-custom/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1780, + "id": "177-shadcn-radar-grid-custom", + "title": "shadcn Radar Chart - Grid Custom", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-grid-custom example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "grid custom", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-grid-custom", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-grid-custom" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-grid-custom card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar grid custom treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/177-shadcn-radar-grid-custom/recharts.ts b/benchmarks/conformance/cases/177-shadcn-radar-grid-custom/recharts.ts new file mode 100644 index 00000000..40e61363 --- /dev/null +++ b/benchmarks/conformance/cases/177-shadcn-radar-grid-custom/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radar-grid-custom') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/177-shadcn-radar-grid-custom/tanstack.ts b/benchmarks/conformance/cases/177-shadcn-radar-grid-custom/tanstack.ts new file mode 100644 index 00000000..38216636 --- /dev/null +++ b/benchmarks/conformance/cases/177-shadcn-radar-grid-custom/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radar-grid-custom') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/178-shadcn-radar-grid-fill/case.json b/benchmarks/conformance/cases/178-shadcn-radar-grid-fill/case.json new file mode 100644 index 00000000..07e42d6c --- /dev/null +++ b/benchmarks/conformance/cases/178-shadcn-radar-grid-fill/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1790, + "id": "178-shadcn-radar-grid-fill", + "title": "shadcn Radar Chart - Grid Filled", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-grid-fill example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "grid fill", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-grid-fill", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-grid-fill" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-grid-fill card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar grid fill treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/178-shadcn-radar-grid-fill/recharts.ts b/benchmarks/conformance/cases/178-shadcn-radar-grid-fill/recharts.ts new file mode 100644 index 00000000..96348200 --- /dev/null +++ b/benchmarks/conformance/cases/178-shadcn-radar-grid-fill/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radar-grid-fill') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/178-shadcn-radar-grid-fill/tanstack.ts b/benchmarks/conformance/cases/178-shadcn-radar-grid-fill/tanstack.ts new file mode 100644 index 00000000..9b0f3286 --- /dev/null +++ b/benchmarks/conformance/cases/178-shadcn-radar-grid-fill/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radar-grid-fill') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/179-shadcn-radar-grid-none/case.json b/benchmarks/conformance/cases/179-shadcn-radar-grid-none/case.json new file mode 100644 index 00000000..f8d6a262 --- /dev/null +++ b/benchmarks/conformance/cases/179-shadcn-radar-grid-none/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1800, + "id": "179-shadcn-radar-grid-none", + "title": "shadcn Radar Chart - Grid None", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-grid-none example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "grid none", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-grid-none", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-grid-none" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-grid-none card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar grid none treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/179-shadcn-radar-grid-none/recharts.ts b/benchmarks/conformance/cases/179-shadcn-radar-grid-none/recharts.ts new file mode 100644 index 00000000..655c829d --- /dev/null +++ b/benchmarks/conformance/cases/179-shadcn-radar-grid-none/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radar-grid-none') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/179-shadcn-radar-grid-none/tanstack.ts b/benchmarks/conformance/cases/179-shadcn-radar-grid-none/tanstack.ts new file mode 100644 index 00000000..6bbb28b8 --- /dev/null +++ b/benchmarks/conformance/cases/179-shadcn-radar-grid-none/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radar-grid-none') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/180-shadcn-radar-icons/case.json b/benchmarks/conformance/cases/180-shadcn-radar-icons/case.json new file mode 100644 index 00000000..0a4d9029 --- /dev/null +++ b/benchmarks/conformance/cases/180-shadcn-radar-icons/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1810, + "id": "180-shadcn-radar-icons", + "title": "shadcn Radar Chart - Icons", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-icons example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "icons", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-icons", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-icons" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-icons card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar icons treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/180-shadcn-radar-icons/recharts.ts b/benchmarks/conformance/cases/180-shadcn-radar-icons/recharts.ts new file mode 100644 index 00000000..7dccc47c --- /dev/null +++ b/benchmarks/conformance/cases/180-shadcn-radar-icons/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radar-icons') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/180-shadcn-radar-icons/tanstack.ts b/benchmarks/conformance/cases/180-shadcn-radar-icons/tanstack.ts new file mode 100644 index 00000000..02a0e031 --- /dev/null +++ b/benchmarks/conformance/cases/180-shadcn-radar-icons/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radar-icons') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/181-shadcn-radar-label-custom/case.json b/benchmarks/conformance/cases/181-shadcn-radar-label-custom/case.json new file mode 100644 index 00000000..386bb3d7 --- /dev/null +++ b/benchmarks/conformance/cases/181-shadcn-radar-label-custom/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1820, + "id": "181-shadcn-radar-label-custom", + "title": "shadcn Radar Chart - Custom Label", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-label-custom example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "label custom", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-label-custom", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-label-custom" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-label-custom card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar label custom treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/181-shadcn-radar-label-custom/recharts.ts b/benchmarks/conformance/cases/181-shadcn-radar-label-custom/recharts.ts new file mode 100644 index 00000000..d3632ae2 --- /dev/null +++ b/benchmarks/conformance/cases/181-shadcn-radar-label-custom/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radar-label-custom') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/181-shadcn-radar-label-custom/tanstack.ts b/benchmarks/conformance/cases/181-shadcn-radar-label-custom/tanstack.ts new file mode 100644 index 00000000..b82fc1e7 --- /dev/null +++ b/benchmarks/conformance/cases/181-shadcn-radar-label-custom/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radar-label-custom') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/182-shadcn-radar-legend/case.json b/benchmarks/conformance/cases/182-shadcn-radar-legend/case.json new file mode 100644 index 00000000..5696fbad --- /dev/null +++ b/benchmarks/conformance/cases/182-shadcn-radar-legend/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1830, + "id": "182-shadcn-radar-legend", + "title": "shadcn Radar Chart - Legend", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-legend example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "legend", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-radar-legend", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-legend" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-legend card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar legend treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/182-shadcn-radar-legend/recharts.ts b/benchmarks/conformance/cases/182-shadcn-radar-legend/recharts.ts new file mode 100644 index 00000000..b545eaab --- /dev/null +++ b/benchmarks/conformance/cases/182-shadcn-radar-legend/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radar-legend') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/182-shadcn-radar-legend/tanstack.ts b/benchmarks/conformance/cases/182-shadcn-radar-legend/tanstack.ts new file mode 100644 index 00000000..21d0c0ac --- /dev/null +++ b/benchmarks/conformance/cases/182-shadcn-radar-legend/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radar-legend') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/183-shadcn-radar-lines-only/case.json b/benchmarks/conformance/cases/183-shadcn-radar-lines-only/case.json new file mode 100644 index 00000000..a2ee23e2 --- /dev/null +++ b/benchmarks/conformance/cases/183-shadcn-radar-lines-only/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1840, + "id": "183-shadcn-radar-lines-only", + "title": "shadcn Radar Chart - Lines Only", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-lines-only example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "lines only", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-lines-only", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-lines-only" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-lines-only card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar lines only treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/183-shadcn-radar-lines-only/recharts.ts b/benchmarks/conformance/cases/183-shadcn-radar-lines-only/recharts.ts new file mode 100644 index 00000000..8a54d61b --- /dev/null +++ b/benchmarks/conformance/cases/183-shadcn-radar-lines-only/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radar-lines-only') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/183-shadcn-radar-lines-only/tanstack.ts b/benchmarks/conformance/cases/183-shadcn-radar-lines-only/tanstack.ts new file mode 100644 index 00000000..2568e8a7 --- /dev/null +++ b/benchmarks/conformance/cases/183-shadcn-radar-lines-only/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radar-lines-only') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/184-shadcn-radar-radius/case.json b/benchmarks/conformance/cases/184-shadcn-radar-radius/case.json new file mode 100644 index 00000000..14a0f200 --- /dev/null +++ b/benchmarks/conformance/cases/184-shadcn-radar-radius/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1850, + "id": "184-shadcn-radar-radius", + "title": "shadcn Radar Chart - Radius Axis", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-radius example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "radius", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-radius", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-radius" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-radius card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar radius treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/184-shadcn-radar-radius/recharts.ts b/benchmarks/conformance/cases/184-shadcn-radar-radius/recharts.ts new file mode 100644 index 00000000..e4b58838 --- /dev/null +++ b/benchmarks/conformance/cases/184-shadcn-radar-radius/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radar-radius') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/184-shadcn-radar-radius/tanstack.ts b/benchmarks/conformance/cases/184-shadcn-radar-radius/tanstack.ts new file mode 100644 index 00000000..494ae63e --- /dev/null +++ b/benchmarks/conformance/cases/184-shadcn-radar-radius/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radar-radius') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/185-shadcn-radial-grid/case.json b/benchmarks/conformance/cases/185-shadcn-radial-grid/case.json new file mode 100644 index 00000000..8e875608 --- /dev/null +++ b/benchmarks/conformance/cases/185-shadcn-radial-grid/case.json @@ -0,0 +1,31 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1860, + "id": "185-shadcn-radial-grid", + "title": "shadcn Radial Chart - Grid", + "family": "radial", + "intent": "Reproduce shadcn/ui's chart-radial-grid example with TanStack Charts.", + "support": "native", + "features": ["radial chart", "grid", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 5, + "rendererRoles": { + "recharts": "arc", + "tanstack": "arc" + } + } + ], + "source": { + "title": "shadcn/ui chart-radial-grid", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radial-grid" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radial-grid card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radial grid treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/185-shadcn-radial-grid/recharts.ts b/benchmarks/conformance/cases/185-shadcn-radial-grid/recharts.ts new file mode 100644 index 00000000..cda8513e --- /dev/null +++ b/benchmarks/conformance/cases/185-shadcn-radial-grid/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radial-grid') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/185-shadcn-radial-grid/tanstack.ts b/benchmarks/conformance/cases/185-shadcn-radial-grid/tanstack.ts new file mode 100644 index 00000000..b6a5d637 --- /dev/null +++ b/benchmarks/conformance/cases/185-shadcn-radial-grid/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radial-grid') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/186-shadcn-radial-label/case.json b/benchmarks/conformance/cases/186-shadcn-radial-label/case.json new file mode 100644 index 00000000..11794508 --- /dev/null +++ b/benchmarks/conformance/cases/186-shadcn-radial-label/case.json @@ -0,0 +1,31 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1870, + "id": "186-shadcn-radial-label", + "title": "shadcn Radial Chart - Label", + "family": "radial", + "intent": "Reproduce shadcn/ui's chart-radial-label example with TanStack Charts.", + "support": "native", + "features": ["radial chart", "label", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 5, + "rendererRoles": { + "recharts": "arc", + "tanstack": "arc" + } + } + ], + "source": { + "title": "shadcn/ui chart-radial-label", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radial-label" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radial-label card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radial label treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/186-shadcn-radial-label/recharts.ts b/benchmarks/conformance/cases/186-shadcn-radial-label/recharts.ts new file mode 100644 index 00000000..a4ad1231 --- /dev/null +++ b/benchmarks/conformance/cases/186-shadcn-radial-label/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radial-label') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/186-shadcn-radial-label/tanstack.ts b/benchmarks/conformance/cases/186-shadcn-radial-label/tanstack.ts new file mode 100644 index 00000000..d579bd32 --- /dev/null +++ b/benchmarks/conformance/cases/186-shadcn-radial-label/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radial-label') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/187-shadcn-radial-shape/case.json b/benchmarks/conformance/cases/187-shadcn-radial-shape/case.json new file mode 100644 index 00000000..05bf5ffb --- /dev/null +++ b/benchmarks/conformance/cases/187-shadcn-radial-shape/case.json @@ -0,0 +1,31 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1880, + "id": "187-shadcn-radial-shape", + "title": "shadcn Radial Chart - Shape", + "family": "radial", + "intent": "Reproduce shadcn/ui's chart-radial-shape example with TanStack Charts.", + "support": "native", + "features": ["radial chart", "shape", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 5, + "rendererRoles": { + "recharts": "arc", + "tanstack": "arc" + } + } + ], + "source": { + "title": "shadcn/ui chart-radial-shape", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radial-shape" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radial-shape card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radial shape treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/187-shadcn-radial-shape/recharts.ts b/benchmarks/conformance/cases/187-shadcn-radial-shape/recharts.ts new file mode 100644 index 00000000..8a7c57c2 --- /dev/null +++ b/benchmarks/conformance/cases/187-shadcn-radial-shape/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radial-shape') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/187-shadcn-radial-shape/tanstack.ts b/benchmarks/conformance/cases/187-shadcn-radial-shape/tanstack.ts new file mode 100644 index 00000000..c6592ae8 --- /dev/null +++ b/benchmarks/conformance/cases/187-shadcn-radial-shape/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radial-shape') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/188-shadcn-radial-simple/case.json b/benchmarks/conformance/cases/188-shadcn-radial-simple/case.json new file mode 100644 index 00000000..69edf264 --- /dev/null +++ b/benchmarks/conformance/cases/188-shadcn-radial-simple/case.json @@ -0,0 +1,31 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1890, + "id": "188-shadcn-radial-simple", + "title": "shadcn Radial Chart", + "family": "radial", + "intent": "Reproduce shadcn/ui's chart-radial-simple example with TanStack Charts.", + "support": "native", + "features": ["radial chart", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 1, + "rendererRoles": { + "recharts": "arc", + "tanstack": "arc" + } + } + ], + "source": { + "title": "shadcn/ui chart-radial-simple", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radial-simple" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radial-simple card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radial simple treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/188-shadcn-radial-simple/recharts.ts b/benchmarks/conformance/cases/188-shadcn-radial-simple/recharts.ts new file mode 100644 index 00000000..7edb27ab --- /dev/null +++ b/benchmarks/conformance/cases/188-shadcn-radial-simple/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radial-simple') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/188-shadcn-radial-simple/tanstack.ts b/benchmarks/conformance/cases/188-shadcn-radial-simple/tanstack.ts new file mode 100644 index 00000000..c49c5783 --- /dev/null +++ b/benchmarks/conformance/cases/188-shadcn-radial-simple/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radial-simple') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/189-shadcn-radial-stacked/case.json b/benchmarks/conformance/cases/189-shadcn-radial-stacked/case.json new file mode 100644 index 00000000..9ebebe00 --- /dev/null +++ b/benchmarks/conformance/cases/189-shadcn-radial-stacked/case.json @@ -0,0 +1,31 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1900, + "id": "189-shadcn-radial-stacked", + "title": "shadcn Radial Chart - Stacked", + "family": "radial", + "intent": "Reproduce shadcn/ui's chart-radial-stacked example with TanStack Charts.", + "support": "native", + "features": ["radial chart", "stacked", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 5, + "rendererRoles": { + "recharts": "arc", + "tanstack": "arc" + } + } + ], + "source": { + "title": "shadcn/ui chart-radial-stacked", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radial-stacked" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radial-stacked card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radial stacked treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/189-shadcn-radial-stacked/recharts.ts b/benchmarks/conformance/cases/189-shadcn-radial-stacked/recharts.ts new file mode 100644 index 00000000..e48f6f48 --- /dev/null +++ b/benchmarks/conformance/cases/189-shadcn-radial-stacked/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-radial-stacked') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/189-shadcn-radial-stacked/tanstack.ts b/benchmarks/conformance/cases/189-shadcn-radial-stacked/tanstack.ts new file mode 100644 index 00000000..31981c9a --- /dev/null +++ b/benchmarks/conformance/cases/189-shadcn-radial-stacked/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-radial-stacked') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/190-shadcn-tooltip-default/case.json b/benchmarks/conformance/cases/190-shadcn-tooltip-default/case.json new file mode 100644 index 00000000..dba70092 --- /dev/null +++ b/benchmarks/conformance/cases/190-shadcn-tooltip-default/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1910, + "id": "190-shadcn-tooltip-default", + "title": "shadcn Tooltip - Default", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's chart-tooltip-default example with TanStack Charts.", + "support": "native", + "features": ["tooltip chart", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 10 + } + ], + "source": { + "title": "shadcn/ui chart-tooltip-default", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-default" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-default card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, tooltip default treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/190-shadcn-tooltip-default/recharts.ts b/benchmarks/conformance/cases/190-shadcn-tooltip-default/recharts.ts new file mode 100644 index 00000000..8058a813 --- /dev/null +++ b/benchmarks/conformance/cases/190-shadcn-tooltip-default/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-tooltip-default') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/190-shadcn-tooltip-default/tanstack.ts b/benchmarks/conformance/cases/190-shadcn-tooltip-default/tanstack.ts new file mode 100644 index 00000000..1c0f2f30 --- /dev/null +++ b/benchmarks/conformance/cases/190-shadcn-tooltip-default/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-tooltip-default') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/191-shadcn-tooltip-formatter/case.json b/benchmarks/conformance/cases/191-shadcn-tooltip-formatter/case.json new file mode 100644 index 00000000..12b32d8f --- /dev/null +++ b/benchmarks/conformance/cases/191-shadcn-tooltip-formatter/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1920, + "id": "191-shadcn-tooltip-formatter", + "title": "shadcn Tooltip - Formatter", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's chart-tooltip-formatter example with TanStack Charts.", + "support": "native", + "features": ["tooltip chart", "formatter", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 10 + } + ], + "source": { + "title": "shadcn/ui chart-tooltip-formatter", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-formatter" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-formatter card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, tooltip formatter treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/191-shadcn-tooltip-formatter/recharts.ts b/benchmarks/conformance/cases/191-shadcn-tooltip-formatter/recharts.ts new file mode 100644 index 00000000..9b822bb4 --- /dev/null +++ b/benchmarks/conformance/cases/191-shadcn-tooltip-formatter/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-tooltip-formatter') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/191-shadcn-tooltip-formatter/tanstack.ts b/benchmarks/conformance/cases/191-shadcn-tooltip-formatter/tanstack.ts new file mode 100644 index 00000000..accb2ef4 --- /dev/null +++ b/benchmarks/conformance/cases/191-shadcn-tooltip-formatter/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-tooltip-formatter') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/192-shadcn-tooltip-icons/case.json b/benchmarks/conformance/cases/192-shadcn-tooltip-icons/case.json new file mode 100644 index 00000000..4fa1fea4 --- /dev/null +++ b/benchmarks/conformance/cases/192-shadcn-tooltip-icons/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1930, + "id": "192-shadcn-tooltip-icons", + "title": "shadcn Tooltip - Icons", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's chart-tooltip-icons example with TanStack Charts.", + "support": "native", + "features": ["tooltip chart", "icons", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 10 + } + ], + "source": { + "title": "shadcn/ui chart-tooltip-icons", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-icons" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-icons card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, tooltip icons treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/192-shadcn-tooltip-icons/recharts.ts b/benchmarks/conformance/cases/192-shadcn-tooltip-icons/recharts.ts new file mode 100644 index 00000000..ec53fa16 --- /dev/null +++ b/benchmarks/conformance/cases/192-shadcn-tooltip-icons/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-tooltip-icons') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/192-shadcn-tooltip-icons/tanstack.ts b/benchmarks/conformance/cases/192-shadcn-tooltip-icons/tanstack.ts new file mode 100644 index 00000000..1e5c0c36 --- /dev/null +++ b/benchmarks/conformance/cases/192-shadcn-tooltip-icons/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-tooltip-icons') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/193-shadcn-tooltip-indicator-line/case.json b/benchmarks/conformance/cases/193-shadcn-tooltip-indicator-line/case.json new file mode 100644 index 00000000..bb4dc7ba --- /dev/null +++ b/benchmarks/conformance/cases/193-shadcn-tooltip-indicator-line/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1940, + "id": "193-shadcn-tooltip-indicator-line", + "title": "shadcn Tooltip - Line Indicator", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's chart-tooltip-indicator-line example with TanStack Charts.", + "support": "native", + "features": ["tooltip chart", "indicator line", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 10 + } + ], + "source": { + "title": "shadcn/ui chart-tooltip-indicator-line", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-indicator-line" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-indicator-line card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, tooltip indicator line treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/193-shadcn-tooltip-indicator-line/recharts.ts b/benchmarks/conformance/cases/193-shadcn-tooltip-indicator-line/recharts.ts new file mode 100644 index 00000000..7fffb4f0 --- /dev/null +++ b/benchmarks/conformance/cases/193-shadcn-tooltip-indicator-line/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-tooltip-indicator-line') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/193-shadcn-tooltip-indicator-line/tanstack.ts b/benchmarks/conformance/cases/193-shadcn-tooltip-indicator-line/tanstack.ts new file mode 100644 index 00000000..ffb2d6fc --- /dev/null +++ b/benchmarks/conformance/cases/193-shadcn-tooltip-indicator-line/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-tooltip-indicator-line') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/194-shadcn-tooltip-indicator-none/case.json b/benchmarks/conformance/cases/194-shadcn-tooltip-indicator-none/case.json new file mode 100644 index 00000000..7c864fd7 --- /dev/null +++ b/benchmarks/conformance/cases/194-shadcn-tooltip-indicator-none/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1950, + "id": "194-shadcn-tooltip-indicator-none", + "title": "shadcn Tooltip - No Indicator", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's chart-tooltip-indicator-none example with TanStack Charts.", + "support": "native", + "features": ["tooltip chart", "indicator none", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 10 + } + ], + "source": { + "title": "shadcn/ui chart-tooltip-indicator-none", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-indicator-none" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-indicator-none card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, tooltip indicator none treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/194-shadcn-tooltip-indicator-none/recharts.ts b/benchmarks/conformance/cases/194-shadcn-tooltip-indicator-none/recharts.ts new file mode 100644 index 00000000..315418eb --- /dev/null +++ b/benchmarks/conformance/cases/194-shadcn-tooltip-indicator-none/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-tooltip-indicator-none') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/194-shadcn-tooltip-indicator-none/tanstack.ts b/benchmarks/conformance/cases/194-shadcn-tooltip-indicator-none/tanstack.ts new file mode 100644 index 00000000..57e36efa --- /dev/null +++ b/benchmarks/conformance/cases/194-shadcn-tooltip-indicator-none/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-tooltip-indicator-none') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/195-shadcn-tooltip-label-custom/case.json b/benchmarks/conformance/cases/195-shadcn-tooltip-label-custom/case.json new file mode 100644 index 00000000..cb815892 --- /dev/null +++ b/benchmarks/conformance/cases/195-shadcn-tooltip-label-custom/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1960, + "id": "195-shadcn-tooltip-label-custom", + "title": "shadcn Tooltip - Custom label", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's chart-tooltip-label-custom example with TanStack Charts.", + "support": "native", + "features": ["tooltip chart", "label custom", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 10 + } + ], + "source": { + "title": "shadcn/ui chart-tooltip-label-custom", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-label-custom" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-label-custom card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, tooltip label custom treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/195-shadcn-tooltip-label-custom/recharts.ts b/benchmarks/conformance/cases/195-shadcn-tooltip-label-custom/recharts.ts new file mode 100644 index 00000000..8892f69e --- /dev/null +++ b/benchmarks/conformance/cases/195-shadcn-tooltip-label-custom/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-tooltip-label-custom') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/195-shadcn-tooltip-label-custom/tanstack.ts b/benchmarks/conformance/cases/195-shadcn-tooltip-label-custom/tanstack.ts new file mode 100644 index 00000000..67907ecf --- /dev/null +++ b/benchmarks/conformance/cases/195-shadcn-tooltip-label-custom/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-tooltip-label-custom') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/196-shadcn-tooltip-label-formatter/case.json b/benchmarks/conformance/cases/196-shadcn-tooltip-label-formatter/case.json new file mode 100644 index 00000000..2835668d --- /dev/null +++ b/benchmarks/conformance/cases/196-shadcn-tooltip-label-formatter/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1970, + "id": "196-shadcn-tooltip-label-formatter", + "title": "shadcn Tooltip - Label Formatter", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's chart-tooltip-label-formatter example with TanStack Charts.", + "support": "native", + "features": ["tooltip chart", "label formatter", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 10 + } + ], + "source": { + "title": "shadcn/ui chart-tooltip-label-formatter", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-label-formatter" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-label-formatter card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, tooltip label formatter treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/196-shadcn-tooltip-label-formatter/recharts.ts b/benchmarks/conformance/cases/196-shadcn-tooltip-label-formatter/recharts.ts new file mode 100644 index 00000000..85e3be51 --- /dev/null +++ b/benchmarks/conformance/cases/196-shadcn-tooltip-label-formatter/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-tooltip-label-formatter') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/196-shadcn-tooltip-label-formatter/tanstack.ts b/benchmarks/conformance/cases/196-shadcn-tooltip-label-formatter/tanstack.ts new file mode 100644 index 00000000..acfbfdc5 --- /dev/null +++ b/benchmarks/conformance/cases/196-shadcn-tooltip-label-formatter/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-tooltip-label-formatter') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/cases/197-shadcn-tooltip-label-none/case.json b/benchmarks/conformance/cases/197-shadcn-tooltip-label-none/case.json new file mode 100644 index 00000000..e8e27867 --- /dev/null +++ b/benchmarks/conformance/cases/197-shadcn-tooltip-label-none/case.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1980, + "id": "197-shadcn-tooltip-label-none", + "title": "shadcn Tooltip - No Label", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's chart-tooltip-label-none example with TanStack Charts.", + "support": "native", + "features": ["tooltip chart", "label none", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 10 + } + ], + "source": { + "title": "shadcn/ui chart-tooltip-label-none", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-label-none" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-label-none card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, tooltip label none treatment, card dimensions, and light and dark tokens." + } +} diff --git a/benchmarks/conformance/cases/197-shadcn-tooltip-label-none/recharts.ts b/benchmarks/conformance/cases/197-shadcn-tooltip-label-none/recharts.ts new file mode 100644 index 00000000..ded331a8 --- /dev/null +++ b/benchmarks/conformance/cases/197-shadcn-tooltip-label-none/recharts.ts @@ -0,0 +1,5 @@ +import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts' + +const example = createShadcnRechartsExample('chart-tooltip-label-none') + +export const mount = example.mount diff --git a/benchmarks/conformance/cases/197-shadcn-tooltip-label-none/tanstack.ts b/benchmarks/conformance/cases/197-shadcn-tooltip-label-none/tanstack.ts new file mode 100644 index 00000000..dcfea0be --- /dev/null +++ b/benchmarks/conformance/cases/197-shadcn-tooltip-label-none/tanstack.ts @@ -0,0 +1,7 @@ +import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack' + +const example = createShadcnTanStackExample('chart-tooltip-label-none') + +export const shadcnDefinition = example.definition +export const catalogCase = example.catalogCase +export const mount = example.mount diff --git a/benchmarks/conformance/catalog-index.json b/benchmarks/conformance/catalog-index.json index 6ec84de2..12032941 100644 --- a/benchmarks/conformance/catalog-index.json +++ b/benchmarks/conformance/catalog-index.json @@ -10630,6 +10630,2488 @@ "path": "benchmarks/conformance/cases/126-drillable-sunburst/recharts.ts" } } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "height": 860, + "order": 1280, + "id": "127-shadcn-dashboard", + "title": "shadcn dashboard", + "family": "application", + "intent": "Reproduce shadcn/ui's canonical dashboard shell and interactive stacked-area card while replacing only Recharts with TanStack Charts.", + "support": "native", + "features": [ + "responsive dashboard shell", + "stacked area chart", + "natural curves", + "linear gradient fills", + "grouped tooltip", + "responsive time-range controls", + "CSS-variable theming", + "application-level catalog viewport" + ], + "geometry": [ + { + "role": "area", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui dashboard-01", + "url": "https://ui.shadcn.com/examples/dashboard" + }, + "ai": { + "create": "Reproduce the official shadcn/ui dashboard-01 layout and data. Keep the dashboard shell independent of the chart renderer, then implement its filtered, stacked, gradient area chart with TanStack Charts.", + "maintain": "Preserve the official dashboard data, responsive sidebar, metric cards, time-range behavior, table shell, natural stacked-area geometry, gradients, axis labels, tooltip content, light and dark themes, and a renderer-only compact catalog preview." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/127-shadcn-dashboard/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/127-shadcn-dashboard/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1290, + "id": "128-shadcn-bar-multiple", + "title": "shadcn bar chart – multiple", + "family": "bar", + "intent": "Reproduce shadcn/ui's grouped bar example while replacing Recharts with TanStack Charts.", + "support": "native", + "features": [ + "grouped bars", + "rounded corners", + "horizontal grid", + "grouped tooltip", + "shadcn card shell" + ], + "geometry": [ + { + "role": "bar", + "count": 12 + } + ], + "source": { + "title": "shadcn/ui chart-bar-multiple", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-multiple" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-multiple card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, grouped layout, axis treatment, tooltip labels, card dimensions, footer, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/128-shadcn-bar-multiple/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/128-shadcn-bar-multiple/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1300, + "id": "129-shadcn-pie-donut-text", + "title": "shadcn pie chart – donut with text", + "family": "pie", + "intent": "Reproduce shadcn/ui's donut-with-center-text example while replacing Recharts with TanStack Charts.", + "support": "native", + "features": [ + "donut chart", + "center text", + "sector separators", + "tooltip", + "shadcn card shell" + ], + "geometry": [ + { + "role": "arc", + "count": 5 + }, + { + "role": "text", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-pie-donut-text", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-donut-text" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-donut-text card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream browser data, donut radii, separators, center total and label, tooltip, card dimensions, footer, and themes." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/129-shadcn-pie-donut-text/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/129-shadcn-pie-donut-text/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1310, + "id": "130-shadcn-radar-multiple", + "title": "shadcn radar chart – multiple", + "family": "radar", + "intent": "Reproduce shadcn/ui's multi-series radar example while replacing Recharts with TanStack Charts.", + "support": "native", + "features": [ + "radar chart", + "multiple series", + "polygon grid", + "angle labels", + "grouped tooltip", + "shadcn card shell" + ], + "geometry": [ + { + "role": "radar", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-radar-multiple", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-multiple" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-multiple card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, polygon guide, angle labels, two filled profiles, tooltip, card dimensions, footer, and themes." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/130-shadcn-radar-multiple/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/130-shadcn-radar-multiple/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1320, + "id": "131-shadcn-radial-text", + "title": "shadcn radial chart – text", + "family": "radial", + "intent": "Reproduce shadcn/ui's partial radial chart with center text while replacing Recharts with TanStack Charts.", + "support": "native", + "features": [ + "partial radial bar", + "rounded caps", + "background track", + "center text", + "shadcn card shell" + ], + "geometry": [ + { + "role": "arc", + "count": 2 + }, + { + "role": "text", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-radial-text", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radial-text" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radial-text card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream value, 250-degree sweep, 80/90 radii, rounded track, center text, card dimensions, footer, and themes." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/131-shadcn-radial-text/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/131-shadcn-radial-text/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1330, + "id": "132-shadcn-tooltip-advanced", + "title": "shadcn tooltip – advanced", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's stacked-bar card and custom total tooltip while replacing Recharts with TanStack Charts.", + "support": "native", + "features": [ + "stacked bars", + "rounded corners", + "rich tooltip body", + "computed total", + "default focus", + "shadcn card shell" + ], + "geometry": [ + { + "role": "bar", + "count": 12 + } + ], + "source": { + "title": "shadcn/ui chart-tooltip-advanced", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-advanced" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-advanced card and data with TanStack Charts and its React tooltip composition entry.", + "maintain": "Preserve the pinned upstream data, stack order, weekday axis, initial focused day, 180-pixel rich tooltip, row labels, kcal units, total, card dimensions, and themes." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/132-shadcn-tooltip-advanced/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/132-shadcn-tooltip-advanced/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1340, + "id": "133-shadcn-area-axes", + "title": "shadcn Area Chart - Axes", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-axes example with TanStack Charts.", + "support": "native", + "features": ["area chart", "axes", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-area-axes", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-axes" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-axes card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area axes treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/133-shadcn-area-axes/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/133-shadcn-area-axes/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1350, + "id": "134-shadcn-area-default", + "title": "shadcn Area Chart", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-default example with TanStack Charts.", + "support": "native", + "features": ["area chart", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-area-default", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-default" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-default card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area default treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/134-shadcn-area-default/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/134-shadcn-area-default/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1360, + "id": "135-shadcn-area-gradient", + "title": "shadcn Area Chart - Gradient", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-gradient example with TanStack Charts.", + "support": "native", + "features": ["area chart", "gradient", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-area-gradient", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-gradient" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-gradient card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area gradient treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/135-shadcn-area-gradient/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/135-shadcn-area-gradient/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1370, + "id": "136-shadcn-area-icons", + "title": "shadcn Area Chart - Icons", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-icons example with TanStack Charts.", + "support": "native", + "features": ["area chart", "icons", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-area-icons", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-icons" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-icons card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area icons treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/136-shadcn-area-icons/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/136-shadcn-area-icons/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1380, + "id": "137-shadcn-area-interactive", + "title": "shadcn Area Chart - Interactive", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-interactive example with TanStack Charts.", + "support": "native", + "features": ["area chart", "interactive", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-area-interactive", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-interactive" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-interactive card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area interactive treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/137-shadcn-area-interactive/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/137-shadcn-area-interactive/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1390, + "id": "138-shadcn-area-legend", + "title": "shadcn Area Chart - Legend", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-legend example with TanStack Charts.", + "support": "native", + "features": ["area chart", "legend", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-area-legend", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-legend" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-legend card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area legend treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/138-shadcn-area-legend/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/138-shadcn-area-legend/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1400, + "id": "139-shadcn-area-linear", + "title": "shadcn Area Chart - Linear", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-linear example with TanStack Charts.", + "support": "native", + "features": ["area chart", "linear", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-area-linear", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-linear" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-linear card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area linear treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/139-shadcn-area-linear/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/139-shadcn-area-linear/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1410, + "id": "140-shadcn-area-stacked-expand", + "title": "shadcn Area Chart - Stacked Expanded", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-stacked-expand example with TanStack Charts.", + "support": "native", + "features": ["area chart", "stacked expand", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 3 + } + ], + "source": { + "title": "shadcn/ui chart-area-stacked-expand", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-stacked-expand" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-stacked-expand card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area stacked expand treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/140-shadcn-area-stacked-expand/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/140-shadcn-area-stacked-expand/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1420, + "id": "141-shadcn-area-stacked", + "title": "shadcn Area Chart - Stacked", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-stacked example with TanStack Charts.", + "support": "native", + "features": ["area chart", "stacked", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-area-stacked", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-stacked" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-stacked card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area stacked treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/141-shadcn-area-stacked/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/141-shadcn-area-stacked/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1430, + "id": "142-shadcn-area-step", + "title": "shadcn Area Chart - Step", + "family": "area", + "intent": "Reproduce shadcn/ui's chart-area-step example with TanStack Charts.", + "support": "native", + "features": ["area chart", "step", "shadcn card shell"], + "geometry": [ + { + "role": "area", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-area-step", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-area-step" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-area-step card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, area step treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/142-shadcn-area-step/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/142-shadcn-area-step/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1440, + "id": "143-shadcn-bar-active", + "title": "shadcn Bar Chart - Active", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-active example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "active", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 6 + } + ], + "source": { + "title": "shadcn/ui chart-bar-active", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-active" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-active card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar active treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/143-shadcn-bar-active/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/143-shadcn-bar-active/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1450, + "id": "144-shadcn-bar-default", + "title": "shadcn Bar Chart", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-default example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 6 + } + ], + "source": { + "title": "shadcn/ui chart-bar-default", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-default" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-default card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar default treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/144-shadcn-bar-default/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/144-shadcn-bar-default/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1460, + "id": "145-shadcn-bar-horizontal", + "title": "shadcn Bar Chart - Horizontal", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-horizontal example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "horizontal", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 6 + } + ], + "source": { + "title": "shadcn/ui chart-bar-horizontal", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-horizontal" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-horizontal card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar horizontal treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/145-shadcn-bar-horizontal/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/145-shadcn-bar-horizontal/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1470, + "id": "146-shadcn-bar-interactive", + "title": "shadcn Bar Chart - Interactive", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-interactive example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "interactive", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 6 + } + ], + "source": { + "title": "shadcn/ui chart-bar-interactive", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-interactive" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-interactive card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar interactive treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/146-shadcn-bar-interactive/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/146-shadcn-bar-interactive/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1480, + "id": "147-shadcn-bar-label-custom", + "title": "shadcn Bar Chart - Custom Label", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-label-custom example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "label custom", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 6 + } + ], + "source": { + "title": "shadcn/ui chart-bar-label-custom", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-label-custom" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-label-custom card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar label custom treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/147-shadcn-bar-label-custom/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/147-shadcn-bar-label-custom/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1490, + "id": "148-shadcn-bar-label", + "title": "shadcn Bar Chart - Label", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-label example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "label", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 6 + } + ], + "source": { + "title": "shadcn/ui chart-bar-label", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-label" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-label card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar label treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/148-shadcn-bar-label/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/148-shadcn-bar-label/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1500, + "id": "149-shadcn-bar-mixed", + "title": "shadcn Bar Chart - Mixed", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-mixed example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "mixed", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 6 + } + ], + "source": { + "title": "shadcn/ui chart-bar-mixed", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-mixed" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-mixed card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar mixed treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/149-shadcn-bar-mixed/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/149-shadcn-bar-mixed/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1510, + "id": "150-shadcn-bar-negative", + "title": "shadcn Bar Chart - Negative", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-negative example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "negative", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 6 + } + ], + "source": { + "title": "shadcn/ui chart-bar-negative", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-negative" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-negative card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar negative treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/150-shadcn-bar-negative/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/150-shadcn-bar-negative/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1520, + "id": "151-shadcn-bar-stacked", + "title": "shadcn Bar Chart - Stacked + Legend", + "family": "bar", + "intent": "Reproduce shadcn/ui's chart-bar-stacked example with TanStack Charts.", + "support": "native", + "features": ["bar chart", "stacked", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 12 + } + ], + "source": { + "title": "shadcn/ui chart-bar-stacked", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-bar-stacked" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-bar-stacked card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, bar stacked treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/151-shadcn-bar-stacked/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/151-shadcn-bar-stacked/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1530, + "id": "152-shadcn-line-default", + "title": "shadcn Line Chart", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-default example with TanStack Charts.", + "support": "native", + "features": ["line chart", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-line-default", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-default" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-default card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line default treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/152-shadcn-line-default/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/152-shadcn-line-default/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1540, + "id": "153-shadcn-line-dots-colors", + "title": "shadcn Line Chart - Dots Colors", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-dots-colors example with TanStack Charts.", + "support": "native", + "features": ["line chart", "dots colors", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-line-dots-colors", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-dots-colors" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-dots-colors card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line dots colors treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/153-shadcn-line-dots-colors/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/153-shadcn-line-dots-colors/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1550, + "id": "154-shadcn-line-dots-custom", + "title": "shadcn Line Chart - Custom Dots", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-dots-custom example with TanStack Charts.", + "support": "native", + "features": ["line chart", "dots custom", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-line-dots-custom", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-dots-custom" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-dots-custom card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line dots custom treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/154-shadcn-line-dots-custom/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/154-shadcn-line-dots-custom/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1560, + "id": "155-shadcn-line-dots", + "title": "shadcn Line Chart - Dots", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-dots example with TanStack Charts.", + "support": "native", + "features": ["line chart", "dots", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-line-dots", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-dots" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-dots card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line dots treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/155-shadcn-line-dots/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/155-shadcn-line-dots/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1570, + "id": "156-shadcn-line-interactive", + "title": "shadcn Line Chart - Interactive", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-interactive example with TanStack Charts.", + "support": "native", + "features": ["line chart", "interactive", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-line-interactive", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-interactive" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-interactive card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line interactive treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/156-shadcn-line-interactive/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/156-shadcn-line-interactive/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1580, + "id": "157-shadcn-line-label-custom", + "title": "shadcn Line Chart - Custom Label", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-label-custom example with TanStack Charts.", + "support": "native", + "features": ["line chart", "label custom", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-line-label-custom", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-label-custom" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-label-custom card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line label custom treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/157-shadcn-line-label-custom/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/157-shadcn-line-label-custom/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1590, + "id": "158-shadcn-line-label", + "title": "shadcn Line Chart - Label", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-label example with TanStack Charts.", + "support": "native", + "features": ["line chart", "label", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-line-label", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-label" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-label card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line label treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/158-shadcn-line-label/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/158-shadcn-line-label/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1600, + "id": "159-shadcn-line-linear", + "title": "shadcn Line Chart - Linear", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-linear example with TanStack Charts.", + "support": "native", + "features": ["line chart", "linear", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-line-linear", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-linear" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-linear card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line linear treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/159-shadcn-line-linear/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/159-shadcn-line-linear/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1610, + "id": "160-shadcn-line-multiple", + "title": "shadcn Line Chart - Multiple", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-multiple example with TanStack Charts.", + "support": "native", + "features": ["line chart", "multiple", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-line-multiple", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-multiple" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-multiple card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line multiple treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/160-shadcn-line-multiple/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/160-shadcn-line-multiple/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1620, + "id": "161-shadcn-line-step", + "title": "shadcn Line Chart - Step", + "family": "line", + "intent": "Reproduce shadcn/ui's chart-line-step example with TanStack Charts.", + "support": "native", + "features": ["line chart", "step", "shadcn card shell"], + "geometry": [ + { + "role": "line", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-line-step", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-line-step" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-line-step card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, line step treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/161-shadcn-line-step/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/161-shadcn-line-step/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1630, + "id": "162-shadcn-pie-donut-active", + "title": "shadcn Pie Chart - Donut Active", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-donut-active example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "donut active", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-donut-active", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-donut-active" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-donut-active card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie donut active treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/162-shadcn-pie-donut-active/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/162-shadcn-pie-donut-active/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1640, + "id": "163-shadcn-pie-donut", + "title": "shadcn Pie Chart - Donut", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-donut example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "donut", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-donut", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-donut" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-donut card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie donut treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/163-shadcn-pie-donut/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/163-shadcn-pie-donut/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1650, + "id": "164-shadcn-pie-interactive", + "title": "shadcn Pie Chart - Interactive", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-interactive example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "interactive", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-interactive", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-interactive" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-interactive card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie interactive treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/164-shadcn-pie-interactive/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/164-shadcn-pie-interactive/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1660, + "id": "165-shadcn-pie-label-custom", + "title": "shadcn Pie Chart - Custom Label", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-label-custom example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "label custom", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-label-custom", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-label-custom" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-label-custom card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie label custom treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/165-shadcn-pie-label-custom/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/165-shadcn-pie-label-custom/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1670, + "id": "166-shadcn-pie-label-list", + "title": "shadcn Pie Chart - Label List", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-label-list example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "label list", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-label-list", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-label-list" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-label-list card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie label list treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/166-shadcn-pie-label-list/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/166-shadcn-pie-label-list/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1680, + "id": "167-shadcn-pie-label", + "title": "shadcn Pie Chart - Label", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-label example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "label", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-label", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-label" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-label card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie label treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/167-shadcn-pie-label/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/167-shadcn-pie-label/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1690, + "id": "168-shadcn-pie-legend", + "title": "shadcn Pie Chart - Legend", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-legend example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "legend", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-legend", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-legend" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-legend card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie legend treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/168-shadcn-pie-legend/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/168-shadcn-pie-legend/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1700, + "id": "169-shadcn-pie-separator-none", + "title": "shadcn Pie Chart - Separator None", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-separator-none example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "separator none", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-separator-none", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-separator-none" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-separator-none card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie separator none treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/169-shadcn-pie-separator-none/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/169-shadcn-pie-separator-none/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1710, + "id": "170-shadcn-pie-simple", + "title": "shadcn Pie Chart", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-simple example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-simple", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-simple" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-simple card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie simple treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/170-shadcn-pie-simple/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/170-shadcn-pie-simple/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1720, + "id": "171-shadcn-pie-stacked", + "title": "shadcn Pie Chart - Stacked", + "family": "pie", + "intent": "Reproduce shadcn/ui's chart-pie-stacked example with TanStack Charts.", + "support": "native", + "features": ["pie chart", "stacked", "shadcn card shell"], + "geometry": [ + { + "role": "arc", + "count": 5 + } + ], + "source": { + "title": "shadcn/ui chart-pie-stacked", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-pie-stacked" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-pie-stacked card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, pie stacked treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/171-shadcn-pie-stacked/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/171-shadcn-pie-stacked/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1730, + "id": "172-shadcn-radar-default", + "title": "shadcn Radar Chart", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-default example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-default", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-default" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-default card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar default treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/172-shadcn-radar-default/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/172-shadcn-radar-default/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1740, + "id": "173-shadcn-radar-dots", + "title": "shadcn Radar Chart - Dots", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-dots example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "dots", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-dots", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-dots" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-dots card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar dots treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/173-shadcn-radar-dots/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/173-shadcn-radar-dots/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1750, + "id": "174-shadcn-radar-grid-circle-fill", + "title": "shadcn Radar Chart - Grid Circle Filled", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-grid-circle-fill example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "grid circle fill", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-grid-circle-fill", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-grid-circle-fill" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-grid-circle-fill card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar grid circle fill treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/174-shadcn-radar-grid-circle-fill/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/174-shadcn-radar-grid-circle-fill/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1760, + "id": "175-shadcn-radar-grid-circle-no-lines", + "title": "shadcn Radar Chart - Grid Circle - No lines", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-grid-circle-no-lines example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "grid circle no lines", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-grid-circle-no-lines", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-grid-circle-no-lines" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-grid-circle-no-lines card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar grid circle no lines treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/175-shadcn-radar-grid-circle-no-lines/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/175-shadcn-radar-grid-circle-no-lines/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1770, + "id": "176-shadcn-radar-grid-circle", + "title": "shadcn Radar Chart - Grid Circle", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-grid-circle example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "grid circle", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-grid-circle", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-grid-circle" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-grid-circle card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar grid circle treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/176-shadcn-radar-grid-circle/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/176-shadcn-radar-grid-circle/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1780, + "id": "177-shadcn-radar-grid-custom", + "title": "shadcn Radar Chart - Grid Custom", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-grid-custom example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "grid custom", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-grid-custom", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-grid-custom" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-grid-custom card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar grid custom treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/177-shadcn-radar-grid-custom/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/177-shadcn-radar-grid-custom/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1790, + "id": "178-shadcn-radar-grid-fill", + "title": "shadcn Radar Chart - Grid Filled", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-grid-fill example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "grid fill", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-grid-fill", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-grid-fill" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-grid-fill card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar grid fill treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/178-shadcn-radar-grid-fill/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/178-shadcn-radar-grid-fill/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1800, + "id": "179-shadcn-radar-grid-none", + "title": "shadcn Radar Chart - Grid None", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-grid-none example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "grid none", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-grid-none", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-grid-none" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-grid-none card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar grid none treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/179-shadcn-radar-grid-none/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/179-shadcn-radar-grid-none/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1810, + "id": "180-shadcn-radar-icons", + "title": "shadcn Radar Chart - Icons", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-icons example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "icons", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-icons", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-icons" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-icons card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar icons treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/180-shadcn-radar-icons/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/180-shadcn-radar-icons/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1820, + "id": "181-shadcn-radar-label-custom", + "title": "shadcn Radar Chart - Custom Label", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-label-custom example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "label custom", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-label-custom", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-label-custom" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-label-custom card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar label custom treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/181-shadcn-radar-label-custom/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/181-shadcn-radar-label-custom/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1830, + "id": "182-shadcn-radar-legend", + "title": "shadcn Radar Chart - Legend", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-legend example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "legend", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 2 + } + ], + "source": { + "title": "shadcn/ui chart-radar-legend", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-legend" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-legend card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar legend treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/182-shadcn-radar-legend/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/182-shadcn-radar-legend/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1840, + "id": "183-shadcn-radar-lines-only", + "title": "shadcn Radar Chart - Lines Only", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-lines-only example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "lines only", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-lines-only", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-lines-only" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-lines-only card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar lines only treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/183-shadcn-radar-lines-only/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/183-shadcn-radar-lines-only/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1850, + "id": "184-shadcn-radar-radius", + "title": "shadcn Radar Chart - Radius Axis", + "family": "radar", + "intent": "Reproduce shadcn/ui's chart-radar-radius example with TanStack Charts.", + "support": "native", + "features": ["radar chart", "radius", "shadcn card shell"], + "geometry": [ + { + "role": "radar", + "count": 1 + } + ], + "source": { + "title": "shadcn/ui chart-radar-radius", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radar-radius" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radar-radius card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radar radius treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/184-shadcn-radar-radius/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/184-shadcn-radar-radius/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1860, + "id": "185-shadcn-radial-grid", + "title": "shadcn Radial Chart - Grid", + "family": "radial", + "intent": "Reproduce shadcn/ui's chart-radial-grid example with TanStack Charts.", + "support": "native", + "features": ["radial chart", "grid", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 5, + "rendererRoles": { + "recharts": "arc", + "tanstack": "arc" + } + } + ], + "source": { + "title": "shadcn/ui chart-radial-grid", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radial-grid" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radial-grid card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radial grid treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/185-shadcn-radial-grid/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/185-shadcn-radial-grid/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1870, + "id": "186-shadcn-radial-label", + "title": "shadcn Radial Chart - Label", + "family": "radial", + "intent": "Reproduce shadcn/ui's chart-radial-label example with TanStack Charts.", + "support": "native", + "features": ["radial chart", "label", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 5, + "rendererRoles": { + "recharts": "arc", + "tanstack": "arc" + } + } + ], + "source": { + "title": "shadcn/ui chart-radial-label", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radial-label" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radial-label card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radial label treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/186-shadcn-radial-label/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/186-shadcn-radial-label/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1880, + "id": "187-shadcn-radial-shape", + "title": "shadcn Radial Chart - Shape", + "family": "radial", + "intent": "Reproduce shadcn/ui's chart-radial-shape example with TanStack Charts.", + "support": "native", + "features": ["radial chart", "shape", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 5, + "rendererRoles": { + "recharts": "arc", + "tanstack": "arc" + } + } + ], + "source": { + "title": "shadcn/ui chart-radial-shape", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radial-shape" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radial-shape card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radial shape treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/187-shadcn-radial-shape/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/187-shadcn-radial-shape/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1890, + "id": "188-shadcn-radial-simple", + "title": "shadcn Radial Chart", + "family": "radial", + "intent": "Reproduce shadcn/ui's chart-radial-simple example with TanStack Charts.", + "support": "native", + "features": ["radial chart", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 1, + "rendererRoles": { + "recharts": "arc", + "tanstack": "arc" + } + } + ], + "source": { + "title": "shadcn/ui chart-radial-simple", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radial-simple" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radial-simple card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radial simple treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/188-shadcn-radial-simple/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/188-shadcn-radial-simple/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1900, + "id": "189-shadcn-radial-stacked", + "title": "shadcn Radial Chart - Stacked", + "family": "radial", + "intent": "Reproduce shadcn/ui's chart-radial-stacked example with TanStack Charts.", + "support": "native", + "features": ["radial chart", "stacked", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 5, + "rendererRoles": { + "recharts": "arc", + "tanstack": "arc" + } + } + ], + "source": { + "title": "shadcn/ui chart-radial-stacked", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-radial-stacked" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-radial-stacked card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, radial stacked treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/189-shadcn-radial-stacked/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/189-shadcn-radial-stacked/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1910, + "id": "190-shadcn-tooltip-default", + "title": "shadcn Tooltip - Default", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's chart-tooltip-default example with TanStack Charts.", + "support": "native", + "features": ["tooltip chart", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 10 + } + ], + "source": { + "title": "shadcn/ui chart-tooltip-default", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-default" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-default card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, tooltip default treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/190-shadcn-tooltip-default/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/190-shadcn-tooltip-default/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1920, + "id": "191-shadcn-tooltip-formatter", + "title": "shadcn Tooltip - Formatter", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's chart-tooltip-formatter example with TanStack Charts.", + "support": "native", + "features": ["tooltip chart", "formatter", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 10 + } + ], + "source": { + "title": "shadcn/ui chart-tooltip-formatter", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-formatter" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-formatter card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, tooltip formatter treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/191-shadcn-tooltip-formatter/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/191-shadcn-tooltip-formatter/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1930, + "id": "192-shadcn-tooltip-icons", + "title": "shadcn Tooltip - Icons", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's chart-tooltip-icons example with TanStack Charts.", + "support": "native", + "features": ["tooltip chart", "icons", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 10 + } + ], + "source": { + "title": "shadcn/ui chart-tooltip-icons", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-icons" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-icons card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, tooltip icons treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/192-shadcn-tooltip-icons/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/192-shadcn-tooltip-icons/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1940, + "id": "193-shadcn-tooltip-indicator-line", + "title": "shadcn Tooltip - Line Indicator", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's chart-tooltip-indicator-line example with TanStack Charts.", + "support": "native", + "features": ["tooltip chart", "indicator line", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 10 + } + ], + "source": { + "title": "shadcn/ui chart-tooltip-indicator-line", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-indicator-line" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-indicator-line card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, tooltip indicator line treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/193-shadcn-tooltip-indicator-line/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/193-shadcn-tooltip-indicator-line/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1950, + "id": "194-shadcn-tooltip-indicator-none", + "title": "shadcn Tooltip - No Indicator", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's chart-tooltip-indicator-none example with TanStack Charts.", + "support": "native", + "features": ["tooltip chart", "indicator none", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 10 + } + ], + "source": { + "title": "shadcn/ui chart-tooltip-indicator-none", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-indicator-none" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-indicator-none card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, tooltip indicator none treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/194-shadcn-tooltip-indicator-none/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/194-shadcn-tooltip-indicator-none/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1960, + "id": "195-shadcn-tooltip-label-custom", + "title": "shadcn Tooltip - Custom label", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's chart-tooltip-label-custom example with TanStack Charts.", + "support": "native", + "features": ["tooltip chart", "label custom", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 10 + } + ], + "source": { + "title": "shadcn/ui chart-tooltip-label-custom", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-label-custom" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-label-custom card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, tooltip label custom treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/195-shadcn-tooltip-label-custom/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/195-shadcn-tooltip-label-custom/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1970, + "id": "196-shadcn-tooltip-label-formatter", + "title": "shadcn Tooltip - Label Formatter", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's chart-tooltip-label-formatter example with TanStack Charts.", + "support": "native", + "features": ["tooltip chart", "label formatter", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 10 + } + ], + "source": { + "title": "shadcn/ui chart-tooltip-label-formatter", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-label-formatter" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-label-formatter card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, tooltip label formatter treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/196-shadcn-tooltip-label-formatter/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/196-shadcn-tooltip-label-formatter/recharts.ts" + } + } + }, + { + "schemaVersion": 1, + "referenceRenderer": "recharts", + "collections": ["shadcn"], + "height": 600, + "order": 1980, + "id": "197-shadcn-tooltip-label-none", + "title": "shadcn Tooltip - No Label", + "family": "tooltip", + "intent": "Reproduce shadcn/ui's chart-tooltip-label-none example with TanStack Charts.", + "support": "native", + "features": ["tooltip chart", "label none", "shadcn card shell"], + "geometry": [ + { + "role": "bar", + "count": 10 + } + ], + "source": { + "title": "shadcn/ui chart-tooltip-label-none", + "url": "https://ui.shadcn.com/view/new-york-v4/chart-tooltip-label-none" + }, + "ai": { + "create": "Reproduce the official shadcn/ui chart-tooltip-label-none card and data with TanStack Charts.", + "maintain": "Preserve the pinned upstream data, tooltip label none treatment, card dimensions, and light and dark tokens." + }, + "entries": { + "tanstack": "benchmarks/conformance/cases/197-shadcn-tooltip-label-none/tanstack.ts", + "reference": { + "renderer": "recharts", + "path": "benchmarks/conformance/cases/197-shadcn-tooltip-label-none/recharts.ts" + } + } } ] } diff --git a/benchmarks/conformance/catalog-loader.test.ts b/benchmarks/conformance/catalog-loader.test.ts index 8aceda6c..89bca21c 100644 --- a/benchmarks/conformance/catalog-loader.test.ts +++ b/benchmarks/conformance/catalog-loader.test.ts @@ -237,6 +237,28 @@ describe('loadCatalogSourceClosure', () => { expect(closure.roles.fixture.files).toBe(1) }) + it('includes reusable shadcn chart primitives and their data', async () => { + const modules = { + './cases/example/tanstack.ts': async () => + "import { example } from '../../shared/shadcn-catalog-tanstack'\nexport { example }\n", + './shared/shadcn-catalog-tanstack.ts': async () => + "import { rows } from './shadcn-catalog-data'\nexport const example = rows\n", + './shared/shadcn-catalog-data.ts': async () => + 'export const rows = [1, 2, 3]\n', + } + + const closure = await loadCatalogSourceClosure( + modules, + './cases/example/tanstack.ts', + ) + + expect(closure.files.map(({ kind, path }) => ({ kind, path }))).toEqual([ + { kind: 'entry', path: 'tanstack.ts' }, + { kind: 'support', path: 'shared/shadcn-catalog-tanstack.ts' }, + { kind: 'fixture', path: 'shared/shadcn-catalog-data.ts' }, + ]) + }) + it('includes case-local model modules as authored support', async () => { const entrySource = "import { selectedRows } from './model'\nexport { selectedRows }\n" diff --git a/benchmarks/conformance/catalog-loader.ts b/benchmarks/conformance/catalog-loader.ts index b8d8900a..48da4f89 100644 --- a/benchmarks/conformance/catalog-loader.ts +++ b/benchmarks/conformance/catalog-loader.ts @@ -238,6 +238,9 @@ function catalogSourceKind( if (path.startsWith('./shared/transforms/')) { return 'support' } + if (path.startsWith('./shared/shadcn-')) { + return path.endsWith('-data.ts') ? 'fixture' : 'support' + } if (path === './shared/data.ts' || path.startsWith('./shared/fixtures/')) { return 'fixture' } diff --git a/benchmarks/conformance/definition-coverage-roadmap.json b/benchmarks/conformance/definition-coverage-roadmap.json index 5e86f3c8..da0c72c6 100644 --- a/benchmarks/conformance/definition-coverage-roadmap.json +++ b/benchmarks/conformance/definition-coverage-roadmap.json @@ -3679,6 +3679,2278 @@ } ] }, + { + "id": "127-shadcn-dashboard", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/127-shadcn-dashboard/tanstack.ts", + "cases/127-shadcn-dashboard/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn dashboard variant through the shared native dashboard chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "133-shadcn-area-axes", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/133-shadcn-area-axes/tanstack.ts", + "cases/133-shadcn-area-axes/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-area-axes variant through the shared native area chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "134-shadcn-area-default", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/134-shadcn-area-default/tanstack.ts", + "cases/134-shadcn-area-default/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-area-default variant through the shared native area chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "135-shadcn-area-gradient", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/135-shadcn-area-gradient/tanstack.ts", + "cases/135-shadcn-area-gradient/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-area-gradient variant through the shared native area chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "136-shadcn-area-icons", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/136-shadcn-area-icons/tanstack.ts", + "cases/136-shadcn-area-icons/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-area-icons variant through the shared native area chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "137-shadcn-area-interactive", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/137-shadcn-area-interactive/tanstack.ts", + "cases/137-shadcn-area-interactive/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-area-interactive variant through the shared native area chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "138-shadcn-area-legend", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/138-shadcn-area-legend/tanstack.ts", + "cases/138-shadcn-area-legend/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-area-legend variant through the shared native area chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "139-shadcn-area-linear", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/139-shadcn-area-linear/tanstack.ts", + "cases/139-shadcn-area-linear/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-area-linear variant through the shared native area chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "140-shadcn-area-stacked-expand", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/140-shadcn-area-stacked-expand/tanstack.ts", + "cases/140-shadcn-area-stacked-expand/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-area-stacked-expand variant through the shared native area chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "141-shadcn-area-stacked", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/141-shadcn-area-stacked/tanstack.ts", + "cases/141-shadcn-area-stacked/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-area-stacked variant through the shared native area chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "142-shadcn-area-step", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/142-shadcn-area-step/tanstack.ts", + "cases/142-shadcn-area-step/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-area-step variant through the shared native area chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "143-shadcn-bar-active", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/143-shadcn-bar-active/tanstack.ts", + "cases/143-shadcn-bar-active/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-bar-active variant through the shared native bar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "144-shadcn-bar-default", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/144-shadcn-bar-default/tanstack.ts", + "cases/144-shadcn-bar-default/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-bar-default variant through the shared native bar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "145-shadcn-bar-horizontal", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/145-shadcn-bar-horizontal/tanstack.ts", + "cases/145-shadcn-bar-horizontal/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-bar-horizontal variant through the shared native bar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "146-shadcn-bar-interactive", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/146-shadcn-bar-interactive/tanstack.ts", + "cases/146-shadcn-bar-interactive/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-bar-interactive variant through the shared native bar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "147-shadcn-bar-label-custom", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/147-shadcn-bar-label-custom/tanstack.ts", + "cases/147-shadcn-bar-label-custom/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-bar-label-custom variant through the shared native bar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "148-shadcn-bar-label", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/148-shadcn-bar-label/tanstack.ts", + "cases/148-shadcn-bar-label/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-bar-label variant through the shared native bar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "149-shadcn-bar-mixed", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/149-shadcn-bar-mixed/tanstack.ts", + "cases/149-shadcn-bar-mixed/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-bar-mixed variant through the shared native bar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "128-shadcn-bar-multiple", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/128-shadcn-bar-multiple/tanstack.ts", + "cases/128-shadcn-bar-multiple/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-bar-multiple variant through the shared native bar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "150-shadcn-bar-negative", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/150-shadcn-bar-negative/tanstack.ts", + "cases/150-shadcn-bar-negative/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-bar-negative variant through the shared native bar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "151-shadcn-bar-stacked", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/151-shadcn-bar-stacked/tanstack.ts", + "cases/151-shadcn-bar-stacked/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-bar-stacked variant through the shared native bar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "152-shadcn-line-default", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/152-shadcn-line-default/tanstack.ts", + "cases/152-shadcn-line-default/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-line-default variant through the shared native line chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "153-shadcn-line-dots-colors", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/153-shadcn-line-dots-colors/tanstack.ts", + "cases/153-shadcn-line-dots-colors/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-line-dots-colors variant through the shared native line chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "154-shadcn-line-dots-custom", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/154-shadcn-line-dots-custom/tanstack.ts", + "cases/154-shadcn-line-dots-custom/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-line-dots-custom variant through the shared native line chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "155-shadcn-line-dots", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/155-shadcn-line-dots/tanstack.ts", + "cases/155-shadcn-line-dots/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-line-dots variant through the shared native line chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "156-shadcn-line-interactive", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/156-shadcn-line-interactive/tanstack.ts", + "cases/156-shadcn-line-interactive/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-line-interactive variant through the shared native line chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "157-shadcn-line-label-custom", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/157-shadcn-line-label-custom/tanstack.ts", + "cases/157-shadcn-line-label-custom/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-line-label-custom variant through the shared native line chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "158-shadcn-line-label", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/158-shadcn-line-label/tanstack.ts", + "cases/158-shadcn-line-label/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-line-label variant through the shared native line chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "159-shadcn-line-linear", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/159-shadcn-line-linear/tanstack.ts", + "cases/159-shadcn-line-linear/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-line-linear variant through the shared native line chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "160-shadcn-line-multiple", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/160-shadcn-line-multiple/tanstack.ts", + "cases/160-shadcn-line-multiple/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-line-multiple variant through the shared native line chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "161-shadcn-line-step", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/161-shadcn-line-step/tanstack.ts", + "cases/161-shadcn-line-step/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-line-step variant through the shared native line chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "162-shadcn-pie-donut-active", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/162-shadcn-pie-donut-active/tanstack.ts", + "cases/162-shadcn-pie-donut-active/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-pie-donut-active variant through the shared native pie chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "129-shadcn-pie-donut-text", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/129-shadcn-pie-donut-text/tanstack.ts", + "cases/129-shadcn-pie-donut-text/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-pie-donut-text variant through the shared native pie chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "163-shadcn-pie-donut", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/163-shadcn-pie-donut/tanstack.ts", + "cases/163-shadcn-pie-donut/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-pie-donut variant through the shared native pie chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "164-shadcn-pie-interactive", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/164-shadcn-pie-interactive/tanstack.ts", + "cases/164-shadcn-pie-interactive/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-pie-interactive variant through the shared native pie chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "165-shadcn-pie-label-custom", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/165-shadcn-pie-label-custom/tanstack.ts", + "cases/165-shadcn-pie-label-custom/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-pie-label-custom variant through the shared native pie chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "166-shadcn-pie-label-list", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/166-shadcn-pie-label-list/tanstack.ts", + "cases/166-shadcn-pie-label-list/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-pie-label-list variant through the shared native pie chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "167-shadcn-pie-label", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/167-shadcn-pie-label/tanstack.ts", + "cases/167-shadcn-pie-label/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-pie-label variant through the shared native pie chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "168-shadcn-pie-legend", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/168-shadcn-pie-legend/tanstack.ts", + "cases/168-shadcn-pie-legend/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-pie-legend variant through the shared native pie chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "169-shadcn-pie-separator-none", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/169-shadcn-pie-separator-none/tanstack.ts", + "cases/169-shadcn-pie-separator-none/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-pie-separator-none variant through the shared native pie chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "170-shadcn-pie-simple", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/170-shadcn-pie-simple/tanstack.ts", + "cases/170-shadcn-pie-simple/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-pie-simple variant through the shared native pie chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "171-shadcn-pie-stacked", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/171-shadcn-pie-stacked/tanstack.ts", + "cases/171-shadcn-pie-stacked/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-pie-stacked variant through the shared native pie chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "172-shadcn-radar-default", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/172-shadcn-radar-default/tanstack.ts", + "cases/172-shadcn-radar-default/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radar-default variant through the shared native radar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "173-shadcn-radar-dots", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/173-shadcn-radar-dots/tanstack.ts", + "cases/173-shadcn-radar-dots/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radar-dots variant through the shared native radar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "174-shadcn-radar-grid-circle-fill", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/174-shadcn-radar-grid-circle-fill/tanstack.ts", + "cases/174-shadcn-radar-grid-circle-fill/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radar-grid-circle-fill variant through the shared native radar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "175-shadcn-radar-grid-circle-no-lines", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/175-shadcn-radar-grid-circle-no-lines/tanstack.ts", + "cases/175-shadcn-radar-grid-circle-no-lines/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radar-grid-circle-no-lines variant through the shared native radar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "176-shadcn-radar-grid-circle", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/176-shadcn-radar-grid-circle/tanstack.ts", + "cases/176-shadcn-radar-grid-circle/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radar-grid-circle variant through the shared native radar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "177-shadcn-radar-grid-custom", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/177-shadcn-radar-grid-custom/tanstack.ts", + "cases/177-shadcn-radar-grid-custom/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radar-grid-custom variant through the shared native radar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "178-shadcn-radar-grid-fill", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/178-shadcn-radar-grid-fill/tanstack.ts", + "cases/178-shadcn-radar-grid-fill/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radar-grid-fill variant through the shared native radar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "179-shadcn-radar-grid-none", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/179-shadcn-radar-grid-none/tanstack.ts", + "cases/179-shadcn-radar-grid-none/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radar-grid-none variant through the shared native radar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "180-shadcn-radar-icons", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/180-shadcn-radar-icons/tanstack.ts", + "cases/180-shadcn-radar-icons/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radar-icons variant through the shared native radar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "181-shadcn-radar-label-custom", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/181-shadcn-radar-label-custom/tanstack.ts", + "cases/181-shadcn-radar-label-custom/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radar-label-custom variant through the shared native radar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "182-shadcn-radar-legend", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/182-shadcn-radar-legend/tanstack.ts", + "cases/182-shadcn-radar-legend/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radar-legend variant through the shared native radar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "183-shadcn-radar-lines-only", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/183-shadcn-radar-lines-only/tanstack.ts", + "cases/183-shadcn-radar-lines-only/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radar-lines-only variant through the shared native radar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "130-shadcn-radar-multiple", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/130-shadcn-radar-multiple/tanstack.ts", + "cases/130-shadcn-radar-multiple/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radar-multiple variant through the shared native radar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "184-shadcn-radar-radius", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/184-shadcn-radar-radius/tanstack.ts", + "cases/184-shadcn-radar-radius/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radar-radius variant through the shared native radar chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "185-shadcn-radial-grid", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/185-shadcn-radial-grid/tanstack.ts", + "cases/185-shadcn-radial-grid/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radial-grid variant through the shared native radial chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "186-shadcn-radial-label", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/186-shadcn-radial-label/tanstack.ts", + "cases/186-shadcn-radial-label/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radial-label variant through the shared native radial chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "187-shadcn-radial-shape", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/187-shadcn-radial-shape/tanstack.ts", + "cases/187-shadcn-radial-shape/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radial-shape variant through the shared native radial chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "188-shadcn-radial-simple", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/188-shadcn-radial-simple/tanstack.ts", + "cases/188-shadcn-radial-simple/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radial-simple variant through the shared native radial chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "189-shadcn-radial-stacked", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/189-shadcn-radial-stacked/tanstack.ts", + "cases/189-shadcn-radial-stacked/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radial-stacked variant through the shared native radial chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "131-shadcn-radial-text", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/131-shadcn-radial-text/tanstack.ts", + "cases/131-shadcn-radial-text/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-radial-text variant through the shared native radial chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "132-shadcn-tooltip-advanced", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/132-shadcn-tooltip-advanced/tanstack.ts", + "cases/132-shadcn-tooltip-advanced/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-tooltip-advanced variant through the shared native tooltip chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "190-shadcn-tooltip-default", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/190-shadcn-tooltip-default/tanstack.ts", + "cases/190-shadcn-tooltip-default/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-tooltip-default variant through the shared native tooltip chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "191-shadcn-tooltip-formatter", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/191-shadcn-tooltip-formatter/tanstack.ts", + "cases/191-shadcn-tooltip-formatter/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-tooltip-formatter variant through the shared native tooltip chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "192-shadcn-tooltip-icons", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/192-shadcn-tooltip-icons/tanstack.ts", + "cases/192-shadcn-tooltip-icons/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-tooltip-icons variant through the shared native tooltip chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "193-shadcn-tooltip-indicator-line", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/193-shadcn-tooltip-indicator-line/tanstack.ts", + "cases/193-shadcn-tooltip-indicator-line/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-tooltip-indicator-line variant through the shared native tooltip chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "194-shadcn-tooltip-indicator-none", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/194-shadcn-tooltip-indicator-none/tanstack.ts", + "cases/194-shadcn-tooltip-indicator-none/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-tooltip-indicator-none variant through the shared native tooltip chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "195-shadcn-tooltip-label-custom", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/195-shadcn-tooltip-label-custom/tanstack.ts", + "cases/195-shadcn-tooltip-label-custom/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-tooltip-label-custom variant through the shared native tooltip chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "196-shadcn-tooltip-label-formatter", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/196-shadcn-tooltip-label-formatter/tanstack.ts", + "cases/196-shadcn-tooltip-label-formatter/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-tooltip-label-formatter variant through the shared native tooltip chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, + { + "id": "197-shadcn-tooltip-label-none", + "coverage": "app-composed", + "disposition": "definition-now", + "phase": "phase-0", + "status": "verified", + "capabilities": ["current-definition-api"], + "evidence": [ + "cases/197-shadcn-tooltip-label-none/tanstack.ts", + "cases/197-shadcn-tooltip-label-none/case.json" + ], + "work": [ + { + "kind": "definition-composition", + "stage": "definition-builder", + "owner": "charts", + "coordinateSpace": "none", + "sources": [ + "benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx" + ], + "summary": "Compose the pinned shadcn chart-tooltip-label-none variant through the shared native tooltip chart definition." + }, + { + "kind": "application-shell", + "stage": "post-render", + "owner": "application", + "coordinateSpace": "dom", + "sources": ["benchmarks/conformance/shared/shadcn-chart-card.tsx"], + "summary": "Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations." + } + ] + }, { "id": "bar-grouped", "coverage": "declarative", diff --git a/benchmarks/conformance/definition-coverage-roadmap.test.ts b/benchmarks/conformance/definition-coverage-roadmap.test.ts index 4cd59f99..b3e9d91a 100644 --- a/benchmarks/conformance/definition-coverage-roadmap.test.ts +++ b/benchmarks/conformance/definition-coverage-roadmap.test.ts @@ -140,12 +140,12 @@ describe('definition coverage roadmap', () => { .filter((entry) => entry.isDirectory()) .map((entry) => entry.name) - expect(auditedIds).toHaveLength(117) - expect(new Set(auditedIds).size).toBe(117) - expect(roadmapIds).toHaveLength(117) - expect(new Set(roadmapIds).size).toBe(117) - expect(overviewIds).toHaveLength(117) - expect(new Set(overviewIds).size).toBe(117) + expect(auditedIds).toHaveLength(188) + expect(new Set(auditedIds).size).toBe(188) + expect(roadmapIds).toHaveLength(188) + expect(new Set(roadmapIds).size).toBe(188) + expect(overviewIds).toHaveLength(188) + expect(new Set(overviewIds).size).toBe(188) expect([...roadmapIds].sort()).toEqual([...auditedIds].sort()) expect([...roadmapIds].sort()).toEqual([...overviewIds].sort()) expect([...roadmapIds].sort()).toEqual([...catalogIds].sort()) @@ -154,13 +154,13 @@ describe('definition coverage roadmap', () => { expect(countBy(roadmap.cases, 'disposition')).toEqual({ 'application-boundary': 2, - 'definition-now': 64, + 'definition-now': 135, 'first-party-primitive': 35, 'inline-custom-mark': 1, 'optional-primitive': 15, }) expect(countBy(roadmap.cases, 'phase')).toEqual({ - 'phase-0': 66, + 'phase-0': 137, 'phase-1': 2, 'phase-2': 26, 'phase-3': 11, @@ -170,8 +170,8 @@ describe('definition coverage roadmap', () => { const roadmapById = new Map(roadmap.cases.map((entry) => [entry.id, entry])) const auditRows = coverageRows(audit, false) const overviewRows = coverageRows(overview, true) - expect(auditRows).toHaveLength(117) - expect(overviewRows).toHaveLength(117) + expect(auditRows).toHaveLength(188) + expect(overviewRows).toHaveLength(188) for (const row of auditRows) { expect(dispositionByLabel[row.disposition]).toBe( roadmapById.get(row.id)?.disposition, diff --git a/benchmarks/conformance/metadata.test.ts b/benchmarks/conformance/metadata.test.ts index 06d7b364..05e73753 100644 --- a/benchmarks/conformance/metadata.test.ts +++ b/benchmarks/conformance/metadata.test.ts @@ -19,6 +19,22 @@ const baseMetadata = { } as const describe('conformance metadata', () => { + it('accepts explicit collection membership', () => { + expect( + parseConformanceCaseMeta( + { ...baseMetadata, collections: ['shadcn'] }, + 'case.json', + ).collections, + ).toEqual(['shadcn']) + }) + + it('accepts a taller catalog viewport for application examples', () => { + expect( + parseConformanceCaseMeta({ ...baseMetadata, height: 860 }, 'case.json') + .height, + ).toBe(860) + }) + it('accepts arc geometry for polar sector comparisons', () => { expect( parseConformanceCaseMeta( diff --git a/benchmarks/conformance/metadata.ts b/benchmarks/conformance/metadata.ts index 83b7e36e..2bf8cb9e 100644 --- a/benchmarks/conformance/metadata.ts +++ b/benchmarks/conformance/metadata.ts @@ -23,6 +23,8 @@ export function parseConformanceCaseMeta( value.schemaVersion !== 1 || ('referenceRenderer' in value && !isReferenceRenderer(value.referenceRenderer)) || + ('collections' in value && !isNonEmptyStringArray(value.collections)) || + ('height' in value && !isCaseHeight(value.height)) || !('order' in value) || typeof value.order !== 'number' || !('id' in value) || @@ -56,6 +58,12 @@ export function parseConformanceCaseMeta( 'referenceRenderer' in value && isReferenceRenderer(value.referenceRenderer) ? value.referenceRenderer : undefined + const collections = + 'collections' in value && isNonEmptyStringArray(value.collections) + ? value.collections + : undefined + const height = + 'height' in value && isCaseHeight(value.height) ? value.height : undefined const guideAssertions = 'guideAssertions' in value && isGuideArray(value.guideAssertions) ? value.guideAssertions @@ -74,6 +82,8 @@ export function parseConformanceCaseMeta( return { schemaVersion: 1, ...(referenceRenderer ? { referenceRenderer } : {}), + ...(collections ? { collections } : {}), + ...(height === undefined ? {} : { height }), order: value.order, id: value.id, title: value.title, @@ -92,6 +102,15 @@ export function parseConformanceCaseMeta( } } +function isCaseHeight(value: unknown): value is number { + return ( + typeof value === 'number' && + Number.isSafeInteger(value) && + value >= 120 && + value <= 1_200 + ) +} + function isReferenceRenderer( value: unknown, ): value is ConformanceReferenceRenderer { @@ -115,6 +134,15 @@ function isStringArray(value: unknown): value is string[] { ) } +function isNonEmptyStringArray(value: unknown): value is string[] { + return ( + isStringArray(value) && + value.length > 0 && + value.every((entry) => entry.length > 0) && + new Set(value).size === value.length + ) +} + function isSource(value: unknown): value is ConformanceCaseMeta['source'] { return ( typeof value === 'object' && diff --git a/benchmarks/conformance/previews/112-motion-entrance.svg b/benchmarks/conformance/previews/112-motion-entrance.svg index a95fa321..9a9319a7 100644 --- a/benchmarks/conformance/previews/112-motion-entrance.svg +++ b/benchmarks/conformance/previews/112-motion-entrance.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/113-motion-updates.svg b/benchmarks/conformance/previews/113-motion-updates.svg index 3c039a76..193215ec 100644 --- a/benchmarks/conformance/previews/113-motion-updates.svg +++ b/benchmarks/conformance/previews/113-motion-updates.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/115-definition-motion.svg b/benchmarks/conformance/previews/115-definition-motion.svg index 996711a3..74f7b684 100644 --- a/benchmarks/conformance/previews/115-definition-motion.svg +++ b/benchmarks/conformance/previews/115-definition-motion.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/119-stacked-bar-band-cursor.svg b/benchmarks/conformance/previews/119-stacked-bar-band-cursor.svg index 6b771059..cf3f1221 100644 --- a/benchmarks/conformance/previews/119-stacked-bar-band-cursor.svg +++ b/benchmarks/conformance/previews/119-stacked-bar-band-cursor.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/121-active-bar-dashboard.svg b/benchmarks/conformance/previews/121-active-bar-dashboard.svg index acca9c2f..3e3d5ade 100644 --- a/benchmarks/conformance/previews/121-active-bar-dashboard.svg +++ b/benchmarks/conformance/previews/121-active-bar-dashboard.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/127-shadcn-dashboard.svg b/benchmarks/conformance/previews/127-shadcn-dashboard.svg new file mode 100644 index 00000000..a860e60a --- /dev/null +++ b/benchmarks/conformance/previews/127-shadcn-dashboard.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/128-shadcn-bar-multiple.svg b/benchmarks/conformance/previews/128-shadcn-bar-multiple.svg new file mode 100644 index 00000000..b24672d0 --- /dev/null +++ b/benchmarks/conformance/previews/128-shadcn-bar-multiple.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/129-shadcn-pie-donut-text.svg b/benchmarks/conformance/previews/129-shadcn-pie-donut-text.svg new file mode 100644 index 00000000..e165c936 --- /dev/null +++ b/benchmarks/conformance/previews/129-shadcn-pie-donut-text.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/13-interval-timeline.svg b/benchmarks/conformance/previews/13-interval-timeline.svg index 8312fe80..d49f9355 100644 --- a/benchmarks/conformance/previews/13-interval-timeline.svg +++ b/benchmarks/conformance/previews/13-interval-timeline.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/130-shadcn-radar-multiple.svg b/benchmarks/conformance/previews/130-shadcn-radar-multiple.svg new file mode 100644 index 00000000..613a72fd --- /dev/null +++ b/benchmarks/conformance/previews/130-shadcn-radar-multiple.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/131-shadcn-radial-text.svg b/benchmarks/conformance/previews/131-shadcn-radial-text.svg new file mode 100644 index 00000000..fd18711f --- /dev/null +++ b/benchmarks/conformance/previews/131-shadcn-radial-text.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/132-shadcn-tooltip-advanced.svg b/benchmarks/conformance/previews/132-shadcn-tooltip-advanced.svg new file mode 100644 index 00000000..a6f7c706 --- /dev/null +++ b/benchmarks/conformance/previews/132-shadcn-tooltip-advanced.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/133-shadcn-area-axes.svg b/benchmarks/conformance/previews/133-shadcn-area-axes.svg new file mode 100644 index 00000000..4fc1e8a9 --- /dev/null +++ b/benchmarks/conformance/previews/133-shadcn-area-axes.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/134-shadcn-area-default.svg b/benchmarks/conformance/previews/134-shadcn-area-default.svg new file mode 100644 index 00000000..481b2c2f --- /dev/null +++ b/benchmarks/conformance/previews/134-shadcn-area-default.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/135-shadcn-area-gradient.svg b/benchmarks/conformance/previews/135-shadcn-area-gradient.svg new file mode 100644 index 00000000..3a629505 --- /dev/null +++ b/benchmarks/conformance/previews/135-shadcn-area-gradient.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/136-shadcn-area-icons.svg b/benchmarks/conformance/previews/136-shadcn-area-icons.svg new file mode 100644 index 00000000..ccd30741 --- /dev/null +++ b/benchmarks/conformance/previews/136-shadcn-area-icons.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/137-shadcn-area-interactive.svg b/benchmarks/conformance/previews/137-shadcn-area-interactive.svg new file mode 100644 index 00000000..19d1ce9e --- /dev/null +++ b/benchmarks/conformance/previews/137-shadcn-area-interactive.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/138-shadcn-area-legend.svg b/benchmarks/conformance/previews/138-shadcn-area-legend.svg new file mode 100644 index 00000000..cc36c6eb --- /dev/null +++ b/benchmarks/conformance/previews/138-shadcn-area-legend.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/139-shadcn-area-linear.svg b/benchmarks/conformance/previews/139-shadcn-area-linear.svg new file mode 100644 index 00000000..e0d12aa4 --- /dev/null +++ b/benchmarks/conformance/previews/139-shadcn-area-linear.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/140-shadcn-area-stacked-expand.svg b/benchmarks/conformance/previews/140-shadcn-area-stacked-expand.svg new file mode 100644 index 00000000..64615bb0 --- /dev/null +++ b/benchmarks/conformance/previews/140-shadcn-area-stacked-expand.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/141-shadcn-area-stacked.svg b/benchmarks/conformance/previews/141-shadcn-area-stacked.svg new file mode 100644 index 00000000..dc287c09 --- /dev/null +++ b/benchmarks/conformance/previews/141-shadcn-area-stacked.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/142-shadcn-area-step.svg b/benchmarks/conformance/previews/142-shadcn-area-step.svg new file mode 100644 index 00000000..2f14ad53 --- /dev/null +++ b/benchmarks/conformance/previews/142-shadcn-area-step.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/143-shadcn-bar-active.svg b/benchmarks/conformance/previews/143-shadcn-bar-active.svg new file mode 100644 index 00000000..fc5efdcc --- /dev/null +++ b/benchmarks/conformance/previews/143-shadcn-bar-active.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/144-shadcn-bar-default.svg b/benchmarks/conformance/previews/144-shadcn-bar-default.svg new file mode 100644 index 00000000..126bc3d6 --- /dev/null +++ b/benchmarks/conformance/previews/144-shadcn-bar-default.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/145-shadcn-bar-horizontal.svg b/benchmarks/conformance/previews/145-shadcn-bar-horizontal.svg new file mode 100644 index 00000000..ac05773c --- /dev/null +++ b/benchmarks/conformance/previews/145-shadcn-bar-horizontal.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/146-shadcn-bar-interactive.svg b/benchmarks/conformance/previews/146-shadcn-bar-interactive.svg new file mode 100644 index 00000000..05ac9bd4 --- /dev/null +++ b/benchmarks/conformance/previews/146-shadcn-bar-interactive.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/147-shadcn-bar-label-custom.svg b/benchmarks/conformance/previews/147-shadcn-bar-label-custom.svg new file mode 100644 index 00000000..cddfe6d7 --- /dev/null +++ b/benchmarks/conformance/previews/147-shadcn-bar-label-custom.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/148-shadcn-bar-label.svg b/benchmarks/conformance/previews/148-shadcn-bar-label.svg new file mode 100644 index 00000000..1b694972 --- /dev/null +++ b/benchmarks/conformance/previews/148-shadcn-bar-label.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/149-shadcn-bar-mixed.svg b/benchmarks/conformance/previews/149-shadcn-bar-mixed.svg new file mode 100644 index 00000000..b5084ab1 --- /dev/null +++ b/benchmarks/conformance/previews/149-shadcn-bar-mixed.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/15-boxplot.svg b/benchmarks/conformance/previews/15-boxplot.svg index 690be4e2..455e6874 100644 --- a/benchmarks/conformance/previews/15-boxplot.svg +++ b/benchmarks/conformance/previews/15-boxplot.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/150-shadcn-bar-negative.svg b/benchmarks/conformance/previews/150-shadcn-bar-negative.svg new file mode 100644 index 00000000..b58fdb14 --- /dev/null +++ b/benchmarks/conformance/previews/150-shadcn-bar-negative.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/151-shadcn-bar-stacked.svg b/benchmarks/conformance/previews/151-shadcn-bar-stacked.svg new file mode 100644 index 00000000..e08db187 --- /dev/null +++ b/benchmarks/conformance/previews/151-shadcn-bar-stacked.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/152-shadcn-line-default.svg b/benchmarks/conformance/previews/152-shadcn-line-default.svg new file mode 100644 index 00000000..5f6d24f5 --- /dev/null +++ b/benchmarks/conformance/previews/152-shadcn-line-default.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/153-shadcn-line-dots-colors.svg b/benchmarks/conformance/previews/153-shadcn-line-dots-colors.svg new file mode 100644 index 00000000..969cdfe1 --- /dev/null +++ b/benchmarks/conformance/previews/153-shadcn-line-dots-colors.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/154-shadcn-line-dots-custom.svg b/benchmarks/conformance/previews/154-shadcn-line-dots-custom.svg new file mode 100644 index 00000000..535e7869 --- /dev/null +++ b/benchmarks/conformance/previews/154-shadcn-line-dots-custom.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/155-shadcn-line-dots.svg b/benchmarks/conformance/previews/155-shadcn-line-dots.svg new file mode 100644 index 00000000..33487940 --- /dev/null +++ b/benchmarks/conformance/previews/155-shadcn-line-dots.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/156-shadcn-line-interactive.svg b/benchmarks/conformance/previews/156-shadcn-line-interactive.svg new file mode 100644 index 00000000..1b526ebb --- /dev/null +++ b/benchmarks/conformance/previews/156-shadcn-line-interactive.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/157-shadcn-line-label-custom.svg b/benchmarks/conformance/previews/157-shadcn-line-label-custom.svg new file mode 100644 index 00000000..c7b35054 --- /dev/null +++ b/benchmarks/conformance/previews/157-shadcn-line-label-custom.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/158-shadcn-line-label.svg b/benchmarks/conformance/previews/158-shadcn-line-label.svg new file mode 100644 index 00000000..c767494b --- /dev/null +++ b/benchmarks/conformance/previews/158-shadcn-line-label.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/159-shadcn-line-linear.svg b/benchmarks/conformance/previews/159-shadcn-line-linear.svg new file mode 100644 index 00000000..8997ccc9 --- /dev/null +++ b/benchmarks/conformance/previews/159-shadcn-line-linear.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/160-shadcn-line-multiple.svg b/benchmarks/conformance/previews/160-shadcn-line-multiple.svg new file mode 100644 index 00000000..2bd7f529 --- /dev/null +++ b/benchmarks/conformance/previews/160-shadcn-line-multiple.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/161-shadcn-line-step.svg b/benchmarks/conformance/previews/161-shadcn-line-step.svg new file mode 100644 index 00000000..e5f5dce7 --- /dev/null +++ b/benchmarks/conformance/previews/161-shadcn-line-step.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/162-shadcn-pie-donut-active.svg b/benchmarks/conformance/previews/162-shadcn-pie-donut-active.svg new file mode 100644 index 00000000..b52950c8 --- /dev/null +++ b/benchmarks/conformance/previews/162-shadcn-pie-donut-active.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/163-shadcn-pie-donut.svg b/benchmarks/conformance/previews/163-shadcn-pie-donut.svg new file mode 100644 index 00000000..cfa45480 --- /dev/null +++ b/benchmarks/conformance/previews/163-shadcn-pie-donut.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/164-shadcn-pie-interactive.svg b/benchmarks/conformance/previews/164-shadcn-pie-interactive.svg new file mode 100644 index 00000000..649e9aa3 --- /dev/null +++ b/benchmarks/conformance/previews/164-shadcn-pie-interactive.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/165-shadcn-pie-label-custom.svg b/benchmarks/conformance/previews/165-shadcn-pie-label-custom.svg new file mode 100644 index 00000000..0e67224d --- /dev/null +++ b/benchmarks/conformance/previews/165-shadcn-pie-label-custom.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/166-shadcn-pie-label-list.svg b/benchmarks/conformance/previews/166-shadcn-pie-label-list.svg new file mode 100644 index 00000000..ea615933 --- /dev/null +++ b/benchmarks/conformance/previews/166-shadcn-pie-label-list.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/167-shadcn-pie-label.svg b/benchmarks/conformance/previews/167-shadcn-pie-label.svg new file mode 100644 index 00000000..e1ec79d5 --- /dev/null +++ b/benchmarks/conformance/previews/167-shadcn-pie-label.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/168-shadcn-pie-legend.svg b/benchmarks/conformance/previews/168-shadcn-pie-legend.svg new file mode 100644 index 00000000..ae47a045 --- /dev/null +++ b/benchmarks/conformance/previews/168-shadcn-pie-legend.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/169-shadcn-pie-separator-none.svg b/benchmarks/conformance/previews/169-shadcn-pie-separator-none.svg new file mode 100644 index 00000000..39b1b5a8 --- /dev/null +++ b/benchmarks/conformance/previews/169-shadcn-pie-separator-none.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/170-shadcn-pie-simple.svg b/benchmarks/conformance/previews/170-shadcn-pie-simple.svg new file mode 100644 index 00000000..ff8d22c2 --- /dev/null +++ b/benchmarks/conformance/previews/170-shadcn-pie-simple.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/171-shadcn-pie-stacked.svg b/benchmarks/conformance/previews/171-shadcn-pie-stacked.svg new file mode 100644 index 00000000..29111e5c --- /dev/null +++ b/benchmarks/conformance/previews/171-shadcn-pie-stacked.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/172-shadcn-radar-default.svg b/benchmarks/conformance/previews/172-shadcn-radar-default.svg new file mode 100644 index 00000000..e48377fb --- /dev/null +++ b/benchmarks/conformance/previews/172-shadcn-radar-default.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/173-shadcn-radar-dots.svg b/benchmarks/conformance/previews/173-shadcn-radar-dots.svg new file mode 100644 index 00000000..58121710 --- /dev/null +++ b/benchmarks/conformance/previews/173-shadcn-radar-dots.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/174-shadcn-radar-grid-circle-fill.svg b/benchmarks/conformance/previews/174-shadcn-radar-grid-circle-fill.svg new file mode 100644 index 00000000..637d5dc0 --- /dev/null +++ b/benchmarks/conformance/previews/174-shadcn-radar-grid-circle-fill.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/175-shadcn-radar-grid-circle-no-lines.svg b/benchmarks/conformance/previews/175-shadcn-radar-grid-circle-no-lines.svg new file mode 100644 index 00000000..51621981 --- /dev/null +++ b/benchmarks/conformance/previews/175-shadcn-radar-grid-circle-no-lines.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/176-shadcn-radar-grid-circle.svg b/benchmarks/conformance/previews/176-shadcn-radar-grid-circle.svg new file mode 100644 index 00000000..3cbd05c0 --- /dev/null +++ b/benchmarks/conformance/previews/176-shadcn-radar-grid-circle.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/177-shadcn-radar-grid-custom.svg b/benchmarks/conformance/previews/177-shadcn-radar-grid-custom.svg new file mode 100644 index 00000000..9ccfa31d --- /dev/null +++ b/benchmarks/conformance/previews/177-shadcn-radar-grid-custom.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/178-shadcn-radar-grid-fill.svg b/benchmarks/conformance/previews/178-shadcn-radar-grid-fill.svg new file mode 100644 index 00000000..9cb0bcd8 --- /dev/null +++ b/benchmarks/conformance/previews/178-shadcn-radar-grid-fill.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/179-shadcn-radar-grid-none.svg b/benchmarks/conformance/previews/179-shadcn-radar-grid-none.svg new file mode 100644 index 00000000..47fd8d6d --- /dev/null +++ b/benchmarks/conformance/previews/179-shadcn-radar-grid-none.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/180-shadcn-radar-icons.svg b/benchmarks/conformance/previews/180-shadcn-radar-icons.svg new file mode 100644 index 00000000..4f3db8c5 --- /dev/null +++ b/benchmarks/conformance/previews/180-shadcn-radar-icons.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/181-shadcn-radar-label-custom.svg b/benchmarks/conformance/previews/181-shadcn-radar-label-custom.svg new file mode 100644 index 00000000..d254bac9 --- /dev/null +++ b/benchmarks/conformance/previews/181-shadcn-radar-label-custom.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/182-shadcn-radar-legend.svg b/benchmarks/conformance/previews/182-shadcn-radar-legend.svg new file mode 100644 index 00000000..f40f45f5 --- /dev/null +++ b/benchmarks/conformance/previews/182-shadcn-radar-legend.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/183-shadcn-radar-lines-only.svg b/benchmarks/conformance/previews/183-shadcn-radar-lines-only.svg new file mode 100644 index 00000000..760f837e --- /dev/null +++ b/benchmarks/conformance/previews/183-shadcn-radar-lines-only.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/184-shadcn-radar-radius.svg b/benchmarks/conformance/previews/184-shadcn-radar-radius.svg new file mode 100644 index 00000000..a87d751e --- /dev/null +++ b/benchmarks/conformance/previews/184-shadcn-radar-radius.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/185-shadcn-radial-grid.svg b/benchmarks/conformance/previews/185-shadcn-radial-grid.svg new file mode 100644 index 00000000..15d0602e --- /dev/null +++ b/benchmarks/conformance/previews/185-shadcn-radial-grid.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/186-shadcn-radial-label.svg b/benchmarks/conformance/previews/186-shadcn-radial-label.svg new file mode 100644 index 00000000..4f7df098 --- /dev/null +++ b/benchmarks/conformance/previews/186-shadcn-radial-label.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/187-shadcn-radial-shape.svg b/benchmarks/conformance/previews/187-shadcn-radial-shape.svg new file mode 100644 index 00000000..6d49c9a9 --- /dev/null +++ b/benchmarks/conformance/previews/187-shadcn-radial-shape.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/188-shadcn-radial-simple.svg b/benchmarks/conformance/previews/188-shadcn-radial-simple.svg new file mode 100644 index 00000000..81767581 --- /dev/null +++ b/benchmarks/conformance/previews/188-shadcn-radial-simple.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/189-shadcn-radial-stacked.svg b/benchmarks/conformance/previews/189-shadcn-radial-stacked.svg new file mode 100644 index 00000000..76a8540c --- /dev/null +++ b/benchmarks/conformance/previews/189-shadcn-radial-stacked.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/190-shadcn-tooltip-default.svg b/benchmarks/conformance/previews/190-shadcn-tooltip-default.svg new file mode 100644 index 00000000..810d2005 --- /dev/null +++ b/benchmarks/conformance/previews/190-shadcn-tooltip-default.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/191-shadcn-tooltip-formatter.svg b/benchmarks/conformance/previews/191-shadcn-tooltip-formatter.svg new file mode 100644 index 00000000..a8d232a6 --- /dev/null +++ b/benchmarks/conformance/previews/191-shadcn-tooltip-formatter.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/192-shadcn-tooltip-icons.svg b/benchmarks/conformance/previews/192-shadcn-tooltip-icons.svg new file mode 100644 index 00000000..be8a12cd --- /dev/null +++ b/benchmarks/conformance/previews/192-shadcn-tooltip-icons.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/193-shadcn-tooltip-indicator-line.svg b/benchmarks/conformance/previews/193-shadcn-tooltip-indicator-line.svg new file mode 100644 index 00000000..59c1b3c3 --- /dev/null +++ b/benchmarks/conformance/previews/193-shadcn-tooltip-indicator-line.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/194-shadcn-tooltip-indicator-none.svg b/benchmarks/conformance/previews/194-shadcn-tooltip-indicator-none.svg new file mode 100644 index 00000000..56b60bf3 --- /dev/null +++ b/benchmarks/conformance/previews/194-shadcn-tooltip-indicator-none.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/195-shadcn-tooltip-label-custom.svg b/benchmarks/conformance/previews/195-shadcn-tooltip-label-custom.svg new file mode 100644 index 00000000..335ef090 --- /dev/null +++ b/benchmarks/conformance/previews/195-shadcn-tooltip-label-custom.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/196-shadcn-tooltip-label-formatter.svg b/benchmarks/conformance/previews/196-shadcn-tooltip-label-formatter.svg new file mode 100644 index 00000000..10d9e494 --- /dev/null +++ b/benchmarks/conformance/previews/196-shadcn-tooltip-label-formatter.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/197-shadcn-tooltip-label-none.svg b/benchmarks/conformance/previews/197-shadcn-tooltip-label-none.svg new file mode 100644 index 00000000..1c2a202d --- /dev/null +++ b/benchmarks/conformance/previews/197-shadcn-tooltip-label-none.svg @@ -0,0 +1 @@ + diff --git a/benchmarks/conformance/previews/26-diverging-likert.svg b/benchmarks/conformance/previews/26-diverging-likert.svg index f8d1b49e..c9739227 100644 --- a/benchmarks/conformance/previews/26-diverging-likert.svg +++ b/benchmarks/conformance/previews/26-diverging-likert.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/29-waterfall.svg b/benchmarks/conformance/previews/29-waterfall.svg index 474f3e50..b4a23fe4 100644 --- a/benchmarks/conformance/previews/29-waterfall.svg +++ b/benchmarks/conformance/previews/29-waterfall.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/59-grouped-reducer-bars.svg b/benchmarks/conformance/previews/59-grouped-reducer-bars.svg index 44556e69..f188f7e7 100644 --- a/benchmarks/conformance/previews/59-grouped-reducer-bars.svg +++ b/benchmarks/conformance/previews/59-grouped-reducer-bars.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/70-composed-chart.svg b/benchmarks/conformance/previews/70-composed-chart.svg index 9879d4cc..080f97c0 100644 --- a/benchmarks/conformance/previews/70-composed-chart.svg +++ b/benchmarks/conformance/previews/70-composed-chart.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/71-recharts-population-pyramid.svg b/benchmarks/conformance/previews/71-recharts-population-pyramid.svg index 6a83b0cb..6b6011eb 100644 --- a/benchmarks/conformance/previews/71-recharts-population-pyramid.svg +++ b/benchmarks/conformance/previews/71-recharts-population-pyramid.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/72-recharts-mixed-bars.svg b/benchmarks/conformance/previews/72-recharts-mixed-bars.svg index cc6aab20..58f8649c 100644 --- a/benchmarks/conformance/previews/72-recharts-mixed-bars.svg +++ b/benchmarks/conformance/previews/72-recharts-mixed-bars.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/84-pinned-nested-chart-tooltip.svg b/benchmarks/conformance/previews/84-pinned-nested-chart-tooltip.svg index bf0531e9..6351adfd 100644 --- a/benchmarks/conformance/previews/84-pinned-nested-chart-tooltip.svg +++ b/benchmarks/conformance/previews/84-pinned-nested-chart-tooltip.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/bar-grouped.svg b/benchmarks/conformance/previews/bar-grouped.svg index 1d760e86..a3094d30 100644 --- a/benchmarks/conformance/previews/bar-grouped.svg +++ b/benchmarks/conformance/previews/bar-grouped.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/bar-horizontal-ranking.svg b/benchmarks/conformance/previews/bar-horizontal-ranking.svg index f25241e7..6def6852 100644 --- a/benchmarks/conformance/previews/bar-horizontal-ranking.svg +++ b/benchmarks/conformance/previews/bar-horizontal-ranking.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/bar-stacked.svg b/benchmarks/conformance/previews/bar-stacked.svg index 3c4ab317..79e1b01d 100644 --- a/benchmarks/conformance/previews/bar-stacked.svg +++ b/benchmarks/conformance/previews/bar-stacked.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/bar-vertical-sorted.svg b/benchmarks/conformance/previews/bar-vertical-sorted.svg index 9fcce601..14fc2d56 100644 --- a/benchmarks/conformance/previews/bar-vertical-sorted.svg +++ b/benchmarks/conformance/previews/bar-vertical-sorted.svg @@ -1 +1 @@ - + diff --git a/benchmarks/conformance/previews/manifest.json b/benchmarks/conformance/previews/manifest.json index 343e2358..2fc9e828 100644 --- a/benchmarks/conformance/previews/manifest.json +++ b/benchmarks/conformance/previews/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "width": 288, "height": 192, - "sourceHash": "93452895c364c1dd0cc6b824137d35b8c43e0a022dca544e38dccea52b94fb88", + "sourceHash": "52f3f890587d74815d2bd2fb3c6ef09a97d84d333a76312ade628afb03062b9f", "assets": [ { "id": "01-line-gaps", @@ -26,23 +26,23 @@ }, { "id": "bar-vertical-sorted", - "sha256": "619bb54274e95dee9d0fdf744c31559b6fe9e48834960007ac829256d0deecca", - "bytes": 4129 + "sha256": "3962f4645adc980cab0b3ce322f77285aff219c03c0b241ecbd96503cb150110", + "bytes": 5117 }, { "id": "bar-horizontal-ranking", - "sha256": "8a330ef99ea0a41b827545435be244fa4b948bba4518500686ed95e53a80c320", - "bytes": 6010 + "sha256": "74035993b087ecc1153b3748ad8dbe71693734f9b7732b102734a1ac5a3587b4", + "bytes": 6314 }, { "id": "bar-grouped", - "sha256": "513ab2ff52f8e765ebe0fa2e25b1f85860872609a67e8accdd039ed0944237be", - "bytes": 1944 + "sha256": "ee36013e993d90facd783866688dfbad1ad8999c6988fba6b69d2b463adf78e8", + "bytes": 2172 }, { "id": "bar-stacked", - "sha256": "b3d39b0dbaf4753352d3b15fd319777102796262aba9385813f5d16595e25b0c", - "bytes": 11479 + "sha256": "ee00e20cf270e9a150cc24c943356a9f48ac5122ae3a619c2aab7d3ec7fd1770", + "bytes": 14215 }, { "id": "scatter-bubble", @@ -66,8 +66,8 @@ }, { "id": "13-interval-timeline", - "sha256": "490ab60daf095965b3da1a265dd0b6d1556442f280cdd7429c54f015a6f38604", - "bytes": 2263 + "sha256": "bb6baa0358b0572804dfe0a76f39d75fcd2bd4edb0264d1c1c2f298e44ce2b59", + "bytes": 2567 }, { "id": "14-error-bars", @@ -76,8 +76,8 @@ }, { "id": "15-boxplot", - "sha256": "48abc37817fe79e93a36ca02f82ca0d4f1b19089b66e2576030507443ef9e37f", - "bytes": 5119 + "sha256": "0d7c6d957037fc68d0e1b5bbd462aeb3468da527f7ba3a2fa9272abe77404c55", + "bytes": 5309 }, { "id": "16-lollipop", @@ -126,8 +126,8 @@ }, { "id": "26-diverging-likert", - "sha256": "cefa185aaa4c38ced8f3bcf23da42878c5b47eafc13a339f509214d9be144bc0", - "bytes": 4989 + "sha256": "2fb3dc56e74413d4ec923c43203a81096c67aa5e93ef92605aa0764df6faf1cd", + "bytes": 5939 }, { "id": "27-parallel-coordinates", @@ -141,8 +141,8 @@ }, { "id": "29-waterfall", - "sha256": "1a328367909686681a28df949e72fe9c9ec7437c5caf62958575894410ea9534", - "bytes": 2229 + "sha256": "32fba7866360d13d3d32b69185992ae378c2371665358292f27a9e0fd2d42b63", + "bytes": 2495 }, { "id": "30-slopegraph", @@ -271,8 +271,8 @@ }, { "id": "59-grouped-reducer-bars", - "sha256": "6b7f891770cdee0886b174576454f012e0a7285ea7986999e02001753eda56a8", - "bytes": 2074 + "sha256": "c188a770d7da9dac5a0ad9811ed3bc1e33c9fbee1dc06f98ba81d01a9918ac14", + "bytes": 2188 }, { "id": "60-lag-autocorrelation", @@ -306,18 +306,18 @@ }, { "id": "70-composed-chart", - "sha256": "cbce365589d75552c75a43d305516bc7737f102fec6d4858d188c43639a50f14", - "bytes": 3655 + "sha256": "6ea42d265d821403ae5823c58b6da307f5bdbd5a740bc2fb555587d288caa5a0", + "bytes": 3883 }, { "id": "71-recharts-population-pyramid", - "sha256": "98c5c30195522236c31885a9426bfe59dd1aa0ffb6e0a4ecdcacdc2b7109bb1c", - "bytes": 1967 + "sha256": "4db93bf6571865d12ebe3dba3fd6043d612149cbfa7acef632530cd86d7b6fc4", + "bytes": 2195 }, { "id": "72-recharts-mixed-bars", - "sha256": "20633e483b48ec10866b44800e43d6c8b4ac154b73d3f8748cc1647e83ee9e6c", - "bytes": 4251 + "sha256": "d44979007445584f8e19cb01fd8b8bd252d28df099ac286ba8da7fca2ad48db1", + "bytes": 5049 }, { "id": "73-many-point-scatter", @@ -371,8 +371,8 @@ }, { "id": "84-pinned-nested-chart-tooltip", - "sha256": "8b9cb3866d0069a78f61d210e3c6977ab945496163d36e3b590149b3bf25a6cc", - "bytes": 21966 + "sha256": "dd61cb71069007281cb2728080ed65cf870af83c307d2645c30523d1d2c111fe", + "bytes": 23030 }, { "id": "85-scrollable-resource-lanes", @@ -516,13 +516,13 @@ }, { "id": "112-motion-entrance", - "sha256": "0296b7d31ec88ae89163516d5ad396ee4c564ae415606f5c58e471a35ac822c1", - "bytes": 2437 + "sha256": "7c33d72d304faedb4597e421da0b5770da1ab11d07098ab387965e974f2c2847", + "bytes": 2741 }, { "id": "113-motion-updates", - "sha256": "1492bbf6f2927727597251d0a2ab01551db65c5a8ae1c3b791bd014492237236", - "bytes": 2435 + "sha256": "d02e0db3f8790c84cec3c419e3070e72256b6a3eca227ad3166cf4618b8f7d3f", + "bytes": 2739 }, { "id": "114-spring-line-motion", @@ -531,8 +531,8 @@ }, { "id": "115-definition-motion", - "sha256": "c6bced9e93514edc33a3887ed0d6fd57075123aa7c73d7ad11f75fd4abdaaeb3", - "bytes": 6012 + "sha256": "707e4511b4f8290a8e23995fce9b280f9c84165d2eccb630904757aed0f305b6", + "bytes": 6240 }, { "id": "116-geometry-morph", @@ -551,8 +551,8 @@ }, { "id": "119-stacked-bar-band-cursor", - "sha256": "5044b45076c324852f65d30f7c60369caa9c9abbae769ce73cf7176b81fcd898", - "bytes": 7610 + "sha256": "3dba265ef8327ed8551d3a1f0bc53eecdc1deab8e99e7c0960b64666021dd39a", + "bytes": 8522 }, { "id": "120-themed-interactive-area", @@ -561,8 +561,8 @@ }, { "id": "121-active-bar-dashboard", - "sha256": "fe4a57c4e21f73f6e270d0746c76e7a9b1c72ad441477bbe5cbf0a328be073f7", - "bytes": 11458 + "sha256": "9c313c465979c8ff4a80f02904af955593d6bc0a582b9e19c2d79ab7deb957a1", + "bytes": 12370 }, { "id": "122-premium-kpi-sparklines", @@ -588,6 +588,361 @@ "id": "126-drillable-sunburst", "sha256": "163fd17e46238cb53209a371477b7bc78e196ccd66a385ee3ed2f6b60f675fc6", "bytes": 9111 + }, + { + "id": "127-shadcn-dashboard", + "sha256": "145c00805997cfac3b00f1380f93d7de82c5d82266992f28cda7f24145565fd8", + "bytes": 17838 + }, + { + "id": "128-shadcn-bar-multiple", + "sha256": "fdde89d276f842819c0d7b4ec8b29177f45bc31b44b530f712e7bbde86d9ef09", + "bytes": 3621 + }, + { + "id": "129-shadcn-pie-donut-text", + "sha256": "cea4adc3f89d3008744f5ec9e6513cc6d713c284c81e65aee974d8896b3dc66a", + "bytes": 3452 + }, + { + "id": "130-shadcn-radar-multiple", + "sha256": "21c4c3bdcb494b53bfc32722c8d34a0ad708bb9fef266cfafa619d6c4e7f1502", + "bytes": 5245 + }, + { + "id": "131-shadcn-radial-text", + "sha256": "a5152ef6e25530cb6b2aab4270ff27183b12603d47a81735c153c30da8aee3eb", + "bytes": 2796 + }, + { + "id": "132-shadcn-tooltip-advanced", + "sha256": "b7d813f5d1514872c2af10cdfccf2de4314f685b996de3b0e3e13295ff3fc61c", + "bytes": 3699 + }, + { + "id": "133-shadcn-area-axes", + "sha256": "046b6c5ded28e024911f19f5e876eff0aa5b68afcdcc52cce21cb65febaeeae6", + "bytes": 4460 + }, + { + "id": "134-shadcn-area-default", + "sha256": "9979dae9a4d4baec3c4e0b8290ecbe8b33cb65cb37e0a83805767901b14a49f1", + "bytes": 3229 + }, + { + "id": "135-shadcn-area-gradient", + "sha256": "cdf70c7fdba41e1a0157f306b7a62c84a312fbe46a86ee1efe835295ffed4d87", + "bytes": 4623 + }, + { + "id": "136-shadcn-area-icons", + "sha256": "bac5bc77762ca413a476d2bd1ff5fa5333d564e9b764ca5ab5fcc04496cd079e", + "bytes": 3790 + }, + { + "id": "137-shadcn-area-interactive", + "sha256": "813af06a599b468956f0484eecef9ee721095ab39b25db7b4c0c5d3348bd97bd", + "bytes": 19371 + }, + { + "id": "138-shadcn-area-legend", + "sha256": "7a3738fabf8b69783d372e5c4b36d30831e7aaadae5a9c0550abb2cb1231aa36", + "bytes": 3791 + }, + { + "id": "139-shadcn-area-linear", + "sha256": "6ec9dbb779f7f9813113e6fddacc2580bd516f4a962e55465aae5594e87f1cb2", + "bytes": 3022 + }, + { + "id": "140-shadcn-area-stacked-expand", + "sha256": "58ce0ba212267e6be27e6fe533c6cc0b7d6c3c158ac0a01d96029b1141eb8559", + "bytes": 4441 + }, + { + "id": "141-shadcn-area-stacked", + "sha256": "6043046de64945431315a6689651e362b994a6a309b188377b429c9914831296", + "bytes": 3792 + }, + { + "id": "142-shadcn-area-step", + "sha256": "51fecea4383f25623f676bde76ce0a04fb34d15547c7adcea6eac7ddeb290871", + "bytes": 3139 + }, + { + "id": "143-shadcn-bar-active", + "sha256": "c9c177369efe51d6aa0a5680af4cee908abe8c37ca7e3b3319c52c4c531cc787", + "bytes": 3592 + }, + { + "id": "144-shadcn-bar-default", + "sha256": "e478cf9890e61fd9459324b8802acabd002cf64aed8c62a38430768865e6e3e1", + "bytes": 3866 + }, + { + "id": "145-shadcn-bar-horizontal", + "sha256": "d3e127ac9c48a39292c61547c8188d23a500d79f10634bb03f83b71e6ff461b4", + "bytes": 3586 + }, + { + "id": "146-shadcn-bar-interactive", + "sha256": "9c7354c654e08d4aa93d8d38eb744d0bd4fdaf4b080a5cdad867b89ffe2bc06b", + "bytes": 20516 + }, + { + "id": "147-shadcn-bar-label-custom", + "sha256": "740689a87d611637eaa37674389e947c4458b9de0b8299c561fb378ac908cbb5", + "bytes": 5118 + }, + { + "id": "148-shadcn-bar-label", + "sha256": "d3fa1c26b13e93fe24bc120caa8a6a35ab93f002a644585cef5e516fba9450f5", + "bytes": 5012 + }, + { + "id": "149-shadcn-bar-mixed", + "sha256": "b09ee46e40e8924d9c1caf13b05b703ec226feaf036bc6549d29b3b38e13acd7", + "bytes": 3189 + }, + { + "id": "150-shadcn-bar-negative", + "sha256": "1adda58583b2f09357886d96736d176e6371ce5cfad863145b11cad0aa5e8e02", + "bytes": 4249 + }, + { + "id": "151-shadcn-bar-stacked", + "sha256": "4a1a335cb6d876dc1408ac6135d4be776f3d760c093d7833d3448839cc617370", + "bytes": 5138 + }, + { + "id": "152-shadcn-line-default", + "sha256": "ca54836082dfbbc13d5d7b32e40d6a90fee1f044ee133aa6f1822d374ae35e99", + "bytes": 3165 + }, + { + "id": "153-shadcn-line-dots-colors", + "sha256": "286b3ddd89651b558ebe6086489685d1b2fd6ced532c8e134ad97a7b90349946", + "bytes": 2806 + }, + { + "id": "154-shadcn-line-dots-custom", + "sha256": "9e3bf2816ee3eb817c89957aebb652b04c8fb1b1503dec15b975b3f39c471367", + "bytes": 5715 + }, + { + "id": "155-shadcn-line-dots", + "sha256": "ff111e61841d72c0810f4fee041e0d240d697aae8c1a0198c49534978d2740f6", + "bytes": 4396 + }, + { + "id": "156-shadcn-line-interactive", + "sha256": "018af8f456cc6984c0d4798a61eb72f8a9bc55ec690ee9a150f9fbcd08e7b6f2", + "bytes": 6603 + }, + { + "id": "157-shadcn-line-label-custom", + "sha256": "f71706e451ff05384b0d4c5880eee10e12c5e4864e716b481e8927892c70868c", + "bytes": 3173 + }, + { + "id": "158-shadcn-line-label", + "sha256": "8638f5aa26825f8e0c4b78f7821e8cf590d28823930a5d6c1b309ae671f52afd", + "bytes": 4461 + }, + { + "id": "159-shadcn-line-linear", + "sha256": "b67b8058ea00dd10f3c63821da5cbb2ece9f625c9c89be3693a1802b2b171c63", + "bytes": 3051 + }, + { + "id": "160-shadcn-line-multiple", + "sha256": "3ae0b06d7ab7530eb4b04d3b8ee0b2d1124aa6bf2dfe951810733122206a41b1", + "bytes": 3701 + }, + { + "id": "161-shadcn-line-step", + "sha256": "244345de610d3e3e848223748556807436d9193441fd4449e2f16f9f5b807847", + "bytes": 3121 + }, + { + "id": "162-shadcn-pie-donut-active", + "sha256": "fef7a72cf0e04ed91669ad9ef9d9ad8272c2a93b213ef7da8d354fb1e3bf1437", + "bytes": 3093 + }, + { + "id": "163-shadcn-pie-donut", + "sha256": "d91273f7fa22f039b21d1922c0e2e447b116302e1d0859650fe1527455d5decd", + "bytes": 2709 + }, + { + "id": "164-shadcn-pie-interactive", + "sha256": "15f2b56f8d92acd04700644cb55fe74ffa9124686e7fa81f41f8b545f0eab84b", + "bytes": 4065 + }, + { + "id": "165-shadcn-pie-label-custom", + "sha256": "f6cd81e25143da67ea82488b918f68bf117ddc2f3ef216f63f55f0fd65bb037a", + "bytes": 3660 + }, + { + "id": "166-shadcn-pie-label-list", + "sha256": "55a22fd89c14fc37aaa8e72f75894af5b809974d95571c240b9d585f76fd132f", + "bytes": 3771 + }, + { + "id": "167-shadcn-pie-label", + "sha256": "303190284a37a4a4556c6dc3ae31f0a7a0d6df4e984c39900ae21982bdb81ed7", + "bytes": 4627 + }, + { + "id": "168-shadcn-pie-legend", + "sha256": "e28f35b7c630f398c1137057f29a8614fffa0ac95773094be5a8cecf0ecf1efd", + "bytes": 2548 + }, + { + "id": "169-shadcn-pie-separator-none", + "sha256": "f556f261057af01a264b7f6c0b93bc14e15d1e0668f6d895fafc66e9f2a5b1c5", + "bytes": 2556 + }, + { + "id": "170-shadcn-pie-simple", + "sha256": "9f06d110eeab69d9deb07086c24948b16335cf5b77867ac44ace4a275c05d708", + "bytes": 2539 + }, + { + "id": "171-shadcn-pie-stacked", + "sha256": "3f5e43f29f4c875bbc5f74947ce72b16597be63bdc57f628d15c230b36832f70", + "bytes": 4038 + }, + { + "id": "172-shadcn-radar-default", + "sha256": "3a50f70c70b90839b4841872d6faa26b438cb5e19530038715c97026ff2f200b", + "bytes": 4953 + }, + { + "id": "173-shadcn-radar-dots", + "sha256": "0066b32e4654adf8e5286cef79c2d2199dbd3ad70bc58079863f039526ed58b1", + "bytes": 5861 + }, + { + "id": "174-shadcn-radar-grid-circle-fill", + "sha256": "55e33e6590390b15776757de6477e03d86234eb79b4fd8f01fd9fbb12b64b311", + "bytes": 5106 + }, + { + "id": "175-shadcn-radar-grid-circle-no-lines", + "sha256": "255f9cd98d662773e31a36d00a9da5dafe47581a06ec7ddaecc235dd551f0357", + "bytes": 5827 + }, + { + "id": "176-shadcn-radar-grid-circle", + "sha256": "15f642113d6c1088190ae35558560edbf81cf57ba8aa0c98f3da099a34d0fe83", + "bytes": 5828 + }, + { + "id": "177-shadcn-radar-grid-custom", + "sha256": "ab64a970c68964c5c82b2c5e2ad4f822dfced12b4871bddc74e4b3b27f387ee8", + "bytes": 4300 + }, + { + "id": "178-shadcn-radar-grid-fill", + "sha256": "79d2b691e7b221aa18ea2001e43cf90a7a91f2be0f276d168f8a16e07c763f27", + "bytes": 5139 + }, + { + "id": "179-shadcn-radar-grid-none", + "sha256": "25be27bc520446d2b5fde1f2fa2b1d018949aa693e2a01963b2e7d6f7c055642", + "bytes": 4879 + }, + { + "id": "180-shadcn-radar-icons", + "sha256": "a9627abd336749b82f0af94d6fa713df90f96b5048ef782d50c03383dd1323ad", + "bytes": 5390 + }, + { + "id": "181-shadcn-radar-label-custom", + "sha256": "f323ed67d5e38352a141706d4de90ac07dfc3a2e611b4a2fee577854c8aee3c8", + "bytes": 6755 + }, + { + "id": "182-shadcn-radar-legend", + "sha256": "6d28b45c6cc6627d091f5a71b8f421ffb1e57a705b3f753c1078c056043aa654", + "bytes": 5391 + }, + { + "id": "183-shadcn-radar-lines-only", + "sha256": "7bc623c11129957e31e27cfed662d95e0d7b9dc44d77df5f9f77716f622c5605", + "bytes": 5388 + }, + { + "id": "184-shadcn-radar-radius", + "sha256": "a9b80a463bbf98680c1076087263a43f21def1d5987c091bef32f3339c93c379", + "bytes": 4382 + }, + { + "id": "185-shadcn-radial-grid", + "sha256": "bce3f5b2cc278477be86183283270e1c9b8efa5c39497d3058b547c8e4a13833", + "bytes": 5069 + }, + { + "id": "186-shadcn-radial-label", + "sha256": "3f710d47b36556a296f0679b0fb7080437c6d12fbf0c50be2b877b8df39903fd", + "bytes": 5490 + }, + { + "id": "187-shadcn-radial-shape", + "sha256": "f3e7c9624f9a2492a099259878b35b4d2073c59d8fc8fa939bede873d884dfd7", + "bytes": 2691 + }, + { + "id": "188-shadcn-radial-simple", + "sha256": "25860b8e806941ccff530415e52d2a35cfe06028c70e7d0be02c78828e58de6a", + "bytes": 4006 + }, + { + "id": "189-shadcn-radial-stacked", + "sha256": "b20a43a82021e8463533621105459b524656603977b25d5727408e9399981b8d", + "bytes": 2817 + }, + { + "id": "190-shadcn-tooltip-default", + "sha256": "2f9be5d22ad3267642ec409fd0de635caa3953428e2a4c116e5b201983c0726f", + "bytes": 4864 + }, + { + "id": "191-shadcn-tooltip-formatter", + "sha256": "200e12d7fc4e2ab18d7c5f74036a2616bb36e6c210a8b0db68b94bf018ad2ef8", + "bytes": 4866 + }, + { + "id": "192-shadcn-tooltip-icons", + "sha256": "413952c585d41eca5d1aec1a6c45b6e5a85a8235e58e7d189da5e6463e7d9482", + "bytes": 4862 + }, + { + "id": "193-shadcn-tooltip-indicator-line", + "sha256": "6127c1ebcce557c8ce777282e334185c946a1f67e94e7011b805e6bec186881f", + "bytes": 4871 + }, + { + "id": "194-shadcn-tooltip-indicator-none", + "sha256": "bdd685138bebafb657f01d0b227975db159806480689dda5c0d7b204a776dd77", + "bytes": 4869 + }, + { + "id": "195-shadcn-tooltip-label-custom", + "sha256": "f15b0381b76c7996e510f662e9bffc7d9d0f9aac508d231973933057d63ab4ef", + "bytes": 4869 + }, + { + "id": "196-shadcn-tooltip-label-formatter", + "sha256": "ec03b2a32dbb8ce07faf36875e615476a78c25ead40471872e152e81eeb9a0ff", + "bytes": 4872 + }, + { + "id": "197-shadcn-tooltip-label-none", + "sha256": "e1c7ff8f10d6e164ac31bee9085f75f22e5001ef94626facbf9306d737148954", + "bytes": 4865 } ] } diff --git a/benchmarks/conformance/shadcn/README.md b/benchmarks/conformance/shadcn/README.md new file mode 100644 index 00000000..2e547d69 --- /dev/null +++ b/benchmarks/conformance/shadcn/README.md @@ -0,0 +1,29 @@ +# shadcn chart mirror + +`catalog.json` pins the canonical 70-example shadcn chart catalog by commit and +Git blob SHA. A `localCaseId` means that upstream example has a TanStack Charts +comparison case in `../cases`. + +```sh +pnpm shadcn:catalog:check +pnpm shadcn:catalog:upstream +``` + +The first command is offline and validates the complete inventory plus all 70 +local mappings. The second compares the pinned blobs with shadcn's current +`main` tree and reports changed, added, and removed paths. + +Run `pnpm shadcn:catalog:generate` after adding an upstream manifest entry. It +assigns a stable case ID and writes the metadata plus thin TanStack and Recharts +entry modules. Existing generated IDs remain stable. + +The catalog uses seven shared family implementations, shared pinned data, and a +shared shadcn card shell. Thin `recharts.ts` and `tanstack.ts` entry points keep +each example addressable and the measured bundle graphs isolated. The five +original spike cases remain standalone parity fixtures. + +Run any reviewed family slice through the quick parity profile with: + +```sh +pnpm conformance:quick -- --case=128-shadcn-bar-multiple,129-shadcn-pie-donut-text,130-shadcn-radar-multiple,131-shadcn-radial-text,132-shadcn-tooltip-advanced +``` diff --git a/benchmarks/conformance/shadcn/area-interactive-data.json b/benchmarks/conformance/shadcn/area-interactive-data.json new file mode 100644 index 00000000..a522abc4 --- /dev/null +++ b/benchmarks/conformance/shadcn/area-interactive-data.json @@ -0,0 +1,457 @@ +[ + { + "date": "2024-04-01", + "desktop": 222, + "mobile": 150 + }, + { + "date": "2024-04-02", + "desktop": 97, + "mobile": 180 + }, + { + "date": "2024-04-03", + "desktop": 167, + "mobile": 120 + }, + { + "date": "2024-04-04", + "desktop": 242, + "mobile": 260 + }, + { + "date": "2024-04-05", + "desktop": 373, + "mobile": 290 + }, + { + "date": "2024-04-06", + "desktop": 301, + "mobile": 340 + }, + { + "date": "2024-04-07", + "desktop": 245, + "mobile": 180 + }, + { + "date": "2024-04-08", + "desktop": 409, + "mobile": 320 + }, + { + "date": "2024-04-09", + "desktop": 59, + "mobile": 110 + }, + { + "date": "2024-04-10", + "desktop": 261, + "mobile": 190 + }, + { + "date": "2024-04-11", + "desktop": 327, + "mobile": 350 + }, + { + "date": "2024-04-12", + "desktop": 292, + "mobile": 210 + }, + { + "date": "2024-04-13", + "desktop": 342, + "mobile": 380 + }, + { + "date": "2024-04-14", + "desktop": 137, + "mobile": 220 + }, + { + "date": "2024-04-15", + "desktop": 120, + "mobile": 170 + }, + { + "date": "2024-04-16", + "desktop": 138, + "mobile": 190 + }, + { + "date": "2024-04-17", + "desktop": 446, + "mobile": 360 + }, + { + "date": "2024-04-18", + "desktop": 364, + "mobile": 410 + }, + { + "date": "2024-04-19", + "desktop": 243, + "mobile": 180 + }, + { + "date": "2024-04-20", + "desktop": 89, + "mobile": 150 + }, + { + "date": "2024-04-21", + "desktop": 137, + "mobile": 200 + }, + { + "date": "2024-04-22", + "desktop": 224, + "mobile": 170 + }, + { + "date": "2024-04-23", + "desktop": 138, + "mobile": 230 + }, + { + "date": "2024-04-24", + "desktop": 387, + "mobile": 290 + }, + { + "date": "2024-04-25", + "desktop": 215, + "mobile": 250 + }, + { + "date": "2024-04-26", + "desktop": 75, + "mobile": 130 + }, + { + "date": "2024-04-27", + "desktop": 383, + "mobile": 420 + }, + { + "date": "2024-04-28", + "desktop": 122, + "mobile": 180 + }, + { + "date": "2024-04-29", + "desktop": 315, + "mobile": 240 + }, + { + "date": "2024-04-30", + "desktop": 454, + "mobile": 380 + }, + { + "date": "2024-05-01", + "desktop": 165, + "mobile": 220 + }, + { + "date": "2024-05-02", + "desktop": 293, + "mobile": 310 + }, + { + "date": "2024-05-03", + "desktop": 247, + "mobile": 190 + }, + { + "date": "2024-05-04", + "desktop": 385, + "mobile": 420 + }, + { + "date": "2024-05-05", + "desktop": 481, + "mobile": 390 + }, + { + "date": "2024-05-06", + "desktop": 498, + "mobile": 520 + }, + { + "date": "2024-05-07", + "desktop": 388, + "mobile": 300 + }, + { + "date": "2024-05-08", + "desktop": 149, + "mobile": 210 + }, + { + "date": "2024-05-09", + "desktop": 227, + "mobile": 180 + }, + { + "date": "2024-05-10", + "desktop": 293, + "mobile": 330 + }, + { + "date": "2024-05-11", + "desktop": 335, + "mobile": 270 + }, + { + "date": "2024-05-12", + "desktop": 197, + "mobile": 240 + }, + { + "date": "2024-05-13", + "desktop": 197, + "mobile": 160 + }, + { + "date": "2024-05-14", + "desktop": 448, + "mobile": 490 + }, + { + "date": "2024-05-15", + "desktop": 473, + "mobile": 380 + }, + { + "date": "2024-05-16", + "desktop": 338, + "mobile": 400 + }, + { + "date": "2024-05-17", + "desktop": 499, + "mobile": 420 + }, + { + "date": "2024-05-18", + "desktop": 315, + "mobile": 350 + }, + { + "date": "2024-05-19", + "desktop": 235, + "mobile": 180 + }, + { + "date": "2024-05-20", + "desktop": 177, + "mobile": 230 + }, + { + "date": "2024-05-21", + "desktop": 82, + "mobile": 140 + }, + { + "date": "2024-05-22", + "desktop": 81, + "mobile": 120 + }, + { + "date": "2024-05-23", + "desktop": 252, + "mobile": 290 + }, + { + "date": "2024-05-24", + "desktop": 294, + "mobile": 220 + }, + { + "date": "2024-05-25", + "desktop": 201, + "mobile": 250 + }, + { + "date": "2024-05-26", + "desktop": 213, + "mobile": 170 + }, + { + "date": "2024-05-27", + "desktop": 420, + "mobile": 460 + }, + { + "date": "2024-05-28", + "desktop": 233, + "mobile": 190 + }, + { + "date": "2024-05-29", + "desktop": 78, + "mobile": 130 + }, + { + "date": "2024-05-30", + "desktop": 340, + "mobile": 280 + }, + { + "date": "2024-05-31", + "desktop": 178, + "mobile": 230 + }, + { + "date": "2024-06-01", + "desktop": 178, + "mobile": 200 + }, + { + "date": "2024-06-02", + "desktop": 470, + "mobile": 410 + }, + { + "date": "2024-06-03", + "desktop": 103, + "mobile": 160 + }, + { + "date": "2024-06-04", + "desktop": 439, + "mobile": 380 + }, + { + "date": "2024-06-05", + "desktop": 88, + "mobile": 140 + }, + { + "date": "2024-06-06", + "desktop": 294, + "mobile": 250 + }, + { + "date": "2024-06-07", + "desktop": 323, + "mobile": 370 + }, + { + "date": "2024-06-08", + "desktop": 385, + "mobile": 320 + }, + { + "date": "2024-06-09", + "desktop": 438, + "mobile": 480 + }, + { + "date": "2024-06-10", + "desktop": 155, + "mobile": 200 + }, + { + "date": "2024-06-11", + "desktop": 92, + "mobile": 150 + }, + { + "date": "2024-06-12", + "desktop": 492, + "mobile": 420 + }, + { + "date": "2024-06-13", + "desktop": 81, + "mobile": 130 + }, + { + "date": "2024-06-14", + "desktop": 426, + "mobile": 380 + }, + { + "date": "2024-06-15", + "desktop": 307, + "mobile": 350 + }, + { + "date": "2024-06-16", + "desktop": 371, + "mobile": 310 + }, + { + "date": "2024-06-17", + "desktop": 475, + "mobile": 520 + }, + { + "date": "2024-06-18", + "desktop": 107, + "mobile": 170 + }, + { + "date": "2024-06-19", + "desktop": 341, + "mobile": 290 + }, + { + "date": "2024-06-20", + "desktop": 408, + "mobile": 450 + }, + { + "date": "2024-06-21", + "desktop": 169, + "mobile": 210 + }, + { + "date": "2024-06-22", + "desktop": 317, + "mobile": 270 + }, + { + "date": "2024-06-23", + "desktop": 480, + "mobile": 530 + }, + { + "date": "2024-06-24", + "desktop": 132, + "mobile": 180 + }, + { + "date": "2024-06-25", + "desktop": 141, + "mobile": 190 + }, + { + "date": "2024-06-26", + "desktop": 434, + "mobile": 380 + }, + { + "date": "2024-06-27", + "desktop": 448, + "mobile": 490 + }, + { + "date": "2024-06-28", + "desktop": 149, + "mobile": 200 + }, + { + "date": "2024-06-29", + "desktop": 103, + "mobile": 160 + }, + { + "date": "2024-06-30", + "desktop": 446, + "mobile": 400 + } +] diff --git a/benchmarks/conformance/shadcn/catalog.json b/benchmarks/conformance/shadcn/catalog.json new file mode 100644 index 00000000..4c99ce98 --- /dev/null +++ b/benchmarks/conformance/shadcn/catalog.json @@ -0,0 +1,502 @@ +{ + "schemaVersion": 1, + "upstream": { + "repository": "shadcn-ui/ui", + "commit": "a85299a9edd2a961e32f01ced86963a852652bd2", + "pathRoot": "apps/v4/registry/new-york-v4/charts", + "registryPath": "apps/v4/registry/new-york-v4/charts/_registry.ts", + "registryBlobSha": "deeda2cba61a75fbfbb16437d56630d8785f4d44" + }, + "cases": [ + { + "name": "chart-area-axes", + "family": "area", + "path": "apps/v4/registry/new-york-v4/charts/chart-area-axes.tsx", + "blobSha": "ee429429fbb7a8afeb411a386140e6406e08e06d", + "localCaseId": "133-shadcn-area-axes" + }, + { + "name": "chart-area-default", + "family": "area", + "path": "apps/v4/registry/new-york-v4/charts/chart-area-default.tsx", + "blobSha": "3dfbcd3fc3c2f6861f4bb4211155710fec609b50", + "localCaseId": "134-shadcn-area-default" + }, + { + "name": "chart-area-gradient", + "family": "area", + "path": "apps/v4/registry/new-york-v4/charts/chart-area-gradient.tsx", + "blobSha": "648355c436faf0f3bb62d8954b5567a11b80424d", + "localCaseId": "135-shadcn-area-gradient" + }, + { + "name": "chart-area-icons", + "family": "area", + "path": "apps/v4/registry/new-york-v4/charts/chart-area-icons.tsx", + "blobSha": "295160e905f7fc05fcf138b9cc88efcc806574b2", + "localCaseId": "136-shadcn-area-icons" + }, + { + "name": "chart-area-interactive", + "family": "area", + "path": "apps/v4/registry/new-york-v4/charts/chart-area-interactive.tsx", + "blobSha": "8f6e69d40971e901b8b0d28b31a3dcf8580ac907", + "localCaseId": "137-shadcn-area-interactive" + }, + { + "name": "chart-area-legend", + "family": "area", + "path": "apps/v4/registry/new-york-v4/charts/chart-area-legend.tsx", + "blobSha": "eddd6c255019fed830a3d58582afff55fcff7664", + "localCaseId": "138-shadcn-area-legend" + }, + { + "name": "chart-area-linear", + "family": "area", + "path": "apps/v4/registry/new-york-v4/charts/chart-area-linear.tsx", + "blobSha": "5e2a36507de6c607f8c5639be11d0ea1179a5e2c", + "localCaseId": "139-shadcn-area-linear" + }, + { + "name": "chart-area-stacked-expand", + "family": "area", + "path": "apps/v4/registry/new-york-v4/charts/chart-area-stacked-expand.tsx", + "blobSha": "95a7851529d85cb9a7c171a1a56167752c54c2b1", + "localCaseId": "140-shadcn-area-stacked-expand" + }, + { + "name": "chart-area-stacked", + "family": "area", + "path": "apps/v4/registry/new-york-v4/charts/chart-area-stacked.tsx", + "blobSha": "92dc3edd1417e0d6c1d99d5502414995f1060ad9", + "localCaseId": "141-shadcn-area-stacked" + }, + { + "name": "chart-area-step", + "family": "area", + "path": "apps/v4/registry/new-york-v4/charts/chart-area-step.tsx", + "blobSha": "2c2dd581772d355c962924a00fd130a8c9b92a04", + "localCaseId": "142-shadcn-area-step" + }, + { + "name": "chart-bar-active", + "family": "bar", + "path": "apps/v4/registry/new-york-v4/charts/chart-bar-active.tsx", + "blobSha": "f76c5812e5e4f1beb32a2ec3fd51cafdcaa67870", + "localCaseId": "143-shadcn-bar-active" + }, + { + "name": "chart-bar-default", + "family": "bar", + "path": "apps/v4/registry/new-york-v4/charts/chart-bar-default.tsx", + "blobSha": "dc921b04f9f34bede4a58dae8e308b582b9d6c4a", + "localCaseId": "144-shadcn-bar-default" + }, + { + "name": "chart-bar-horizontal", + "family": "bar", + "path": "apps/v4/registry/new-york-v4/charts/chart-bar-horizontal.tsx", + "blobSha": "2016a9785c6da00c39e86bdd014dc6d4f0c48d70", + "localCaseId": "145-shadcn-bar-horizontal" + }, + { + "name": "chart-bar-interactive", + "family": "bar", + "path": "apps/v4/registry/new-york-v4/charts/chart-bar-interactive.tsx", + "blobSha": "124e381442e7872f5b49b75afcbfff09484f747f", + "localCaseId": "146-shadcn-bar-interactive" + }, + { + "name": "chart-bar-label-custom", + "family": "bar", + "path": "apps/v4/registry/new-york-v4/charts/chart-bar-label-custom.tsx", + "blobSha": "bde2b2bfd8c5830824a2326ed44d3eac9af16101", + "localCaseId": "147-shadcn-bar-label-custom" + }, + { + "name": "chart-bar-label", + "family": "bar", + "path": "apps/v4/registry/new-york-v4/charts/chart-bar-label.tsx", + "blobSha": "390e1b03282fdbb51e86f1f19827290441bc8dce", + "localCaseId": "148-shadcn-bar-label" + }, + { + "name": "chart-bar-mixed", + "family": "bar", + "path": "apps/v4/registry/new-york-v4/charts/chart-bar-mixed.tsx", + "blobSha": "bac07d75f0ac98f892ab4618471e085a2b1f1512", + "localCaseId": "149-shadcn-bar-mixed" + }, + { + "name": "chart-bar-multiple", + "family": "bar", + "path": "apps/v4/registry/new-york-v4/charts/chart-bar-multiple.tsx", + "blobSha": "99b894f293093e1a88bab71ed1f3d5097d0e205c", + "localCaseId": "128-shadcn-bar-multiple" + }, + { + "name": "chart-bar-negative", + "family": "bar", + "path": "apps/v4/registry/new-york-v4/charts/chart-bar-negative.tsx", + "blobSha": "982bccdb9cd4ebb5010a10cbfd43bfff876dd94c", + "localCaseId": "150-shadcn-bar-negative" + }, + { + "name": "chart-bar-stacked", + "family": "bar", + "path": "apps/v4/registry/new-york-v4/charts/chart-bar-stacked.tsx", + "blobSha": "99ceda588e9b49e54f9c5e41c267314cc73d6dce", + "localCaseId": "151-shadcn-bar-stacked" + }, + { + "name": "chart-line-default", + "family": "line", + "path": "apps/v4/registry/new-york-v4/charts/chart-line-default.tsx", + "blobSha": "669ebf5022ac06d94a016b84dc4706d27d19595f", + "localCaseId": "152-shadcn-line-default" + }, + { + "name": "chart-line-dots-colors", + "family": "line", + "path": "apps/v4/registry/new-york-v4/charts/chart-line-dots-colors.tsx", + "blobSha": "451c24fd34f9ccac3da83e2fb2c24eeefaa0aead", + "localCaseId": "153-shadcn-line-dots-colors" + }, + { + "name": "chart-line-dots-custom", + "family": "line", + "path": "apps/v4/registry/new-york-v4/charts/chart-line-dots-custom.tsx", + "blobSha": "355f23a1071b60c8c4563320e79e9f30ff36ec3d", + "localCaseId": "154-shadcn-line-dots-custom" + }, + { + "name": "chart-line-dots", + "family": "line", + "path": "apps/v4/registry/new-york-v4/charts/chart-line-dots.tsx", + "blobSha": "0d1df7b6e6ef141f599848faa14b0e2f342a21a3", + "localCaseId": "155-shadcn-line-dots" + }, + { + "name": "chart-line-interactive", + "family": "line", + "path": "apps/v4/registry/new-york-v4/charts/chart-line-interactive.tsx", + "blobSha": "3feeb25b0d4452fb35e57299997b6e089e671460", + "localCaseId": "156-shadcn-line-interactive" + }, + { + "name": "chart-line-label-custom", + "family": "line", + "path": "apps/v4/registry/new-york-v4/charts/chart-line-label-custom.tsx", + "blobSha": "14aaa7f92af490d14ab6e4e36c112ed949c805b4", + "localCaseId": "157-shadcn-line-label-custom" + }, + { + "name": "chart-line-label", + "family": "line", + "path": "apps/v4/registry/new-york-v4/charts/chart-line-label.tsx", + "blobSha": "7a333fc3a29b097f9918e7a5ead51a81073fe800", + "localCaseId": "158-shadcn-line-label" + }, + { + "name": "chart-line-linear", + "family": "line", + "path": "apps/v4/registry/new-york-v4/charts/chart-line-linear.tsx", + "blobSha": "e0c2d34ac8328611c6d572b19043ce95ca00f64d", + "localCaseId": "159-shadcn-line-linear" + }, + { + "name": "chart-line-multiple", + "family": "line", + "path": "apps/v4/registry/new-york-v4/charts/chart-line-multiple.tsx", + "blobSha": "fb162420976cc77ce7a7fa3e36b7fdf63a9ab18e", + "localCaseId": "160-shadcn-line-multiple" + }, + { + "name": "chart-line-step", + "family": "line", + "path": "apps/v4/registry/new-york-v4/charts/chart-line-step.tsx", + "blobSha": "5dce7fcbbcbc32f34441e3793bdbe152d91e45f7", + "localCaseId": "161-shadcn-line-step" + }, + { + "name": "chart-pie-donut-active", + "family": "pie", + "path": "apps/v4/registry/new-york-v4/charts/chart-pie-donut-active.tsx", + "blobSha": "b2e41b5ebbd9fe392435e1f3417aecf2df35ae8b", + "localCaseId": "162-shadcn-pie-donut-active" + }, + { + "name": "chart-pie-donut-text", + "family": "pie", + "path": "apps/v4/registry/new-york-v4/charts/chart-pie-donut-text.tsx", + "blobSha": "c7e3d834954dbee3af6a33eac6a8cf74b333350f", + "localCaseId": "129-shadcn-pie-donut-text" + }, + { + "name": "chart-pie-donut", + "family": "pie", + "path": "apps/v4/registry/new-york-v4/charts/chart-pie-donut.tsx", + "blobSha": "c4ca8674ee42a22e9572fa385318cb4b42da2fac", + "localCaseId": "163-shadcn-pie-donut" + }, + { + "name": "chart-pie-interactive", + "family": "pie", + "path": "apps/v4/registry/new-york-v4/charts/chart-pie-interactive.tsx", + "blobSha": "dfe17b688b3dd35a36ccda5435966b9f4160c128", + "localCaseId": "164-shadcn-pie-interactive" + }, + { + "name": "chart-pie-label-custom", + "family": "pie", + "path": "apps/v4/registry/new-york-v4/charts/chart-pie-label-custom.tsx", + "blobSha": "465c78c2a8d83f4c924433db265f524bb4962a6e", + "localCaseId": "165-shadcn-pie-label-custom" + }, + { + "name": "chart-pie-label-list", + "family": "pie", + "path": "apps/v4/registry/new-york-v4/charts/chart-pie-label-list.tsx", + "blobSha": "ff179593352b9f541743e50eb8303fcf92320913", + "localCaseId": "166-shadcn-pie-label-list" + }, + { + "name": "chart-pie-label", + "family": "pie", + "path": "apps/v4/registry/new-york-v4/charts/chart-pie-label.tsx", + "blobSha": "3257c585110fb58deb95c4d5d61fe15c5ec3bde5", + "localCaseId": "167-shadcn-pie-label" + }, + { + "name": "chart-pie-legend", + "family": "pie", + "path": "apps/v4/registry/new-york-v4/charts/chart-pie-legend.tsx", + "blobSha": "aa6ca2705c5bdd3a4e2f6be9fc08870298eb37ef", + "localCaseId": "168-shadcn-pie-legend" + }, + { + "name": "chart-pie-separator-none", + "family": "pie", + "path": "apps/v4/registry/new-york-v4/charts/chart-pie-separator-none.tsx", + "blobSha": "e313ea46e768bf54d89ee2db59c8d6f519a40b46", + "localCaseId": "169-shadcn-pie-separator-none" + }, + { + "name": "chart-pie-simple", + "family": "pie", + "path": "apps/v4/registry/new-york-v4/charts/chart-pie-simple.tsx", + "blobSha": "1c90420f93ce32ecfa3810dfa264f51ad2e501c6", + "localCaseId": "170-shadcn-pie-simple" + }, + { + "name": "chart-pie-stacked", + "family": "pie", + "path": "apps/v4/registry/new-york-v4/charts/chart-pie-stacked.tsx", + "blobSha": "e7e984972554d171a5a957a059558db827e0e307", + "localCaseId": "171-shadcn-pie-stacked" + }, + { + "name": "chart-radar-default", + "family": "radar", + "path": "apps/v4/registry/new-york-v4/charts/chart-radar-default.tsx", + "blobSha": "425afde7ff3e7c972e20934b63218c537455f82d", + "localCaseId": "172-shadcn-radar-default" + }, + { + "name": "chart-radar-dots", + "family": "radar", + "path": "apps/v4/registry/new-york-v4/charts/chart-radar-dots.tsx", + "blobSha": "be84261030eaa17694843243463244a016a311b0", + "localCaseId": "173-shadcn-radar-dots" + }, + { + "name": "chart-radar-grid-circle-fill", + "family": "radar", + "path": "apps/v4/registry/new-york-v4/charts/chart-radar-grid-circle-fill.tsx", + "blobSha": "bb58bce241bf01efcb649cbba144a7dc55fc7637", + "localCaseId": "174-shadcn-radar-grid-circle-fill" + }, + { + "name": "chart-radar-grid-circle-no-lines", + "family": "radar", + "path": "apps/v4/registry/new-york-v4/charts/chart-radar-grid-circle-no-lines.tsx", + "blobSha": "e098f0f1b91900fc4950610f8da0d987f0a1fe35", + "localCaseId": "175-shadcn-radar-grid-circle-no-lines" + }, + { + "name": "chart-radar-grid-circle", + "family": "radar", + "path": "apps/v4/registry/new-york-v4/charts/chart-radar-grid-circle.tsx", + "blobSha": "67601f06ae0e5449167607683564a96a04b80fd7", + "localCaseId": "176-shadcn-radar-grid-circle" + }, + { + "name": "chart-radar-grid-custom", + "family": "radar", + "path": "apps/v4/registry/new-york-v4/charts/chart-radar-grid-custom.tsx", + "blobSha": "9ab8ad6127646c42c4a005ad7c369ff25b60994a", + "localCaseId": "177-shadcn-radar-grid-custom" + }, + { + "name": "chart-radar-grid-fill", + "family": "radar", + "path": "apps/v4/registry/new-york-v4/charts/chart-radar-grid-fill.tsx", + "blobSha": "b3ebb61358b3c51528385402fee7697ae11516c5", + "localCaseId": "178-shadcn-radar-grid-fill" + }, + { + "name": "chart-radar-grid-none", + "family": "radar", + "path": "apps/v4/registry/new-york-v4/charts/chart-radar-grid-none.tsx", + "blobSha": "0bdb7769a9113342db51b3a1250433cfb9c3fc4d", + "localCaseId": "179-shadcn-radar-grid-none" + }, + { + "name": "chart-radar-icons", + "family": "radar", + "path": "apps/v4/registry/new-york-v4/charts/chart-radar-icons.tsx", + "blobSha": "3c4eec62fb8756910bf8968973d616b918f9764b", + "localCaseId": "180-shadcn-radar-icons" + }, + { + "name": "chart-radar-label-custom", + "family": "radar", + "path": "apps/v4/registry/new-york-v4/charts/chart-radar-label-custom.tsx", + "blobSha": "bf16fa743613a25c3641593c7a60ef052fe861ca", + "localCaseId": "181-shadcn-radar-label-custom" + }, + { + "name": "chart-radar-legend", + "family": "radar", + "path": "apps/v4/registry/new-york-v4/charts/chart-radar-legend.tsx", + "blobSha": "31bdb565433854385b152710a3fade68f5823bc7", + "localCaseId": "182-shadcn-radar-legend" + }, + { + "name": "chart-radar-lines-only", + "family": "radar", + "path": "apps/v4/registry/new-york-v4/charts/chart-radar-lines-only.tsx", + "blobSha": "c62cddf7355f0a003d072a93c77456883deed4ab", + "localCaseId": "183-shadcn-radar-lines-only" + }, + { + "name": "chart-radar-multiple", + "family": "radar", + "path": "apps/v4/registry/new-york-v4/charts/chart-radar-multiple.tsx", + "blobSha": "b7d2102f9588a4b86f6cc281ae61eb7efe97e5ed", + "localCaseId": "130-shadcn-radar-multiple" + }, + { + "name": "chart-radar-radius", + "family": "radar", + "path": "apps/v4/registry/new-york-v4/charts/chart-radar-radius.tsx", + "blobSha": "61f29bb5b8262c6917d9b0a19f1e4509f228e09c", + "localCaseId": "184-shadcn-radar-radius" + }, + { + "name": "chart-radial-grid", + "family": "radial", + "path": "apps/v4/registry/new-york-v4/charts/chart-radial-grid.tsx", + "blobSha": "032eb98a9e2cab56d7b7a1413af496193477d04c", + "localCaseId": "185-shadcn-radial-grid" + }, + { + "name": "chart-radial-label", + "family": "radial", + "path": "apps/v4/registry/new-york-v4/charts/chart-radial-label.tsx", + "blobSha": "effb735c84fb1ecee61fd8bab07f1daba1d65135", + "localCaseId": "186-shadcn-radial-label" + }, + { + "name": "chart-radial-shape", + "family": "radial", + "path": "apps/v4/registry/new-york-v4/charts/chart-radial-shape.tsx", + "blobSha": "9550a5bf0f2eab53933f8709d25ca1439964ec3d", + "localCaseId": "187-shadcn-radial-shape" + }, + { + "name": "chart-radial-simple", + "family": "radial", + "path": "apps/v4/registry/new-york-v4/charts/chart-radial-simple.tsx", + "blobSha": "b13e20e5b584ca18b44201ba4c147fefa8a00f1d", + "localCaseId": "188-shadcn-radial-simple" + }, + { + "name": "chart-radial-stacked", + "family": "radial", + "path": "apps/v4/registry/new-york-v4/charts/chart-radial-stacked.tsx", + "blobSha": "71fbf5756f9811672c7881fa9d1ef87db549391d", + "localCaseId": "189-shadcn-radial-stacked" + }, + { + "name": "chart-radial-text", + "family": "radial", + "path": "apps/v4/registry/new-york-v4/charts/chart-radial-text.tsx", + "blobSha": "f10347522cd27689bd960a4ed7e5033c5659b93c", + "localCaseId": "131-shadcn-radial-text" + }, + { + "name": "chart-tooltip-advanced", + "family": "tooltip", + "path": "apps/v4/registry/new-york-v4/charts/chart-tooltip-advanced.tsx", + "blobSha": "6c55b5e514836c30ae1ff570dee3d3217aca6e9c", + "localCaseId": "132-shadcn-tooltip-advanced" + }, + { + "name": "chart-tooltip-default", + "family": "tooltip", + "path": "apps/v4/registry/new-york-v4/charts/chart-tooltip-default.tsx", + "blobSha": "829cc53633afd526f25b7982c2516ff9b590420d", + "localCaseId": "190-shadcn-tooltip-default" + }, + { + "name": "chart-tooltip-formatter", + "family": "tooltip", + "path": "apps/v4/registry/new-york-v4/charts/chart-tooltip-formatter.tsx", + "blobSha": "6d0ebf02dda90e7a688b53cdd1c8be735e192f69", + "localCaseId": "191-shadcn-tooltip-formatter" + }, + { + "name": "chart-tooltip-icons", + "family": "tooltip", + "path": "apps/v4/registry/new-york-v4/charts/chart-tooltip-icons.tsx", + "blobSha": "1117d4494cb8316edc4a9096549adf339ec65d17", + "localCaseId": "192-shadcn-tooltip-icons" + }, + { + "name": "chart-tooltip-indicator-line", + "family": "tooltip", + "path": "apps/v4/registry/new-york-v4/charts/chart-tooltip-indicator-line.tsx", + "blobSha": "2d09ce8039b05c753efde134dc3e8710d92c8e63", + "localCaseId": "193-shadcn-tooltip-indicator-line" + }, + { + "name": "chart-tooltip-indicator-none", + "family": "tooltip", + "path": "apps/v4/registry/new-york-v4/charts/chart-tooltip-indicator-none.tsx", + "blobSha": "6416520431aeeda2bfc170066385538b60e6e920", + "localCaseId": "194-shadcn-tooltip-indicator-none" + }, + { + "name": "chart-tooltip-label-custom", + "family": "tooltip", + "path": "apps/v4/registry/new-york-v4/charts/chart-tooltip-label-custom.tsx", + "blobSha": "b8095ffe6b9accbcc8480c125931db7294effa81", + "localCaseId": "195-shadcn-tooltip-label-custom" + }, + { + "name": "chart-tooltip-label-formatter", + "family": "tooltip", + "path": "apps/v4/registry/new-york-v4/charts/chart-tooltip-label-formatter.tsx", + "blobSha": "0b885d71e18d581793bc2e316c3f1402daf59182", + "localCaseId": "196-shadcn-tooltip-label-formatter" + }, + { + "name": "chart-tooltip-label-none", + "family": "tooltip", + "path": "apps/v4/registry/new-york-v4/charts/chart-tooltip-label-none.tsx", + "blobSha": "f5f220bb1f20928bd1c9934ddf014cb72b89fb98", + "localCaseId": "197-shadcn-tooltip-label-none" + } + ] +} diff --git a/benchmarks/conformance/shadcn/reference/chart-area-axes.png b/benchmarks/conformance/shadcn/reference/chart-area-axes.png new file mode 100644 index 00000000..e937f467 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-area-axes.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-area-default.png b/benchmarks/conformance/shadcn/reference/chart-area-default.png new file mode 100644 index 00000000..fd7897e0 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-area-default.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-area-gradient.png b/benchmarks/conformance/shadcn/reference/chart-area-gradient.png new file mode 100644 index 00000000..7d55f2a6 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-area-gradient.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-area-icons.png b/benchmarks/conformance/shadcn/reference/chart-area-icons.png new file mode 100644 index 00000000..660f115b Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-area-icons.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-area-interactive.png b/benchmarks/conformance/shadcn/reference/chart-area-interactive.png new file mode 100644 index 00000000..f0edd708 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-area-interactive.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-area-legend.png b/benchmarks/conformance/shadcn/reference/chart-area-legend.png new file mode 100644 index 00000000..500ba1fb Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-area-legend.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-area-linear.png b/benchmarks/conformance/shadcn/reference/chart-area-linear.png new file mode 100644 index 00000000..3f6575bd Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-area-linear.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-area-stacked-expand.png b/benchmarks/conformance/shadcn/reference/chart-area-stacked-expand.png new file mode 100644 index 00000000..53c570f9 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-area-stacked-expand.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-area-stacked.png b/benchmarks/conformance/shadcn/reference/chart-area-stacked.png new file mode 100644 index 00000000..bcd77b34 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-area-stacked.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-area-step.png b/benchmarks/conformance/shadcn/reference/chart-area-step.png new file mode 100644 index 00000000..fb36a14d Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-area-step.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-bar-active.png b/benchmarks/conformance/shadcn/reference/chart-bar-active.png new file mode 100644 index 00000000..e17c8187 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-bar-active.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-bar-default.png b/benchmarks/conformance/shadcn/reference/chart-bar-default.png new file mode 100644 index 00000000..a11d00a6 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-bar-default.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-bar-horizontal.png b/benchmarks/conformance/shadcn/reference/chart-bar-horizontal.png new file mode 100644 index 00000000..d83959ca Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-bar-horizontal.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-bar-interactive.png b/benchmarks/conformance/shadcn/reference/chart-bar-interactive.png new file mode 100644 index 00000000..fb58da98 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-bar-interactive.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-bar-label-custom.png b/benchmarks/conformance/shadcn/reference/chart-bar-label-custom.png new file mode 100644 index 00000000..a99b6f62 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-bar-label-custom.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-bar-label.png b/benchmarks/conformance/shadcn/reference/chart-bar-label.png new file mode 100644 index 00000000..e0176d49 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-bar-label.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-bar-mixed.png b/benchmarks/conformance/shadcn/reference/chart-bar-mixed.png new file mode 100644 index 00000000..abbeee00 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-bar-mixed.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-bar-multiple.png b/benchmarks/conformance/shadcn/reference/chart-bar-multiple.png new file mode 100644 index 00000000..6fac3bf2 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-bar-multiple.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-bar-negative.png b/benchmarks/conformance/shadcn/reference/chart-bar-negative.png new file mode 100644 index 00000000..26875852 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-bar-negative.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-bar-stacked.png b/benchmarks/conformance/shadcn/reference/chart-bar-stacked.png new file mode 100644 index 00000000..d3736371 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-bar-stacked.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-line-default.png b/benchmarks/conformance/shadcn/reference/chart-line-default.png new file mode 100644 index 00000000..8aaae5b4 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-line-default.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-line-dots-colors.png b/benchmarks/conformance/shadcn/reference/chart-line-dots-colors.png new file mode 100644 index 00000000..33c2e6e2 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-line-dots-colors.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-line-dots-custom.png b/benchmarks/conformance/shadcn/reference/chart-line-dots-custom.png new file mode 100644 index 00000000..23b4e24f Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-line-dots-custom.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-line-dots.png b/benchmarks/conformance/shadcn/reference/chart-line-dots.png new file mode 100644 index 00000000..397800cb Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-line-dots.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-line-interactive.png b/benchmarks/conformance/shadcn/reference/chart-line-interactive.png new file mode 100644 index 00000000..2919c517 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-line-interactive.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-line-label-custom.png b/benchmarks/conformance/shadcn/reference/chart-line-label-custom.png new file mode 100644 index 00000000..797ada4e Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-line-label-custom.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-line-label.png b/benchmarks/conformance/shadcn/reference/chart-line-label.png new file mode 100644 index 00000000..90502cfc Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-line-label.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-line-linear.png b/benchmarks/conformance/shadcn/reference/chart-line-linear.png new file mode 100644 index 00000000..8769a60d Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-line-linear.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-line-multiple.png b/benchmarks/conformance/shadcn/reference/chart-line-multiple.png new file mode 100644 index 00000000..65ada71d Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-line-multiple.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-line-step.png b/benchmarks/conformance/shadcn/reference/chart-line-step.png new file mode 100644 index 00000000..b52485ef Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-line-step.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-pie-donut-active.png b/benchmarks/conformance/shadcn/reference/chart-pie-donut-active.png new file mode 100644 index 00000000..74e449e3 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-pie-donut-active.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-pie-donut-text.png b/benchmarks/conformance/shadcn/reference/chart-pie-donut-text.png new file mode 100644 index 00000000..56403b14 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-pie-donut-text.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-pie-donut.png b/benchmarks/conformance/shadcn/reference/chart-pie-donut.png new file mode 100644 index 00000000..638f0d04 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-pie-donut.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-pie-interactive.png b/benchmarks/conformance/shadcn/reference/chart-pie-interactive.png new file mode 100644 index 00000000..2ef41bbe Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-pie-interactive.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-pie-label-custom.png b/benchmarks/conformance/shadcn/reference/chart-pie-label-custom.png new file mode 100644 index 00000000..78fa4269 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-pie-label-custom.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-pie-label-list.png b/benchmarks/conformance/shadcn/reference/chart-pie-label-list.png new file mode 100644 index 00000000..2891231c Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-pie-label-list.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-pie-label.png b/benchmarks/conformance/shadcn/reference/chart-pie-label.png new file mode 100644 index 00000000..a7e677cd Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-pie-label.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-pie-legend.png b/benchmarks/conformance/shadcn/reference/chart-pie-legend.png new file mode 100644 index 00000000..170ab487 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-pie-legend.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-pie-separator-none.png b/benchmarks/conformance/shadcn/reference/chart-pie-separator-none.png new file mode 100644 index 00000000..167bcf6d Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-pie-separator-none.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-pie-simple.png b/benchmarks/conformance/shadcn/reference/chart-pie-simple.png new file mode 100644 index 00000000..d8e72ff1 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-pie-simple.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-pie-stacked.png b/benchmarks/conformance/shadcn/reference/chart-pie-stacked.png new file mode 100644 index 00000000..1d4ad29e Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-pie-stacked.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radar-default.png b/benchmarks/conformance/shadcn/reference/chart-radar-default.png new file mode 100644 index 00000000..5c809d52 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radar-default.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radar-dots.png b/benchmarks/conformance/shadcn/reference/chart-radar-dots.png new file mode 100644 index 00000000..ded8e88e Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radar-dots.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radar-grid-circle-fill.png b/benchmarks/conformance/shadcn/reference/chart-radar-grid-circle-fill.png new file mode 100644 index 00000000..9a9b58a8 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radar-grid-circle-fill.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radar-grid-circle-no-lines.png b/benchmarks/conformance/shadcn/reference/chart-radar-grid-circle-no-lines.png new file mode 100644 index 00000000..5b41637e Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radar-grid-circle-no-lines.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radar-grid-circle.png b/benchmarks/conformance/shadcn/reference/chart-radar-grid-circle.png new file mode 100644 index 00000000..32c2f93a Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radar-grid-circle.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radar-grid-custom.png b/benchmarks/conformance/shadcn/reference/chart-radar-grid-custom.png new file mode 100644 index 00000000..86919e07 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radar-grid-custom.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radar-grid-fill.png b/benchmarks/conformance/shadcn/reference/chart-radar-grid-fill.png new file mode 100644 index 00000000..6f8dc78c Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radar-grid-fill.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radar-grid-none.png b/benchmarks/conformance/shadcn/reference/chart-radar-grid-none.png new file mode 100644 index 00000000..14983547 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radar-grid-none.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radar-icons.png b/benchmarks/conformance/shadcn/reference/chart-radar-icons.png new file mode 100644 index 00000000..583b03e1 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radar-icons.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radar-label-custom.png b/benchmarks/conformance/shadcn/reference/chart-radar-label-custom.png new file mode 100644 index 00000000..87297cf9 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radar-label-custom.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radar-legend.png b/benchmarks/conformance/shadcn/reference/chart-radar-legend.png new file mode 100644 index 00000000..bfeca858 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radar-legend.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radar-lines-only.png b/benchmarks/conformance/shadcn/reference/chart-radar-lines-only.png new file mode 100644 index 00000000..16b1decf Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radar-lines-only.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radar-multiple.png b/benchmarks/conformance/shadcn/reference/chart-radar-multiple.png new file mode 100644 index 00000000..44ea6411 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radar-multiple.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radar-radius.png b/benchmarks/conformance/shadcn/reference/chart-radar-radius.png new file mode 100644 index 00000000..2b6c3f80 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radar-radius.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radial-grid.png b/benchmarks/conformance/shadcn/reference/chart-radial-grid.png new file mode 100644 index 00000000..7d2fbf42 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radial-grid.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radial-label.png b/benchmarks/conformance/shadcn/reference/chart-radial-label.png new file mode 100644 index 00000000..a37b41ee Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radial-label.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radial-shape.png b/benchmarks/conformance/shadcn/reference/chart-radial-shape.png new file mode 100644 index 00000000..4e580da2 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radial-shape.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radial-simple.png b/benchmarks/conformance/shadcn/reference/chart-radial-simple.png new file mode 100644 index 00000000..f5ee6ecd Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radial-simple.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radial-stacked.png b/benchmarks/conformance/shadcn/reference/chart-radial-stacked.png new file mode 100644 index 00000000..8589a769 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radial-stacked.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-radial-text.png b/benchmarks/conformance/shadcn/reference/chart-radial-text.png new file mode 100644 index 00000000..470eb3ce Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-radial-text.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-tooltip-advanced.png b/benchmarks/conformance/shadcn/reference/chart-tooltip-advanced.png new file mode 100644 index 00000000..b20787a0 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-tooltip-advanced.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-tooltip-default.png b/benchmarks/conformance/shadcn/reference/chart-tooltip-default.png new file mode 100644 index 00000000..de8c224b Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-tooltip-default.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-tooltip-formatter.png b/benchmarks/conformance/shadcn/reference/chart-tooltip-formatter.png new file mode 100644 index 00000000..5151bbdc Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-tooltip-formatter.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-tooltip-icons.png b/benchmarks/conformance/shadcn/reference/chart-tooltip-icons.png new file mode 100644 index 00000000..98fa724c Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-tooltip-icons.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-tooltip-indicator-line.png b/benchmarks/conformance/shadcn/reference/chart-tooltip-indicator-line.png new file mode 100644 index 00000000..f78d4cf7 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-tooltip-indicator-line.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-tooltip-indicator-none.png b/benchmarks/conformance/shadcn/reference/chart-tooltip-indicator-none.png new file mode 100644 index 00000000..8f2b1e40 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-tooltip-indicator-none.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-tooltip-label-custom.png b/benchmarks/conformance/shadcn/reference/chart-tooltip-label-custom.png new file mode 100644 index 00000000..72a30c55 Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-tooltip-label-custom.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-tooltip-label-formatter.png b/benchmarks/conformance/shadcn/reference/chart-tooltip-label-formatter.png new file mode 100644 index 00000000..6582446e Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-tooltip-label-formatter.png differ diff --git a/benchmarks/conformance/shadcn/reference/chart-tooltip-label-none.png b/benchmarks/conformance/shadcn/reference/chart-tooltip-label-none.png new file mode 100644 index 00000000..6cab1e8f Binary files /dev/null and b/benchmarks/conformance/shadcn/reference/chart-tooltip-label-none.png differ diff --git a/benchmarks/conformance/shadcn/reference/manifest.json b/benchmarks/conformance/shadcn/reference/manifest.json new file mode 100644 index 00000000..34e44086 --- /dev/null +++ b/benchmarks/conformance/shadcn/reference/manifest.json @@ -0,0 +1,82 @@ +{ + "schemaVersion": 1, + "viewport": { + "width": 640, + "deviceScaleFactor": 1, + "theme": "light" + }, + "source": "https://ui.shadcn.com/view/new-york-v4/{name}", + "capturedAt": "2026-08-12T19:29:26.607Z", + "cases": [ + "chart-area-axes", + "chart-area-default", + "chart-area-gradient", + "chart-area-icons", + "chart-area-interactive", + "chart-area-legend", + "chart-area-linear", + "chart-area-stacked-expand", + "chart-area-stacked", + "chart-area-step", + "chart-bar-active", + "chart-bar-default", + "chart-bar-horizontal", + "chart-bar-interactive", + "chart-bar-label-custom", + "chart-bar-label", + "chart-bar-mixed", + "chart-bar-multiple", + "chart-bar-negative", + "chart-bar-stacked", + "chart-line-default", + "chart-line-dots-colors", + "chart-line-dots-custom", + "chart-line-dots", + "chart-line-interactive", + "chart-line-label-custom", + "chart-line-label", + "chart-line-linear", + "chart-line-multiple", + "chart-line-step", + "chart-pie-donut-active", + "chart-pie-donut-text", + "chart-pie-donut", + "chart-pie-interactive", + "chart-pie-label-custom", + "chart-pie-label-list", + "chart-pie-label", + "chart-pie-legend", + "chart-pie-separator-none", + "chart-pie-simple", + "chart-pie-stacked", + "chart-radar-default", + "chart-radar-dots", + "chart-radar-grid-circle-fill", + "chart-radar-grid-circle-no-lines", + "chart-radar-grid-circle", + "chart-radar-grid-custom", + "chart-radar-grid-fill", + "chart-radar-grid-none", + "chart-radar-icons", + "chart-radar-label-custom", + "chart-radar-legend", + "chart-radar-lines-only", + "chart-radar-multiple", + "chart-radar-radius", + "chart-radial-grid", + "chart-radial-label", + "chart-radial-shape", + "chart-radial-simple", + "chart-radial-stacked", + "chart-radial-text", + "chart-tooltip-advanced", + "chart-tooltip-default", + "chart-tooltip-formatter", + "chart-tooltip-icons", + "chart-tooltip-indicator-line", + "chart-tooltip-indicator-none", + "chart-tooltip-label-custom", + "chart-tooltip-label-formatter", + "chart-tooltip-label-none" + ] +} diff --git a/benchmarks/conformance/shared/shadcn-catalog-data.ts b/benchmarks/conformance/shared/shadcn-catalog-data.ts new file mode 100644 index 00000000..5030c176 --- /dev/null +++ b/benchmarks/conformance/shared/shadcn-catalog-data.ts @@ -0,0 +1,216 @@ +export type ShadcnChartFamily = + 'area' | 'bar' | 'line' | 'pie' | 'radar' | 'radial' | 'tooltip' + +export interface ShadcnCatalogSpec { + name: string + family: ShadcnChartFamily + variant: string + title: string + description: string + footerNote: string + square: boolean + legend: boolean +} + +export interface ShadcnMonthDatum { + month: string + desktop: number + mobile: number + tablet: number +} + +export interface ShadcnSeriesDatum { + month: string + series: 'desktop' | 'mobile' | 'tablet' | 'other' + value: number +} + +export interface ShadcnBrowserDatum { + browser: string + visitors: number +} + +export interface ShadcnRadarDatum { + month: string + desktop: number + mobile?: number +} + +export interface ShadcnActivityDatum { + date: string + activity: 'running' | 'swimming' + value: number +} + +export const shadcnMonths: readonly ShadcnMonthDatum[] = [ + { month: 'January', desktop: 186, mobile: 80, tablet: 44 }, + { month: 'February', desktop: 305, mobile: 200, tablet: 72 }, + { month: 'March', desktop: 237, mobile: 120, tablet: 58 }, + { month: 'April', desktop: 73, mobile: 190, tablet: 91 }, + { month: 'May', desktop: 209, mobile: 130, tablet: 67 }, + { month: 'June', desktop: 214, mobile: 140, tablet: 82 }, +] + +export const shadcnSeriesRows: readonly ShadcnSeriesDatum[] = + shadcnMonths.flatMap((row) => [ + { month: row.month, series: 'desktop', value: row.desktop }, + { month: row.month, series: 'mobile', value: row.mobile }, + { month: row.month, series: 'tablet', value: row.tablet }, + ]) + +export const shadcnBrowsers: readonly ShadcnBrowserDatum[] = [ + { browser: 'chrome', visitors: 275 }, + { browser: 'safari', visitors: 200 }, + { browser: 'firefox', visitors: 187 }, + { browser: 'edge', visitors: 173 }, + { browser: 'other', visitors: 90 }, +] + +export const shadcnRadarDefault: readonly ShadcnRadarDatum[] = [ + { month: 'January', desktop: 186 }, + { month: 'February', desktop: 305 }, + { month: 'March', desktop: 237 }, + { month: 'April', desktop: 273 }, + { month: 'May', desktop: 209 }, + { month: 'June', desktop: 214 }, +] + +export const shadcnRadarFilled: readonly ShadcnRadarDatum[] = [ + { month: 'January', desktop: 186 }, + { month: 'February', desktop: 285 }, + { month: 'March', desktop: 237 }, + { month: 'April', desktop: 203 }, + { month: 'May', desktop: 209 }, + { month: 'June', desktop: 264 }, +] + +export const shadcnRadarMultiple: readonly ShadcnRadarDatum[] = + shadcnMonths.map(({ month, desktop, mobile }) => ({ + month, + desktop, + mobile, + })) + +export const shadcnRadarLines: readonly ShadcnRadarDatum[] = [ + { month: 'January', desktop: 186, mobile: 160 }, + { month: 'February', desktop: 185, mobile: 170 }, + { month: 'March', desktop: 207, mobile: 180 }, + { month: 'April', desktop: 173, mobile: 160 }, + { month: 'May', desktop: 160, mobile: 190 }, + { month: 'June', desktop: 174, mobile: 204 }, +] + +export const shadcnActivities: readonly ShadcnActivityDatum[] = [ + { date: '2024-07-15', activity: 'running', value: 450 }, + { date: '2024-07-15', activity: 'swimming', value: 300 }, + { date: '2024-07-16', activity: 'running', value: 380 }, + { date: '2024-07-16', activity: 'swimming', value: 420 }, + { date: '2024-07-17', activity: 'running', value: 520 }, + { date: '2024-07-17', activity: 'swimming', value: 120 }, + { date: '2024-07-18', activity: 'running', value: 140 }, + { date: '2024-07-18', activity: 'swimming', value: 550 }, + { date: '2024-07-19', activity: 'running', value: 600 }, + { date: '2024-07-19', activity: 'swimming', value: 350 }, + { date: '2024-07-20', activity: 'running', value: 480 }, + { date: '2024-07-20', activity: 'swimming', value: 400 }, +] + +export const shadcnColors = [ + 'var(--chart-1, var(--ts-chart-1))', + 'var(--chart-2, var(--ts-chart-2))', + 'var(--chart-3, var(--ts-chart-3))', + 'var(--chart-4, var(--ts-chart-4))', + 'var(--chart-5, var(--ts-chart-5))', +] as const + +const titleOverrides: Record = { + 'chart-area-default': 'Area Chart', + 'chart-area-stacked-expand': 'Area Chart - Stacked Expanded', + 'chart-bar-default': 'Bar Chart', + 'chart-bar-label-custom': 'Bar Chart - Custom Label', + 'chart-bar-stacked': 'Bar Chart - Stacked + Legend', + 'chart-line-default': 'Line Chart', + 'chart-line-dots-custom': 'Line Chart - Custom Dots', + 'chart-line-label-custom': 'Line Chart - Custom Label', + 'chart-pie-simple': 'Pie Chart', + 'chart-pie-donut-text': 'Pie Chart - Donut with Text', + 'chart-pie-label-custom': 'Pie Chart - Custom Label', + 'chart-radar-default': 'Radar Chart', + 'chart-radar-grid-circle-fill': 'Radar Chart - Grid Circle Filled', + 'chart-radar-grid-circle-no-lines': 'Radar Chart - Grid Circle - No lines', + 'chart-radar-grid-fill': 'Radar Chart - Grid Filled', + 'chart-radar-label-custom': 'Radar Chart - Custom Label', + 'chart-radar-radius': 'Radar Chart - Radius Axis', + 'chart-radial-simple': 'Radial Chart', + 'chart-tooltip-indicator-line': 'Tooltip - Line Indicator', + 'chart-tooltip-indicator-none': 'Tooltip - No Indicator', + 'chart-tooltip-label-custom': 'Tooltip - Custom label', + 'chart-tooltip-label-none': 'Tooltip - No Label', +} + +export function getShadcnCatalogSpec(name: string): ShadcnCatalogSpec { + const parts = name.split('-') + const family = parts[1] + if (!isShadcnFamily(family)) { + throw new TypeError(`Unknown shadcn chart family in ${name}`) + } + const variant = parts.slice(2).join('-') + const title = + titleOverrides[name] ?? + `${family === 'tooltip' ? 'Tooltip' : `${titleCase(family)} Chart`} - ${variant.split('-').map(titleCase).join(' ')}` + return { + name, + family, + variant, + title, + description: + (family === 'area' || family === 'bar' || family === 'line') && + variant === 'interactive' + ? 'Showing total visitors for the last 3 months' + : family === 'area' || family === 'radar' + ? 'Showing total visitors for the last 6 months' + : family === 'tooltip' + ? tooltipDescription(variant) + : 'January - June 2024', + footerNote: + family === 'area' || family === 'radar' + ? 'January - June 2024' + : 'Showing total visitors for the last 6 months', + square: family === 'pie' || family === 'radar' || family === 'radial', + legend: + variant.includes('legend') || + variant === 'icons' || + (variant === 'stacked' && family === 'bar') || + (family === 'area' && variant === 'interactive'), + } +} + +function tooltipDescription(variant: string): string { + if (variant === 'advanced') return 'Tooltip with custom formatter and total.' + if (variant === 'default') return 'Default tooltip with ChartTooltipContent.' + if (variant === 'formatter') return 'Tooltip with custom formatter.' + if (variant === 'icons') return 'Tooltip with icons.' + if (variant === 'indicator-line') return 'Tooltip with line indicator.' + if (variant === 'indicator-none') return 'Tooltip with no indicator.' + if (variant === 'label-custom') + return 'Tooltip with custom label from chartConfig.' + if (variant === 'label-formatter') return 'Tooltip with label formatter.' + if (variant === 'label-none') return 'Tooltip with no label.' + return 'A chart tooltip.' +} + +function titleCase(value: string): string { + return value.charAt(0).toUpperCase() + value.slice(1) +} + +function isShadcnFamily(value: string | undefined): value is ShadcnChartFamily { + return ( + value === 'area' || + value === 'bar' || + value === 'line' || + value === 'pie' || + value === 'radar' || + value === 'radial' || + value === 'tooltip' + ) +} diff --git a/benchmarks/conformance/shared/shadcn-catalog-recharts.tsx b/benchmarks/conformance/shared/shadcn-catalog-recharts.tsx new file mode 100644 index 00000000..fb3ad6cd --- /dev/null +++ b/benchmarks/conformance/shared/shadcn-catalog-recharts.tsx @@ -0,0 +1,570 @@ +import { + Area, + AreaChart, + Bar, + BarChart, + CartesianGrid, + Cell, + LabelList, + Legend, + Line, + LineChart, + Pie, + PieChart, + PolarAngleAxis, + PolarGrid, + Radar, + RadarChart, + RadialBar, + RadialBarChart, + Tooltip, + XAxis, + YAxis, +} from 'recharts' +import { + getShadcnCatalogSpec, + shadcnActivities, + shadcnBrowsers, + shadcnColors, + shadcnMonths, + type ShadcnCatalogSpec, +} from './shadcn-catalog-data' +import { + ShadcnChartCard, + ShadcnTrendFooter, + shadcnChartMount, +} from './shadcn-chart-card' +import type { ReactNode } from 'react' +import type { ConformanceInput } from '../types' + +export function createShadcnRechartsExample(name: string) { + const spec = getShadcnCatalogSpec(name) + + function RechartsView({ input }: { input: ConformanceInput }) { + return ( + : undefined} + footer={ + spec.family === 'tooltip' ? undefined : ( + + ) + } + > + {({ width, height }) => renderRechartsChart(spec, width, height)} + + ) + } + + return { mount: shadcnChartMount(RechartsView) } +} + +function renderRechartsChart( + spec: ShadcnCatalogSpec, + width: number, + height: number, +): ReactNode { + if (spec.family === 'area') return areaChart(spec, width, height) + if (spec.family === 'bar') return barChart(spec, width, height) + if (spec.family === 'line') return lineChart(spec, width, height) + if (spec.family === 'pie') return pieChart(spec, width, height) + if (spec.family === 'radar') return radarChart(spec, width, height) + if (spec.family === 'radial') return radialChart(spec, width, height) + return tooltipChart(spec, width, height) +} + +function areaChart(spec: ShadcnCatalogSpec, width: number, height: number) { + const multi = + spec.variant === 'axes' || + spec.variant === 'gradient' || + spec.variant === 'icons' || + spec.variant === 'interactive' || + spec.variant === 'legend' || + spec.variant.startsWith('stacked') + const type = + spec.variant === 'linear' + ? 'linear' + : spec.variant === 'step' + ? 'step' + : 'natural' + return ( + + {spec.variant === 'gradient' ? ( + + + + + + + + + + + ) : null} + + + {spec.variant === 'axes' ? ( + + ) : null} + + + {multi ? ( + + ) : null} + {spec.variant === 'stacked-expand' ? ( + + ) : null} + {spec.legend ? : null} + + ) +} + +function barChart(spec: ShadcnCatalogSpec, width: number, height: number) { + const horizontal = + spec.variant === 'horizontal' || + spec.variant === 'label-custom' || + spec.variant === 'mixed' + const negativeData = shadcnMonths.map((row, index) => ({ + ...row, + desktop: index === 2 || index === 4 ? -row.desktop : row.desktop, + })) + const data = spec.variant === 'negative' ? negativeData : shadcnMonths + if (horizontal) { + return ( + + + + + + {spec.variant === 'label-custom' ? ( + + ) : null} + + + ) + } + return ( + + + + + + {spec.variant === 'active' + ? data.map((row) => ( + + )) + : null} + {spec.variant === 'label' ? ( + + ) : null} + + {spec.variant === 'multiple' || spec.variant === 'stacked' ? ( + + ) : null} + {spec.legend ? : null} + + ) +} + +function lineChart(spec: ShadcnCatalogSpec, width: number, height: number) { + const type = + spec.variant === 'linear' + ? 'linear' + : spec.variant === 'step' + ? 'step' + : 'natural' + const dots = spec.variant.includes('dots') + const labels = spec.variant.includes('label') + const multiple = spec.variant === 'multiple' || spec.variant === 'interactive' + return ( + + + + + + {labels ? ( + + ) : null} + + {multiple ? ( + + ) : null} + + ) +} + +function pieChart(spec: ShadcnCatalogSpec, width: number, height: number) { + const donut = spec.variant.includes('donut') || spec.variant === 'stacked' + const labels = spec.variant.includes('label') + return ( + + + + {shadcnBrowsers.map((row, index) => ( + + ))} + + {spec.variant === 'donut-text' ? ( + <> + + 1,125 + + + Visitors + + + ) : null} + {spec.legend ? : null} + + ) +} + +function radarChart(spec: ShadcnCatalogSpec, width: number, height: number) { + const multiple = spec.variant === 'multiple' || spec.variant === 'legend' + return ( + + {spec.variant === 'grid-none' ? null : ( + + )} + + + + {multiple ? ( + + ) : null} + {spec.legend ? : null} + + ) +} + +function radialChart(spec: ShadcnCatalogSpec, width: number, height: number) { + const data = + spec.variant === 'simple' || spec.variant === 'text' + ? shadcnBrowsers.slice(1, 2) + : shadcnBrowsers + const startAngle = spec.variant === 'shape' ? 180 : 200 + const endAngle = spec.variant === 'shape' ? 0 : -50 + return ( + + + + {data.map((row, index) => ( + + ))} + {spec.variant === 'label' ? ( + + ) : null} + + {spec.variant === 'text' ? ( + <> + + 200 + + + Visitors + + + ) : null} + + ) +} + +function tooltipChart(spec: ShadcnCatalogSpec, width: number, height: number) { + return ( +
+ + + + + + +
+ ) +} + +function tooltipRows() { + return [...new Set(shadcnActivities.map((row) => row.date))].map((date) => ({ + date, + running: + shadcnActivities.find( + (row) => row.date === date && row.activity === 'running', + )?.value ?? 0, + swimming: + shadcnActivities.find( + (row) => row.date === date && row.activity === 'swimming', + )?.value ?? 0, + })) +} + +function RechartsLegend({ spec }: { spec: ShadcnCatalogSpec }) { + const labels = + spec.family === 'pie' || spec.family === 'radial' + ? shadcnBrowsers.map((row) => row.browser) + : ['desktop', 'mobile'] + return ( + <> + {labels.map((label, index) => ( + + + {titleCase(label)} + + ))} + + ) +} + +function RechartsStaticTooltip({ variant }: { variant: string }) { + const noLabel = variant === 'label-none' + const noIndicator = variant === 'indicator-none' + const lineIndicator = variant === 'indicator-line' + const formatted = variant === 'formatter' || variant === 'advanced' + return ( +
+ {noLabel ? null : ( + + {variant === 'label-formatter' + ? 'Tuesday, July 16' + : variant === 'label-custom' + ? 'Running & Swimming' + : 'Jul 16'} + + )} + {['running', 'swimming'].map((activity, index) => ( +
+ {noIndicator ? null : ( + + )} + + {variant === 'icons' + ? `${index === 0 ? '●' : '◆'} ${titleCase(activity)}` + : titleCase(activity)} + + + {formatted + ? `${index === 0 ? 380 : 420} kcal` + : index === 0 + ? '380' + : '420'} + +
+ ))} + {variant === 'advanced' ? ( +
+ Total + 800 kcal +
+ ) : null} +
+ ) +} + +function shortMonth(value: string) { + return value.slice(0, 3) +} + +function shortDate(value: string) { + return new Intl.DateTimeFormat('en-US', { + month: 'short', + day: 'numeric', + timeZone: 'UTC', + }).format(new Date(value)) +} + +function titleCase(value: string) { + return value.charAt(0).toUpperCase() + value.slice(1) +} diff --git a/benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx b/benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx new file mode 100644 index 00000000..2c843e2a --- /dev/null +++ b/benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx @@ -0,0 +1,2152 @@ +import { useMemo, useRef, useState } from 'react' +import { + areaY, + barX, + barY, + d3Curve, + defineChart, + dot, + group, + isResponsiveChartDefinition, + lineY, + stack, + text, +} from '@tanstack/charts' +import { + angleGrid, + focusGroupAngle, + pie, + polar, + radialArc, + radialArea, + radialBarAngle, + radialDot, + radialGrid, + radialRule, + radialText, + type PieDatum, +} from '@tanstack/charts/polar' +import { Chart } from '@tanstack/charts/react/core' +import { RendererChart as TooltipChart } from '@tanstack/charts/react/tooltip' +import { tooltip } from '@tanstack/charts/tooltip' +import { scaleBand, scaleLinear, scalePoint } from 'd3-scale' +import { + curveLinear, + curveLinearClosed, + curveMonotoneX, + curveNatural, + curveStep, +} from 'd3-shape' +import { + getShadcnCatalogSpec, + shadcnActivities, + shadcnBrowsers, + shadcnColors, + shadcnMonths, + shadcnRadarDefault, + shadcnRadarFilled, + shadcnRadarLines, + shadcnRadarMultiple, + shadcnSeriesRows, + type ShadcnActivityDatum, + type ShadcnBrowserDatum, + type ShadcnCatalogSpec, + type ShadcnMonthDatum, + type ShadcnSeriesDatum, +} from './shadcn-catalog-data' +import { + ShadcnChartCard, + ShadcnTrendFooter, + shadcnChartMount, +} from './shadcn-chart-card' +import { createShadcnSpringRenderer } from './shadcn-motion' +import interactiveAreaData from '../shadcn/area-interactive-data.json' +import { tanstackCase } from './mount' +import type { ChartPoint, ChartTooltipOptions } from '@tanstack/charts' +import type { ChartDefinition, ChartValue } from '@tanstack/charts' +import type { ConformanceInput } from '../types' + +const monthSeries = ['desktop', 'mobile', 'tablet'] as const +const twoSeries = ['desktop', 'mobile'] as const +const browserNames = shadcnBrowsers.map((row) => row.browser) +const activityNames = ['running', 'swimming'] as const +const horizontalVariants = new Set(['horizontal', 'label-custom', 'mixed']) +const interactiveAreaRows = interactiveAreaData as readonly { + date: string + desktop: number + mobile: number +}[] +const interactiveBarRows: readonly ShadcnMonthDatum[] = interactiveAreaRows.map( + (row) => ({ + month: row.date, + desktop: row.desktop, + mobile: row.mobile, + tablet: 0, + }), +) +const interactivePieRows: readonly ShadcnBrowserDatum[] = [ + { browser: 'january', visitors: 186 }, + { browser: 'february', visitors: 305 }, + { browser: 'march', visitors: 237 }, + { browser: 'april', visitors: 173 }, + { browser: 'may', visitors: 209 }, +] +const interactiveTimeRangeOptions = [ + { value: '90d', label: 'Last 3 months' }, + { value: '30d', label: 'Last 30 days' }, + { value: '7d', label: 'Last 7 days' }, +] as const + +type InteractiveTimeRange = '90d' | '30d' | '7d' +type InteractiveSeries = (typeof twoSeries)[number] + +interface ShadcnInteractiveState { + timeRange: InteractiveTimeRange + series: InteractiveSeries + month: string +} + +type ShadcnInteractiveDefinitionFactory< + TDatum, + TXValue extends ChartValue, + TYValue extends ChartValue, +> = (state: ShadcnInteractiveState) => ChartDefinition + +type ShadcnRadialCatalogDatum = + | ShadcnBrowserDatum + | PieDatum<{ id: string; value: number }> + | { id: string; angle: number; radius: number; text: string } + | { browser: string; visitors: number; ring: string } + | { + id: string + ring: string + start: number + end: number + fill: string + } + +export function createShadcnTanStackExample(name: string) { + const spec = getShadcnCatalogSpec(name) + if (spec.family === 'area') { + return createShadcnExample( + spec, + buildAreaDefinition(spec), + spec.variant === 'interactive' + ? ({ timeRange }) => buildInteractiveAreaDefinition(timeRange) + : undefined, + ) + } + if (spec.family === 'bar') { + return createShadcnExample< + ShadcnMonthDatum | ShadcnSeriesDatum | ShadcnBrowserDatum, + string | number, + string | number + >( + spec, + buildBarDefinition(spec), + spec.variant === 'interactive' + ? ({ series }) => buildInteractiveBarDefinition(series) + : undefined, + ) + } + if (spec.family === 'line') { + return createShadcnExample( + spec, + buildLineDefinition(spec), + spec.variant === 'interactive' + ? ({ series }) => buildInteractiveLineDefinition(series) + : undefined, + ) + } + if (spec.family === 'pie') { + return createShadcnExample( + spec, + buildPieDefinition(spec), + spec.variant === 'interactive' + ? ({ month }) => buildInteractivePieDefinition(month) + : undefined, + ) + } + if (spec.family === 'radar') { + return createShadcnExample(spec, buildRadarDefinition(spec)) + } + if (spec.family === 'radial') { + return createShadcnExample< + ShadcnRadialCatalogDatum, + number, + string | number + >(spec, buildRadialDefinition(spec)) + } + return createShadcnExample(spec, buildTooltipDefinition()) +} + +function createShadcnExample< + TDatum, + TXValue extends ChartValue, + TYValue extends ChartValue, +>( + spec: ShadcnCatalogSpec, + definition: ChartDefinition, + interactiveDefinitionFactory?: ShadcnInteractiveDefinitionFactory< + TDatum, + TXValue, + TYValue + >, +) { + const tooltipOptions: ChartTooltipOptions = { + className: 'sc-chart-tooltip', + anchor: + spec.family === 'tooltip' + ? (_points, context) => ({ + x: context.surface.width * 0.271, + y: context.surface.height * 0.554, + }) + : 'group-center', + placement: spec.family === 'tooltip' ? 'bottom-right' : 'auto', + offset: spec.family === 'tooltip' ? 0 : undefined, + sort: 'color-domain', + content: + spec.family === 'tooltip' + ? () => ({ rows: [] }) + : (points) => shadcnTooltipContent(spec, points), + } + const chartOptions = { + svgAnimation: false, + focus: + spec.family === 'radar' + ? focusGroupAngle + : spec.family === 'pie' || spec.family === 'radial' + ? 'nearest' + : 'group-x', + keyboard: spec.family !== 'radial', + tooltip: { use: tooltip, ...tooltipOptions }, + } as const + const applyChartOptions = ( + source: ChartDefinition, + ) => + isResponsiveChartDefinition(source) + ? defineChart(source, chartOptions) + : defineChart(source, chartOptions) + const interactiveDefinition = applyChartOptions(definition) + const catalogCase = tanstackCase( + () => interactiveDefinition, + `${spec.title} implemented with TanStack Charts`, + true, + { guides: spec.family !== 'pie', margin: true }, + ) + + function TanStackView({ input }: { input: ConformanceInput }) { + const seededTooltip = useRef(false) + const [timeRange, setTimeRange] = useState('90d') + const [activeSeries, setActiveSeries] = + useState('desktop') + const [activeMonth, setActiveMonth] = useState('january') + const chartDefinition = useMemo( + () => + interactiveDefinitionFactory + ? applyChartOptions( + interactiveDefinitionFactory({ + timeRange, + series: activeSeries, + month: activeMonth, + }), + ) + : interactiveDefinition, + [timeRange, activeSeries, activeMonth], + ) + const renderer = useMemo( + () => createShadcnSpringRenderer(), + [], + ) + const legend = spec.legend ? : undefined + const interactive = spec.variant === 'interactive' + const footer = + spec.family === 'tooltip' || + interactive || + (spec.family === 'pie' && spec.variant === 'legend') ? undefined : ( + + ) + const headerAction = + (spec.family === 'bar' || spec.family === 'line') && interactive ? ( + + ) : interactive ? ( + spec.family === 'area' ? ( + setTimeRange(value as InteractiveTimeRange)} + /> + ) : ( + ({ + value: row.browser, + label: titleCase(row.browser), + swatch: shadcnColors[index], + }))} + onChange={setActiveMonth} + /> + ) + ) : undefined + + return ( + + {({ width, height }) => + spec.family === 'tooltip' ? ( + { + if (seededTooltip.current) return + const point = scene.points.find( + (candidate) => + (candidate.datum as ShadcnActivityDatum).date === + '2024-07-16', + ) + if (!point) return + seededTooltip.current = true + interaction.setControlledFocus(point, { + source: 'programmatic', + }) + }} + renderTooltipBody={({ points }) => ( + []} + variant={spec.variant} + /> + )} + /> + ) : ( + + ) + } + + ) + } + + return { + definition: interactiveDefinition, + catalogCase, + mount: shadcnChartMount(TanStackView), + } +} + +function shadcnTooltipContent( + spec: ShadcnCatalogSpec, + points: readonly ChartPoint[], +) { + if (spec.family === 'pie' || spec.family === 'radial') { + const point = points.find((candidate) => browserMetric(candidate.datum)) + const metric = point && browserMetric(point.datum) + return metric + ? { + title: titleCase(metric.browser), + rows: [ + { + label: 'Visitors', + value: metric.visitors.toLocaleString('en-US'), + color: point.color, + }, + ], + } + : { rows: [] } + } + + return { + title: String(points[0]?.xValue ?? ''), + rows: points.map((point) => ({ + label: titleCase( + String( + point.group ?? + point.markId.replace( + /-?(bars|lines|areas|slices|values|radar)$/u, + '', + ), + ), + ), + value: Number(point.yValue ?? point.xValue ?? 0).toLocaleString('en-US'), + color: point.color, + })), + } +} + +function browserMetric(datum: unknown) { + if (!datum || typeof datum !== 'object') return undefined + const browser = Reflect.get(datum, 'browser') + const visitors = Reflect.get(datum, 'visitors') + return typeof browser === 'string' && + typeof visitors === 'number' && + Number.isFinite(visitors) + ? { browser, visitors } + : undefined +} + +function buildAreaDefinition(spec: ShadcnCatalogSpec) { + if (spec.variant === 'interactive') return buildInteractiveAreaDefinition() + const multi = + spec.variant === 'axes' || + spec.variant === 'gradient' || + spec.variant === 'icons' || + spec.variant === 'interactive' || + spec.variant === 'legend' || + spec.variant.startsWith('stacked') + const expanded = spec.variant === 'stacked-expand' + const expandedRows = shadcnMonths.flatMap((row, index) => [ + { + month: row.month, + series: 'other', + value: [45, 100, 150, 50, 100, 160][index]!, + }, + { month: row.month, series: 'mobile', value: row.mobile }, + { month: row.month, series: 'desktop', value: row.desktop }, + ]) + const rows = expanded + ? expandedRows + : multi + ? shadcnSeriesRows.filter((row) => row.series !== 'tablet') + : shadcnMonths + const curve = + spec.variant === 'linear' + ? curveLinear + : spec.variant === 'step' + ? curveStep + : curveNatural + const gradients = spec.variant === 'gradient' + + return defineChart({ + marks: [ + multi + ? areaY(rows as readonly ShadcnSeriesDatum[], { + id: 'visitor-areas', + x: 'month', + y: 'value', + z: 'series', + color: 'series', + key: (row) => `${row.month}:${row.series}`, + layout: stack({ + order: expanded + ? ['other', 'mobile', 'desktop'] + : ['mobile', 'desktop'], + ...(expanded ? { offset: 'normalize' } : {}), + }), + curve: d3Curve(curve), + fill: expanded + ? (row) => + row.series === 'other' + ? `color-mix(in oklch, ${shadcnColors[2]} 10%, transparent)` + : `color-mix(in oklch, ${row.series === 'mobile' ? shadcnColors[1] : shadcnColors[0]} 40%, transparent)` + : gradients + ? (row) => `url(#shadcn-area-${row.series})` + : undefined, + fillOpacity: expanded ? 1 : 0.4, + stroke: expanded + ? (row) => + row.series === 'other' + ? shadcnColors[2] + : row.series === 'mobile' + ? shadcnColors[1] + : shadcnColors[0] + : (row) => + row.series === 'mobile' ? shadcnColors[1] : shadcnColors[0], + strokeWidth: 1.5, + }) + : areaY(rows as readonly ShadcnMonthDatum[], { + id: 'visitor-area', + x: 'month', + y: 'desktop', + curve: d3Curve(curve), + fill: shadcnColors[0], + fillOpacity: 0.4, + stroke: shadcnColors[0], + strokeWidth: 1.5, + }), + ], + x: shadcnPointXAxis(), + y: { + scale: + spec.variant === 'axes' + ? scaleLinear().domain([0, 600]) + : expanded + ? scaleLinear().domain([0, 1]) + : scaleLinear, + grid: true, + axis: + spec.variant === 'axes' + ? { line: false, ticks: { size: 0, values: [0, 300, 600] } } + : { line: false, ticks: false, tickLabels: false }, + }, + color: { + domain: expanded ? ['other', 'mobile', 'desktop'] : monthSeries, + range: expanded + ? [shadcnColors[2], shadcnColors[1], shadcnColors[0]] + : shadcnColors.slice(0, 3), + }, + gradients: gradients + ? twoSeries.map((series, index) => ({ + id: `shadcn-area-${series}`, + x1: 0, + y1: 1, + x2: 0, + y2: 0, + stops: [ + { offset: 0.05, color: shadcnColors[index], opacity: 0.08 }, + { offset: 0.95, color: shadcnColors[index], opacity: 0.8 }, + ], + })) + : undefined, + margin: + spec.variant === 'axes' + ? { top: 12, right: 12, bottom: 28, left: 40 } + : { top: 5, right: 12, bottom: 35, left: 12 }, + theme: shadcnTheme(), + }) +} + +function buildInteractiveAreaDefinition( + timeRange: InteractiveTimeRange = '90d', +) { + const filteredRows = filterInteractiveAreaRows(timeRange) + const rows: ShadcnSeriesDatum[] = filteredRows.flatMap((row) => [ + { month: row.date, series: 'mobile', value: row.mobile }, + { month: row.date, series: 'desktop', value: row.desktop }, + ]) + return defineChart(({ width }) => ({ + marks: [ + areaY(rows, { + id: 'visitor-areas', + x: 'month', + y: 'value', + z: 'series', + color: 'series', + key: (row) => `${row.month}:${row.series}`, + layout: stack({ order: ['mobile', 'desktop'] }), + curve: d3Curve(curveNatural), + fill: (row) => `url(#shadcn-interactive-${row.series})`, + fillOpacity: 1, + stroke: (row) => + row.series === 'mobile' ? shadcnColors[1] : shadcnColors[0], + strokeWidth: 1, + }), + ], + x: { + scale: scalePoint, + axis: { + line: false, + ticks: { + values: interactiveDateTicks(timeRange), + size: 0, + padding: 10, + format: formatMonthDay, + }, + }, + }, + y: { + scale: scaleLinear().domain([0, 1200]), + grid: true, + axis: { + line: false, + ticks: { values: [0, 300, 600, 900, 1200], size: 0 }, + tickLabels: false, + }, + }, + color: { domain: twoSeries, range: shadcnColors.slice(0, 2) }, + gradients: twoSeries.map((series, index) => ({ + id: `shadcn-interactive-${series}`, + x1: 0, + y1: 1, + x2: 0, + y2: 0, + stops: [ + { offset: 0.05, color: shadcnColors[index], opacity: 0.1 }, + { offset: 0.95, color: shadcnColors[index], opacity: 0.8 }, + ], + })), + margin: { + top: 32, + right: width < 400 ? 14 : 5, + bottom: 35, + left: 5, + }, + theme: shadcnTheme(), + })) +} + +function buildBarDefinition(spec: ShadcnCatalogSpec) { + if (spec.variant === 'interactive') { + return buildInteractiveBarDefinition() + } + if (spec.variant === 'active') { + const rows = shadcnBrowsers.map((row) => + row.browser === 'chrome' + ? { ...row, visitors: 187 } + : row.browser === 'firefox' + ? { ...row, visitors: 275 } + : row, + ) + return defineChart({ + marks: [ + barY(rows, { + id: 'visitor-bars', + x: 'browser', + y: 'visitors', + color: 'browser', + fill: (row) => + row.browser === 'firefox' + ? `color-mix(in srgb, ${shadcnColors[2]} 80%, transparent)` + : shadcnColors[browserNames.indexOf(row.browser)]!, + stroke: (row) => + row.browser === 'firefox' ? shadcnColors[2] : 'none', + strokeWidth: 2, + strokeDasharray: (row) => + row.browser === 'firefox' ? '4 4' : 'none', + radius: 8, + }), + ], + x: shadcnBrowserXAxis(), + y: { scale: scaleLinear, grid: true, axis: false }, + color: { domain: browserNames, range: shadcnColors }, + margin: { top: 5, right: 5, bottom: 35, left: 5 }, + theme: shadcnTheme(), + }) + } + if (spec.variant === 'mixed') { + return defineChart({ + marks: [ + barX(shadcnBrowsers, { + id: 'visitor-bars', + x: 'visitors', + y: 'browser', + color: 'browser', + radius: 5, + }), + ], + x: { scale: scaleLinear, axis: false }, + y: shadcnBrowserYAxis(), + color: { domain: browserNames, range: shadcnColors }, + margin: { top: 5, right: 5, bottom: 5, left: 60 }, + theme: shadcnTheme(), + }) + } + if (spec.variant === 'label-custom') { + return defineChart({ + marks: [ + barX(shadcnMonths, { + id: 'visitor-bars', + x: 'desktop', + y: 'month', + fill: shadcnColors[1], + radius: 4, + }), + text(shadcnMonths, { + id: 'month-labels', + x: () => 0, + y: 'month', + text: 'month', + dx: 8, + anchor: 'start', + fill: 'var(--background)', + fontSize: 12, + }), + text(shadcnMonths, { + id: 'visitor-labels', + x: 'desktop', + y: 'month', + text: (row) => row.desktop.toLocaleString('en-US'), + dx: 8, + anchor: 'start', + fill: 'var(--foreground)', + fontSize: 12, + }), + ], + x: { scale: scaleLinear, grid: true, axis: false }, + y: { + scale: () => scaleBand().paddingInner(0.18).paddingOuter(0.08), + axis: false, + }, + margin: { top: 5, right: 36, bottom: 5, left: 0 }, + theme: shadcnTheme(), + }) + } + if (spec.variant === 'multiple') { + return groupedBarDefinition() + } + if (spec.variant === 'stacked') { + const rows = shadcnSeriesRows.filter((row) => row.series !== 'tablet') + return defineChart({ + marks: [ + barY(rows, { + id: 'visitor-bars', + x: 'month', + y: 'value', + z: 'series', + color: 'series', + key: (row) => `${row.month}:${row.series}`, + layout: stack({ order: twoSeries }), + radius: 4, + }), + ], + x: shadcnXAxis(), + y: { scale: scaleLinear, grid: true, axis: false }, + color: { domain: twoSeries, range: shadcnColors.slice(0, 2) }, + margin: { top: 5, right: 5, bottom: 35, left: 5 }, + theme: shadcnTheme(), + }) + } + + const negativeRows = [ + { month: 'January', desktop: 186, mobile: 0, tablet: 0 }, + { month: 'February', desktop: 205, mobile: 0, tablet: 0 }, + { month: 'March', desktop: -207, mobile: 0, tablet: 0 }, + { month: 'April', desktop: 173, mobile: 0, tablet: 0 }, + { month: 'May', desktop: -209, mobile: 0, tablet: 0 }, + { month: 'June', desktop: 214, mobile: 0, tablet: 0 }, + ] + const rows = spec.variant === 'negative' ? negativeRows : shadcnMonths + const horizontal = horizontalVariants.has(spec.variant) + const labels = + spec.variant === 'label' || + spec.variant === 'label-custom' || + spec.variant === 'negative' + const marks = horizontal + ? [ + barX(rows, { + id: 'visitor-bars', + x: 'desktop', + y: 'month', + fill: shadcnColors[0], + radius: 5, + }), + ...(labels + ? [ + text(rows, { + id: 'visitor-labels', + x: 'desktop', + y: 'month', + text: (row) => row.desktop.toLocaleString('en-US'), + dx: -7, + anchor: 'end', + fill: 'var(--background)', + fontSize: 11, + fontWeight: 600, + }), + ] + : []), + ] + : [ + barY(rows, { + id: 'visitor-bars', + x: 'month', + y: 'desktop', + fill: (row) => + spec.variant === 'negative' && row.desktop < 0 + ? shadcnColors[1] + : shadcnColors[0], + radius: spec.variant === 'negative' ? 0 : 8, + }), + ...(labels + ? [ + text(rows, { + id: 'visitor-labels', + x: 'month', + y: 'desktop', + text: (row) => row.desktop.toLocaleString('en-US'), + ...(spec.variant === 'negative' + ? { + text: (row: ShadcnMonthDatum) => row.month, + dy: (row: ShadcnMonthDatum) => + row.desktop < 0 ? 14 : -10, + fill: shadcnColors[0], + fontSize: 12, + } + : { + dy: -12, + fill: 'var(--foreground)', + fontSize: 12, + }), + }), + ] + : []), + ] + + return defineChart({ + marks, + x: horizontal + ? { scale: scaleLinear, axis: false } + : spec.variant === 'negative' + ? { + scale: () => + scaleBand().paddingInner(0.2).paddingOuter(0.1), + axis: false, + } + : shadcnXAxis(), + y: horizontal + ? { + scale: () => + scaleBand().paddingInner(0.18).paddingOuter(0.08), + axis: { line: false, ticks: { size: 0 } }, + } + : { scale: scaleLinear, grid: true, axis: false }, + margin: horizontal + ? { top: 5, right: 5, bottom: 5, left: 72 } + : spec.variant === 'negative' + ? { top: 24, right: 5, bottom: 24, left: 5 } + : { top: labels ? 24 : 5, right: 5, bottom: 35, left: 5 }, + theme: shadcnTheme(), + }) +} + +function buildInteractiveBarDefinition( + activeSeries: InteractiveSeries = 'desktop', +) { + return defineChart({ + marks: [ + barY(interactiveBarRows, { + id: 'daily-bars', + x: 'month', + y: (row) => row[activeSeries], + z: () => activeSeries, + key: 'month', + fill: activeSeries === 'desktop' ? shadcnColors[1] : shadcnColors[0], + }), + ], + x: { + scale: () => scaleBand().paddingInner(0.2).paddingOuter(0.1), + axis: { + line: false, + ticks: { + values: [ + '2024-04-01', + '2024-04-11', + '2024-04-22', + '2024-05-03', + '2024-05-14', + '2024-05-26', + '2024-06-06', + '2024-06-17', + '2024-06-29', + ], + size: 0, + padding: 10, + format: formatMonthDay, + }, + }, + }, + y: { scale: scaleLinear, grid: true, axis: false }, + color: { domain: twoSeries, range: shadcnColors.slice(0, 2) }, + margin: { top: 5, right: 12, bottom: 25, left: 12 }, + theme: shadcnTheme(), + }) +} + +function groupedBarDefinition() { + const rows = shadcnSeriesRows.filter( + (row) => row.series === 'desktop' || row.series === 'mobile', + ) + const groupScale = scaleBand() + .domain(twoSeries) + .paddingInner(0.08) + .paddingOuter(0) + return defineChart({ + marks: [ + barY(rows, { + id: 'visitor-bars', + x: 'month', + y: 'value', + z: 'series', + color: 'series', + key: (row) => `${row.month}:${row.series}`, + layout: group({ scale: groupScale }), + radius: 4, + }), + ], + x: shadcnXAxis(), + y: { scale: scaleLinear, grid: true, axis: false }, + color: { domain: twoSeries, range: shadcnColors.slice(0, 2) }, + margin: { top: 5, right: 5, bottom: 35, left: 5 }, + theme: shadcnTheme(), + }) +} + +function buildLineDefinition(spec: ShadcnCatalogSpec) { + if (spec.variant === 'interactive') return buildInteractiveLineDefinition() + const multiple = spec.variant === 'multiple' + const browserVariant = + spec.variant === 'dots-colors' || spec.variant === 'label-custom' + const rows = multiple + ? shadcnSeriesRows.filter((row) => row.series !== 'tablet') + : browserVariant + ? shadcnBrowsers + : shadcnMonths + const curve = + spec.variant === 'linear' + ? curveLinear + : spec.variant === 'step' + ? curveStep + : multiple + ? curveMonotoneX + : curveNatural + const dots = spec.variant.includes('dots') + const labels = spec.variant.includes('label') + const dotMarks = !dots + ? [] + : browserVariant + ? [ + dot(shadcnBrowsers, { + id: 'visitor-dots', + x: 'browser', + y: 'visitors', + color: 'browser', + r: 5, + }), + ] + : [ + ...(spec.variant === 'dots-custom' + ? [ + text(shadcnMonths, { + id: 'visitor-dot-stems', + x: 'month', + y: 'desktop', + text: () => '│', + fill: shadcnColors[0], + fontSize: 24, + }), + ] + : []), + dot(shadcnMonths, { + id: 'visitor-dots', + x: 'month', + y: 'desktop', + r: spec.variant === 'dots-custom' ? 5 : 4, + fill: + spec.variant === 'dots-custom' + ? 'var(--background)' + : shadcnColors[0], + stroke: shadcnColors[0], + strokeWidth: spec.variant === 'dots-custom' ? 2 : 1, + }), + ] + const labelMarks = !labels + ? [] + : browserVariant + ? [ + text(shadcnBrowsers, { + id: 'visitor-labels', + x: 'browser', + y: 'visitors', + text: (row) => titleCase(row.browser), + dy: -12, + fill: 'var(--foreground)', + fontSize: 12, + }), + ] + : [ + text(shadcnMonths, { + id: 'visitor-labels', + x: 'month', + y: 'desktop', + text: (row) => row.desktop.toLocaleString('en-US'), + dy: -12, + fill: 'var(--foreground)', + fontSize: 12, + }), + ] + return defineChart(({ width }) => ({ + marks: [ + multiple + ? lineY(rows as readonly ShadcnSeriesDatum[], { + id: 'visitor-lines', + x: 'month', + y: 'value', + z: 'series', + color: 'series', + key: (row) => `${row.month}:${row.series}`, + curve: d3Curve(curve), + strokeWidth: 2, + }) + : browserVariant + ? lineY(rows as readonly ShadcnBrowserDatum[], { + id: 'visitor-line', + x: 'browser', + y: 'visitors', + curve: d3Curve(curve), + stroke: shadcnColors[1], + strokeWidth: 2, + }) + : lineY(rows as readonly ShadcnMonthDatum[], { + id: 'visitor-line', + x: 'month', + y: 'desktop', + curve: d3Curve(curve), + stroke: shadcnColors[0], + strokeWidth: 2, + }), + ...dotMarks, + ...labelMarks, + ], + x: browserVariant ? { scale: scalePoint, axis: false } : shadcnPointXAxis(), + y: { + scale: scaleLinear().domain([0, browserVariant ? 300 : 320]), + grid: true, + axis: { + line: false, + ticks: { + values: browserVariant + ? [0, 75, 150, 225, 300] + : [0, 80, 160, 240, 320], + size: 0, + }, + tickLabels: false, + }, + }, + color: { + domain: browserVariant ? browserNames : twoSeries, + range: browserVariant ? shadcnColors : shadcnColors.slice(0, 2), + }, + margin: browserVariant + ? { top: 24, right: 24, bottom: 5, left: 24 } + : { + top: + spec.variant === 'dots-custom' && width < 400 + ? 16 + : labels + ? 24 + : 8, + right: 12, + bottom: 35, + left: 12, + }, + theme: shadcnTheme(), + })) +} + +function buildInteractiveLineDefinition( + activeSeries: InteractiveSeries = 'desktop', +) { + return defineChart(({ width }) => ({ + marks: [ + lineY(interactiveBarRows, { + id: 'daily-line', + x: 'month', + y: (row) => row[activeSeries], + z: () => activeSeries, + key: 'month', + curve: d3Curve(curveMonotoneX), + stroke: activeSeries === 'desktop' ? shadcnColors[0] : shadcnColors[1], + strokeWidth: 2, + }), + ], + x: { + scale: scalePoint, + axis: { + line: false, + ticks: { + values: [ + '2024-04-10', + '2024-04-21', + '2024-05-02', + '2024-05-13', + '2024-05-25', + '2024-06-05', + '2024-06-16', + '2024-06-29', + ], + size: 0, + padding: 10, + format: formatMonthDay, + }, + }, + }, + y: { + scale: scaleLinear().domain([0, 600]), + grid: true, + axis: { + line: false, + ticks: { values: [0, 150, 300, 450, 600], size: 0 }, + tickLabels: false, + }, + }, + color: { domain: twoSeries, range: shadcnColors.slice(0, 2) }, + margin: { + top: 5, + right: width < 400 ? 24 : 12, + bottom: 35, + left: 12, + }, + theme: shadcnTheme(), + })) +} + +function buildPieDefinition(spec: ShadcnCatalogSpec) { + if (spec.variant === 'interactive') return buildInteractivePieDefinition() + if (spec.variant === 'stacked') return buildStackedPieDefinition() + const arcs = pie(shadcnBrowsers, { + value: 'visitors', + startAngle: Math.PI / 2, + endAngle: (-Math.PI * 3) / 2, + }) + const donut = spec.variant.includes('donut') + const active = spec.variant === 'donut-active' + const centerText = spec.variant === 'donut-text' + const labels = spec.variant.includes('label') + const externalLabels = spec.variant === 'label' + const customLabels = spec.variant === 'label-custom' + const labelList = spec.variant === 'label-list' + const separator = spec.variant === 'separator-none' ? 0 : active ? 5 : 1 + const marks = [ + radialArc(arcs, { + id: 'browser-slices', + key: 'browser', + innerRadius: donut ? 60 : undefined, + color: 'browser', + stroke: 'var(--background)', + strokeWidth: separator, + }), + ...(active + ? [ + radialArc(arcs.slice(0, 1), { + id: 'active-browser-slice', + key: 'browser', + innerRadius: 60, + outerRadius: ({ radius }) => radius + 10, + color: 'browser', + stroke: 'var(--background)', + strokeWidth: 5, + }), + ] + : []), + ...(externalLabels + ? [ + radialRule(arcs, { + id: 'browser-leaders', + angle: 'angle', + radius1: 1, + radius2: 1, + radius2Offset: 14, + key: 'browser', + stroke: 'var(--border)', + }), + ] + : []), + ...(labels + ? [ + radialText(arcs, { + id: 'browser-labels', + angle: 'angle', + radius: externalLabels || customLabels ? 1 : 0.68, + radiusOffset: externalLabels || customLabels ? 17 : 0, + text: (row) => + labelList + ? titleCase(row.browser) + : row.visitors.toLocaleString('en-US'), + key: 'browser', + anchor: externalLabels || customLabels ? 'outside' : 'middle', + fill: + externalLabels || customLabels + ? 'var(--foreground)' + : 'var(--background)', + fontSize: 12, + fontWeight: labelList ? 400 : undefined, + }), + ] + : []), + ...(centerText + ? [ + radialText([{ id: 'total', angle: 0, radius: 0, text: '1,125' }], { + id: 'visitor-total', + angle: 'angle', + radius: 'radius', + key: 'id', + text: 'text', + dy: -5, + fill: 'var(--foreground)', + fontSize: 30, + fontWeight: 700, + }), + radialText([{ id: 'label', angle: 0, radius: 0, text: 'Visitors' }], { + id: 'visitor-label', + angle: 'angle', + radius: 'radius', + key: 'id', + text: 'text', + dy: 19, + fill: 'var(--muted-foreground)', + fontSize: 14, + }), + ] + : []), + ] + + return defineChart({ + marks: [ + polar({ + radiusRatio: 0.78, + angle: { scale: scaleLinear().domain([0, Math.PI * 2]) }, + radius: { scale: scaleLinear().domain([0, 1]) }, + marks, + }), + ], + color: { domain: browserNames, range: shadcnColors }, + margin: 0, + }) +} + +function buildStackedPieDefinition() { + const desktopRows = [186, 305, 237, 173, 209].map((visitors, index) => ({ + browser: ['january', 'february', 'march', 'april', 'may'][index]!, + visitors, + })) + const mobileRows = [80, 200, 120, 190, 130].map((visitors, index) => ({ + browser: ['january', 'february', 'march', 'april', 'may'][index]!, + visitors, + })) + const desktopArcs = pie(desktopRows, { + value: 'visitors', + startAngle: Math.PI / 2, + endAngle: (-Math.PI * 3) / 2, + }) + const mobileArcs = pie(mobileRows, { + value: 'visitors', + startAngle: Math.PI / 2, + endAngle: (-Math.PI * 3) / 2, + }) + const months = desktopRows.map((row) => row.browser) + return defineChart({ + marks: [ + polar({ + radiusRatio: 1, + angle: { scale: scaleLinear().domain([0, Math.PI * 2]) }, + radius: { scale: scaleLinear().domain([0, 1]) }, + marks: [ + radialArc(desktopArcs, { + id: 'desktop-slices', + key: 'browser', + outerRadius: 60, + color: 'browser', + stroke: 'var(--background)', + strokeWidth: 1, + }), + radialArc(mobileArcs, { + id: 'mobile-slices', + key: 'browser', + innerRadius: 70, + outerRadius: 90, + color: 'browser', + stroke: 'var(--background)', + strokeWidth: 1, + }), + ], + }), + ], + color: { domain: months, range: shadcnColors }, + margin: 0, + }) +} + +function buildInteractivePieDefinition(activeMonth = 'january') { + const arcs = pie(interactivePieRows, { + value: 'visitors', + startAngle: Math.PI / 2, + endAngle: (-Math.PI * 3) / 2, + }) + const activeIndex = Math.max( + 0, + interactivePieRows.findIndex((row) => row.browser === activeMonth), + ) + const activeRow = interactivePieRows[activeIndex]! + const active = arcs.filter((row) => row.browser === activeRow.browser) + const activeColor = shadcnColors[activeIndex]! + return defineChart({ + marks: [ + polar({ + radiusRatio: 0.78, + angle: { scale: scaleLinear().domain([0, Math.PI * 2]) }, + radius: { scale: scaleLinear().domain([0, 1]) }, + marks: [ + radialArc(arcs, { + id: 'month-slices', + key: 'browser', + innerRadius: 60, + color: 'browser', + stroke: 'var(--background)', + strokeWidth: 1, + }), + radialArc(active, { + id: 'active-month', + key: 'browser', + innerRadius: 60, + outerRadius: ({ radius }) => radius + 10, + fill: activeColor, + stroke: 'var(--background)', + strokeWidth: 5, + }), + radialArc(active, { + id: 'active-month-ring', + key: 'browser', + innerRadius: ({ radius }) => radius + 12, + outerRadius: ({ radius }) => radius + 25, + fill: activeColor, + stroke: 'var(--background)', + strokeWidth: 3, + }), + radialText( + [ + { + id: 'total', + angle: 0, + radius: 0, + text: String(activeRow.visitors), + }, + ], + { + id: 'active-total', + angle: 'angle', + radius: 'radius', + key: 'id', + text: 'text', + dy: -5, + fill: 'var(--foreground)', + fontSize: 30, + fontWeight: 700, + }, + ), + radialText([{ id: 'label', angle: 0, radius: 0, text: 'Visitors' }], { + id: 'active-label', + angle: 'angle', + radius: 'radius', + key: 'id', + text: 'text', + dy: 20, + fill: 'var(--muted-foreground)', + fontSize: 12, + }), + ], + }), + ], + color: { + domain: interactivePieRows.map((row) => row.browser), + range: shadcnColors, + }, + margin: 0, + }) +} + +function buildRadarDefinition(spec: ShadcnCatalogSpec) { + const circle = spec.variant.includes('circle') + const noGrid = spec.variant === 'grid-none' + const noSpokes = + spec.variant === 'grid-circle-no-lines' || + spec.variant === 'grid-custom' || + spec.variant === 'lines-only' || + noGrid + const multiple = + spec.variant === 'multiple' || + spec.variant === 'legend' || + spec.variant === 'icons' || + spec.variant === 'label-custom' || + spec.variant === 'radius' || + spec.variant === 'lines-only' + const filledGrid = spec.variant.includes('fill') + const rows = + spec.variant === 'lines-only' + ? shadcnRadarLines + : filledGrid + ? shadcnRadarFilled + : spec.variant === 'grid-circle-no-lines' + ? shadcnRadarDefault.map((row) => + row.month === 'April' ? { ...row, desktop: 203 } : row, + ) + : multiple + ? shadcnRadarMultiple + : shadcnRadarDefault + const months = rows.map((row) => row.month) + const radiusMax = spec.variant === 'lines-only' ? 220 : filledGrid ? 300 : 320 + const gridValues = Array.from({ length: 4 }, (_, index) => + Math.round((radiusMax * (index + 1)) / 4), + ) + const customLabels = spec.variant === 'label-custom' + const radiusAxis = spec.variant === 'radius' + const dots = + spec.variant === 'dots' || + spec.variant === 'grid-circle' || + spec.variant === 'grid-circle-no-lines' || + noGrid + const guides = [ + ...(!noGrid + ? [ + radialGrid({ + values: + spec.variant === 'grid-custom' + ? [Math.round(radiusMax * 0.947)] + : radiusAxis + ? [0, 80, 160, 240, 320] + : gridValues, + shape: circle ? 'circle' : 'polygon', + stroke: filledGrid ? 'transparent' : 'var(--border)', + strokeOpacity: filledGrid ? 0 : 1, + ...(filledGrid ? { fill: shadcnColors[0], fillOpacity: 0.2 } : {}), + ...(radiusAxis + ? { + labels: true, + labelAngle: Math.PI / 6, + labelFill: 'var(--foreground)', + labelFontSize: 12, + labelRotate: 30, + labelOffset: 1, + } + : {}), + }), + ] + : []), + ...(!radiusAxis + ? [ + angleGrid({ + values: months, + labels: !customLabels, + labelOffset: 10, + labelFill: 'var(--muted-foreground)', + labelFontSize: 12, + stroke: noSpokes || filledGrid ? 'transparent' : 'var(--border)', + strokeOpacity: filledGrid ? 0 : 1, + }), + ] + : []), + ] + const chartSpec = (radiusRatio: number) => ({ + marks: [ + polar({ + radiusRatio, + angle: { scale: scalePoint().domain(months), wrap: true }, + radius: { scale: scaleLinear().domain([0, radiusMax]) }, + guides, + marks: [ + radialArea(rows, { + id: 'desktop-radar', + className: 'ts-chart__radar', + angle: 'month', + radius: 'desktop', + key: 'month', + z: () => 'desktop', + curve: curveLinearClosed, + fill: shadcnColors[0], + fillOpacity: + spec.variant === 'lines-only' ? 0 : filledGrid ? 0.5 : 0.6, + stroke: shadcnColors[0], + strokeOpacity: filledGrid ? 0 : 1, + strokeWidth: spec.variant === 'lines-only' ? 2 : 1, + }), + ...(multiple + ? [ + radialArea(rows, { + id: 'mobile-radar', + className: 'ts-chart__radar', + angle: 'month', + radius: 'mobile', + key: 'month', + z: () => 'mobile', + curve: curveLinearClosed, + fill: shadcnColors[1], + fillOpacity: spec.variant === 'lines-only' ? 0 : 1, + stroke: shadcnColors[1], + strokeWidth: spec.variant === 'lines-only' ? 2 : 1, + }), + ] + : []), + ...(dots + ? [ + radialDot(rows, { + id: 'radar-dots', + angle: 'month', + radius: 'desktop', + key: 'month', + z: () => 'desktop', + r: 4, + fill: shadcnColors[0], + }), + ] + : []), + ...(customLabels + ? [ + radialText(rows, { + id: 'radar-values', + angle: 'month', + radius: () => radiusMax, + key: 'month', + z: () => 'desktop', + text: (row) => `${row.desktop}/${row.mobile ?? 0}`, + anchor: 'outside', + radiusOffset: 15, + dy: -7, + fill: 'var(--foreground)', + fontSize: 12, + fontWeight: 500, + }), + radialText(rows, { + id: 'radar-months', + angle: 'month', + radius: () => radiusMax, + key: 'month', + z: () => 'desktop', + text: 'month', + anchor: 'outside', + radiusOffset: 15, + dy: 10, + fill: 'var(--muted-foreground)', + fontSize: 12, + }), + ] + : []), + ], + }), + ], + color: { domain: twoSeries, range: shadcnColors.slice(0, 2) }, + margin: 0, + }) + return customLabels + ? defineChart(({ height }) => chartSpec(height < 220 ? 0.64 : 0.76)) + : defineChart(chartSpec(0.76)) +} + +function buildRadialDefinition(spec: ShadcnCatalogSpec) { + if (spec.variant === 'stacked') return buildStackedRadialDefinition() + if (spec.variant === 'shape') { + return buildSingleRadialDefinition({ + id: 'shape', + visitors: 1_260, + endDegrees: 100, + innerRadius: 65, + outerRadius: 95, + backgroundInnerRadius: 74, + backgroundOuterRadius: 86, + cornerRadius: 0, + total: '1,260', + }) + } + if (spec.variant === 'text') { + return buildSingleRadialDefinition({ + id: 'text', + visitors: 200, + endDegrees: 250, + innerRadius: 80, + outerRadius: 90, + backgroundInnerRadius: 80, + backgroundOuterRadius: 90, + cornerRadius: 10, + total: '200', + }) + } + + const label = spec.variant === 'label' + const grid = spec.variant === 'grid' + const startAngle = rechartsPolarAngle(label ? -90 : 0) + const endAngle = rechartsPolarAngle(label ? 380 : 360) + const backgroundRows = shadcnBrowsers.map((row) => ({ + ...row, + background: 300, + })) + const chartSpec = (radiusFactor: number) => ({ + marks: [ + polar({ + radiusRatio: grid ? 0.8 : 1, + startAngle, + endAngle, + angle: { scale: scaleLinear().domain([0, 300]) }, + radius: { + scale: scaleBand().domain(browserNames).paddingInner(0.2), + range: [ + 30 * radiusFactor, + (label ? 110 : grid ? 100 : 110) * radiusFactor, + ], + }, + guides: grid + ? [ + radialGrid({ + values: browserNames, + shape: 'circle', + stroke: 'var(--border)', + }), + angleGrid({ + values: Array.from({ length: 12 }, (_, index) => index * 25), + labels: false, + stroke: 'var(--border)', + }), + ] + : [], + marks: [ + ...(!grid + ? [ + radialBarAngle(backgroundRows, { + id: 'radial-backgrounds', + angle: 'background', + radius: 'browser', + key: 'browser', + fill: 'var(--muted)', + }), + ] + : []), + radialBarAngle(shadcnBrowsers, { + id: 'radial-values', + angle: 'visitors', + radius: 'browser', + key: 'browser', + fill: (row) => shadcnColors[browserNames.indexOf(row.browser)]!, + }), + ], + }), + ...(label + ? [ + polar({ + startAngle, + endAngle, + angle: { scale: scaleLinear().domain([0, 300]) }, + radius: { + scale: scaleLinear().domain([0, 110]), + range: [0, 110 * radiusFactor], + }, + marks: [ + radialText( + shadcnBrowsers.map((row, index) => ({ + ...row, + angle: 10, + labelRadius: 38 + index * 16, + })), + { + id: 'radial-labels', + angle: 'angle', + radius: 'labelRadius', + key: 'browser', + text: (row) => titleCase(row.browser), + fill: 'white', + fontSize: 11, + rotate: -25, + }, + ), + ], + }), + ] + : []), + ], + color: { domain: browserNames, range: shadcnColors }, + margin: 0, + }) + return label + ? defineChart(({ height }) => chartSpec(height < 220 ? 0.8 : 1)) + : defineChart(chartSpec(1)) +} + +function buildSingleRadialDefinition(options: { + id: string + visitors: number + endDegrees: number + innerRadius: number + outerRadius: number + backgroundInnerRadius: number + backgroundOuterRadius: number + cornerRadius: number + total: string +}) { + const rows = [ + { browser: 'safari', visitors: options.visitors, ring: 'visitors' }, + ] + const background = pie([{ id: 'background', value: 1 }], { value: 'value' }) + return defineChart({ + marks: [ + polar({ + marks: [ + radialArc(background, { + id: `${options.id}-background`, + key: 'id', + innerRadius: options.backgroundInnerRadius, + outerRadius: options.backgroundOuterRadius, + fill: 'var(--muted)', + }), + ], + }), + polar({ + startAngle: rechartsPolarAngle(0), + endAngle: rechartsPolarAngle(options.endDegrees), + angle: { scale: scaleLinear().domain([0, options.visitors]) }, + radius: { + scale: scaleBand().domain(['visitors']), + range: [options.innerRadius, options.outerRadius], + }, + marks: [ + radialBarAngle(rows, { + id: `${options.id}-value`, + angle: 'visitors', + radius: 'ring', + key: 'browser', + fill: shadcnColors[1], + cornerRadius: options.cornerRadius, + }), + ], + }), + radialCenterLabels(options.total, 36, 0, 24), + ], + margin: 0, + }) +} + +function buildStackedRadialDefinition() { + const rows = [ + { + id: 'mobile', + ring: 'visitors', + start: 0, + end: 570, + fill: shadcnColors[1], + }, + { + id: 'desktop', + ring: 'visitors', + start: 570, + end: 1_260, + fill: shadcnColors[0], + }, + ] + return defineChart({ + marks: [ + polar({ + startAngle: rechartsPolarAngle(0), + endAngle: rechartsPolarAngle(180), + angle: { scale: scaleLinear().domain([0, 1_260]) }, + radius: { + scale: scaleBand().domain(['visitors']), + range: [80, 110], + }, + marks: [ + radialBarAngle(rows, { + id: 'stacked-values', + angle1: 'start', + angle2: 'end', + angle: 'end', + radius: 'ring', + key: 'id', + fill: (row) => row.fill, + cornerRadius: 5, + stroke: 'transparent', + strokeWidth: 2, + }), + ], + }), + radialCenterLabels('1,830', 24, -16, 4), + ], + color: { domain: twoSeries, range: shadcnColors.slice(0, 2) }, + margin: 0, + }) +} + +function radialCenterLabels( + total: string, + fontSize: number, + totalDy: number, + labelDy: number, +) { + return polar({ + angle: { scale: scaleLinear().domain([0, 1]) }, + radius: { scale: scaleLinear().domain([0, 1]) }, + marks: [ + radialText([{ id: 'total', angle: 0, radius: 0, text: total }], { + id: `radial-total-${total}`, + angle: 'angle', + radius: 'radius', + key: 'id', + text: 'text', + dy: totalDy, + fill: 'var(--foreground)', + fontSize, + fontWeight: 700, + }), + radialText([{ id: 'label', angle: 0, radius: 0, text: 'Visitors' }], { + id: `radial-label-${total}`, + angle: 'angle', + radius: 'radius', + key: 'id', + text: 'text', + dy: labelDy, + fill: 'var(--muted-foreground)', + fontSize: 14, + }), + ], + }) +} + +function rechartsPolarAngle(degrees: number) { + return ((90 - degrees) * Math.PI) / 180 +} + +function buildTooltipDefinition() { + return defineChart({ + marks: [ + barY(shadcnActivities, { + id: 'activity-bars', + x: 'date', + y: 'value', + z: 'activity', + color: 'activity', + key: (row) => `${row.date}:${row.activity}`, + layout: stack({ order: activityNames }), + radius: 4, + }), + ], + x: { + scale: () => scaleBand().paddingInner(0.2).paddingOuter(0.1), + axis: { + line: false, + ticks: { size: 0, padding: 10, format: formatWeekday }, + }, + }, + y: { scale: scaleLinear().domain([0, 1_000]), axis: false }, + color: { domain: activityNames, range: shadcnColors.slice(0, 2) }, + margin: { top: 0, right: 7, bottom: 32, left: 7 }, + theme: shadcnTheme(), + }) +} + +function ShadcnLegend({ spec }: { spec: ShadcnCatalogSpec }) { + const labels = + spec.family === 'pie' || spec.family === 'radial' ? browserNames : twoSeries + const items = labels.map((label, index) => ( + + {spec.variant === 'icons' ? ( + + + + ) : ( + + )} + {label.charAt(0).toUpperCase() + label.slice(1)} + + )) + return spec.family === 'pie' && spec.variant === 'legend' ? ( +
{items}
+ ) : ( + <>{items} + ) +} + +function ShadcnTooltipBody({ + variant, + points, +}: { + variant: string + points: readonly ChartPoint[] +}) { + const ordered = [...points].sort( + (left, right) => + activityNames.indexOf(left.datum.activity) - + activityNames.indexOf(right.datum.activity), + ) + const noLabel = + variant === 'label-none' || + variant === 'formatter' || + variant === 'icons' || + variant === 'advanced' + const noIndicator = + variant === 'indicator-none' || + variant === 'label-none' || + variant === 'formatter' || + variant === 'icons' + const lineIndicator = variant === 'indicator-line' + const formatted = variant === 'formatter' || variant === 'advanced' + const label = + variant === 'label-formatter' + ? 'July 15, 2024' + : variant === 'label-custom' + ? 'Activities' + : '2024-07-16' + return ( +
+ {noLabel ? null : {label}} + {ordered.map((point, index) => ( +
+ {variant === 'icons' ? ( + + ) : noIndicator ? null : ( + + )} + {titleCase(point.datum.activity)} + + {point.datum.value} + {formatted ? kcal : null} + +
+ ))} + {variant === 'advanced' ? ( +
+ Total + + {ordered.reduce((total, point) => total + point.datum.value, 0)} + kcal + +
+ ) : null} +
+ ) +} + +function ShadcnActivityIcon({ activity }: { activity: string }) { + return ( + + {activity === 'running' ? ( + <> + + + + ) : ( + <> + + + + )} + + ) +} + +function ShadcnSelectControl({ + ariaLabel, + value, + options, + onChange, +}: { + ariaLabel: string + value: string + options: readonly { value: string; label: string; swatch?: string }[] + onChange: (value: string) => void +}) { + const selected = options.find((option) => option.value === value) + return ( + + ) +} + +function ShadcnBarMetrics({ + active, + onChange, +}: { + active: InteractiveSeries + onChange: (series: InteractiveSeries) => void +}) { + const totals = { + desktop: interactiveBarRows.reduce((sum, row) => sum + row.desktop, 0), + mobile: interactiveBarRows.reduce((sum, row) => sum + row.mobile, 0), + } + return ( + <> + {twoSeries.map((series) => ( + + ))} + + ) +} + +function filterInteractiveAreaRows(timeRange: InteractiveTimeRange) { + const days = timeRange === '30d' ? 30 : timeRange === '7d' ? 7 : 90 + const start = new Date('2024-06-30T00:00:00Z') + start.setUTCDate(start.getUTCDate() - days) + const firstDate = start.toISOString().slice(0, 10) + return interactiveAreaRows.filter((row) => row.date >= firstDate) +} + +function interactiveDateTicks(timeRange: InteractiveTimeRange) { + if (timeRange === '7d') { + return ['2024-06-23', '2024-06-25', '2024-06-27', '2024-06-29'] + } + if (timeRange === '30d') { + return [ + '2024-06-01', + '2024-06-08', + '2024-06-15', + '2024-06-22', + '2024-06-29', + ] + } + return [ + '2024-04-10', + '2024-04-21', + '2024-05-02', + '2024-05-13', + '2024-05-25', + '2024-06-05', + '2024-06-16', + '2024-06-29', + ] +} + +function shadcnXAxis() { + return { + scale: () => scaleBand().paddingInner(0.2).paddingOuter(0.1), + axis: { + line: false, + ticks: { + size: 0, + padding: 10, + format: (value: string) => value.slice(0, 3), + }, + }, + } +} + +function shadcnPointXAxis() { + return { + scale: scalePoint, + axis: { + line: false, + ticks: { + size: 0, + padding: 10, + format: (value: string) => value.slice(0, 3), + }, + }, + } +} + +function shadcnBrowserXAxis() { + return { + scale: () => scaleBand().paddingInner(0.2).paddingOuter(0.1), + axis: { + line: false, + ticks: { + size: 0, + padding: 10, + format: titleCase, + }, + }, + } +} + +function shadcnBrowserYAxis() { + return { + scale: () => scaleBand().paddingInner(0.18).paddingOuter(0.08), + axis: { + line: false, + ticks: { size: 0, padding: 10, format: titleCase }, + }, + } +} + +function shadcnTheme() { + return { + foreground: 'var(--muted-foreground, var(--muted))', + grid: 'var(--border)', + background: 'transparent', + } +} + +function formatWeekday(value: string) { + return new Intl.DateTimeFormat('en-US', { weekday: 'short' }).format( + new Date(value), + ) +} + +function formatMonthDay(value: string) { + return new Intl.DateTimeFormat('en-US', { + month: 'short', + day: 'numeric', + timeZone: 'UTC', + }).format(new Date(value)) +} + +function titleCase(value: string) { + return value.charAt(0).toUpperCase() + value.slice(1) +} diff --git a/benchmarks/conformance/shared/shadcn-chart-card.tsx b/benchmarks/conformance/shared/shadcn-chart-card.tsx new file mode 100644 index 00000000..6be50035 --- /dev/null +++ b/benchmarks/conformance/shared/shadcn-chart-card.tsx @@ -0,0 +1,313 @@ +import { forwardRef, useImperativeHandle, useRef } from 'react' +import { reactMount } from './react-mount' +import type { ComponentType, ReactNode } from 'react' +import type { ConformanceInput, ConformanceTestDriver } from '../types' +import type { ReactConformanceProps } from './react-mount' + +export interface ShadcnChartSize { + width: number + height: number +} + +interface ShadcnChartCardProps { + input: ConformanceInput + title: string + description: string + chartShape?: 'wide' | 'square' + centered?: boolean + headerInsetBottom?: number + footer?: ReactNode + chartFooter?: ReactNode + headerAction?: ReactNode + variant?: + | 'default' + | 'interactive-area' + | 'interactive-bar' + | 'interactive-line' + | 'interactive-pie' + chartHeight?: number + chartClassName?: string + children: (size: ShadcnChartSize) => ReactNode +} + +export function ShadcnChartCard({ + input, + title, + description, + chartShape = 'wide', + centered = false, + headerInsetBottom = 0, + footer, + chartFooter, + headerAction, + variant = 'default', + chartHeight, + chartClassName, + children, +}: ShadcnChartCardProps) { + const cardWidth = Math.max(1, input.width) + const contentWidth = Math.max(1, cardWidth - 50) + const chartSize = + chartShape === 'square' + ? { + width: Math.min( + variant === 'interactive-pie' ? 300 : 250, + contentWidth, + ), + height: Math.min( + variant === 'interactive-pie' ? 300 : 250, + contentWidth, + ), + } + : { + width: contentWidth, + height: chartHeight ?? (contentWidth * 9) / 16, + } + + return ( +
+
+
0 + ? { paddingBottom: headerInsetBottom } + : undefined + } + > +
+

{title}

+

{description}

+
+ {headerAction ? ( +
{headerAction}
+ ) : null} +
+
+
+ {children(chartSize)} +
+ {chartFooter ? ( +
{chartFooter}
+ ) : null} +
+ {footer ? ( +
+ {footer} +
+ ) : null} +
+
+ ) +} + +export function ShadcnTrendFooter({ + note = 'Showing total visitors for the last 6 months', +}: { + centered?: boolean + note?: string +}) { + return ( + <> +
+ Trending up by 5.2% this month + +
+
{note}
+ + ) +} + +export function shadcnChartMount( + Component: ComponentType<{ input: ConformanceInput }>, +) { + const View = forwardRef( + function ShadcnChartView({ input }, ref) { + const rootRef = useRef(null) + useImperativeHandle( + ref, + () => ({ + resolveTarget: () => null, + readState: () => ({}), + viewBounds(view) { + if (view && view !== 'main') return null + const bounds = rootRef.current?.getBoundingClientRect() + return bounds + ? { + x: bounds.left, + y: bounds.top, + width: bounds.width, + height: bounds.height, + } + : null + }, + }), + [], + ) + return ( +
+ +
+ ) + }, + ) + const mount = reactMount(View) + return (container: HTMLElement, input: ConformanceInput) => { + ensureShadcnChartStyles(container.ownerDocument) + return mount(container, input) + } +} + +function ensureShadcnChartStyles(document: Document) { + const id = 'shadcn-chart-card-styles' + let style = document.getElementById(id) + if (!(style instanceof HTMLStyleElement)) { + style = document.createElement('style') + style.id = id + document.head.append(style) + } + if (style.textContent !== shadcnChartCardStyles) { + style.textContent = shadcnChartCardStyles + } +} + +export const shadcnChartCardStyles = ` + .sc-example { + --background: oklch(1 0 0); + --foreground: oklch(0 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --border: oklch(0.922 0 0); + --chart-1: oklch(0.809 0.105 251.813); + --chart-2: oklch(0.623 0.214 259.815); + --chart-3: oklch(0.546 0.245 262.881); + --chart-4: oklch(0.488 0.243 264.376); + --chart-5: oklch(0.424 0.199 265.638); + display: flex; + justify-content: center; + align-items: flex-start; + overflow: hidden; + color: var(--foreground); + background: var(--background); + font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + text-rendering: geometricPrecision; + } + :root[data-theme='dark'] .sc-example { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --border: oklch(1 0 0 / 10%); + --chart-1: oklch(0.809 0.105 251.813); + --chart-2: oklch(0.623 0.214 259.815); + --chart-3: oklch(0.546 0.245 262.881); + --chart-4: oklch(0.488 0.243 264.376); + --chart-5: oklch(0.424 0.199 265.638); + } + .sc-example, .sc-example * { box-sizing: border-box; } + .sc-card { + display: flex; + flex-direction: column; + gap: 24px; + border: 1px solid var(--border); + border-radius: 14px; + background: var(--card); + color: var(--card-foreground); + padding: 24px 0; + } + .sc-card-header { display: grid; gap: 8px; padding: 0 24px; } + .sc-card-heading { display: grid; gap: 8px; } + .sc-card-header h2 { margin: 0; font-size: 16px; font-weight: 600; line-height: 1; letter-spacing: -0.01em; } + .sc-card-header p { margin: 0; color: var(--muted-foreground); font-size: 14px; line-height: 20px; } + .sc-card-content { display: flex; min-height: 0; flex-direction: column; padding: 0 24px; } + .sc-chart { position: relative; flex: none; } + .sc-chart > * { display: block; } + .sc-chart-clip .ts-chart { overflow: hidden !important; } + .sc-card-footer { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; padding: 0 24px; font-size: 14px; line-height: 1; } + .sc-chart-footer { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; margin-top: 12px; color: var(--muted-foreground); font-size: 12px; } + .sc-legend-item { display: inline-flex; align-items: center; gap: 6px; } + .sc-legend-dot { width: 8px; height: 8px; border-radius: 2px; } + .sc-legend-icon { width: 12px; height: 12px; fill: none; stroke: var(--muted-foreground); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; } + .sc-pie-legend { display: flex; width: 260px; flex-wrap: wrap; justify-content: center; gap: 10px 16px; } + .sc-pie-legend .sc-legend-item { flex: 0 0 70px; justify-content: center; } + .sc-centered { justify-content: center; align-items: center; text-align: center; } + .sc-trend { display: flex; align-items: center; gap: 8px; font-weight: 500; } + .sc-trend svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; } + .sc-footer-note { color: var(--muted-foreground); } + .sc-example .ts-chart { color: var(--muted-foreground); } + .sc-example .recharts-surface { overflow: visible; } + .sc-example .ts-chart__grid line, .sc-example .ts-chart__polar-grid path, .sc-example .ts-chart__polar-grid line { stroke: var(--border); } + .sc-example .ts-chart__axes text, .sc-example .ts-chart__polar-grid text { fill: var(--muted-foreground); font-size: 12px; } + .sc-example .ts-chart-tooltip { + min-width: 128px; + padding: 7px 10px !important; + border: 1px solid var(--border) !important; + border-radius: 8px !important; + background: var(--card) !important; + color: var(--card-foreground) !important; + box-shadow: 0 2px 6px rgb(0 0 0 / 8%); + font-size: 12px; + } + .sc-advanced-tooltip { display: grid; width: 158px; gap: 6px; } + .sc-advanced-row { display: grid; grid-template-columns: 10px 1fr auto; align-items: center; gap: 6px; } + .sc-advanced-swatch { width: 10px; height: 10px; border-radius: 2px; } + .sc-advanced-value { display: flex; align-items: baseline; gap: 2px; color: var(--foreground); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 500; font-variant-numeric: tabular-nums; } + .sc-advanced-unit { color: var(--muted-foreground); font-family: inherit; font-weight: 400; } + .sc-advanced-total { display: flex; align-items: center; margin-top: 2px; padding-top: 7px; border-top: 1px solid var(--border); color: var(--foreground); font-weight: 500; } + .sc-advanced-total .sc-advanced-value { margin-left: auto; } + .sc-shadcn-tooltip { display: grid; gap: 5px; } + .sc-tooltip-label { color: var(--foreground); font-weight: 500; } + .sc-shadcn-tooltip-row { display: grid; grid-template-columns: 9px minmax(0, 1fr) auto; align-items: center; gap: 7px; color: var(--muted-foreground); } + .sc-tooltip-no-indicator .sc-shadcn-tooltip-row { grid-template-columns: minmax(0, 1fr) auto; } + .sc-tooltip-dot { width: 8px; height: 8px; border-radius: 2px; } + .sc-tooltip-line { width: 3px; height: 16px; border-radius: 2px; } + .sc-tooltip-icon { width: 11px; height: 11px; fill: none; stroke: var(--muted-foreground); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; } + .sc-tooltip-value { display: flex; align-items: baseline; gap: 2px; color: var(--foreground); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 500; font-variant-numeric: tabular-nums; } + .sc-tooltip-value span { color: var(--muted-foreground); font-family: inherit; font-weight: 400; } + .sc-tooltip-total { display: flex; justify-content: space-between; margin-top: 1px; padding-top: 7px; border-top: 1px solid var(--border); color: var(--foreground); font-weight: 500; } + .sc-interactive-area { gap: 0; padding-top: 0; } + .sc-interactive-area .sc-card-header { display: flex; align-items: center; gap: 16px; padding-top: 20px; padding-bottom: 20px; border-bottom: 1px solid var(--border); } + .sc-interactive-area .sc-card-heading { flex: 1; gap: 4px; } + .sc-interactive-area .sc-card-content { padding-top: 24px; } + .sc-interactive-area .sc-chart-footer { margin-top: 8px; } + .sc-interactive-bar { gap: 0; padding: 0 0 30px; } + .sc-interactive-bar .sc-card-header { display: flex; min-height: 99px; align-items: stretch; gap: 0; padding: 0; border-bottom: 1px solid var(--border); } + .sc-interactive-bar .sc-card-heading { flex: 1; align-content: center; gap: 4px; padding: 16px 24px; } + .sc-interactive-bar .sc-card-action { display: flex; margin: 0; } + .sc-interactive-bar .sc-card-content { padding: 43px 24px 0; } + .sc-interactive-line { gap: 0; padding: 0; } + .sc-interactive-line .sc-card-header { display: flex; min-height: 99px; align-items: stretch; gap: 0; padding: 0; border-bottom: 1px solid var(--border); } + .sc-interactive-line .sc-card-heading { flex: 1; align-content: center; gap: 4px; padding: 16px 24px; } + .sc-interactive-line .sc-card-action { display: flex; margin: 0; } + .sc-interactive-line .sc-card-content { padding: 43px 24px 0; } + .sc-interactive-pie .sc-card-header { display: flex; align-items: flex-start; gap: 16px; padding-bottom: 0; } + .sc-interactive-pie .sc-card-heading { flex: 1; gap: 4px; } + .sc-interactive-pie .sc-card-content { padding-bottom: 44px; } + .sc-interactive-pie .sc-chart { transform: translateY(55px); } + .sc-interactive-pie .sc-card-action { transform: translateY(48px); } + .sc-card-action { margin-left: auto; } + .sc-select-display { position: relative; display: flex; min-width: 130px; height: 36px; align-items: center; gap: 8px; padding: 0 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--background); color: var(--foreground); font-size: 14px; box-shadow: 0 1px 2px rgb(0 0 0 / 4%); } + .sc-select-display:focus-within { outline: 2px solid var(--foreground); outline-offset: 2px; } + .sc-select-display select { min-width: 0; flex: 1; appearance: none; border: 0; outline: 0; background: transparent; color: inherit; font: inherit; cursor: pointer; } + .sc-select-display svg { width: 14px; height: 14px; flex: none; color: var(--muted-foreground); pointer-events: none; } + .sc-select-swatch { width: 12px; height: 12px; flex: none; border-radius: 3px; } + .sc-bar-metric { display: grid; width: 168px; align-content: center; gap: 6px; padding: 16px 32px; border: 0; border-left: 1px solid var(--border); background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; } + .sc-bar-metric[data-active='true'] { background: var(--muted); } + .sc-bar-metric:focus-visible { outline: 2px solid var(--foreground); outline-offset: -3px; } + .sc-bar-metric span { color: var(--muted-foreground); font-size: 12px; } + .sc-bar-metric strong { font-size: 30px; line-height: 1; letter-spacing: -0.03em; } +` diff --git a/benchmarks/conformance/shared/shadcn-interaction.test.ts b/benchmarks/conformance/shared/shadcn-interaction.test.ts new file mode 100644 index 00000000..d98d19ee --- /dev/null +++ b/benchmarks/conformance/shared/shadcn-interaction.test.ts @@ -0,0 +1,132 @@ +import { act } from 'react' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { createShadcnTanStackExample } from './shadcn-catalog-tanstack' + +const input = { + width: 600, + height: 500, + revision: 0, + interactive: true, +} as const + +let matchMediaDescriptor: PropertyDescriptor | undefined + +beforeEach(() => { + matchMediaDescriptor = Object.getOwnPropertyDescriptor(window, 'matchMedia') + Object.defineProperty(window, 'matchMedia', { + configurable: true, + value: vi.fn(() => ({ matches: true })), + }) +}) + +afterEach(() => { + if (matchMediaDescriptor) { + Object.defineProperty(window, 'matchMedia', matchMediaDescriptor) + } else { + Reflect.deleteProperty(window, 'matchMedia') + } +}) + +describe('shadcn interactive chart shells', () => { + it('filters the interactive area chart from its range control', () => { + const container = document.createElement('div') + const handle = createShadcnTanStackExample('chart-area-interactive').mount( + container, + input, + ) + const select = container.querySelector('select') + const path = () => + container + .querySelector('g.ts-chart__area path') + ?.getAttribute('d') + const initialPath = path() + + expect(select?.value).toBe('90d') + act(() => { + if (!select) return + select.value = '7d' + select.dispatchEvent(new Event('change', { bubbles: true })) + }) + + expect(select?.value).toBe('7d') + expect(path()).not.toBe(initialPath) + expect( + [...container.querySelectorAll('g.ts-chart__axes text')].map( + (label) => label.textContent, + ), + ).toEqual(['Jun 23', 'Jun 25', 'Jun 27', 'Jun 29']) + + handle.destroy() + }) + + it.each(['bar', 'line'] as const)( + 'switches the interactive %s chart between desktop and mobile', + (family) => { + const container = document.createElement('div') + const handle = createShadcnTanStackExample( + `chart-${family}-interactive`, + ).mount(container, input) + const mobile = [...container.querySelectorAll('button')].find((button) => + button.textContent?.includes('Mobile'), + ) + const geometry = () => + family === 'bar' + ? container + .querySelector('g.ts-chart__bar-y rect') + ?.getAttribute('height') + : container.querySelector('g.ts-chart__line path')?.getAttribute('d') + const paint = () => + family === 'bar' + ? container + .querySelector('g.ts-chart__bar-y rect') + ?.getAttribute('fill') + : container + .querySelector('g.ts-chart__line path') + ?.getAttribute('stroke') + const initialGeometry = geometry() + const initialPaint = paint() + + act(() => mobile?.click()) + + expect(mobile?.getAttribute('aria-pressed')).toBe('true') + expect(geometry()).not.toBe(initialGeometry) + expect(paint()).not.toBe(initialPaint) + + handle.destroy() + }, + ) + + it('changes the active pie slice, swatch, and center value', () => { + const container = document.createElement('div') + const handle = createShadcnTanStackExample('chart-pie-interactive').mount( + container, + input, + ) + const select = container.querySelector('select') + + expect(select?.options).toHaveLength(5) + expect(container.querySelector('svg.ts-chart')?.textContent).toContain( + '186Visitors', + ) + act(() => { + if (!select) return + select.value = 'may' + select.dispatchEvent(new Event('change', { bubbles: true })) + }) + + expect(container.querySelector('svg.ts-chart')?.textContent).toContain( + '209Visitors', + ) + expect( + container + .querySelector('g.ts-chart__arc[data-ts-key="active-month"] path') + ?.getAttribute('fill'), + ).toContain('--chart-5') + expect( + container.querySelector('.sc-select-swatch')?.style + .background, + ).toContain('--chart-5') + + handle.destroy() + }) +}) diff --git a/benchmarks/conformance/shared/shadcn-motion.test.ts b/benchmarks/conformance/shared/shadcn-motion.test.ts new file mode 100644 index 00000000..fb649678 --- /dev/null +++ b/benchmarks/conformance/shared/shadcn-motion.test.ts @@ -0,0 +1,97 @@ +import { describe, expect, it } from 'vitest' +import { createChartScene, type StaticChartDefinition } from '@tanstack/charts' +import { shadcnDashboardChartDefinition } from '../cases/127-shadcn-dashboard/tanstack' +import { barMultipleDefinition } from '../cases/128-shadcn-bar-multiple/tanstack' +import { pieDonutTextDefinition } from '../cases/129-shadcn-pie-donut-text/tanstack' +import { radarMultipleDefinition } from '../cases/130-shadcn-radar-multiple/tanstack' +import { radialTextDefinition } from '../cases/131-shadcn-radial-text/tanstack' +import { advancedTooltipDefinition } from '../cases/132-shadcn-tooltip-advanced/tanstack' +import { createShadcnTanStackExample } from './shadcn-catalog-tanstack' +import type { ShadcnMonthDatum } from './shadcn-catalog-data' +import { focusGroupAngle } from '@tanstack/charts/polar' +import { + createShadcnSpringRenderer, + shadcnSpringTransition, +} from './shadcn-motion' + +describe('shadcn chart motion', () => { + it('uses one physical spring preset for the collection', () => { + expect(shadcnSpringTransition).toEqual({ + type: 'spring', + stiffness: 170, + damping: 18, + mass: 1, + }) + expect(createShadcnSpringRenderer().id).toBe('svg:svg-motion') + }) + + it('keeps dedicated and generated definitions free of duplicate spring policy', () => { + const dedicatedDefinitions = [ + shadcnDashboardChartDefinition([]), + barMultipleDefinition, + pieDonutTextDefinition, + radarMultipleDefinition, + radialTextDefinition, + advancedTooltipDefinition, + ] + const generatedDefinitions = [ + 'chart-area-default', + 'chart-bar-default', + 'chart-line-default', + 'chart-pie-simple', + 'chart-radar-default', + 'chart-radial-simple', + 'chart-tooltip-default', + ].map((name) => createShadcnTanStackExample(name).definition) + + for (const definition of [ + ...dedicatedDefinitions, + ...generatedDefinitions, + ]) { + expect( + 'motion' in definition ? definition.motion : undefined, + ).toBeUndefined() + } + }) + + it('keeps the native focus marker enabled across generated families', () => { + const definitions = [ + 'chart-area-default', + 'chart-bar-default', + 'chart-line-default', + 'chart-pie-simple', + 'chart-radar-default', + 'chart-radial-simple', + 'chart-tooltip-default', + ].map((name) => createShadcnTanStackExample(name).definition) + + for (const definition of definitions) { + expect(definition.focus).not.toBe(false) + expect(definition.focusRing).not.toBe(false) + } + expect(definitions[4]?.focus).toBe(focusGroupAngle) + expect(definitions[3]?.focus).toBe('nearest') + expect(definitions[5]?.focus).toBe('nearest') + }) + + it('groups generated radar points by semantic angle and series', () => { + const definition = + createShadcnTanStackExample('chart-radar-legend').definition + expect('chart' in definition).toBe(false) + if ('chart' in definition) return + const scene = createChartScene( + definition as unknown as StaticChartDefinition< + ShadcnMonthDatum, + string, + number + >, + { width: 300, height: 250 }, + ) + const january = scene.points.filter((point) => point.xValue === 'January') + + expect(january.map((point) => point.group)).toEqual(['desktop', 'mobile']) + expect(focusGroupAngle.group(scene.points, { point: january[0]! })).toEqual( + january, + ) + }) +}) diff --git a/benchmarks/conformance/shared/shadcn-motion.ts b/benchmarks/conformance/shared/shadcn-motion.ts new file mode 100644 index 00000000..34331961 --- /dev/null +++ b/benchmarks/conformance/shared/shadcn-motion.ts @@ -0,0 +1,21 @@ +import { motion } from '@tanstack/charts/motion' +import type { ChartValue } from '@tanstack/charts' +import type { ChartMotionSpringTransition } from '@tanstack/charts/motion' + +export const shadcnSpringTransition = { + type: 'spring', + stiffness: 170, + damping: 18, + mass: 1, +} as const satisfies ChartMotionSpringTransition + +export function createShadcnSpringRenderer< + TDatum = unknown, + TXValue extends ChartValue = ChartValue, + TYValue extends ChartValue = ChartValue, +>() { + return motion({ + initial: 'always', + transition: shadcnSpringTransition, + }) +} diff --git a/benchmarks/conformance/types.ts b/benchmarks/conformance/types.ts index 85c30f8f..923d850b 100644 --- a/benchmarks/conformance/types.ts +++ b/benchmarks/conformance/types.ts @@ -348,6 +348,8 @@ export interface ConformanceInteractionScenario { export interface ConformanceCaseMeta { schemaVersion: 1 referenceRenderer?: ConformanceReferenceRenderer + collections?: readonly string[] + height?: number order: number id: string title: string diff --git a/benchmarks/entries/charts-motion-definition.ts b/benchmarks/entries/charts-motion-definition.ts new file mode 100644 index 00000000..ee59f8c1 --- /dev/null +++ b/benchmarks/entries/charts-motion-definition.ts @@ -0,0 +1 @@ +export { stagger } from '@tanstack/charts/motion/definition' diff --git a/docs/comparison.md b/docs/comparison.md index 70a08e42..b4316fc2 100644 --- a/docs/comparison.md +++ b/docs/comparison.md @@ -12,14 +12,14 @@ turning untested behavior into a checkmark. | Library | Package | Measured source | | -------------------------------------------------------------------------------------- | -------------------- | ------------------- | -| [TanStack Charts](./overview.md) | `@tanstack/charts` | workspace `b9d8c54` | +| [TanStack Charts](./overview.md) | `@tanstack/charts` | workspace `56567ec` | | [Chart.js](https://www.chartjs.org/docs/latest/) | `chart.js` | npm `4.5.1` | | [Apache ECharts](https://echarts.apache.org/handbook/en/best-practices/canvas-vs-svg/) | `echarts` | npm `6.1.0` | | [Recharts](https://recharts.github.io/en-US/) | `recharts` | npm `3.10.1` | | [Observable Plot](https://observablehq.com/plot/features/plots) | `@observablehq/plot` | npm `0.6.17` | The competitor versions are exact package pins, not latest versions inferred -at page render time. The measured TanStack workspace revision is `b9d8c54`. +at page render time. The measured TanStack workspace revision is `56567ec`. ## Capability matrix @@ -90,7 +90,7 @@ output model. ## Bundle snapshot -Baseline date: `2026-08-12`. +Baseline date: `2026-08-14`. Controlled ranges cover 12 independently built, minified browser consumers: line, bar, area, and scatter at basic, interactive, and advanced tiers. Only @@ -106,7 +106,7 @@ Vega-Lite, AG Charts, and uPlot main exports were read from Bundlephobia on July | Library | Bundle size | React externalized | Evidence | | ------------------ | -------------------------------------- | -----------------: | ---------------------------------------------------------- | -| TanStack Charts | 37.46–43.41 KiB | — | Controlled suite | +| TanStack Charts | 37.63–43.59 KiB | — | Controlled suite | | D3 | 90 KB gzip | — | External main export | | Chart.js | 44.70–58.21 KiB | — | Controlled suite | | Apache ECharts | 153.10–173.18 KiB | — | Controlled suite | diff --git a/docs/config.json b/docs/config.json index 6964efdf..406ec229 100644 --- a/docs/config.json +++ b/docs/config.json @@ -766,6 +766,290 @@ { "label": "Drillable Flare sunburst", "to": "/charts/catalog/charts/126-drillable-sunburst/" + }, + { + "label": "shadcn dashboard", + "to": "/charts/catalog/charts/127-shadcn-dashboard/" + }, + { + "label": "shadcn bar chart – multiple", + "to": "/charts/catalog/charts/128-shadcn-bar-multiple/" + }, + { + "label": "shadcn pie chart – donut with text", + "to": "/charts/catalog/charts/129-shadcn-pie-donut-text/" + }, + { + "label": "shadcn radar chart – multiple", + "to": "/charts/catalog/charts/130-shadcn-radar-multiple/" + }, + { + "label": "shadcn radial chart – text", + "to": "/charts/catalog/charts/131-shadcn-radial-text/" + }, + { + "label": "shadcn tooltip – advanced", + "to": "/charts/catalog/charts/132-shadcn-tooltip-advanced/" + }, + { + "label": "shadcn Area Chart - Axes", + "to": "/charts/catalog/charts/133-shadcn-area-axes/" + }, + { + "label": "shadcn Area Chart", + "to": "/charts/catalog/charts/134-shadcn-area-default/" + }, + { + "label": "shadcn Area Chart - Gradient", + "to": "/charts/catalog/charts/135-shadcn-area-gradient/" + }, + { + "label": "shadcn Area Chart - Icons", + "to": "/charts/catalog/charts/136-shadcn-area-icons/" + }, + { + "label": "shadcn Area Chart - Interactive", + "to": "/charts/catalog/charts/137-shadcn-area-interactive/" + }, + { + "label": "shadcn Area Chart - Legend", + "to": "/charts/catalog/charts/138-shadcn-area-legend/" + }, + { + "label": "shadcn Area Chart - Linear", + "to": "/charts/catalog/charts/139-shadcn-area-linear/" + }, + { + "label": "shadcn Area Chart - Stacked Expanded", + "to": "/charts/catalog/charts/140-shadcn-area-stacked-expand/" + }, + { + "label": "shadcn Area Chart - Stacked", + "to": "/charts/catalog/charts/141-shadcn-area-stacked/" + }, + { + "label": "shadcn Area Chart - Step", + "to": "/charts/catalog/charts/142-shadcn-area-step/" + }, + { + "label": "shadcn Bar Chart - Active", + "to": "/charts/catalog/charts/143-shadcn-bar-active/" + }, + { + "label": "shadcn Bar Chart", + "to": "/charts/catalog/charts/144-shadcn-bar-default/" + }, + { + "label": "shadcn Bar Chart - Horizontal", + "to": "/charts/catalog/charts/145-shadcn-bar-horizontal/" + }, + { + "label": "shadcn Bar Chart - Interactive", + "to": "/charts/catalog/charts/146-shadcn-bar-interactive/" + }, + { + "label": "shadcn Bar Chart - Custom Label", + "to": "/charts/catalog/charts/147-shadcn-bar-label-custom/" + }, + { + "label": "shadcn Bar Chart - Label", + "to": "/charts/catalog/charts/148-shadcn-bar-label/" + }, + { + "label": "shadcn Bar Chart - Mixed", + "to": "/charts/catalog/charts/149-shadcn-bar-mixed/" + }, + { + "label": "shadcn Bar Chart - Negative", + "to": "/charts/catalog/charts/150-shadcn-bar-negative/" + }, + { + "label": "shadcn Bar Chart - Stacked + Legend", + "to": "/charts/catalog/charts/151-shadcn-bar-stacked/" + }, + { + "label": "shadcn Line Chart", + "to": "/charts/catalog/charts/152-shadcn-line-default/" + }, + { + "label": "shadcn Line Chart - Dots Colors", + "to": "/charts/catalog/charts/153-shadcn-line-dots-colors/" + }, + { + "label": "shadcn Line Chart - Custom Dots", + "to": "/charts/catalog/charts/154-shadcn-line-dots-custom/" + }, + { + "label": "shadcn Line Chart - Dots", + "to": "/charts/catalog/charts/155-shadcn-line-dots/" + }, + { + "label": "shadcn Line Chart - Interactive", + "to": "/charts/catalog/charts/156-shadcn-line-interactive/" + }, + { + "label": "shadcn Line Chart - Custom Label", + "to": "/charts/catalog/charts/157-shadcn-line-label-custom/" + }, + { + "label": "shadcn Line Chart - Label", + "to": "/charts/catalog/charts/158-shadcn-line-label/" + }, + { + "label": "shadcn Line Chart - Linear", + "to": "/charts/catalog/charts/159-shadcn-line-linear/" + }, + { + "label": "shadcn Line Chart - Multiple", + "to": "/charts/catalog/charts/160-shadcn-line-multiple/" + }, + { + "label": "shadcn Line Chart - Step", + "to": "/charts/catalog/charts/161-shadcn-line-step/" + }, + { + "label": "shadcn Pie Chart - Donut Active", + "to": "/charts/catalog/charts/162-shadcn-pie-donut-active/" + }, + { + "label": "shadcn Pie Chart - Donut", + "to": "/charts/catalog/charts/163-shadcn-pie-donut/" + }, + { + "label": "shadcn Pie Chart - Interactive", + "to": "/charts/catalog/charts/164-shadcn-pie-interactive/" + }, + { + "label": "shadcn Pie Chart - Custom Label", + "to": "/charts/catalog/charts/165-shadcn-pie-label-custom/" + }, + { + "label": "shadcn Pie Chart - Label List", + "to": "/charts/catalog/charts/166-shadcn-pie-label-list/" + }, + { + "label": "shadcn Pie Chart - Label", + "to": "/charts/catalog/charts/167-shadcn-pie-label/" + }, + { + "label": "shadcn Pie Chart - Legend", + "to": "/charts/catalog/charts/168-shadcn-pie-legend/" + }, + { + "label": "shadcn Pie Chart - Separator None", + "to": "/charts/catalog/charts/169-shadcn-pie-separator-none/" + }, + { + "label": "shadcn Pie Chart", + "to": "/charts/catalog/charts/170-shadcn-pie-simple/" + }, + { + "label": "shadcn Pie Chart - Stacked", + "to": "/charts/catalog/charts/171-shadcn-pie-stacked/" + }, + { + "label": "shadcn Radar Chart", + "to": "/charts/catalog/charts/172-shadcn-radar-default/" + }, + { + "label": "shadcn Radar Chart - Dots", + "to": "/charts/catalog/charts/173-shadcn-radar-dots/" + }, + { + "label": "shadcn Radar Chart - Grid Circle Filled", + "to": "/charts/catalog/charts/174-shadcn-radar-grid-circle-fill/" + }, + { + "label": "shadcn Radar Chart - Grid Circle - No lines", + "to": "/charts/catalog/charts/175-shadcn-radar-grid-circle-no-lines/" + }, + { + "label": "shadcn Radar Chart - Grid Circle", + "to": "/charts/catalog/charts/176-shadcn-radar-grid-circle/" + }, + { + "label": "shadcn Radar Chart - Grid Custom", + "to": "/charts/catalog/charts/177-shadcn-radar-grid-custom/" + }, + { + "label": "shadcn Radar Chart - Grid Filled", + "to": "/charts/catalog/charts/178-shadcn-radar-grid-fill/" + }, + { + "label": "shadcn Radar Chart - Grid None", + "to": "/charts/catalog/charts/179-shadcn-radar-grid-none/" + }, + { + "label": "shadcn Radar Chart - Icons", + "to": "/charts/catalog/charts/180-shadcn-radar-icons/" + }, + { + "label": "shadcn Radar Chart - Custom Label", + "to": "/charts/catalog/charts/181-shadcn-radar-label-custom/" + }, + { + "label": "shadcn Radar Chart - Legend", + "to": "/charts/catalog/charts/182-shadcn-radar-legend/" + }, + { + "label": "shadcn Radar Chart - Lines Only", + "to": "/charts/catalog/charts/183-shadcn-radar-lines-only/" + }, + { + "label": "shadcn Radar Chart - Radius Axis", + "to": "/charts/catalog/charts/184-shadcn-radar-radius/" + }, + { + "label": "shadcn Radial Chart - Grid", + "to": "/charts/catalog/charts/185-shadcn-radial-grid/" + }, + { + "label": "shadcn Radial Chart - Label", + "to": "/charts/catalog/charts/186-shadcn-radial-label/" + }, + { + "label": "shadcn Radial Chart - Shape", + "to": "/charts/catalog/charts/187-shadcn-radial-shape/" + }, + { + "label": "shadcn Radial Chart", + "to": "/charts/catalog/charts/188-shadcn-radial-simple/" + }, + { + "label": "shadcn Radial Chart - Stacked", + "to": "/charts/catalog/charts/189-shadcn-radial-stacked/" + }, + { + "label": "shadcn Tooltip - Default", + "to": "/charts/catalog/charts/190-shadcn-tooltip-default/" + }, + { + "label": "shadcn Tooltip - Formatter", + "to": "/charts/catalog/charts/191-shadcn-tooltip-formatter/" + }, + { + "label": "shadcn Tooltip - Icons", + "to": "/charts/catalog/charts/192-shadcn-tooltip-icons/" + }, + { + "label": "shadcn Tooltip - Line Indicator", + "to": "/charts/catalog/charts/193-shadcn-tooltip-indicator-line/" + }, + { + "label": "shadcn Tooltip - No Indicator", + "to": "/charts/catalog/charts/194-shadcn-tooltip-indicator-none/" + }, + { + "label": "shadcn Tooltip - Custom label", + "to": "/charts/catalog/charts/195-shadcn-tooltip-label-custom/" + }, + { + "label": "shadcn Tooltip - Label Formatter", + "to": "/charts/catalog/charts/196-shadcn-tooltip-label-formatter/" + }, + { + "label": "shadcn Tooltip - No Label", + "to": "/charts/catalog/charts/197-shadcn-tooltip-label-none/" } ] }, diff --git a/docs/guides/dynamic-data-and-animation.md b/docs/guides/dynamic-data-and-animation.md index 2bf081de..6d84776c 100644 --- a/docs/guides/dynamic-data-and-animation.md +++ b/docs/guides/dynamic-data-and-animation.md @@ -109,12 +109,14 @@ Use `motion()` when animation quality is part of the chart contract: ```ts import { scaleUtc } from 'd3-scale' import { motion } from '@tanstack/charts/motion' +import { stagger } from '@tanstack/charts/motion/definition' import { mountChartRenderer } from '@tanstack/charts/renderer' import { scaleLinear } from '@tanstack/charts/scales/linear' const definition = defineChart({ motion: { - transition: { type: 'spring', stiffness: 170, damping: 18, mass: 1 }, + path: 'morph', + ...stagger({ each: 35, roles: 'line', by: 'series' }), }, marks: [ lineY(rows, { @@ -135,7 +137,9 @@ const definition = defineChart({ const host = mountChartRenderer(container, { definition, - renderer: motion(), + renderer: motion({ + transition: { type: 'spring', stiffness: 170, damping: 18, mass: 1 }, + }), width: 640, height: 360, ariaLabel: 'Actual and forecast revenue', @@ -178,17 +182,20 @@ rules, bands, labels, and marker retain DOM identity and spring velocity while focus retargets. Default SVG, Canvas, and native surfaces paint the same guide at its current target without importing the browser motion runtime. -Spring updates always retarget immediately; a returned update delay is ignored -so incoming momentum cannot freeze. Use delays for spring enter/exit -choreography or any tween phase. +`stagger()` contributes only a context-aware delay, so it composes with +transition and path fields through normal object spread. Spring updates always +retarget immediately; an update delay is ignored so incoming momentum cannot +freeze. Use delays for spring enter/exit choreography or any tween phase. -The renderer grows entering bars from their semantic baseline, reveals entering -line groups, staggers bar entrances, morphs compatible numeric SVG geometry, -and fades incompatible keyed topology. Authored delay replaces automatic -staggering for that target. +The renderer grows entering Cartesian bars, lines, and areas from their +semantic baseline; grows radial paths from the polar center; sweeps arcs +through their authored angle; staggers bar entrances; morphs compatible +numeric SVG geometry; and keeps removed keys painted through their exit +transition. Authored delay replaces automatic staggering for that target. `motion()` respects reduced motion and does not animate resize-only updates by -default. It adopts server-rendered SVG without replaying entrance motion. +default. It adopts server-rendered SVG without replaying entrance motion by +default; pass `initial: 'always'` to replay entrance motion after hydration. Static SVG and Canvas accept the same definitions but paint the final state. See the [Motion reference](../reference/motion.md) for the complete cascade, diff --git a/docs/guides/tooltips-and-focus.md b/docs/guides/tooltips-and-focus.md index aa66960b..15f9ef4c 100644 --- a/docs/guides/tooltips-and-focus.md +++ b/docs/guides/tooltips-and-focus.md @@ -255,6 +255,51 @@ transient tooltip. Click, Enter, or Space can still pin the focused point; only the pinned surface and adapter body are mounted. The default is `visibility: 'focus'`. +## Tooltip motion + +The built-in tooltip uses the active motion renderer's transition for entry, +movement between points, retargeting, and exit: + +```ts +import { motion } from '@tanstack/charts/motion' + +const renderer = motion({ + transition: { + type: 'spring', + stiffness: 170, + damping: 18, + mass: 1, + }, +}) + +const definition = defineChart({ marks, tooltip }) + +mountChartRenderer(container, { + definition, + renderer, + width: 640, + height: 360, + ariaLabel: 'Monthly visitors', +}) +``` + +A static chart-level transition can refine the renderer fallback: + +```ts +const definition = defineChart({ + marks, + motion: { + transition: { type: 'spring', stiffness: 170, damping: 18, mass: 1 }, + }, + tooltip, +}) +``` + +Set `tooltip.motion` to another transition to override both, or set it to +`false` to keep the tooltip immediate. These options customize the active +motion renderer; a static renderer stays immediate and does not import spring +physics. The renderer's reduced-motion policy also applies to the tooltip. + ## Application-owned pointer timing Set definition `pointer: false` when the application decides when inspection diff --git a/docs/reference/index.md b/docs/reference/index.md index 42259a6f..af31ef73 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -156,7 +156,8 @@ aggregate `/scales` export. | `@tanstack/charts/mark/composite` | `compositeMark` and `CompositeMarkOptions` | | `@tanstack/charts/mark/decorative` | `decorative` | | `@tanstack/charts/mark/scale-values` | `createMarkWithScaleValues` | -| `@tanstack/charts/motion` | `motion`, `ChartMotionOptions`, and renderer-neutral motion types | +| `@tanstack/charts/motion` | `motion`, `stagger`, `ChartMotionOptions`, and renderer-neutral motion types | +| `@tanstack/charts/motion/definition` | Isolated `stagger` and `ChartMotionStaggerOptions` without the renderer or spring solver | | `@tanstack/charts/network/force` | `forceLayout`, built-in descriptors, named D3-compatible force factories, private working-clone context, settled node/link result, and lineage types | | `@tanstack/charts/network/sankey` | `sankeyDiagram`, shorthand and callable alignment types, responsive layout options, immutable node/link values, comparator contexts, and lineage types | | `@tanstack/charts/polar` | `pie`, `polar`, `focusGroupAngle`, `radialArc`, `radialBarRadius`, `radialBarAngle`, other radial marks, and radial/angle guides | diff --git a/docs/reference/marks/bar-and-rect.md b/docs/reference/marks/bar-and-rect.md index b5d3d4a2..a5147b45 100644 --- a/docs/reference/marks/bar-and-rect.md +++ b/docs/reference/marks/bar-and-rect.md @@ -32,23 +32,27 @@ function barY( ### Options -| Option | Type | Default | Meaning | -| -------------- | ------------------------------------ | ------------------------------ | ------------------------------------------------ | -| `id` | `string` | Layer-derived | Stable mark ID | -| `x` | `Channel` | Row index | Bar category or center | -| `y` | `Channel` | Numeric datum | Length; implicitly stacked at each x | -| `y1` | `number \| Channel` | Implicit stack start | Explicit baseline endpoint | -| `y2` | `number \| Channel` | Implicit stack end | Explicit value endpoint; takes precedence over y | -| `z` | `Channel` | No group | Group identity; color fallback when omitted | -| `color` | `Channel` | `z` | Independent value sent to the chart color scale | -| `key` | `Channel` | Top/nested `id`, x, then index | Stable scene and interaction identity | -| `fill` | `VisualChannel` | Resolved `color` | Final bar paint override | -| `fillOpacity` | `number` | SVG default | Fill opacity | -| `layout` | `GroupLayout \| StackLayout` | Implicit diverging stack | Configures grouping or stack order/offset | -| `inset` | `number` | `0` | Pixels removed from both categorical edges | -| `maxThickness` | `number` | Unbounded | Maximum painted width after grouping and inset | -| `radius` | `number` | None | SVG rectangle corner radius | -| `states` | `readonly ChartMarkState[]` | None | Focus-driven presentation overrides | +| Option | Type | Default | Meaning | +| ----------------- | ------------------------------------ | ------------------------------ | ------------------------------------------------ | +| `id` | `string` | Layer-derived | Stable mark ID | +| `x` | `Channel` | Row index | Bar category or center | +| `y` | `Channel` | Numeric datum | Length; implicitly stacked at each x | +| `y1` | `number \| Channel` | Implicit stack start | Explicit baseline endpoint | +| `y2` | `number \| Channel` | Implicit stack end | Explicit value endpoint; takes precedence over y | +| `z` | `Channel` | No group | Group identity; color fallback when omitted | +| `color` | `Channel` | `z` | Independent value sent to the chart color scale | +| `key` | `Channel` | Top/nested `id`, x, then index | Stable scene and interaction identity | +| `fill` | `VisualChannel` | Resolved `color` | Final bar paint override | +| `fillOpacity` | `number` | SVG default | Fill opacity | +| `stroke` | `VisualChannel` | None | Bar outline color | +| `strokeOpacity` | `number` | SVG default | Bar outline opacity | +| `strokeWidth` | `number` | SVG default | Bar outline width | +| `strokeDasharray` | `VisualChannel` | None | SVG outline dash pattern | +| `layout` | `GroupLayout \| StackLayout` | Implicit diverging stack | Configures grouping or stack order/offset | +| `inset` | `number` | `0` | Pixels removed from both categorical edges | +| `maxThickness` | `number` | Unbounded | Maximum painted width after grouping and inset | +| `radius` | `number` | None | SVG rectangle corner radius | +| `states` | `readonly ChartMarkState[]` | None | Focus-driven presentation overrides | The interaction point is at the group-band center and the `y2`/`y` endpoint. Its semantic `xValue` is x and its `yValue` is the value endpoint. diff --git a/docs/reference/marks/polar.md b/docs/reference/marks/polar.md index 560234bf..53f09798 100644 --- a/docs/reference/marks/polar.md +++ b/docs/reference/marks/polar.md @@ -338,7 +338,8 @@ function angleGrid(options?: AngleGridOptions): PolarGuide `radialGrid` draws radius values as circles or polygons. Supply explicit `values`, or let `ticks` request values from the configured radius scale. Labels are off by default. Label angle, offset, rotation, format, fill, and -font size are configurable. +font size are configurable. Ring `fill` and `fillOpacity` can layer filled +circle or polygon grids behind the chart marks. `angleGrid` draws spokes for explicit `values` or the configured angle domain. It can show labels around the circumference with `format` and `labelOffset`. diff --git a/docs/reference/motion.md b/docs/reference/motion.md index 0d83ddca..9316efea 100644 --- a/docs/reference/motion.md +++ b/docs/reference/motion.md @@ -24,29 +24,41 @@ function motion< >(options?: ChartMotionOptions): ChartRenderer interface ChartMotionOptions { - initial?: boolean + initial?: boolean | 'always' transition?: ChartMotionTransition respectReducedMotion?: boolean resize?: boolean } ``` -| Option | Default | Meaning | -| ---------------------- | --------------------------------------------- | -------------------------------------------------- | -| `initial` | `true` | Animate the first client render | -| `transition` | 1,100 ms tween with the default entrance ease | Renderer-wide fallback | -| `respectReducedMotion` | `true` | Snap when `prefers-reduced-motion: reduce` matches | -| `resize` | `false` | Animate updates caused only by a chart size change | - -Server-rendered SVG is adopted without replaying entrance motion. Keyed updates -start from painted geometry. An interrupted spring carries its sampled value and -velocity into the new target. A spring has no duration; it finishes when both -`restSpeed` and `restDelta` are satisfied, with a 10-second safety limit. +| Option | Default | Meaning | +| ---------------------- | --------------------------------------------- | ------------------------------------------------------------- | +| `initial` | `true` | Animate first client paint; `always` also replays adopted SVG | +| `transition` | 1,100 ms tween with the default entrance ease | Renderer-wide fallback | +| `respectReducedMotion` | `true` | Snap when `prefers-reduced-motion: reduce` matches | +| `resize` | `false` | Animate updates caused only by a chart size change | + +Server-rendered SVG is adopted without replaying entrance motion by default. +Set `initial: 'always'` when a hydrated chart should replay the same entrance +as a client-only mount. Keyed updates start from painted geometry. An +interrupted spring carries its sampled value and velocity into the new target. +A spring has no duration; it finishes when both `restSpeed` and `restDelta` are +satisfied, with a 10-second safety limit. + +Initial choreography follows geometry: Cartesian bars and paths grow from +their semantic baseline, radial lines and areas grow from the polar center, +and arcs sweep through their authored angle. Keyed removals stay painted +through their exit transition. Data-less `crosshair` marks use the same keyed focus-motion path. Rapid pointer or keyboard retargeting preserves the guide elements and incoming spring velocity; labels remain aligned to their moving rules. +The built-in HTML tooltip also consumes this renderer's transition. Entry, +movement, retargeting, and exit therefore use the same spring without copying +the transition into the chart definition. A static renderer keeps the tooltip +immediate and does not import the motion runtime. + Use the renderer-neutral host in vanilla applications: ```ts @@ -111,6 +123,8 @@ const definition = defineChart({ The cascade is renderer default, chart, mark, axis, specific guide, then the active focus-state transition. Same-type transitions inherit omitted fields. +Set `motion: false` at any definition scope to suppress inherited motion for +that scope. A more specific child can re-enable motion with its own definition. An authored `delay` replaces automatic entrance staggering for that target. Spring updates begin immediately even when a definition returns a delay, so a retarget cannot freeze incoming momentum. Spring enter and exit delays, and @@ -119,6 +133,58 @@ tween delays in every phase, are honored. All built-in marks accept `ChartMarkMotionOptions`. Nested polar marks also accept `motion`; their timing is merged below the parent `polar` mark. +| Scope | Motion input | +| ------------------------------ | ---------------------------------------------------- | +| Renderer fallback | `motion({ transition })` | +| Whole chart | `defineChart({ motion })` | +| Any built-in mark | The mark's `motion` option | +| Axis, including its grid lines | `x.axis.motion` or `y.axis.motion` | +| Tick rules | `axis.ticks.motion` | +| Tick labels | `axis.tickLabels.motion` | +| Axis label | `axis.label.motion` when `label` is an object | +| Crosshair or focus guide | The guide mark's `motion` option | +| HTML tooltip | Inherits chart motion; `tooltip.motion` overrides it | + +Grid lines use their axis policy because they are part of that axis's guide +system. `tooltip.motion: false` keeps the tooltip immediate even when chart +geometry animates. Legends and application-owned controls are not marks and do +not currently participate in the motion cascade. + +## Spreading timing utilities + +Use the isolated timing entry when the definition only needs motion policy: + +```ts +import { stagger } from '@tanstack/charts/motion/definition' + +const definition = defineChart({ + motion: { + path: 'morph', + ...stagger({ each: 35, by: 'series', roles: ['arc', 'bar'] }), + }, + marks, +}) +``` + +`stagger()` returns one context-aware `delay` field for direct object spread. +It uses `datumIndex` by default, can use `seriesIndex`, defaults to the `enter` +phase, and can filter by phase and semantic role. `offset` delays the first +target. Normal object-spread order controls precedence, so an explicit `delay` +written after `...stagger()` replaces it. + +```ts +interface ChartMotionStaggerOptions { + each: number + offset?: number + by?: 'datum' | 'series' + phase?: ChartMotionPhase | readonly ChartMotionPhase[] + roles?: ChartMotionRole | readonly ChartMotionRole[] +} +``` + +`stagger()` is also exported from `@tanstack/charts/motion`. The dedicated +`/motion/definition` entry excludes the SVG renderer and spring solver. + ## Timing types ```ts @@ -152,15 +218,18 @@ interface ChartRollingPathMotion { type ChartMotionPath = 'morph' | ChartRollingPathMotion -interface ChartMotionTiming { - delay?: number +interface ChartMotionTiming { + delay?: number | ((context: ChartMotionContext) => number | undefined) transition?: ChartMotionTransition path?: ChartMotionPath } type ChartMotionDefinition = - | ChartMotionTiming - | ((context: ChartMotionContext) => ChartMotionTiming | undefined) + | false + | ChartMotionTiming + | (( + context: ChartMotionContext, + ) => false | ChartMotionTiming | undefined) interface ChartMarkMotionOptions { motion?: ChartMotionDefinition diff --git a/examples/conformance/src/main.tsx b/examples/conformance/src/main.tsx index 6355de43..28419351 100644 --- a/examples/conformance/src/main.tsx +++ b/examples/conformance/src/main.tsx @@ -136,6 +136,14 @@ function CatalogApp() { ) } + if (route.view === 'collection') { + return route.collectionId === 'shadcn' ? ( + + ) : ( + + ) + } + if (route.view === 'case') { const entry = casesById.get(route.caseId) if (!entry) return @@ -162,6 +170,243 @@ function CatalogApp() { return } +function ShadcnCollectionPage({ + dark, + link, + setDark, +}: { + dark: boolean + link: RouteLinkFactory + setDark: (value: boolean) => void +}) { + const allEntries = useMemo(() => collectionCases('shadcn'), []) + const [search, setSearch] = useState('') + const [family, setFamily] = useState('all') + const [copied, setCopied] = useState(false) + const collectionFamilies = useMemo( + () => + [...new Set(allEntries.map((entry) => entry.family))].sort( + (left, right) => left.localeCompare(right), + ), + [allEntries], + ) + const entries = useMemo( + () => + allEntries.filter((entry) => { + if (family !== 'all' && entry.family !== family) return false + if (!search) return true + return [entry.title, entry.family, entry.intent, ...entry.features] + .join(' ') + .toLowerCase() + .includes(search) + }), + [allEntries, family, search], + ) + useDocumentMeta( + 'shadcn charts · TanStack Charts', + 'TanStack Charts implementations of the official shadcn chart catalog.', + ) + + useEffect(() => { + document.body.classList.add('shadcn-catalog') + return () => document.body.classList.remove('shadcn-catalog') + }, []) + + return ( + <> + +
+
+
+

shadcn charts

+

{allEntries.length} examples, rebuilt with TanStack Charts.

+
+
+ npm install @tanstack/charts + +
+
+
+ + + +
+
+ {entries.length ? ( + <> +

+ {entries.length} chart{entries.length === 1 ? '' : 's'} +

+
+ {entries.map((entry) => ( + + ))} +
+ + ) : ( +

No charts match those filters.

+ )} +
+
+ + ) +} + +function ShadcnCollectionCard({ + entry, + link, +}: { + entry: ConformanceCaseMeta + link: RouteLinkFactory +}) { + return ( + + ) +} + +function ShadcnCollectionPreview({ entry }: { entry: ConformanceCaseMeta }) { + const containerRef = useRef(null) + const [visible, setVisible] = useState(false) + const [error, setError] = useState('') + + useEffect(() => { + const container = containerRef.current + if (!container || visible) return + const observer = new IntersectionObserver( + ([observation]) => { + if (!observation?.isIntersecting) return + setVisible(true) + observer.disconnect() + }, + { rootMargin: '500px 0px' }, + ) + observer.observe(container) + return () => observer.disconnect() + }, [visible]) + + useEffect(() => { + if (!visible) return + let active = true + let handle: ConformanceHandle | undefined + let resizeObserver: ResizeObserver | undefined + let cardResizeObserver: ResizeObserver | undefined + const mount = async () => { + try { + await document.fonts?.ready + const implementation = await loadTanStackImplementation(entry.id) + const container = containerRef.current + if (!active || !implementation || !container) return + container.replaceChildren() + let width = Math.max(1, container.clientWidth) + const height = caseChartHeight(entry) + handle = implementation.mount(container, { + width, + height, + revision: 0, + interactive: true, + }) + + const card = container.querySelector('.sc-card') + if (card) { + const fitPreviewToCard = () => { + const cardHeight = Math.ceil(card.getBoundingClientRect().height) + if (cardHeight > 0) container.style.height = `${cardHeight}px` + } + fitPreviewToCard() + cardResizeObserver = new ResizeObserver(fitPreviewToCard) + cardResizeObserver.observe(card) + } + + resizeObserver = new ResizeObserver(() => { + const nextWidth = Math.max(1, container.clientWidth) + if (nextWidth === width) return + width = nextWidth + handle?.update({ + width, + height, + revision: 0, + interactive: true, + }) + }) + resizeObserver.observe(container) + } catch (reason) { + if (!active) return + setError(reason instanceof Error ? reason.message : String(reason)) + } + } + void mount() + return () => { + active = false + cardResizeObserver?.disconnect() + resizeObserver?.disconnect() + handle?.destroy() + } + }, [entry, visible]) + + return ( +
+ {error ? `Renderer failed: ${error}` : 'Loading chart…'} +
+ ) +} + interface SharedControls { dark: boolean family: string @@ -315,9 +560,18 @@ function CasePage({ setDark: (value: boolean) => void update: () => void }) { - const index = conformanceCases.indexOf(entry) - const previous = conformanceCases[index - 1] - const next = conformanceCases[index + 1] + const siblings = entry.collections?.includes('shadcn') + ? collectionCases('shadcn') + : conformanceCases + const index = siblings.indexOf(entry) + const previous = siblings[index - 1] + const next = siblings[index + 1] + const backRoute = entry.collections?.includes('shadcn') + ? ({ view: 'collection', collectionId: 'shadcn' } as const) + : ({ view: 'index' } as const) + const backLabel = entry.collections?.includes('shadcn') + ? 'shadcn charts' + : 'Catalog' useDocumentMeta( `${entry.title} · TanStack Charts Catalog`, comparisonMode @@ -330,8 +584,8 @@ function CasePage({
- - ← Catalog + + ← {backLabel}

{String(entry.order).padStart(2, '0')} · {entry.family} @@ -489,7 +743,8 @@ function CaseCard({ }) { const referenceRenderer = getConformanceReferenceRenderer(entry) const renderers = catalogRenderers(referenceRenderer, comparisonMode) - const docsDirective = `` + const height = caseChartHeight(entry) + const docsDirective = `` return (

@@ -573,9 +828,10 @@ function RendererPanel({ }) { const containerRef = useRef(null) const handleRef = useRef(null) + const height = caseChartHeight(entry) const inputRef = useRef({ width: chartWidth, - height: chartHeight, + height, revision, }) const [metric, setMetric] = useState('pending') @@ -586,7 +842,7 @@ function RendererPanel({ 'pending', ) const [error, setError] = useState('') - inputRef.current = { width: chartWidth, height: chartHeight, revision } + inputRef.current = { width: chartWidth, height, revision } useEffect(() => { let active = true @@ -657,26 +913,20 @@ function RendererPanel({ {metric}
{state === 'gap' ? ( -
+

Not implemented. This is a recorded capability gap.

) : state === 'error' ? ( -
+

{error}

) : (
)}
@@ -691,7 +941,11 @@ function RendererPanel({ function EmbedPage({ entry }: { entry: ConformanceCaseMeta }) { const params = new URLSearchParams(window.location.search) - const height = parseChartEmbedHeight(params.get('height')) + const requestedHeight = params.get('height') + const height = + requestedHeight === null + ? caseChartHeight(entry) + : parseChartEmbedHeight(requestedHeight) const revision = parseChartEmbedRevision(params.get('revision')) const preview = params.get('preview') === '1' const parentOrigin = resolveChartEmbedParentOrigin(document.referrer) @@ -820,7 +1074,7 @@ function SiteHeader({ active, link, }: { - active?: 'catalog' | 'all' + active?: 'catalog' | 'all' | 'shadcn' link: RouteLinkFactory }) { return ( @@ -854,6 +1108,12 @@ function SiteHeader({ > All charts + + shadcn + ) @@ -1039,6 +1299,39 @@ function filterCases(search: string, family: string): ConformanceCaseMeta[] { }) } +function collectionCases(collectionId: string): ConformanceCaseMeta[] { + const familyOrder = [ + 'dashboard', + 'area', + 'bar', + 'line', + 'pie', + 'radar', + 'radial', + 'tooltip', + ] + return conformanceCases + .filter((entry) => entry.collections?.includes(collectionId)) + .sort( + (left, right) => + familyOrder.indexOf(left.family) - familyOrder.indexOf(right.family) || + left.source.url.localeCompare(right.source.url), + ) +} + +function shadcnDisplayTitle(title: string): string { + const display = title.replace(/^shadcn\s+/iu, '') + return display.charAt(0).toUpperCase() + display.slice(1) +} + +function titleCase(value: string): string { + return value.charAt(0).toUpperCase() + value.slice(1) +} + +function caseChartHeight(entry: ConformanceCaseMeta): number { + return entry.height ?? chartHeight +} + function readCatalogLocation() { return { pathname: window.location.pathname, diff --git a/examples/conformance/src/routes.test.ts b/examples/conformance/src/routes.test.ts index 24c3499b..f7212c46 100644 --- a/examples/conformance/src/routes.test.ts +++ b/examples/conformance/src/routes.test.ts @@ -5,6 +5,7 @@ describe('catalog routes', () => { it.each([ ['/', { view: 'index' }], ['/all/', { view: 'all' }], + ['/collections/shadcn/', { view: 'collection', collectionId: 'shadcn' }], ['/charts/01-line/', { view: 'case', caseId: '01-line' }], ['/embed/01-line/', { view: 'embed', caseId: '01-line' }], ['/unknown/', { view: 'not-found' }], @@ -28,6 +29,12 @@ describe('catalog routes', () => { '/catalog', ), ).toBe('/catalog/charts/chart%20with%20spaces/') + expect( + catalogRouteHref( + { view: 'collection', collectionId: 'shadcn charts' }, + '/catalog', + ), + ).toBe('/catalog/collections/shadcn%20charts/') }) it('preserves the production catalog base for direct embed routes', () => { diff --git a/examples/conformance/src/routes.ts b/examples/conformance/src/routes.ts index c54f1260..765d04e5 100644 --- a/examples/conformance/src/routes.ts +++ b/examples/conformance/src/routes.ts @@ -1,6 +1,7 @@ export type CatalogRoute = | { view: 'index' } | { view: 'all' } + | { view: 'collection'; collectionId: string } | { view: 'case'; caseId: string } | { view: 'embed'; caseId: string } | { view: 'not-found' } @@ -24,6 +25,10 @@ export function parseCatalogRoute( if (segments.length === 0) return { view: 'index' } if (segments.length === 1 && segments[0] === 'all') return { view: 'all' } + if (segments.length === 2 && segments[0] === 'collections' && segments[1]) { + return { view: 'collection', collectionId: segments[1] } + } + if (segments.length === 2 && segments[0] === 'charts' && segments[1]) { return { view: 'case', caseId: segments[1] } } @@ -43,6 +48,9 @@ export function catalogRouteHref( if (route.view === 'index') return base if (route.view === 'all') return `${base}all/` + if (route.view === 'collection') { + return `${base}collections/${encodeURIComponent(route.collectionId)}/` + } if (route.view === 'case') { return `${base}charts/${encodeURIComponent(route.caseId)}/` } diff --git a/examples/conformance/src/styles.css b/examples/conformance/src/styles.css index 4a397426..a43ba5a1 100644 --- a/examples/conformance/src/styles.css +++ b/examples/conformance/src/styles.css @@ -55,6 +55,18 @@ body { margin: 0; } +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + a { color: var(--accent); } @@ -761,6 +773,188 @@ dd { padding-block: 18vh; } +body.shadcn-catalog { + color: oklch(0.145 0 0); + background: oklch(1 0 0); + --panel: oklch(1 0 0); + --panel-muted: oklch(0.97 0 0); + --border: oklch(0.922 0 0); + --muted: oklch(0.556 0 0); + --accent: oklch(0.205 0 0); + --accent-muted: oklch(0.97 0 0); +} + +:root[data-theme='dark'] body.shadcn-catalog { + color: oklch(0.985 0 0); + background: oklch(0.145 0 0); + --panel: oklch(0.205 0 0); + --panel-muted: oklch(0.269 0 0); + --border: oklch(1 0 0 / 10%); + --muted: oklch(0.708 0 0); + --accent: oklch(0.985 0 0); + --accent-muted: oklch(0.269 0 0); +} + +.shadcn-catalog .site-header, +.shadcn-gallery { + width: min(100% - 32px, 1440px); + margin-inline: auto; +} + +.shadcn-gallery { + padding: 64px 0 96px; +} + +.shadcn-gallery-header { + display: flex; + align-items: end; + justify-content: space-between; + gap: 32px; + margin-bottom: 44px; +} + +.shadcn-gallery-header h1 { + margin-bottom: 10px; + font-size: clamp(2.5rem, 5vw, 4.5rem); + line-height: 1; + letter-spacing: -0.055em; +} + +.shadcn-gallery-header p { + margin: 0; + color: var(--muted); + font-size: 1rem; +} + +.shadcn-install { + display: flex; + overflow: hidden; + border: 1px solid var(--border); + border-radius: 10px; + background: var(--panel-muted); +} + +.shadcn-install code { + display: flex; + align-items: center; + padding: 0 14px; + font-size: 0.8rem; +} + +.shadcn-install button { + min-height: 42px; + border: 0; + border-left: 1px solid var(--border); + border-radius: 0; + background: transparent; + font-size: 0.78rem; + font-weight: 600; +} + +.shadcn-gallery-controls { + position: sticky; + z-index: 20; + top: 12px; + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 32px; + padding: 10px; + border: 1px solid color-mix(in srgb, var(--border) 85%, transparent); + border-radius: 12px; + background: color-mix(in srgb, var(--panel) 92%, transparent); + box-shadow: 0 10px 30px rgb(0 0 0 / 6%); + backdrop-filter: blur(16px); +} + +.shadcn-gallery-controls > label { + width: min(240px, 25vw); + flex: none; +} + +.shadcn-gallery-controls input, +.shadcn-gallery-controls > button, +.shadcn-gallery-controls nav button { + min-height: 36px; + border-radius: 8px; + font-size: 0.8rem; +} + +.shadcn-gallery-controls nav { + display: flex; + min-width: 0; + flex: 1; + gap: 4px; + overflow-x: auto; +} + +.shadcn-gallery-controls nav button { + flex: none; + border-color: transparent; + background: transparent; +} + +.shadcn-gallery-controls nav button[aria-pressed='true'] { + border-color: var(--border); + color: var(--accent); + background: var(--panel-muted); +} + +.shadcn-result-count { + margin-bottom: 14px; + color: var(--muted); + font-size: 0.78rem; +} + +.shadcn-chart-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(min(100%, 380px), 1fr)); + gap: 40px 24px; +} + +.shadcn-chart-card { + min-width: 0; +} + +.shadcn-chart-preview { + display: grid; + min-width: 0; + overflow: hidden; + place-items: start center; + border-radius: 14px; + color: var(--muted); + background: var(--panel); + font-size: 0.8rem; +} + +.shadcn-chart-preview > span { + align-self: center; +} + +.shadcn-chart-card > footer { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 16px; + padding: 14px 2px 0; +} + +.shadcn-chart-card nav { + display: flex; + gap: 12px; +} + +.shadcn-chart-card nav a { + color: var(--muted); + font-size: 0.76rem; + font-weight: 550; + text-decoration: none; +} + +.shadcn-chart-card nav a:hover { + color: var(--accent); +} + html.embed-mode, body.embed-mode { min-width: 0; @@ -809,6 +1003,40 @@ body.embed-mode { padding-top: 48px; } + .shadcn-gallery { + padding-top: 40px; + } + + .shadcn-gallery-header { + align-items: stretch; + flex-direction: column; + margin-bottom: 28px; + } + + .shadcn-install { + width: max-content; + max-width: 100%; + } + + .shadcn-gallery-controls { + position: static; + align-items: stretch; + flex-wrap: wrap; + } + + .shadcn-gallery-controls > label { + width: 100%; + } + + .shadcn-gallery-controls nav { + order: 3; + width: 100%; + } + + .shadcn-gallery-controls > button { + margin-left: auto; + } + .summary { width: 100%; min-width: 0; diff --git a/package.json b/package.json index c78543d7..888a1296 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,11 @@ "catalog:index:check": "nx run charts-workspace:catalog-index-check", "catalog:previews": "node scripts/catalog-preview.mjs --write", "catalog:previews:check": "nx run charts-workspace:catalog-preview-check", + "shadcn:catalog:check": "node scripts/shadcn-catalog.mjs", + "shadcn:catalog:generate": "node scripts/generate-shadcn-cases.mjs", + "shadcn:references": "node scripts/shadcn-reference-screenshots.mjs", + "shadcn:visual": "node scripts/shadcn-visual-parity.mjs", + "shadcn:catalog:upstream": "node scripts/shadcn-catalog.mjs --remote", "dev:charts-react": "pnpm --filter @charts-poc/react-example dev", "dev:charts-octane": "pnpm --filter @charts-poc/octane-example dev", "dev:conformance": "pnpm --filter @charts-poc/conformance-example dev", @@ -129,7 +134,9 @@ "jsdom": "^29.0.2", "nx": "22.7.5", "octane": "0.1.13", + "pixelmatch": "7.1.0", "playwright": "1.62.0", + "pngjs": "7.0.0", "prettier": "^3.8.4", "react": "19.2.3", "react-dom": "19.2.3", diff --git a/packages/charts-core/docs/comparison.md b/packages/charts-core/docs/comparison.md index 70a08e42..b4316fc2 100644 --- a/packages/charts-core/docs/comparison.md +++ b/packages/charts-core/docs/comparison.md @@ -12,14 +12,14 @@ turning untested behavior into a checkmark. | Library | Package | Measured source | | -------------------------------------------------------------------------------------- | -------------------- | ------------------- | -| [TanStack Charts](./overview.md) | `@tanstack/charts` | workspace `b9d8c54` | +| [TanStack Charts](./overview.md) | `@tanstack/charts` | workspace `56567ec` | | [Chart.js](https://www.chartjs.org/docs/latest/) | `chart.js` | npm `4.5.1` | | [Apache ECharts](https://echarts.apache.org/handbook/en/best-practices/canvas-vs-svg/) | `echarts` | npm `6.1.0` | | [Recharts](https://recharts.github.io/en-US/) | `recharts` | npm `3.10.1` | | [Observable Plot](https://observablehq.com/plot/features/plots) | `@observablehq/plot` | npm `0.6.17` | The competitor versions are exact package pins, not latest versions inferred -at page render time. The measured TanStack workspace revision is `b9d8c54`. +at page render time. The measured TanStack workspace revision is `56567ec`. ## Capability matrix @@ -90,7 +90,7 @@ output model. ## Bundle snapshot -Baseline date: `2026-08-12`. +Baseline date: `2026-08-14`. Controlled ranges cover 12 independently built, minified browser consumers: line, bar, area, and scatter at basic, interactive, and advanced tiers. Only @@ -106,7 +106,7 @@ Vega-Lite, AG Charts, and uPlot main exports were read from Bundlephobia on July | Library | Bundle size | React externalized | Evidence | | ------------------ | -------------------------------------- | -----------------: | ---------------------------------------------------------- | -| TanStack Charts | 37.46–43.41 KiB | — | Controlled suite | +| TanStack Charts | 37.63–43.59 KiB | — | Controlled suite | | D3 | 90 KB gzip | — | External main export | | Chart.js | 44.70–58.21 KiB | — | Controlled suite | | Apache ECharts | 153.10–173.18 KiB | — | Controlled suite | diff --git a/packages/charts-core/docs/config.json b/packages/charts-core/docs/config.json index 6964efdf..406ec229 100644 --- a/packages/charts-core/docs/config.json +++ b/packages/charts-core/docs/config.json @@ -766,6 +766,290 @@ { "label": "Drillable Flare sunburst", "to": "/charts/catalog/charts/126-drillable-sunburst/" + }, + { + "label": "shadcn dashboard", + "to": "/charts/catalog/charts/127-shadcn-dashboard/" + }, + { + "label": "shadcn bar chart – multiple", + "to": "/charts/catalog/charts/128-shadcn-bar-multiple/" + }, + { + "label": "shadcn pie chart – donut with text", + "to": "/charts/catalog/charts/129-shadcn-pie-donut-text/" + }, + { + "label": "shadcn radar chart – multiple", + "to": "/charts/catalog/charts/130-shadcn-radar-multiple/" + }, + { + "label": "shadcn radial chart – text", + "to": "/charts/catalog/charts/131-shadcn-radial-text/" + }, + { + "label": "shadcn tooltip – advanced", + "to": "/charts/catalog/charts/132-shadcn-tooltip-advanced/" + }, + { + "label": "shadcn Area Chart - Axes", + "to": "/charts/catalog/charts/133-shadcn-area-axes/" + }, + { + "label": "shadcn Area Chart", + "to": "/charts/catalog/charts/134-shadcn-area-default/" + }, + { + "label": "shadcn Area Chart - Gradient", + "to": "/charts/catalog/charts/135-shadcn-area-gradient/" + }, + { + "label": "shadcn Area Chart - Icons", + "to": "/charts/catalog/charts/136-shadcn-area-icons/" + }, + { + "label": "shadcn Area Chart - Interactive", + "to": "/charts/catalog/charts/137-shadcn-area-interactive/" + }, + { + "label": "shadcn Area Chart - Legend", + "to": "/charts/catalog/charts/138-shadcn-area-legend/" + }, + { + "label": "shadcn Area Chart - Linear", + "to": "/charts/catalog/charts/139-shadcn-area-linear/" + }, + { + "label": "shadcn Area Chart - Stacked Expanded", + "to": "/charts/catalog/charts/140-shadcn-area-stacked-expand/" + }, + { + "label": "shadcn Area Chart - Stacked", + "to": "/charts/catalog/charts/141-shadcn-area-stacked/" + }, + { + "label": "shadcn Area Chart - Step", + "to": "/charts/catalog/charts/142-shadcn-area-step/" + }, + { + "label": "shadcn Bar Chart - Active", + "to": "/charts/catalog/charts/143-shadcn-bar-active/" + }, + { + "label": "shadcn Bar Chart", + "to": "/charts/catalog/charts/144-shadcn-bar-default/" + }, + { + "label": "shadcn Bar Chart - Horizontal", + "to": "/charts/catalog/charts/145-shadcn-bar-horizontal/" + }, + { + "label": "shadcn Bar Chart - Interactive", + "to": "/charts/catalog/charts/146-shadcn-bar-interactive/" + }, + { + "label": "shadcn Bar Chart - Custom Label", + "to": "/charts/catalog/charts/147-shadcn-bar-label-custom/" + }, + { + "label": "shadcn Bar Chart - Label", + "to": "/charts/catalog/charts/148-shadcn-bar-label/" + }, + { + "label": "shadcn Bar Chart - Mixed", + "to": "/charts/catalog/charts/149-shadcn-bar-mixed/" + }, + { + "label": "shadcn Bar Chart - Negative", + "to": "/charts/catalog/charts/150-shadcn-bar-negative/" + }, + { + "label": "shadcn Bar Chart - Stacked + Legend", + "to": "/charts/catalog/charts/151-shadcn-bar-stacked/" + }, + { + "label": "shadcn Line Chart", + "to": "/charts/catalog/charts/152-shadcn-line-default/" + }, + { + "label": "shadcn Line Chart - Dots Colors", + "to": "/charts/catalog/charts/153-shadcn-line-dots-colors/" + }, + { + "label": "shadcn Line Chart - Custom Dots", + "to": "/charts/catalog/charts/154-shadcn-line-dots-custom/" + }, + { + "label": "shadcn Line Chart - Dots", + "to": "/charts/catalog/charts/155-shadcn-line-dots/" + }, + { + "label": "shadcn Line Chart - Interactive", + "to": "/charts/catalog/charts/156-shadcn-line-interactive/" + }, + { + "label": "shadcn Line Chart - Custom Label", + "to": "/charts/catalog/charts/157-shadcn-line-label-custom/" + }, + { + "label": "shadcn Line Chart - Label", + "to": "/charts/catalog/charts/158-shadcn-line-label/" + }, + { + "label": "shadcn Line Chart - Linear", + "to": "/charts/catalog/charts/159-shadcn-line-linear/" + }, + { + "label": "shadcn Line Chart - Multiple", + "to": "/charts/catalog/charts/160-shadcn-line-multiple/" + }, + { + "label": "shadcn Line Chart - Step", + "to": "/charts/catalog/charts/161-shadcn-line-step/" + }, + { + "label": "shadcn Pie Chart - Donut Active", + "to": "/charts/catalog/charts/162-shadcn-pie-donut-active/" + }, + { + "label": "shadcn Pie Chart - Donut", + "to": "/charts/catalog/charts/163-shadcn-pie-donut/" + }, + { + "label": "shadcn Pie Chart - Interactive", + "to": "/charts/catalog/charts/164-shadcn-pie-interactive/" + }, + { + "label": "shadcn Pie Chart - Custom Label", + "to": "/charts/catalog/charts/165-shadcn-pie-label-custom/" + }, + { + "label": "shadcn Pie Chart - Label List", + "to": "/charts/catalog/charts/166-shadcn-pie-label-list/" + }, + { + "label": "shadcn Pie Chart - Label", + "to": "/charts/catalog/charts/167-shadcn-pie-label/" + }, + { + "label": "shadcn Pie Chart - Legend", + "to": "/charts/catalog/charts/168-shadcn-pie-legend/" + }, + { + "label": "shadcn Pie Chart - Separator None", + "to": "/charts/catalog/charts/169-shadcn-pie-separator-none/" + }, + { + "label": "shadcn Pie Chart", + "to": "/charts/catalog/charts/170-shadcn-pie-simple/" + }, + { + "label": "shadcn Pie Chart - Stacked", + "to": "/charts/catalog/charts/171-shadcn-pie-stacked/" + }, + { + "label": "shadcn Radar Chart", + "to": "/charts/catalog/charts/172-shadcn-radar-default/" + }, + { + "label": "shadcn Radar Chart - Dots", + "to": "/charts/catalog/charts/173-shadcn-radar-dots/" + }, + { + "label": "shadcn Radar Chart - Grid Circle Filled", + "to": "/charts/catalog/charts/174-shadcn-radar-grid-circle-fill/" + }, + { + "label": "shadcn Radar Chart - Grid Circle - No lines", + "to": "/charts/catalog/charts/175-shadcn-radar-grid-circle-no-lines/" + }, + { + "label": "shadcn Radar Chart - Grid Circle", + "to": "/charts/catalog/charts/176-shadcn-radar-grid-circle/" + }, + { + "label": "shadcn Radar Chart - Grid Custom", + "to": "/charts/catalog/charts/177-shadcn-radar-grid-custom/" + }, + { + "label": "shadcn Radar Chart - Grid Filled", + "to": "/charts/catalog/charts/178-shadcn-radar-grid-fill/" + }, + { + "label": "shadcn Radar Chart - Grid None", + "to": "/charts/catalog/charts/179-shadcn-radar-grid-none/" + }, + { + "label": "shadcn Radar Chart - Icons", + "to": "/charts/catalog/charts/180-shadcn-radar-icons/" + }, + { + "label": "shadcn Radar Chart - Custom Label", + "to": "/charts/catalog/charts/181-shadcn-radar-label-custom/" + }, + { + "label": "shadcn Radar Chart - Legend", + "to": "/charts/catalog/charts/182-shadcn-radar-legend/" + }, + { + "label": "shadcn Radar Chart - Lines Only", + "to": "/charts/catalog/charts/183-shadcn-radar-lines-only/" + }, + { + "label": "shadcn Radar Chart - Radius Axis", + "to": "/charts/catalog/charts/184-shadcn-radar-radius/" + }, + { + "label": "shadcn Radial Chart - Grid", + "to": "/charts/catalog/charts/185-shadcn-radial-grid/" + }, + { + "label": "shadcn Radial Chart - Label", + "to": "/charts/catalog/charts/186-shadcn-radial-label/" + }, + { + "label": "shadcn Radial Chart - Shape", + "to": "/charts/catalog/charts/187-shadcn-radial-shape/" + }, + { + "label": "shadcn Radial Chart", + "to": "/charts/catalog/charts/188-shadcn-radial-simple/" + }, + { + "label": "shadcn Radial Chart - Stacked", + "to": "/charts/catalog/charts/189-shadcn-radial-stacked/" + }, + { + "label": "shadcn Tooltip - Default", + "to": "/charts/catalog/charts/190-shadcn-tooltip-default/" + }, + { + "label": "shadcn Tooltip - Formatter", + "to": "/charts/catalog/charts/191-shadcn-tooltip-formatter/" + }, + { + "label": "shadcn Tooltip - Icons", + "to": "/charts/catalog/charts/192-shadcn-tooltip-icons/" + }, + { + "label": "shadcn Tooltip - Line Indicator", + "to": "/charts/catalog/charts/193-shadcn-tooltip-indicator-line/" + }, + { + "label": "shadcn Tooltip - No Indicator", + "to": "/charts/catalog/charts/194-shadcn-tooltip-indicator-none/" + }, + { + "label": "shadcn Tooltip - Custom label", + "to": "/charts/catalog/charts/195-shadcn-tooltip-label-custom/" + }, + { + "label": "shadcn Tooltip - Label Formatter", + "to": "/charts/catalog/charts/196-shadcn-tooltip-label-formatter/" + }, + { + "label": "shadcn Tooltip - No Label", + "to": "/charts/catalog/charts/197-shadcn-tooltip-label-none/" } ] }, diff --git a/packages/charts-core/docs/guides/dynamic-data-and-animation.md b/packages/charts-core/docs/guides/dynamic-data-and-animation.md index 2bf081de..6d84776c 100644 --- a/packages/charts-core/docs/guides/dynamic-data-and-animation.md +++ b/packages/charts-core/docs/guides/dynamic-data-and-animation.md @@ -109,12 +109,14 @@ Use `motion()` when animation quality is part of the chart contract: ```ts import { scaleUtc } from 'd3-scale' import { motion } from '@tanstack/charts/motion' +import { stagger } from '@tanstack/charts/motion/definition' import { mountChartRenderer } from '@tanstack/charts/renderer' import { scaleLinear } from '@tanstack/charts/scales/linear' const definition = defineChart({ motion: { - transition: { type: 'spring', stiffness: 170, damping: 18, mass: 1 }, + path: 'morph', + ...stagger({ each: 35, roles: 'line', by: 'series' }), }, marks: [ lineY(rows, { @@ -135,7 +137,9 @@ const definition = defineChart({ const host = mountChartRenderer(container, { definition, - renderer: motion(), + renderer: motion({ + transition: { type: 'spring', stiffness: 170, damping: 18, mass: 1 }, + }), width: 640, height: 360, ariaLabel: 'Actual and forecast revenue', @@ -178,17 +182,20 @@ rules, bands, labels, and marker retain DOM identity and spring velocity while focus retargets. Default SVG, Canvas, and native surfaces paint the same guide at its current target without importing the browser motion runtime. -Spring updates always retarget immediately; a returned update delay is ignored -so incoming momentum cannot freeze. Use delays for spring enter/exit -choreography or any tween phase. +`stagger()` contributes only a context-aware delay, so it composes with +transition and path fields through normal object spread. Spring updates always +retarget immediately; an update delay is ignored so incoming momentum cannot +freeze. Use delays for spring enter/exit choreography or any tween phase. -The renderer grows entering bars from their semantic baseline, reveals entering -line groups, staggers bar entrances, morphs compatible numeric SVG geometry, -and fades incompatible keyed topology. Authored delay replaces automatic -staggering for that target. +The renderer grows entering Cartesian bars, lines, and areas from their +semantic baseline; grows radial paths from the polar center; sweeps arcs +through their authored angle; staggers bar entrances; morphs compatible +numeric SVG geometry; and keeps removed keys painted through their exit +transition. Authored delay replaces automatic staggering for that target. `motion()` respects reduced motion and does not animate resize-only updates by -default. It adopts server-rendered SVG without replaying entrance motion. +default. It adopts server-rendered SVG without replaying entrance motion by +default; pass `initial: 'always'` to replay entrance motion after hydration. Static SVG and Canvas accept the same definitions but paint the final state. See the [Motion reference](../reference/motion.md) for the complete cascade, diff --git a/packages/charts-core/docs/guides/tooltips-and-focus.md b/packages/charts-core/docs/guides/tooltips-and-focus.md index aa66960b..15f9ef4c 100644 --- a/packages/charts-core/docs/guides/tooltips-and-focus.md +++ b/packages/charts-core/docs/guides/tooltips-and-focus.md @@ -255,6 +255,51 @@ transient tooltip. Click, Enter, or Space can still pin the focused point; only the pinned surface and adapter body are mounted. The default is `visibility: 'focus'`. +## Tooltip motion + +The built-in tooltip uses the active motion renderer's transition for entry, +movement between points, retargeting, and exit: + +```ts +import { motion } from '@tanstack/charts/motion' + +const renderer = motion({ + transition: { + type: 'spring', + stiffness: 170, + damping: 18, + mass: 1, + }, +}) + +const definition = defineChart({ marks, tooltip }) + +mountChartRenderer(container, { + definition, + renderer, + width: 640, + height: 360, + ariaLabel: 'Monthly visitors', +}) +``` + +A static chart-level transition can refine the renderer fallback: + +```ts +const definition = defineChart({ + marks, + motion: { + transition: { type: 'spring', stiffness: 170, damping: 18, mass: 1 }, + }, + tooltip, +}) +``` + +Set `tooltip.motion` to another transition to override both, or set it to +`false` to keep the tooltip immediate. These options customize the active +motion renderer; a static renderer stays immediate and does not import spring +physics. The renderer's reduced-motion policy also applies to the tooltip. + ## Application-owned pointer timing Set definition `pointer: false` when the application decides when inspection diff --git a/packages/charts-core/docs/reference/index.md b/packages/charts-core/docs/reference/index.md index 42259a6f..af31ef73 100644 --- a/packages/charts-core/docs/reference/index.md +++ b/packages/charts-core/docs/reference/index.md @@ -156,7 +156,8 @@ aggregate `/scales` export. | `@tanstack/charts/mark/composite` | `compositeMark` and `CompositeMarkOptions` | | `@tanstack/charts/mark/decorative` | `decorative` | | `@tanstack/charts/mark/scale-values` | `createMarkWithScaleValues` | -| `@tanstack/charts/motion` | `motion`, `ChartMotionOptions`, and renderer-neutral motion types | +| `@tanstack/charts/motion` | `motion`, `stagger`, `ChartMotionOptions`, and renderer-neutral motion types | +| `@tanstack/charts/motion/definition` | Isolated `stagger` and `ChartMotionStaggerOptions` without the renderer or spring solver | | `@tanstack/charts/network/force` | `forceLayout`, built-in descriptors, named D3-compatible force factories, private working-clone context, settled node/link result, and lineage types | | `@tanstack/charts/network/sankey` | `sankeyDiagram`, shorthand and callable alignment types, responsive layout options, immutable node/link values, comparator contexts, and lineage types | | `@tanstack/charts/polar` | `pie`, `polar`, `focusGroupAngle`, `radialArc`, `radialBarRadius`, `radialBarAngle`, other radial marks, and radial/angle guides | diff --git a/packages/charts-core/docs/reference/marks/bar-and-rect.md b/packages/charts-core/docs/reference/marks/bar-and-rect.md index b5d3d4a2..a5147b45 100644 --- a/packages/charts-core/docs/reference/marks/bar-and-rect.md +++ b/packages/charts-core/docs/reference/marks/bar-and-rect.md @@ -32,23 +32,27 @@ function barY( ### Options -| Option | Type | Default | Meaning | -| -------------- | ------------------------------------ | ------------------------------ | ------------------------------------------------ | -| `id` | `string` | Layer-derived | Stable mark ID | -| `x` | `Channel` | Row index | Bar category or center | -| `y` | `Channel` | Numeric datum | Length; implicitly stacked at each x | -| `y1` | `number \| Channel` | Implicit stack start | Explicit baseline endpoint | -| `y2` | `number \| Channel` | Implicit stack end | Explicit value endpoint; takes precedence over y | -| `z` | `Channel` | No group | Group identity; color fallback when omitted | -| `color` | `Channel` | `z` | Independent value sent to the chart color scale | -| `key` | `Channel` | Top/nested `id`, x, then index | Stable scene and interaction identity | -| `fill` | `VisualChannel` | Resolved `color` | Final bar paint override | -| `fillOpacity` | `number` | SVG default | Fill opacity | -| `layout` | `GroupLayout \| StackLayout` | Implicit diverging stack | Configures grouping or stack order/offset | -| `inset` | `number` | `0` | Pixels removed from both categorical edges | -| `maxThickness` | `number` | Unbounded | Maximum painted width after grouping and inset | -| `radius` | `number` | None | SVG rectangle corner radius | -| `states` | `readonly ChartMarkState[]` | None | Focus-driven presentation overrides | +| Option | Type | Default | Meaning | +| ----------------- | ------------------------------------ | ------------------------------ | ------------------------------------------------ | +| `id` | `string` | Layer-derived | Stable mark ID | +| `x` | `Channel` | Row index | Bar category or center | +| `y` | `Channel` | Numeric datum | Length; implicitly stacked at each x | +| `y1` | `number \| Channel` | Implicit stack start | Explicit baseline endpoint | +| `y2` | `number \| Channel` | Implicit stack end | Explicit value endpoint; takes precedence over y | +| `z` | `Channel` | No group | Group identity; color fallback when omitted | +| `color` | `Channel` | `z` | Independent value sent to the chart color scale | +| `key` | `Channel` | Top/nested `id`, x, then index | Stable scene and interaction identity | +| `fill` | `VisualChannel` | Resolved `color` | Final bar paint override | +| `fillOpacity` | `number` | SVG default | Fill opacity | +| `stroke` | `VisualChannel` | None | Bar outline color | +| `strokeOpacity` | `number` | SVG default | Bar outline opacity | +| `strokeWidth` | `number` | SVG default | Bar outline width | +| `strokeDasharray` | `VisualChannel` | None | SVG outline dash pattern | +| `layout` | `GroupLayout \| StackLayout` | Implicit diverging stack | Configures grouping or stack order/offset | +| `inset` | `number` | `0` | Pixels removed from both categorical edges | +| `maxThickness` | `number` | Unbounded | Maximum painted width after grouping and inset | +| `radius` | `number` | None | SVG rectangle corner radius | +| `states` | `readonly ChartMarkState[]` | None | Focus-driven presentation overrides | The interaction point is at the group-band center and the `y2`/`y` endpoint. Its semantic `xValue` is x and its `yValue` is the value endpoint. diff --git a/packages/charts-core/docs/reference/marks/polar.md b/packages/charts-core/docs/reference/marks/polar.md index 560234bf..53f09798 100644 --- a/packages/charts-core/docs/reference/marks/polar.md +++ b/packages/charts-core/docs/reference/marks/polar.md @@ -338,7 +338,8 @@ function angleGrid(options?: AngleGridOptions): PolarGuide `radialGrid` draws radius values as circles or polygons. Supply explicit `values`, or let `ticks` request values from the configured radius scale. Labels are off by default. Label angle, offset, rotation, format, fill, and -font size are configurable. +font size are configurable. Ring `fill` and `fillOpacity` can layer filled +circle or polygon grids behind the chart marks. `angleGrid` draws spokes for explicit `values` or the configured angle domain. It can show labels around the circumference with `format` and `labelOffset`. diff --git a/packages/charts-core/docs/reference/motion.md b/packages/charts-core/docs/reference/motion.md index 0d83ddca..9316efea 100644 --- a/packages/charts-core/docs/reference/motion.md +++ b/packages/charts-core/docs/reference/motion.md @@ -24,29 +24,41 @@ function motion< >(options?: ChartMotionOptions): ChartRenderer interface ChartMotionOptions { - initial?: boolean + initial?: boolean | 'always' transition?: ChartMotionTransition respectReducedMotion?: boolean resize?: boolean } ``` -| Option | Default | Meaning | -| ---------------------- | --------------------------------------------- | -------------------------------------------------- | -| `initial` | `true` | Animate the first client render | -| `transition` | 1,100 ms tween with the default entrance ease | Renderer-wide fallback | -| `respectReducedMotion` | `true` | Snap when `prefers-reduced-motion: reduce` matches | -| `resize` | `false` | Animate updates caused only by a chart size change | - -Server-rendered SVG is adopted without replaying entrance motion. Keyed updates -start from painted geometry. An interrupted spring carries its sampled value and -velocity into the new target. A spring has no duration; it finishes when both -`restSpeed` and `restDelta` are satisfied, with a 10-second safety limit. +| Option | Default | Meaning | +| ---------------------- | --------------------------------------------- | ------------------------------------------------------------- | +| `initial` | `true` | Animate first client paint; `always` also replays adopted SVG | +| `transition` | 1,100 ms tween with the default entrance ease | Renderer-wide fallback | +| `respectReducedMotion` | `true` | Snap when `prefers-reduced-motion: reduce` matches | +| `resize` | `false` | Animate updates caused only by a chart size change | + +Server-rendered SVG is adopted without replaying entrance motion by default. +Set `initial: 'always'` when a hydrated chart should replay the same entrance +as a client-only mount. Keyed updates start from painted geometry. An +interrupted spring carries its sampled value and velocity into the new target. +A spring has no duration; it finishes when both `restSpeed` and `restDelta` are +satisfied, with a 10-second safety limit. + +Initial choreography follows geometry: Cartesian bars and paths grow from +their semantic baseline, radial lines and areas grow from the polar center, +and arcs sweep through their authored angle. Keyed removals stay painted +through their exit transition. Data-less `crosshair` marks use the same keyed focus-motion path. Rapid pointer or keyboard retargeting preserves the guide elements and incoming spring velocity; labels remain aligned to their moving rules. +The built-in HTML tooltip also consumes this renderer's transition. Entry, +movement, retargeting, and exit therefore use the same spring without copying +the transition into the chart definition. A static renderer keeps the tooltip +immediate and does not import the motion runtime. + Use the renderer-neutral host in vanilla applications: ```ts @@ -111,6 +123,8 @@ const definition = defineChart({ The cascade is renderer default, chart, mark, axis, specific guide, then the active focus-state transition. Same-type transitions inherit omitted fields. +Set `motion: false` at any definition scope to suppress inherited motion for +that scope. A more specific child can re-enable motion with its own definition. An authored `delay` replaces automatic entrance staggering for that target. Spring updates begin immediately even when a definition returns a delay, so a retarget cannot freeze incoming momentum. Spring enter and exit delays, and @@ -119,6 +133,58 @@ tween delays in every phase, are honored. All built-in marks accept `ChartMarkMotionOptions`. Nested polar marks also accept `motion`; their timing is merged below the parent `polar` mark. +| Scope | Motion input | +| ------------------------------ | ---------------------------------------------------- | +| Renderer fallback | `motion({ transition })` | +| Whole chart | `defineChart({ motion })` | +| Any built-in mark | The mark's `motion` option | +| Axis, including its grid lines | `x.axis.motion` or `y.axis.motion` | +| Tick rules | `axis.ticks.motion` | +| Tick labels | `axis.tickLabels.motion` | +| Axis label | `axis.label.motion` when `label` is an object | +| Crosshair or focus guide | The guide mark's `motion` option | +| HTML tooltip | Inherits chart motion; `tooltip.motion` overrides it | + +Grid lines use their axis policy because they are part of that axis's guide +system. `tooltip.motion: false` keeps the tooltip immediate even when chart +geometry animates. Legends and application-owned controls are not marks and do +not currently participate in the motion cascade. + +## Spreading timing utilities + +Use the isolated timing entry when the definition only needs motion policy: + +```ts +import { stagger } from '@tanstack/charts/motion/definition' + +const definition = defineChart({ + motion: { + path: 'morph', + ...stagger({ each: 35, by: 'series', roles: ['arc', 'bar'] }), + }, + marks, +}) +``` + +`stagger()` returns one context-aware `delay` field for direct object spread. +It uses `datumIndex` by default, can use `seriesIndex`, defaults to the `enter` +phase, and can filter by phase and semantic role. `offset` delays the first +target. Normal object-spread order controls precedence, so an explicit `delay` +written after `...stagger()` replaces it. + +```ts +interface ChartMotionStaggerOptions { + each: number + offset?: number + by?: 'datum' | 'series' + phase?: ChartMotionPhase | readonly ChartMotionPhase[] + roles?: ChartMotionRole | readonly ChartMotionRole[] +} +``` + +`stagger()` is also exported from `@tanstack/charts/motion`. The dedicated +`/motion/definition` entry excludes the SVG renderer and spring solver. + ## Timing types ```ts @@ -152,15 +218,18 @@ interface ChartRollingPathMotion { type ChartMotionPath = 'morph' | ChartRollingPathMotion -interface ChartMotionTiming { - delay?: number +interface ChartMotionTiming { + delay?: number | ((context: ChartMotionContext) => number | undefined) transition?: ChartMotionTransition path?: ChartMotionPath } type ChartMotionDefinition = - | ChartMotionTiming - | ((context: ChartMotionContext) => ChartMotionTiming | undefined) + | false + | ChartMotionTiming + | (( + context: ChartMotionContext, + ) => false | ChartMotionTiming | undefined) interface ChartMarkMotionOptions { motion?: ChartMotionDefinition diff --git a/packages/charts-core/package.json b/packages/charts-core/package.json index 725c6d46..c8110b76 100644 --- a/packages/charts-core/package.json +++ b/packages/charts-core/package.json @@ -154,6 +154,7 @@ "./link": "./src/link.ts", "./lit": "./src/lit.ts", "./motion": "./src/motion.ts", + "./motion/definition": "./src/motion-definition.ts", "./network/force": "./src/network-force.ts", "./network/sankey": "./src/network-sankey.ts", "./octane": "./src/octane.ts", @@ -404,6 +405,10 @@ "types": "./dist/motion.d.ts", "import": "./dist/motion.js" }, + "./motion/definition": { + "types": "./dist/motion-definition.d.ts", + "import": "./dist/motion-definition.js" + }, "./network/force": { "types": "./dist/network-force.d.ts", "import": "./dist/network-force.js" diff --git a/packages/charts-core/src/bar.ts b/packages/charts-core/src/bar.ts index f91fee1d..e9ca90c5 100644 --- a/packages/charts-core/src/bar.ts +++ b/packages/charts-core/src/bar.ts @@ -43,6 +43,10 @@ export interface BarYOptions extends ChartMarkMotionOptions { key?: Channel fill?: VisualChannel fillOpacity?: number + stroke?: VisualChannel + strokeOpacity?: number + strokeWidth?: number + strokeDasharray?: VisualChannel layout?: BarLayout /** Pixels removed from both categorical edges after band layout. */ inset?: number @@ -63,6 +67,10 @@ export interface BarXOptions extends ChartMarkMotionOptions { key?: Channel fill?: VisualChannel fillOpacity?: number + stroke?: VisualChannel + strokeOpacity?: number + strokeWidth?: number + strokeDasharray?: VisualChannel layout?: BarLayout /** Pixels removed from both categorical edges after band layout. */ inset?: number @@ -198,6 +206,20 @@ export function barY( data, resolvedColor, ) + const stroke = visualValue( + options.stroke, + datum, + datumIndex, + data, + 'none', + ) + const strokeDasharray = visualValue( + options.strokeDasharray, + datum, + datumIndex, + data, + 'none', + ) const center = scales.x.map(xValue) const baselinePosition = scales.y.map(y1Value) const valuePosition = scales.y.map(y2Value) @@ -239,6 +261,10 @@ export function barY( style: { fill, fillOpacity: options.fillOpacity, + stroke, + strokeOpacity: options.strokeOpacity, + strokeWidth: options.strokeWidth, + strokeDasharray, }, }) }) @@ -385,6 +411,20 @@ export function barX( data, resolvedColor, ) + const stroke = visualValue( + options.stroke, + datum, + datumIndex, + data, + 'none', + ) + const strokeDasharray = visualValue( + options.strokeDasharray, + datum, + datumIndex, + data, + 'none', + ) const baselinePosition = scales.x.map(x1Value) const valuePosition = scales.x.map(x2Value) const center = scales.y.map(yValue) @@ -426,6 +466,10 @@ export function barX( style: { fill, fillOpacity: options.fillOpacity, + stroke, + strokeOpacity: options.strokeOpacity, + strokeWidth: options.strokeWidth, + strokeDasharray, }, }) }) diff --git a/packages/charts-core/src/composite-motion-internal.ts b/packages/charts-core/src/composite-motion-internal.ts index fc223a3a..10b96e54 100644 --- a/packages/charts-core/src/composite-motion-internal.ts +++ b/packages/charts-core/src/composite-motion-internal.ts @@ -5,10 +5,12 @@ import type { ChartMotionTransition, } from './types' +type ResolvedCompositeMotion = false | ChartMotionTiming + export function resolveCompositeMotion( definition: ChartMotionDefinition | undefined, context: ChartMotionContext, -): ChartMotionTiming | undefined { +): ResolvedCompositeMotion | undefined { return typeof definition === 'function' ? definition(context) : definition } @@ -16,7 +18,7 @@ export function resolveCompositeChildMotion( parent: ChartMotionDefinition | undefined, children: ReadonlyMap>, context: ChartMotionContext, -): ChartMotionTiming | undefined { +): ResolvedCompositeMotion | undefined { let childId: string | undefined for (const candidate of children.keys()) { if ( @@ -35,12 +37,13 @@ export function resolveCompositeChildMotion( ) } -export function mergeCompositeMotion( - parent: ChartMotionTiming | undefined, - child: ChartMotionTiming | undefined, -): ChartMotionTiming | undefined { - if (!parent) return child - if (!child) return parent +export function mergeCompositeMotion( + parent: ResolvedCompositeMotion | undefined, + child: ResolvedCompositeMotion | undefined, +): ResolvedCompositeMotion | undefined { + if (child === false) return false + if (child === undefined) return parent + if (parent === false || parent === undefined) return child const path = child.path ?? parent.path return { delay: child.delay ?? parent.delay, diff --git a/packages/charts-core/src/exports.test.ts b/packages/charts-core/src/exports.test.ts index 25ed4202..6fbdbb2c 100644 --- a/packages/charts-core/src/exports.test.ts +++ b/packages/charts-core/src/exports.test.ts @@ -90,6 +90,24 @@ describe('public package exports', () => { expect(portalModule.portal.id).toBe('portal') }) + it('keeps motion and timing utilities on the optional motion subpath', async () => { + const [root, universal, motionModule, definitionModule] = await Promise.all( + [ + import('@tanstack/charts'), + import('@tanstack/charts/universal'), + import('@tanstack/charts/motion'), + import('@tanstack/charts/motion/definition'), + ], + ) + + for (const name of ['motion', 'stagger']) { + expect(root).not.toHaveProperty(name) + expect(universal).not.toHaveProperty(name) + expect(motionModule).toHaveProperty(name) + } + expect(Object.keys(definitionModule)).toEqual(['stagger']) + }) + it('keeps focus guide marks on their exact subpath', async () => { const [root, universal, guide] = await Promise.all([ import('@tanstack/charts'), diff --git a/packages/charts-core/src/mark-composite.test.ts b/packages/charts-core/src/mark-composite.test.ts index ee8167e7..801474a0 100644 --- a/packages/charts-core/src/mark-composite.test.ts +++ b/packages/charts-core/src/mark-composite.test.ts @@ -163,6 +163,57 @@ describe('compositeMark', () => { }) }) + it('lets child motion disable or re-enable its composite parent', () => { + const context: ChartMotionContext = { + phase: 'enter', + role: 'dot', + key: 'compound:dots:a', + markId: 'compound:dots', + seriesKey: 'compound:dots', + seriesIndex: 0, + datumIndex: 0, + datumCount: 2, + datum: rows[0], + point: undefined, + } + const disabledChild = compositeMark( + [ + dot(rows, { + id: 'dots', + x: 'category', + y: 'value', + motion: false, + }), + ], + { + id: 'compound', + motion: { transition: { type: 'spring', damping: 18 } }, + }, + ).initialize({ markIndex: 0 }) + const enabledChild = compositeMark( + [ + dot(rows, { + id: 'dots', + x: 'category', + y: 'value', + motion: { delay: 20 }, + }), + ], + { id: 'compound', motion: false }, + ).initialize({ markIndex: 0 }) + + expect( + typeof disabledChild.motion === 'function' + ? disabledChild.motion(context) + : disabledChild.motion, + ).toBe(false) + expect( + typeof enabledChild.motion === 'function' + ? enabledChild.motion(context) + : enabledChild.motion, + ).toEqual({ delay: 20 }) + }) + it('rejects duplicate ids and nested resolved layouts', () => { const duplicate = compositeMark([ dot(rows, { id: 'same', x: 'category', y: 'value' }), diff --git a/packages/charts-core/src/marks.test.ts b/packages/charts-core/src/marks.test.ts index 10cf9b46..7cf29605 100644 --- a/packages/charts-core/src/marks.test.ts +++ b/packages/charts-core/src/marks.test.ts @@ -58,6 +58,69 @@ describe('core marks and categorical scales', () => { expect(scene.points.map((point) => point.datum)).toEqual(data) }) + it('resolves authored bar outlines in both orientations', () => { + const data = [ + { id: 'a', category: 'Alpha', value: 12, stroke: 'red' }, + { id: 'b', category: 'Beta', value: 18, stroke: 'blue' }, + ] + const vertical = createChartScene( + defineChart({ + marks: [ + barY(data, { + x: 'category', + y: 'value', + stroke: (row) => row.stroke, + strokeOpacity: 0.75, + strokeWidth: 2, + strokeDasharray: (row) => (row.id === 'a' ? '4 2' : '2 1'), + }), + ], + ...bandXAxes(['Alpha', 'Beta'], [0, 18]), + }), + { width: 480, height: 260 }, + ) + const horizontal = createChartScene( + defineChart({ + marks: [ + barX(data, { + x: 'value', + y: 'category', + stroke: (row) => row.stroke, + strokeOpacity: 0.75, + strokeWidth: 2, + strokeDasharray: (row) => (row.id === 'a' ? '4 2' : '2 1'), + }), + ], + ...bandYAxes([0, 18], ['Alpha', 'Beta']), + }), + { width: 480, height: 260 }, + ) + const verticalBars = flatten(vertical.nodes).filter( + (node) => node.kind === 'rect', + ) + const horizontalBars = flatten(horizontal.nodes).filter( + (node) => node.kind === 'rect', + ) + + expect(verticalBars.map((node) => node.style)).toEqual([ + expect.objectContaining({ + stroke: 'red', + strokeOpacity: 0.75, + strokeWidth: 2, + strokeDasharray: '4 2', + }), + expect.objectContaining({ + stroke: 'blue', + strokeOpacity: 0.75, + strokeWidth: 2, + strokeDasharray: '2 1', + }), + ]) + expect(horizontalBars.map((node) => node.style)).toEqual( + verticalBars.map((node) => node.style), + ) + }) + it('caps resolved bar thickness without guessing final chart dimensions', () => { const data = [ { id: 'a', category: 'Alpha', value: 12 }, diff --git a/packages/charts-core/src/motion-definition.test.ts b/packages/charts-core/src/motion-definition.test.ts new file mode 100644 index 00000000..990a9030 --- /dev/null +++ b/packages/charts-core/src/motion-definition.test.ts @@ -0,0 +1,75 @@ +import { describe, expect, it } from 'vitest' +import { stagger } from './motion-definition' +import type { ChartMotionContext, ChartMotionTiming } from './types' + +const context: ChartMotionContext<{ id: string }> = { + phase: 'enter', + role: 'bar', + key: 'bar:2', + markId: 'bars', + seriesKey: 'desktop', + seriesIndex: 1, + datumIndex: 2, + datumCount: 4, + datum: { id: 'c' }, + point: undefined, +} + +describe('motion definition utilities', () => { + it('staggers enter timing by datum index by default', () => { + const timing = stagger({ each: 24 }) + + expect(resolveDelay(timing, context)).toBe(48) + expect( + resolveDelay(timing, { ...context, phase: 'update' }), + ).toBeUndefined() + }) + + it('can stagger selected phases and roles by series index', () => { + const timing = stagger({ + each: 30, + offset: 10, + by: 'series', + phase: ['enter', 'exit'], + roles: ['bar', 'arc'], + }) + + expect(resolveDelay(timing, context)).toBe(40) + expect(resolveDelay(timing, { ...context, phase: 'exit' })).toBe(40) + expect(resolveDelay(timing, { ...context, role: 'line' })).toBeUndefined() + }) + + it('clamps invalid delays instead of producing negative timing', () => { + const timing = stagger({ each: -20, offset: Number.NaN }) + + expect(resolveDelay(timing, context)).toBe(0) + }) + + it('composes with timing fields through ordinary object spread', () => { + const timing = { + transition: { type: 'spring' as const, stiffness: 170 }, + path: 'morph' as const, + delay: 5, + ...stagger<{ id: string }>({ each: 20 }), + } + + expect(timing).toMatchObject({ + transition: { type: 'spring', stiffness: 170 }, + path: 'morph', + }) + expect(resolveDelay(timing, context)).toBe(40) + + const explicitDelay = { + ...stagger({ each: 20 }), + delay: 60, + } + expect(explicitDelay.delay).toBe(60) + }) +}) + +function resolveDelay( + timing: Pick, 'delay'>, + value: ChartMotionContext, +) { + return typeof timing.delay === 'function' ? timing.delay(value) : timing.delay +} diff --git a/packages/charts-core/src/motion-definition.ts b/packages/charts-core/src/motion-definition.ts new file mode 100644 index 00000000..87c5d84b --- /dev/null +++ b/packages/charts-core/src/motion-definition.ts @@ -0,0 +1,50 @@ +import type { + ChartMotionContext, + ChartMotionPhase, + ChartMotionRole, + ChartMotionTiming, +} from './types' + +export interface ChartMotionStaggerOptions { + /** Index used to calculate the delay. Defaults to `datum`. */ + by?: 'datum' | 'series' + /** Milliseconds between adjacent entries. */ + each: number + /** Fixed milliseconds before the first entry. Defaults to zero. */ + offset?: number + /** Restricts staggering to one or more lifecycle phases. Defaults to `enter`. */ + phase?: ChartMotionPhase | readonly ChartMotionPhase[] + /** Restricts staggering to one or more semantic roles. */ + roles?: ChartMotionRole | readonly ChartMotionRole[] +} + +/** Creates a delay timing field that composes through object spread. */ +export function stagger( + options: ChartMotionStaggerOptions, +): Pick, 'delay'> { + const phases = values(options.phase ?? 'enter') + const roles = options.roles === undefined ? undefined : values(options.roles) + const each = nonNegative(options.each) + const offset = nonNegative(options.offset ?? 0) + return { + delay(context: ChartMotionContext) { + if ( + !phases.includes(context.phase) || + (roles !== undefined && !roles.includes(context.role)) + ) { + return undefined + } + const index = + options.by === 'series' ? context.seriesIndex : context.datumIndex + return offset + each * Math.max(0, index) + }, + } +} + +function values(value: TValue | readonly TValue[]): readonly TValue[] { + return Array.isArray(value) ? value : [value as TValue] +} + +function nonNegative(value: number) { + return Number.isFinite(value) ? Math.max(0, value) : 0 +} diff --git a/packages/charts-core/src/motion.test.ts b/packages/charts-core/src/motion.test.ts index 863fed40..dbc2af25 100644 --- a/packages/charts-core/src/motion.test.ts +++ b/packages/charts-core/src/motion.test.ts @@ -8,8 +8,8 @@ import { dot } from './dot' import { whenFocused } from './focus-mark' import { lineY } from './line' import { createMark } from './mark' -import { motion } from './motion' -import { polar, radialBarRadius } from './polar' +import { motion, stagger } from './motion' +import { polar, radialArea, radialBarRadius } from './polar' import { mountChartRenderer } from './renderer' import { createChartScene, defineChart } from './scene' import { chartSceneSource } from './scene-source' @@ -164,6 +164,44 @@ describe('SVG motion', () => { request.mockRestore() }) + it('replays initial motion on adopted SVG only when explicitly requested', () => { + const scene = createChartScene( + defineChart({ + marks: [barY(rows, { x: 'category', y: 'value', key: 'id' })], + x: { scale: scaleBand().domain(['A', 'B']) }, + y: { scale: scaleLinear().domain([0, 100]) }, + guides: false, + }), + { width: 300, height: 200 }, + ) + const container = document.createElement('div') + container.innerHTML = renderChartSvg(scene, { ariaLabel: 'Adopted chart' }) + const frames = installFrames() + const surface = motion({ + initial: 'always', + transition: { type: 'tween', duration: 100, easing: 'linear' }, + }).mount(container, () => {}) + + surface.render(scene, { ariaLabel: 'Adopted chart' }) + + expect(frames.request).toHaveBeenCalledOnce() + expect(container.querySelector('svg')?.dataset.tsMotionState).toBe( + 'running', + ) + expect( + Number( + container + .querySelector('g.ts-chart__bar-y rect') + ?.getAttribute('height'), + ), + ).toBe(0) + + frames.run(0) + frames.run(100) + surface.destroy() + frames.restore() + }) + it('is the sole animation owner when the definition also enables animate', () => { const makeDefinition = (value: number) => defineChart({ @@ -1010,17 +1048,11 @@ describe('SVG motion', () => { }) it('grows bars from their semantic baseline with bounded datum timing', () => { - const timing = vi.fn((context) => - context.datum?.id === 'b' - ? { - delay: 40, - transition: { type: 'tween' as const, duration: 50 }, - } - : undefined, - ) const scene = createChartScene( defineChart({ - motion: timing, + motion: { + ...stagger<(typeof rows)[number]>({ each: 40, roles: 'bar' }), + }, marks: [ barY(rows, { x: 'category', @@ -1049,21 +1081,16 @@ describe('SVG motion', () => { expect(rectangles).toHaveLength(2) expect(Number(rectangles[0]?.getAttribute('y'))).toBeCloseTo(baseline) expect(rectangles[0]?.getAttribute('height')).toBe('0') - expect(timing.mock.calls.map(([context]) => context.datum?.id)).toEqual([ - 'a', - 'b', - ]) - frames.run(0) frames.run(50) expect(Number(rectangles[0]?.getAttribute('height'))).toBeCloseTo( scene.chart.height * 0.4 * 0.5, ) expect(Number(rectangles[1]?.getAttribute('height'))).toBeCloseTo( - scene.chart.height * 0.8 * 0.2, + scene.chart.height * 0.8 * 0.1, ) - frames.run(120) + frames.run(140) expect(rectangles[0]?.hasAttribute('data-ts-motion-role')).toBe(false) expect( container.querySelector('svg')?.getAttribute('data-ts-motion-state'), @@ -1073,6 +1100,123 @@ describe('SVG motion', () => { frames.restore() }) + it('lets a mark opt out of inherited chart motion', () => { + const animated = [{ id: 'animated', category: 'A', value: 40 }] + const immediate = [{ id: 'immediate', category: 'B', value: 80 }] + const scene = createChartScene( + defineChart({ + motion: { + transition: { type: 'tween', duration: 100, easing: 'linear' }, + }, + marks: [ + barY(animated, { + id: 'animated-bars', + x: 'category', + y: 'value', + key: 'id', + }), + barY(immediate, { + id: 'immediate-bars', + x: 'category', + y: 'value', + key: 'id', + motion: false, + }), + ], + x: { scale: scaleBand().domain(['A', 'B']) }, + y: { scale: scaleLinear().domain([0, 100]) }, + guides: false, + }), + { width: 300, height: 200 }, + ) + const container = document.createElement('div') + const frames = installFrames() + const surface = motion().mount(container, () => {}) + surface.render(scene, { ariaLabel: 'Mixed motion bars' }) + const animatedPoint = scene.points.find( + (point) => point.datum.id === 'animated', + ) + const immediatePoint = scene.points.find( + (point) => point.datum.id === 'immediate', + ) + const animatedRectangle = container.querySelector( + `[data-ts-key="${animatedPoint?.key}"]`, + ) + const immediateRectangle = container.querySelector( + `[data-ts-key="${immediatePoint?.key}"]`, + ) + + expect(animatedRectangle?.getAttribute('height')).toBe('0') + expect(animatedRectangle?.dataset.tsMotionRole).toBe('bar') + expect(Number(immediateRectangle?.getAttribute('y'))).toBeCloseTo( + immediatePoint?.y ?? Number.NaN, + ) + expect(immediateRectangle?.hasAttribute('data-ts-motion-role')).toBe(false) + expect(frames.request).toHaveBeenCalledOnce() + + frames.run(0) + frames.run(100) + surface.destroy() + frames.restore() + }) + + it('lets guide children re-enable motion below an axis opt-out', () => { + const definition = defineChart({ + motion: { + transition: { type: 'tween', duration: 100, easing: 'linear' }, + }, + marks: [lineY([0, 1])], + margin: 0, + x: { + scale: scaleLinear().domain([0, 1]), + grid: true, + axis: { + motion: false, + ticks: { + values: [0, 1], + motion: { + transition: { type: 'tween', duration: 100, easing: 'linear' }, + }, + }, + tickLabels: { motion: false }, + label: { text: 'Period' }, + }, + }, + y: { scale: scaleLinear().domain([0, 1]), axis: false }, + }) + const first = createChartScene(definition, { width: 300, height: 200 }) + const next = createChartScene(definition, { width: 400, height: 200 }) + const container = document.createElement('div') + const surface = motion({ initial: false, resize: true }).mount( + container, + () => {}, + ) + surface.render(first, { ariaLabel: 'Guide motion scopes' }) + const frames = installManagedFrames() + surface.render(next, { ariaLabel: 'Guide motion scopes' }) + + expect( + [...container.querySelectorAll('[data-ts-key^="x-tick-rule:"]')].some( + (element) => element.getAttribute('data-ts-motion-role') === 'tick', + ), + ).toBe(true) + for (const selector of [ + '[data-ts-key="x-axis"]', + '[data-ts-key^="x-grid:"]', + '[data-ts-key^="x-tick-label:"]', + '[data-ts-key="x-label"]', + ]) { + expect( + container.querySelector(selector)?.hasAttribute('data-ts-motion-role'), + ).toBe(false) + } + + frames.run(0) + frames.run(100) + surface.destroy() + frames.restore() + }) + it('cascades definition motion through marks, axes, ticks, and labels', () => { const chartRoles: string[] = [] const markRoles: string[] = [] @@ -2230,6 +2374,17 @@ describe('SVG motion', () => { marks: [ barY(rows, { x: 'category', y: 'value', key: 'id' }), lineY(rows, { x: 'category', y: 'value', key: 'id' }), + polar({ + angle: { scale: scaleBand().domain(['A']) }, + radius: { scale: scaleLinear().domain([0, 100]) }, + marks: [ + radialBarRadius(rows, { + angle: 'category', + radius: 'value', + key: 'id', + }), + ], + }), ], x: { scale: scaleBand().domain(['A', 'B']) }, y: { scale: scaleLinear().domain([0, 100]) }, @@ -2245,7 +2400,7 @@ describe('SVG motion', () => { const surface = renderer.mount(container, () => {}) surface.render(scene, { ariaLabel: 'Composed chart' }) - expect(container.querySelectorAll('[data-ts-motion-role]').length).toBe(3) + expect(container.querySelectorAll('[data-ts-motion-role]').length).toBe(4) expect( container.querySelector('clipPath[id^="ts-chart-motion-clip"]'), ).not.toBeNull() @@ -2722,10 +2877,15 @@ describe('SVG motion', () => { frames.restore() }) - it('reveals a complete line group through a chart-space clip', () => { + it('grows Cartesian line and area groups from the semantic baseline', () => { const scene = createChartScene( defineChart({ marks: [ + areaY(rows, { + x: 'category', + y: 'value', + key: 'id', + }), lineY(rows, { x: 'category', y: 'value', @@ -2747,21 +2907,65 @@ describe('SVG motion', () => { const surface = renderer.mount(container, () => {}) surface.render(scene, { ariaLabel: 'Line' }) const line = container.querySelector('g.ts-chart__line') - const clipRectangle = container.querySelector( - 'clipPath[id^="ts-chart-motion-clip"] rect', + const area = container.querySelector('g.ts-chart__area') + const baseline = scene.scales.y?.map(0) ?? Number.NaN + + expect(matrixTransform(line)).toEqual([1, 0, 0, 0, 0, baseline]) + expect(matrixTransform(area)).toEqual([1, 0, 0, 0, 0, baseline]) + frames.run(0) + frames.run(50) + expect(matrixTransform(line)).toEqual([1, 0, 0, 0.5, 0, baseline / 2]) + expect(matrixTransform(area)).toEqual([1, 0, 0, 0.5, 0, baseline / 2]) + frames.run(100) + expect(line?.hasAttribute('transform')).toBe(false) + expect(area?.hasAttribute('transform')).toBe(false) + + surface.destroy() + frames.restore() + }) + + it('sweeps arcs around their polar angle on initial render', () => { + const scene = createChartScene( + defineChart({ + marks: [ + polar({ + angle: { scale: scaleBand().domain(['A']) }, + radius: { scale: scaleLinear().domain([0, 100]) }, + marks: [ + radialBarRadius(rows, { + angle: 'category', + radius: 'value', + key: 'id', + }), + ], + }), + ], + guides: false, + margin: 0, + }), + { width: 200, height: 200 }, + ) + const container = document.createElement('div') + const renderer = motion({ + transition: { type: 'tween', duration: 100, easing: 'linear' }, + }) + const frames = installFrames() + const surface = renderer.mount(container, () => {}) + surface.render(scene, { ariaLabel: 'Radial bars' }) + const arcs = container.querySelector('g.ts-chart__arc') + const clipPath = container.querySelector( + 'clipPath[id^="ts-chart-motion-clip"] path', ) - expect(line?.getAttribute('clip-path')).toMatch( + expect(arcs?.getAttribute('clip-path')).toMatch( /^url\(#ts-chart-motion-clip-/, ) - expect(clipRectangle?.getAttribute('width')).toBe('0') + expect(clipPath?.getAttribute('d')).toBe('M0 0Z') frames.run(0) frames.run(50) - expect(Number(clipRectangle?.getAttribute('width'))).toBeCloseTo( - scene.chart.width / 2, - ) + expect(clipPath?.getAttribute('d')).not.toBe('M0 0Z') frames.run(100) - expect(line?.hasAttribute('clip-path')).toBe(false) + expect(arcs?.hasAttribute('clip-path')).toBe(false) expect( container.querySelector('clipPath[id^="ts-chart-motion-clip"]'), ).toBeNull() @@ -2769,6 +2973,107 @@ describe('SVG motion', () => { surface.destroy() frames.restore() }) + + it('keeps removed arcs painted through their exit transition', () => { + const radialRows = [ + { id: 'a', category: 'A', value: 40 }, + { id: 'b', category: 'B', value: 80 }, + ] + const makeScene = (data: typeof radialRows) => + createChartScene( + defineChart({ + marks: [ + polar({ + angle: { scale: scaleBand().domain(['A', 'B']) }, + radius: { scale: scaleLinear().domain([0, 100]) }, + marks: [ + radialBarRadius(data, { + angle: 'category', + radius: 'value', + key: 'id', + }), + ], + }), + ], + guides: false, + margin: 0, + }), + { width: 200, height: 200 }, + ) + const container = document.createElement('div') + const surface = motion({ + initial: false, + transition: { type: 'tween', duration: 100, easing: 'linear' }, + }).mount(container, () => {}) + surface.render(makeScene(radialRows), { ariaLabel: 'Radial bars' }) + const frames = installFrames() + + surface.render(makeScene(radialRows.slice(0, 1)), { + ariaLabel: 'Radial bars', + }) + + const arcs = () => [ + ...container.querySelectorAll('g.ts-chart__arc > path'), + ] + expect(arcs()).toHaveLength(2) + frames.run(0) + frames.run(50) + expect(arcs()).toHaveLength(2) + expect(arcs().some((arc) => arc.getAttribute('opacity') === '0.5')).toBe( + true, + ) + frames.run(100) + expect(arcs()).toHaveLength(1) + + surface.destroy() + frames.restore() + }) + + it('grows radial path groups out from the polar center', () => { + const radialRows = [ + { angle: 0, value: 40 }, + { angle: 1, value: 80 }, + { angle: 2, value: 60 }, + { angle: 3, value: 70 }, + ] + const scene = createChartScene( + defineChart({ + marks: [ + polar({ + angle: { scale: scaleLinear().domain([0, 3]) }, + radius: { scale: scaleLinear().domain([0, 100]) }, + marks: [ + radialArea(radialRows, { + angle: 'angle', + radius: 'value', + }), + ], + }), + ], + guides: false, + margin: 0, + }), + { width: 200, height: 200 }, + ) + const container = document.createElement('div') + const renderer = motion({ + transition: { type: 'tween', duration: 100, easing: 'linear' }, + }) + const frames = installFrames() + const surface = renderer.mount(container, () => {}) + surface.render(scene, { ariaLabel: 'Radar area' }) + const area = container.querySelector('g.ts-chart__radial-area') + + expect(area?.getAttribute('transform')).toBe('scale(0)') + frames.run(0) + frames.run(50) + expect(area?.getAttribute('transform')).toBe('scale(0.5)') + frames.run(100) + expect(area?.hasAttribute('transform')).toBe(false) + + surface.destroy() + frames.restore() + }) }) function installFrames() { diff --git a/packages/charts-core/src/motion.ts b/packages/charts-core/src/motion.ts index 6c28e0bd..c55c2eaa 100644 --- a/packages/charts-core/src/motion.ts +++ b/packages/charts-core/src/motion.ts @@ -15,6 +15,12 @@ import { renderChartSvgWithResources } from './svg-resources' import { svgClientToScene } from './svg-coordinates' import { valueKey } from './scales' import { resolveRollingPathPlan } from './motion-path' +import { + chartRendererMotion, + type ChartRendererMotionCapability, + type ChartTooltipMotionController, + type ChartTooltipMotionSnapshot, +} from './renderer-motion-internal' import type { RollingPathPlan, RollingPathSnapshot, @@ -70,10 +76,12 @@ export type { ChartMotionTransition, ChartMotionTweenTransition, } from './types' +export { stagger } from './motion-definition' +export type { ChartMotionStaggerOptions } from './motion-definition' export interface ChartMotionOptions { - /** Animate the first client render. Server-rendered SVG is always adopted. */ - initial?: boolean + /** Animate the first client render. Use `always` to replay adopted server-rendered SVG. */ + initial?: boolean | 'always' /** Default transition. Definition-local motion can refine or replace it. */ transition?: ChartMotionTransition /** Snap when the user requests reduced motion. Defaults to true. */ @@ -105,13 +113,14 @@ interface ChartSvgMotionFragmentContext { interface ChartSvgMotionDriver { readonly id: string - readonly initial: boolean + readonly initial: boolean | 'always' readonly resize: boolean readonly respectReducedMotion: boolean animateSvg: (context: ChartSvgMotionContext) => () => void animateSvgFragment: ( context: ChartSvgMotionFragmentContext, ) => () => void + createTooltip: ChartRendererMotionCapability['createTooltip'] } interface MotionValueState { @@ -127,6 +136,7 @@ interface MotionValueBinding { } interface MotionTrack { + disabled: boolean delay: number transition: ResolvedTransition values: MotionValueBinding[] @@ -177,6 +187,7 @@ type SceneMotionSource = readonly [ ] type ResolvedTiming = Pick & { + disabled: boolean path: ChartMotionPath } type TimingResolver = (context: ChartMotionContext) => ResolvedTiming @@ -226,6 +237,8 @@ function createSvgMotionRuntime( return { id: 'svg-motion', ...policy, + createTooltip: (context) => + createTooltipMotionController(options, policy, context), animateSvg(context) { let runtime = runtimes.get(context.container) if (!runtime) { @@ -266,7 +279,9 @@ function createSvgMotionRuntime( ) const tracks = [ ...createBarTracks(root, context.scene, points, timing, runtime), - ...createLineTracks(root, context.scene, timing), + ...createCartesianPathTracks(root, context.scene, timing), + ...createRadialPathTracks(root, context.scene, timing), + ...createArcTracks(root, context.scene, timing), ] const presentation = createPresentationTracks( root, @@ -323,6 +338,414 @@ function createSvgMotionRuntime( } } +function createTooltipMotionController( + options: ResolvedMotionOptions, + policy: Pick, + context: { + container: HTMLElement + transition: () => false | ChartMotionTransition | undefined + }, +): ChartTooltipMotionController { + const view = context.container.ownerDocument.defaultView + let presenceAnimation: Animation | undefined + let presencePhase: 'enter' | 'exit' | undefined + let movementAnimation: Animation | undefined + let movementFrame: number | undefined + let springMovement: TooltipSpringMovement | undefined + let hideGeneration = 0 + + const prefersReducedMotion = () => + policy.respectReducedMotion && + Boolean(view?.matchMedia?.('(prefers-reduced-motion: reduce)').matches) + + const transition = (override: false | ChartMotionTransition | undefined) => { + if (override === false || prefersReducedMotion()) return undefined + const inherited = override ?? context.transition() + if (inherited === false) return undefined + return resolveTransition( + inherited, + defaultDuration, + undefined, + options.transition, + ) + } + + const now = () => view?.performance.now() ?? Date.now() + + const stopMovement = (element: HTMLElement | undefined) => { + if (movementFrame !== undefined) { + view?.cancelAnimationFrame?.(movementFrame) + movementFrame = undefined + } + movementAnimation?.cancel() + movementAnimation = undefined + springMovement = undefined + element?.style.removeProperty('translate') + } + + const sampleSpringMovement = ( + timestamp: number, + element: HTMLElement, + ): TooltipMovementSnapshot => { + 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 = undefined + element.style.removeProperty('translate') + return emptyTooltipMovement + } + return snapshot + } + + const sampleSpringFrame = (timestamp: number, element: HTMLElement) => { + movementFrame = undefined + sampleSpringMovement(timestamp, element) + if (!springMovement) return + movementFrame = view?.requestAnimationFrame((nextTimestamp) => { + sampleSpringFrame(nextTimestamp, element) + }) + } + + const captureMovement = (element: HTMLElement) => { + let snapshot: TooltipMovementSnapshot + 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: HTMLElement, + resolved: ResolvedTransition, + movement: TooltipMovementSnapshot, + ) => { + 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 = undefined + } + 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: HTMLElement, + resolved: ResolvedTransition, + from: TooltipPresenceState, + to: TooltipPresenceState, + ) => { + if (typeof element.animate !== 'function') return undefined + 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) + : undefined + if (resumingExit) { + presenceAnimation?.cancel() + presenceAnimation = undefined + presencePhase = undefined + } + 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 = undefined + presencePhase = undefined + 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 === undefined || + nextLeft === undefined + ? 0 + : snapshot.previousLeft + snapshot.movementX - nextLeft, + y: + snapshot.wasHidden || + snapshot.previousTop === undefined || + nextTop === undefined + ? 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 = undefined + element.style.removeProperty('opacity') + element.style.removeProperty('transform') + return + } + animation.onfinish = () => { + if (presenceAnimation !== animation) return + element.style.removeProperty('opacity') + element.style.removeProperty('transform') + presenceAnimation = undefined + presencePhase = undefined + } + }, + hide(element, override, complete) { + const resolved = transition(override) + if (!resolved) { + stopMovement(element) + presenceAnimation?.cancel() + presenceAnimation = undefined + presencePhase = undefined + 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 = undefined + stopMovement(element) + return false + } + animation.onfinish = () => { + if (generation !== hideGeneration) return + complete() + element.style.removeProperty('opacity') + element.style.removeProperty('transform') + presenceAnimation = undefined + presencePhase = undefined + stopMovement(element) + } + return true + }, + destroy(element) { + hideGeneration += 1 + presenceAnimation?.cancel() + presenceAnimation = undefined + presencePhase = undefined + stopMovement(element) + element?.style.removeProperty('opacity') + element?.style.removeProperty('transform') + }, + } +} + +interface TooltipPresenceState { + opacity: number + scale: number +} + +interface TooltipMovementSnapshot { + x: number + y: number + velocityX: number + velocityY: number +} + +interface TooltipSpringMovement { + spring: ChartSpring + startedAt: number + fromX: number + fromY: number + velocityX: number + velocityY: number +} + +const emptyTooltipMovement: TooltipMovementSnapshot = { + x: 0, + y: 0, + velocityX: 0, + velocityY: 0, +} + +function tooltipMotionSamples(transition: ResolvedTransition) { + if (transition.type === 'tween') { + const offsets = Array.from({ length: 31 }, (_, index) => index / 30) + return { + duration: transition.duration, + offsets, + values: offsets.map(transition.easing), + } + } + const offsets: number[] = [] + const values: number[] = [] + let duration = 0 + for (let elapsed = 0; elapsed <= 2_000; 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: number, to: number, progress: number) { + return from + (to - from) * progress +} + +function finiteStyleNumber(value: string) { + const number = Number.parseFloat(value) + return Number.isFinite(number) ? number : undefined +} + +function readTooltipPresenceState(element: HTMLElement): TooltipPresenceState { + const style = element.ownerDocument.defaultView?.getComputedStyle(element) + return { + opacity: finiteStyleNumber(style?.opacity ?? '') ?? 1, + scale: readTransformScale(style?.transform ?? ''), + } +} + +function readTooltipTranslate(element: HTMLElement) { + 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: string) { + 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: SVGElement, markup: string) { const template = current.ownerDocument.createElement('template') template.innerHTML = `${markup}` @@ -352,8 +775,11 @@ function createMotionSvgChartRenderer< TYValue > = renderChartSvgWithResources, ): ChartRenderer { - const renderer: ChartRenderer = { + const renderer: ChartRenderer & { + [chartRendererMotion]: ChartRendererMotionCapability + } = { id: `svg:${motion.id}`, + [chartRendererMotion]: { createTooltip: motion.createTooltip }, prerender: renderSvg, mount(container) { const adoptedRoot = @@ -504,7 +930,7 @@ function createMotionSvgChartRenderer< const animate = !reduced && (initial - ? motion.initial && !adoptedRoot + ? motion.initial && (!adoptedRoot || motion.initial === 'always') : motion.resize || !resized) const viewportMoved = Boolean( previousScene && @@ -837,17 +1263,138 @@ function createBarTracks( return tracks } -function createLineTracks( +function createCartesianPathTracks( root: SVGSVGElement, scene: ChartScene, timingFor: TimingResolver, ): MotionTrack[] { - const groups = [...root.querySelectorAll('g.ts-chart__line')] + 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 seriesKey = group.getAttribute('data-ts-key') ?? `line:${seriesIndex}` + const role: ChartMotionRole = group.classList.contains('ts-chart__area') + ? 'area' + : 'line' + const seriesKey = + group.getAttribute('data-ts-key') ?? `${role}:${seriesIndex}` const timing = timingFor({ phase: 'enter', - role: 'line', + role, + key: seriesKey, + markId: motionMarkId(scene, seriesKey), + seriesKey, + seriesIndex, + datumIndex: 0, + datumCount: 1, + datum: undefined, + point: undefined, + }) + const horizontal = scenePathAffinity(scene, seriesKey) === 'y' + const baseline = resolvePathBaseline(scene, horizontal) + const previousTransform = group.getAttribute('transform') + group.dataset.tsMotionRole = role + const apply = (values: readonly number[]) => { + 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(undefined, [0], [1]), + apply, + finish: cleanup, + cancel: cleanup, + } + }) +} + +function createRadialPathTracks( + root: SVGSVGElement, + scene: ChartScene, + timingFor: TimingResolver, +): MotionTrack[] { + 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: ChartMotionRole = 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: undefined, + point: undefined, + }) + const previousTransform = group.getAttribute('transform') + group.dataset.tsMotionRole = role + const apply = (values: readonly number[]) => { + 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(undefined, [0], [1]), + apply, + finish: cleanup, + cancel: cleanup, + } + }) +} + +function createArcTracks( + root: SVGSVGElement, + scene: ChartScene, + timingFor: TimingResolver, +): MotionTrack[] { + 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: ChartMotionRole = group.classList.contains('ts-chart__bar') + ? 'bar' + : 'arc' + const timing = timingFor({ + phase: 'enter', + role, key: seriesKey, markId: motionMarkId(scene, seriesKey), seriesKey, @@ -859,53 +1406,179 @@ function createLineTracks( }) const document = root.ownerDocument let definitions = root.querySelector('defs') - let ownsDefinitions = false if (!definitions) { definitions = document.createElementNS( 'http://www.w3.org/2000/svg', 'defs', ) + definitions.dataset.tsMotionDefs = '' root.prepend(definitions) - ownsDefinitions = true } const clip = document.createElementNS( 'http://www.w3.org/2000/svg', 'clipPath', ) - const rectangle = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'rect', - ) + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path') const id = `ts-chart-motion-clip-${++clipId}` clip.id = id - rectangle.setAttribute('x', formatNumber(scene.chart.x)) - rectangle.setAttribute('y', formatNumber(scene.chart.y)) - rectangle.setAttribute('width', '0') - rectangle.setAttribute('height', formatNumber(scene.chart.height)) - clip.append(rectangle) + clip.append(path) definitions.append(clip) const previousClip = group.getAttribute('clip-path') group.setAttribute('clip-path', `url(#${id})`) - group.dataset.tsMotionRole = 'line' + group.dataset.tsMotionRole = role + const apply = (values: readonly number[]) => { + 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 (ownsDefinitions && !definitions?.children.length) definitions.remove() + if ( + definitions?.dataset.tsMotionDefs !== undefined && + !definitions.children.length + ) { + definitions.remove() + } } - return { - ...timing, - values: bindMotionValues(undefined, [0], [scene.chart.width]), - apply(values) { - rectangle.setAttribute('width', formatNumber(values[0] ?? 0)) + apply([0]) + return [ + { + ...timing, + values: bindMotionValues(undefined, [0], [1]), + apply, + finish: cleanup, + cancel: cleanup, }, - finish: cleanup, - cancel: cleanup, - } + ] }) } +function resolvePathBaseline(scene: ChartScene, horizontal: boolean) { + 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: ChartScene, + key: string, +): 'x' | 'y' | 'xy' | 'geometry' | undefined { + const node = findSceneNodeContext(scene.nodes, key)?.node + if (!node) return undefined + const visit = ( + candidate: SceneNode, + ): 'x' | 'y' | 'xy' | 'geometry' | undefined => { + if (candidate.kind === 'group') { + for (const child of candidate.children) { + const affinity = visit(child) + if (affinity) return affinity + } + return undefined + } + return 'interaction' in candidate + ? candidate.interaction?.affinity + : undefined + } + return visit(node) +} + +function sceneArcGeometry( + scene: ChartScene, + key: string, +): { startAngle: number; sweep: number; radius: number } | undefined { + const node = findSceneNodeContext(scene.nodes, key)?.node + if (!node) return undefined + const pointSets: (readonly (readonly [number, number])[])[] = [] + const visit = (candidate: SceneNode) => { + 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 undefined + 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 undefined + 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 undefined + return { startAngle, sweep: sweep * direction, radius: radius + 2 } +} + +function polarPointAngle(point: readonly [number, number]) { + return Math.atan2(point[0], -point[1]) +} + +function signedAngleDelta(from: number, to: number) { + const tau = Math.PI * 2 + return ((((to - from + Math.PI) % tau) + tau) % tau) - Math.PI +} + +function positiveAngle(angle: number) { + const tau = Math.PI * 2 + return ((angle % tau) + tau) % tau +} + +function radialSweepClipPath( + startAngle: number, + sweep: number, + radius: number, +) { + 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: ChartSvgMotionContext, options: ResolvedMotionOptions, @@ -2633,11 +3306,26 @@ function resolveTiming( let delay = automaticDelay let transition = options.transition let path: ChartMotionPath = 'morph' + let disabled = false const apply = (definition: ChartMotionDefinition | undefined) => { const authored = typeof definition === 'function' ? definition(context) : definition - if (!authored) return - if (authored.delay !== undefined) delay = nonNegative(authored.delay, delay) + if (authored === undefined) 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 !== undefined) { + delay = nonNegative(authoredDelay, delay) + } if (authored.path !== undefined) path = authored.path transition = resolveTransition( authored.transition, @@ -2679,7 +3367,7 @@ function resolveTiming( // A delayed physical retarget would freeze the sampled velocity. Spring // updates therefore begin immediately; use delay for enter/exit choreography. if (context.phase === 'update' && transition.type === 'spring') delay = 0 - return { delay, transition, path } + return { disabled, delay, transition, path } } function createTimingResolver( @@ -2787,7 +3475,13 @@ function runTracks( tracks: readonly MotionTrack[], lifecycle: { publish?: () => void; finish?: () => void } = {}, ) { - if (!tracks.length) { + 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 () => {} } @@ -2795,13 +3489,13 @@ function runTracks( const requestFrame = view?.requestAnimationFrame?.bind(view) const cancelFrame = view?.cancelAnimationFrame?.bind(view) if (!requestFrame || !cancelFrame) { - tracks.forEach(completeMotionTrack) + activeTracks.forEach(completeMotionTrack) lifecycle.finish?.() return () => {} } const safetyLimit = Math.max( - ...tracks.map( + ...activeTracks.map( (track) => track.delay + (track.transition.type === 'tween' @@ -2810,7 +3504,7 @@ function runTracks( ), ) if (safetyLimit <= 0) { - tracks.forEach(completeMotionTrack) + activeTracks.forEach(completeMotionTrack) lifecycle.finish?.() return () => {} } @@ -2826,7 +3520,7 @@ function runTracks( if (cancelled) return start ??= time const elapsed = time - start - for (const track of tracks) { + for (const track of activeTracks) { if (finished.has(track)) continue if (sampleMotionTrack(track, elapsed)) { completeMotionTrack(track) @@ -2834,12 +3528,12 @@ function runTracks( } } lifecycle.publish?.() - root.dataset.tsMotionProgress = String(finished.size / tracks.length) - if (finished.size < tracks.length && elapsed < safetyLimit) { + root.dataset.tsMotionProgress = String(finished.size / activeTracks.length) + if (finished.size < activeTracks.length && elapsed < safetyLimit) { frame = requestFrame(tick) return } - for (const track of tracks) { + for (const track of activeTracks) { if (!finished.has(track)) completeMotionTrack(track) } lifecycle.finish?.() @@ -2852,7 +3546,7 @@ function runTracks( if (cancelled) return cancelled = true cancelFrame(frame) - tracks.forEach((track) => { + activeTracks.forEach((track) => { if (!finished.has(track)) track.cancel?.() }) lifecycle.publish?.() diff --git a/packages/charts-core/src/network-sankey.test.ts b/packages/charts-core/src/network-sankey.test.ts index 719720d2..bfecb029 100644 --- a/packages/charts-core/src/network-sankey.test.ts +++ b/packages/charts-core/src/network-sankey.test.ts @@ -579,6 +579,6 @@ function motionContext(markId: string): ChartMotionContext { function resolveMotion( motion: ChartMotionDefinition | undefined, context: ChartMotionContext, -): ChartMotionTiming | undefined { +): false | ChartMotionTiming | undefined { return typeof motion === 'function' ? motion(context) : motion } diff --git a/packages/charts-core/src/polar.test.ts b/packages/charts-core/src/polar.test.ts index bea063b8..b5ab099f 100644 --- a/packages/charts-core/src/polar.test.ts +++ b/packages/charts-core/src/polar.test.ts @@ -297,6 +297,8 @@ describe('polar marks', () => { values: [50, 100], shape: 'polygon', labels: true, + fill: '#dbeafe', + fillOpacity: 0.2, }), angleGrid(), ], @@ -382,6 +384,11 @@ describe('polar marks', () => { expect( nodes.filter((node) => node.kind === 'polyline').length, ).toBeGreaterThanOrEqual(3) + expect( + nodes.find( + (node) => node.kind === 'polyline' && node.style?.fill === '#dbeafe', + ), + ).toMatchObject({ style: { fillOpacity: 0.2 } }) expect(scene.points).toHaveLength(9) expect( Math.max(...scene.points.map((point) => point.x)), @@ -1222,7 +1229,7 @@ function authoredMotionContext(markId: string): ChartMotionContext { function resolveAuthoredMotion( motion: ChartMotionDefinition | undefined, context: ChartMotionContext, -): ChartMotionTiming | undefined { +): false | ChartMotionTiming | undefined { return typeof motion === 'function' ? motion(context) : motion } diff --git a/packages/charts-core/src/polar.ts b/packages/charts-core/src/polar.ts index e5dc3462..80ddb960 100644 --- a/packages/charts-core/src/polar.ts +++ b/packages/charts-core/src/polar.ts @@ -1726,6 +1726,8 @@ interface PolarGuideStyle { strokeOpacity?: number strokeWidth?: number strokeDasharray?: string + fill?: string + fillOpacity?: number labels?: boolean labelFill?: string labelFontSize?: number @@ -1778,7 +1780,8 @@ export function radialGrid(options: RadialGridOptions = {}): PolarGuide { points: [], path, style: { - fill: 'none', + fill: options.fill ?? 'none', + fillOpacity: options.fillOpacity, stroke, strokeOpacity: options.strokeOpacity, strokeWidth: options.strokeWidth ?? 1, diff --git a/packages/charts-core/src/renderer-motion-internal.ts b/packages/charts-core/src/renderer-motion-internal.ts new file mode 100644 index 00000000..d16d6526 --- /dev/null +++ b/packages/charts-core/src/renderer-motion-internal.ts @@ -0,0 +1,82 @@ +import type { ChartRenderer, ChartTooltipExtensionContext } from './dom-types' +import type { ChartMotionTransition, ChartValue } from './types' + +export const chartRendererMotion = Symbol('tanstack-charts-renderer-motion') + +export interface ChartTooltipMotionSnapshot { + wasHidden: boolean + showPresence: boolean + previousLeft?: number + previousTop?: number + movementX: number + movementY: number + velocityX: number + velocityY: number + presence?: { opacity: number; scale: number } +} + +export interface ChartTooltipMotionController { + beforePaint: (element: HTMLElement) => ChartTooltipMotionSnapshot + afterPaint: ( + element: HTMLElement, + snapshot: ChartTooltipMotionSnapshot, + transition: false | ChartMotionTransition | undefined, + ) => void + hide: ( + element: HTMLElement, + transition: false | ChartMotionTransition | undefined, + complete: () => void, + ) => boolean + destroy: (element: HTMLElement | undefined) => void +} + +export interface ChartRendererMotionCapability { + createTooltip: (context: { + container: HTMLElement + transition: () => false | ChartMotionTransition | undefined + }) => ChartTooltipMotionController +} + +type MotionRenderer = ChartRenderer & { + [chartRendererMotion]?: ChartRendererMotionCapability +} + +type MotionTooltipContext = ChartTooltipExtensionContext & { + [chartRendererMotion]?: ChartTooltipMotionController +} + +export function rendererMotionCapability( + renderer: ChartRenderer, +): ChartRendererMotionCapability | undefined { + return (renderer as MotionRenderer)[chartRendererMotion] +} + +export function tooltipMotionController< + TDatum, + TXValue extends ChartValue, + TYValue extends ChartValue, +>( + context: ChartTooltipExtensionContext, +): ChartTooltipMotionController | undefined { + return (context as MotionTooltipContext)[chartRendererMotion] +} + +export function connectRendererTooltipMotion< + TDatum, + TXValue extends ChartValue, + TYValue extends ChartValue, +>( + renderer: ChartRenderer, + context: ChartTooltipExtensionContext, + transition: () => false | ChartMotionTransition | undefined, +) { + const capability = rendererMotionCapability(renderer) + if (capability) { + const motionContext = context as MotionTooltipContext + motionContext[chartRendererMotion] = capability.createTooltip({ + container: context.container, + transition, + }) + } + return context +} diff --git a/packages/charts-core/src/renderer.test.ts b/packages/charts-core/src/renderer.test.ts index 6f57b2ee..5b8fa267 100644 --- a/packages/charts-core/src/renderer.test.ts +++ b/packages/charts-core/src/renderer.test.ts @@ -3,6 +3,7 @@ import { scaleBand, scaleLinear } from 'd3-scale' import { barY } from './bar' import { createChartCursor, cursorHost } from './cursor' import { lineY } from './line' +import { motion } from './motion' import { mountChartRenderer } from './renderer' import { defineChart, findNearestPoint } from './scene' import { stack } from './stack' @@ -173,6 +174,231 @@ describe('renderer-neutral chart host', () => { expect(container.childElementCount).toBe(0) }) + it('inherits chart spring motion for tooltip entry and exit', () => { + const descriptor = Object.getOwnPropertyDescriptor( + window.HTMLElement.prototype, + 'animate', + ) + const animations: Array<{ + cancel: ReturnType + onfinish: (() => void) | null + }> = [] + const animate = vi.fn( + (frames: Keyframe[], options: KeyframeAnimationOptions) => { + const animation = { cancel: vi.fn(), onfinish: null } + animations.push(animation) + return animation as unknown as Animation + }, + ) + Object.defineProperty(window.HTMLElement.prototype, 'animate', { + configurable: true, + value: animate, + }) + const container = document.createElement('div') + const host = mountChartRenderer(container, { + definition: defineChart(definition, { + maxFocusDistance: 1_000, + tooltip: tooltipExtension, + }), + renderer: motion({ + initial: false, + transition: { + type: 'spring', + stiffness: 170, + damping: 18, + mass: 1, + }, + }), + width: 480, + height: 260, + ariaLabel: 'Spring tooltip', + }) + + try { + host.interaction.setControlledFocus(host.getScene().points[0]!) + const enterFrames = animate.mock.calls[0]?.[0] + const enterOptions = animate.mock.calls[0]?.[1] + expect(enterFrames?.length).toBeGreaterThan(2) + expect(enterOptions).toMatchObject({ easing: 'linear', fill: 'both' }) + + host.interaction.setControlledFocus(null) + const tooltip = container.querySelector('.ts-chart-tooltip') + expect(animate).toHaveBeenCalledTimes(2) + expect(tooltip?.hidden).toBe(false) + animations.at(-1)?.onfinish?.() + expect(tooltip?.hidden).toBe(true) + } finally { + host.destroy() + restoreProperty(window.HTMLElement.prototype, 'animate', descriptor) + } + }) + + it('lets chart motion disable tooltip motion while a tooltip override re-enables it', () => { + const descriptor = Object.getOwnPropertyDescriptor( + window.HTMLElement.prototype, + 'animate', + ) + const animate = vi.fn(() => ({ + cancel: vi.fn(), + onfinish: null, + })) + Object.defineProperty(window.HTMLElement.prototype, 'animate', { + configurable: true, + value: animate, + }) + const immediateContainer = document.createElement('div') + const immediate = mountChartRenderer(immediateContainer, { + definition: defineChart(definition, { + maxFocusDistance: 1_000, + motion: false, + tooltip: tooltipExtension, + }), + renderer: motion({ initial: false }), + width: 480, + height: 260, + ariaLabel: 'Immediate tooltip', + }) + const animatedContainer = document.createElement('div') + const animated = mountChartRenderer(animatedContainer, { + definition: defineChart(definition, { + maxFocusDistance: 1_000, + motion: false, + tooltip: { + use: tooltipExtension, + motion: { type: 'tween', duration: 100 }, + }, + }), + renderer: motion({ initial: false }), + width: 480, + height: 260, + ariaLabel: 'Animated tooltip override', + }) + + try { + immediate.interaction.setControlledFocus(immediate.getScene().points[0]!) + expect(animate).not.toHaveBeenCalled() + animated.interaction.setControlledFocus(animated.getScene().points[0]!) + expect(animate).toHaveBeenCalledOnce() + } finally { + immediate.destroy() + animated.destroy() + restoreProperty(window.HTMLElement.prototype, 'animate', descriptor) + } + }) + + it('keeps tooltips immediate without a renderer motion capability', () => { + const descriptor = Object.getOwnPropertyDescriptor( + window.HTMLElement.prototype, + 'animate', + ) + const animate = vi.fn() + Object.defineProperty(window.HTMLElement.prototype, 'animate', { + configurable: true, + value: animate, + }) + const fake = createFakeRenderer() + const container = document.createElement('div') + const host = mountChartRenderer(container, { + definition: defineChart(definition, { + motion: { + transition: { type: 'spring', stiffness: 170, damping: 18 }, + }, + tooltip: { + use: tooltipExtension, + motion: { type: 'spring', stiffness: 200 }, + }, + }), + renderer: fake.renderer, + width: 480, + height: 260, + ariaLabel: 'Static tooltip', + }) + + try { + host.interaction.setControlledFocus(host.getScene().points[0]!) + host.interaction.setControlledFocus(null) + + expect(animate).not.toHaveBeenCalled() + expect( + container.querySelector('.ts-chart-tooltip')?.hidden, + ).toBe(true) + } finally { + host.destroy() + restoreProperty(window.HTMLElement.prototype, 'animate', descriptor) + } + }) + + it('retargets tooltip springs from the live position and velocity', () => { + let currentTime = 1_000 + const frames: Array = [] + const requestFrame = vi + .spyOn(window, 'requestAnimationFrame') + .mockImplementation((callback) => { + frames.push(callback) + return frames.length + }) + const cancelFrame = vi + .spyOn(window, 'cancelAnimationFrame') + .mockImplementation(() => {}) + const performanceNow = vi + .spyOn(window.performance, 'now') + .mockImplementation(() => currentTime) + const container = document.createElement('div') + const host = mountChartRenderer(container, { + definition: defineChart(definition, { + maxFocusDistance: 1_000, + tooltip: tooltipExtension, + }), + renderer: motion({ + initial: false, + transition: { + type: 'spring', + stiffness: 170, + damping: 18, + mass: 1, + }, + }), + width: 480, + height: 260, + ariaLabel: 'Retargeted spring tooltip', + }) + + try { + host.interaction.setControlledFocus(host.getScene().points[0]!) + const tooltip = container.querySelector('.ts-chart-tooltip') + if (!tooltip) throw new Error('Expected chart tooltip') + const firstLeft = Number.parseFloat(tooltip.style.left) + + host.interaction.setControlledFocus(host.getScene().points[1]!) + const secondLeft = Number.parseFloat(tooltip.style.left) + expect(secondLeft).not.toBe(firstLeft) + expect(requestFrame).toHaveBeenCalled() + + frames.at(-1)?.(1_016) + currentTime = 1_016 + const movingX = Number.parseFloat(tooltip.style.translate) + const visualLeftBeforeRetarget = secondLeft + movingX + + host.interaction.setControlledFocus(host.getScene().points[0]!) + const retargetedLeft = Number.parseFloat(tooltip.style.left) + const retargetedX = Number.parseFloat(tooltip.style.translate) + expect(retargetedLeft + retargetedX).toBeCloseTo( + visualLeftBeforeRetarget, + 5, + ) + + frames.at(-1)?.(1_017) + expect(Number.parseFloat(tooltip.style.translate)).toBeGreaterThan( + retargetedX, + ) + } finally { + host.destroy() + requestFrame.mockRestore() + cancelFrame.mockRestore() + performanceNow.mockRestore() + } + }) + it('delegates controlled client coordinate conversion to the mounted surface', () => { const fake = createFakeRenderer() const container = document.createElement('div') diff --git a/packages/charts-core/src/renderer.ts b/packages/charts-core/src/renderer.ts index ef870dc4..da3fb94f 100644 --- a/packages/charts-core/src/renderer.ts +++ b/packages/charts-core/src/renderer.ts @@ -4,6 +4,7 @@ import { createDomTextMeasurer } from './dom-text' import { findNearestPoint, viewportInteractionPoints } from './scene' import { focusDisabled } from './focus-disabled' import { nearestPoint } from './nearest' +import { connectRendererTooltipMotion } from './renderer-motion-internal' import { chartPointFromNavigationOrder, chartPointFromSceneOrder, @@ -42,6 +43,7 @@ import type { ChartTooltipInput, ChartTooltipOptions, ChartTooltipPosition, + ChartMotionTransition, ChartValue, } from './types' @@ -1020,11 +1022,17 @@ export function mountChartRenderer< if (tooltipExtension !== input.extension || !tooltipInstance) { destroyTooltip() tooltipExtension = input.extension - tooltipInstance = input.extension.create({ - container, - dismiss: dismissTooltip, - bodyChange: () => options.onTooltipBodyChange, - }) + tooltipInstance = input.extension.create( + connectRendererTooltipMotion( + surface.renderer, + { + container, + dismiss: dismissTooltip, + bodyChange: () => options.onTooltipBodyChange, + }, + resolveTooltipMotion, + ), + ) } const instance = tooltipInstance instance.update(input.options) @@ -1044,6 +1052,12 @@ export function mountChartRenderer< }) } + function resolveTooltipMotion(): false | ChartMotionTransition | undefined { + const definition = options.definition.motion + if (definition === false) return false + return typeof definition === 'function' ? undefined : definition?.transition + } + function syncTooltip() { const input = resolveTooltipInput(options.definition.tooltip) if (!input) { diff --git a/packages/charts-core/src/tooltip.ts b/packages/charts-core/src/tooltip.ts index c3c62ede..8dbc4c82 100644 --- a/packages/charts-core/src/tooltip.ts +++ b/packages/charts-core/src/tooltip.ts @@ -1,4 +1,5 @@ import { placeTooltip } from './tooltip-position' +import { tooltipMotionController } from './renderer-motion-internal' import { createChartTooltipContent, orderChartTooltipPoints, @@ -83,6 +84,7 @@ function createTooltipExtension< let portalInstance: ChartTooltipPortalExtensionInstance | undefined const { container } = extensionContext const view = container.ownerDocument.defaultView + const tooltipMotion = tooltipMotionController(extensionContext) function update(nextOptions: ChartTooltipOptions) { if (options !== nextOptions) bodyDirty = true @@ -100,6 +102,9 @@ function createTooltipExtension< } const tooltipElement = ensureElement() syncPortal() + const motionSnapshot = tooltipMotion?.beforePaint(tooltipElement) + tooltipElement.style.visibility = 'hidden' + tooltipElement.removeAttribute('hidden') tooltipElement.className = options.className ? `ts-chart-tooltip ${options.className}` : 'ts-chart-tooltip' @@ -137,8 +142,6 @@ function createTooltipExtension< tooltipElement.style.pointerEvents = nextContext.pinned ? 'auto' : 'none' tooltipElement.style.userSelect = nextContext.pinned ? 'text' : 'none' tooltipElement.dataset.sticky = String(nextContext.pinned) - tooltipElement.style.visibility = 'hidden' - tooltipElement.removeAttribute('hidden') anchor = resolveChartTooltipAnchor( nextContext.point, points, @@ -149,6 +152,9 @@ function createTooltipExtension< ) position() tooltipElement.style.removeProperty('visibility') + if (motionSnapshot) { + tooltipMotion?.afterPaint(tooltipElement, motionSnapshot, options.motion) + } } function ensureElement() { @@ -317,9 +323,19 @@ function createTooltipExtension< function hide() { paintContext = undefined anchor = null - portalInstance?.hide() - element?.setAttribute('hidden', '') - hideTooltipBody() + const currentElement = element + if (!currentElement || currentElement.hidden) { + portalInstance?.hide() + hideTooltipBody() + return + } + const complete = () => { + portalInstance?.hide() + currentElement.setAttribute('hidden', '') + hideTooltipBody() + } + if (tooltipMotion?.hide(currentElement, options.motion, complete)) return + complete() } function destroy() { @@ -332,6 +348,7 @@ function createTooltipExtension< view?.cancelAnimationFrame?.(positionFrame) positionFrame = undefined } + tooltipMotion?.destroy(element) resizeObserver?.disconnect() resizeObserver = undefined element?.remove() diff --git a/packages/charts-core/src/types.ts b/packages/charts-core/src/types.ts index 8442e47a..17059fbf 100644 --- a/packages/charts-core/src/types.ts +++ b/packages/charts-core/src/types.ts @@ -687,16 +687,19 @@ export interface ChartRollingPathMotion { export type ChartMotionPath = 'morph' | ChartRollingPathMotion -export interface ChartMotionTiming { - delay?: number +export interface ChartMotionTiming { + delay?: number | ((context: ChartMotionContext) => number | undefined) transition?: ChartMotionTransition /** How line and area paths move between compatible keyed updates. */ path?: ChartMotionPath } export type ChartMotionDefinition = - | ChartMotionTiming - | ((context: ChartMotionContext) => ChartMotionTiming | undefined) + | false + | ChartMotionTiming + | (( + context: ChartMotionContext, + ) => false | ChartMotionTiming | undefined) export interface ChartMarkMotionOptions { motion?: ChartMotionDefinition @@ -1403,6 +1406,8 @@ export interface ChartTooltipOptions< TYValue extends ChartValue = ChartValue, > { className?: string + /** Overrides tooltip motion from the active motion renderer; `false` keeps it immediate. */ + motion?: false | ChartMotionTransition portal?: ChartTooltipPortalInput items?: readonly ChartTooltipItem[] sort?: ChartTooltipSort diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 31744f11..a184774b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -199,9 +199,15 @@ importers: octane: specifier: 0.1.13 version: 0.1.13(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.27.7)(less@4.8.1(supports-color@10.2.2))(sass@1.102.0)(terser@5.49.0)(yaml@2.9.0)) + pixelmatch: + specifier: 7.1.0 + version: 7.1.0 playwright: specifier: 1.62.0 version: 1.62.0 + pngjs: + specifier: 7.0.0 + version: 7.0.0 prettier: specifier: ^3.8.4 version: 3.9.6 @@ -7866,6 +7872,13 @@ packages: } engines: { node: '>=20.x' } + pixelmatch@7.1.0: + resolution: + { + integrity: sha512-1wrVzJ2STrpmONHKBy228LM1b84msXDUoAzVEl0R8Mz4Ce6EPr+IVtxm8+yvrqLYMHswREkjYFaMxnyGnaY3Ng==, + } + hasBin: true + pkg-dir@8.0.0: resolution: { @@ -7903,6 +7916,13 @@ packages: } engines: { node: '>=4.0.0' } + pngjs@7.0.0: + resolution: + { + integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==, + } + engines: { node: '>=14.19.0' } + postcss@8.5.23: resolution: { @@ -14280,6 +14300,10 @@ snapshots: optionalDependencies: '@napi-rs/nice': 1.1.1 + pixelmatch@7.1.0: + dependencies: + pngjs: 7.0.0 + pkg-dir@8.0.0: dependencies: find-up-simple: 1.0.1 @@ -14300,6 +14324,8 @@ snapshots: pngjs@3.4.0: {} + pngjs@7.0.0: {} + postcss@8.5.23: dependencies: nanoid: 3.3.16 diff --git a/scripts/catalog-definition-shapes.test.mjs b/scripts/catalog-definition-shapes.test.mjs index 3ee3123e..1b1c2acf 100644 --- a/scripts/catalog-definition-shapes.test.mjs +++ b/scripts/catalog-definition-shapes.test.mjs @@ -31,6 +31,7 @@ describe('catalog definition shapes', () => { (entry) => entry.isFile() && (entry.name === 'tanstack.ts' || + entry.name === 'tanstack-view.tsx' || entry.name === 'view.tsx' || entry.name === 'chart.ts'), ) @@ -57,9 +58,9 @@ describe('catalog definition shapes', () => { ) expect(classification.parameterless).toEqual([]) - expect(classification.static).toBe(117) + expect(classification.static).toBe(123) expect(classification.responsive.sort()).toEqual(responsiveDefinitions) - expect(classification.static + classification.responsive.length).toBe(122) + expect(classification.static + classification.responsive.length).toBe(128) }) it('classifies the base definition once when options are added', () => { diff --git a/scripts/catalog-preview.mjs b/scripts/catalog-preview.mjs index 28494304..8e4fd1b7 100644 --- a/scripts/catalog-preview.mjs +++ b/scripts/catalog-preview.mjs @@ -81,6 +81,7 @@ export const catalogGuidePreviewCaseIds = [ '118-token-usage-calendar', '120-themed-interactive-area', '121-active-bar-dashboard', + '130-shadcn-radar-multiple', '80-echarts-axis-pointer', 'bar-horizontal-ranking', ] @@ -88,6 +89,8 @@ export const catalogLegendPreviewCaseIds = ['81-recharts-interactive-legend'] export const catalogMarginPreviewCaseIds = [ '115-definition-motion', '118-token-usage-calendar', + '128-shadcn-bar-multiple', + '132-shadcn-tooltip-advanced', '80-echarts-axis-pointer', '81-recharts-interactive-legend', '88-echarts-free-cursor', @@ -118,6 +121,9 @@ export const catalogTextPreviewCaseIds = [ '123-active-donut-metric', '125-sales-funnel', '126-drillable-sunburst', + '129-shadcn-pie-donut-text', + '130-shadcn-radar-multiple', + '131-shadcn-radial-text', 'bar-horizontal-ranking', 'heatmap-labeled', ] @@ -177,6 +183,7 @@ export async function writeCatalogPreviews() { darkSvg, entry.id, JSDOM, + catalogPreviewPresentation(entry), ) validateCatalogPreviewXml(portableSvg, entry.id, JSDOM) const assetPath = path.join(previewsDirectory, `${entry.id}.svg`) @@ -248,6 +255,7 @@ export function isTransientCatalogPreviewBrowserError(error) { export async function checkCatalogPreviews() { const cases = await orderedCatalogCases() + const casesById = new Map(cases.map((entry) => [entry.id, entry])) const { JSDOM } = await import('jsdom') const expectedIds = cases.map((entry) => entry.id) const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8')) @@ -275,7 +283,11 @@ export async function checkCatalogPreviews() { ) validateCatalogPreviewSvg(source, asset.id) validateCatalogPreviewXml(source, asset.id, JSDOM) - validateCatalogPreviewPresentation(source, asset.id) + validateCatalogPreviewPresentation( + source, + asset.id, + catalogPreviewPresentation(casesById.get(asset.id)), + ) const actual = assetRecord(asset.id, source) assert( actual.sha256 === asset.sha256 && actual.bytes === asset.bytes, @@ -291,6 +303,7 @@ export function createPortableCatalogPreviewSvg( darkSvg, caseId, JSDOM, + presentation = {}, ) { validateCatalogPreviewSvg(lightSvg, caseId, false) validateCatalogPreviewSvg(darkSvg, caseId, false) @@ -304,7 +317,7 @@ export function createPortableCatalogPreviewSvg( lightRoot.insertAdjacentHTML('afterbegin', portableThemeStyle(paints)) const result = `${lightRoot.outerHTML}\n` validateCatalogPreviewSvg(result, caseId) - validateCatalogPreviewPresentation(result, caseId) + validateCatalogPreviewPresentation(result, caseId, presentation) return result } finally { lightDocument.window.close() @@ -340,26 +353,32 @@ export function validateCatalogPreviewSvg( `catalog preview ${caseId} is missing its portable catalog theme`, ) assert( - !trimmed.includes('background:'), + !/[{;]background\s*:/u.test(trimmed), `catalog preview ${caseId} must keep a transparent background`, ) } } -export function validateCatalogPreviewPresentation(svg, caseId) { - if (!guidePreviewCaseIdSet.has(caseId)) { +export function validateCatalogPreviewPresentation( + svg, + caseId, + presentation = {}, +) { + if (!presentation.guides && !guidePreviewCaseIdSet.has(caseId)) { assert( !svg.includes('ts-chart__axes') && !svg.includes('ts-chart__grid'), `catalog preview ${caseId} must omit axes and grids`, ) } - if (!legendPreviewCaseIdSet.has(caseId)) { + if (!presentation.legend && !legendPreviewCaseIdSet.has(caseId)) { assert( !svg.includes('ts-chart__legend'), `catalog preview ${caseId} must omit its legend`, ) } - if (textPreviewCaseIdSet.has(caseId)) { + if (presentation.text === 'retain') { + // Collection previews retain whichever labels define each upstream variant. + } else if (textPreviewCaseIdSet.has(caseId)) { assert( svg.includes(' { - const elements = allElements.filter((element) => { - const bounds = element.getBoundingClientRect() - const style = getComputedStyle(element) - return ( - bounds.width > 0 && - bounds.height > 0 && - style.display !== 'none' && - style.visibility !== 'hidden' + return await page + .locator('svg.ts-chart') + .evaluateAll((allElements) => { + const elements = allElements.filter((element) => { + const bounds = element.getBoundingClientRect() + const style = getComputedStyle(element) + return ( + bounds.width > 0 && + bounds.height > 0 && + style.display !== 'none' && + style.visibility !== 'hidden' + ) + }) + const container = elements[0]?.closest('.embed-chart') + if (!container) + throw new Error('Catalog preview has no chart container') + const containerBounds = container.getBoundingClientRect() + const overflow = elements.flatMap((element) => + [...element.querySelectorAll('text')].flatMap((label) => { + if (!label.textContent?.trim()) return [] + const style = getComputedStyle(label) + if (style.display === 'none' || style.visibility === 'hidden') + return [] + const bounds = label.getBoundingClientRect() + const tolerance = 1 + return bounds.left < containerBounds.left - tolerance || + bounds.top < containerBounds.top - tolerance || + bounds.right > containerBounds.right + tolerance || + bounds.bottom > containerBounds.bottom + tolerance + ? [ + `${label.textContent.trim()} (${Math.round(bounds.left - containerBounds.left)},${Math.round(bounds.top - containerBounds.top)} ${Math.round(bounds.width)}×${Math.round(bounds.height)})`, + ] + : [] + }), ) - }) - const container = elements[0]?.closest('.embed-chart') - if (!container) throw new Error('Catalog preview has no chart container') - const containerBounds = container.getBoundingClientRect() - const overflow = elements.flatMap((element) => - [...element.querySelectorAll('text')].flatMap((label) => { - if (!label.textContent?.trim()) return [] - const style = getComputedStyle(label) - if (style.display === 'none' || style.visibility === 'hidden') - return [] - const bounds = label.getBoundingClientRect() - const tolerance = 1 - return bounds.left < containerBounds.left - tolerance || - bounds.top < containerBounds.top - tolerance || - bounds.right > containerBounds.right + tolerance || - bounds.bottom > containerBounds.bottom + tolerance - ? [ - `${label.textContent.trim()} (${Math.round(bounds.left - containerBounds.left)},${Math.round(bounds.top - containerBounds.top)} ${Math.round(bounds.width)}×${Math.round(bounds.height)})`, - ] - : [] - }), - ) - if (overflow.length > 0) { - throw new Error( - `Catalog preview has clipped SVG labels:\n${overflow.join('\n')}`, - ) - } + if (overflow.length > 0) { + throw new Error( + `Catalog preview has clipped SVG labels:\n${overflow.join('\n')}`, + ) + } - function cloneSurface(element) { - const source = element.cloneNode(true) - const controls = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'g', - ) - controls.setAttribute('data-tanstack-catalog-preview-controls', '') - controls.setAttribute('aria-hidden', 'true') - for (const overlay of element.parentElement?.querySelectorAll( - ':scope > svg:not(.ts-chart)', - ) ?? []) { - const clone = overlay.cloneNode(true) - clone - .querySelectorAll( - '.overlay, [data-chart-handle-surface], [style*="display: none"]', - ) - .forEach((node) => node.remove()) - controls.append(...clone.childNodes) + function cloneSurface(element) { + const source = element.cloneNode(true) + const controls = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'g', + ) + controls.setAttribute('data-tanstack-catalog-preview-controls', '') + controls.setAttribute('aria-hidden', 'true') + for (const overlay of element.parentElement?.querySelectorAll( + ':scope > svg:not(.ts-chart)', + ) ?? []) { + const clone = overlay.cloneNode(true) + clone + .querySelectorAll( + '.overlay, [data-chart-handle-surface], [style*="display: none"]', + ) + .forEach((node) => node.remove()) + controls.append(...clone.childNodes) + } + if (controls.childNodes.length > 0) source.append(controls) + return source } - if (controls.childNodes.length > 0) source.append(controls) - return source - } - if (elements.length === 1) { - return cloneSurface(elements[0]).outerHTML - } + if (elements.length === 1) { + return cloneSurface(elements[0]).outerHTML + } - const composed = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'svg', - ) - composed.setAttribute('class', 'ts-chart') - composed.setAttribute('width', '100%') - composed.setAttribute('height', '100%') - composed.setAttribute('viewBox', '0 0 288 192') - composed.setAttribute('role', 'img') - composed.setAttribute( - 'aria-label', - container.closest('[aria-label]')?.getAttribute('aria-label') ?? - 'Chart preview', - ) - composed.setAttribute('data-tanstack-catalog-preview-surfaces', '') - - for (const element of elements) { - const bounds = element.getBoundingClientRect() - const source = cloneSurface(element) - source.setAttribute('x', String(bounds.left - containerBounds.left)) - source.setAttribute('y', String(bounds.top - containerBounds.top)) - source.setAttribute('width', String(bounds.width)) - source.setAttribute('height', String(bounds.height)) - source.setAttribute('aria-hidden', 'true') - composed.append(source) - } - return composed.outerHTML - }) + const composed = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'svg', + ) + composed.setAttribute('class', 'ts-chart') + composed.setAttribute('width', '100%') + composed.setAttribute('height', '100%') + composed.setAttribute('viewBox', '0 0 288 192') + composed.setAttribute('role', 'img') + composed.setAttribute( + 'aria-label', + container.closest('[aria-label]')?.getAttribute('aria-label') ?? + 'Chart preview', + ) + composed.setAttribute('data-tanstack-catalog-preview-surfaces', '') + + for (const element of elements) { + const bounds = element.getBoundingClientRect() + const source = cloneSurface(element) + source.setAttribute('x', String(bounds.left - containerBounds.left)) + source.setAttribute('y', String(bounds.top - containerBounds.top)) + source.setAttribute('width', String(bounds.width)) + source.setAttribute('height', String(bounds.height)) + source.setAttribute('aria-hidden', 'true') + composed.append(source) + } + return composed.outerHTML + }) + .catch((error) => { + throw new Error( + `catalog preview ${entry.id} (${theme}) failed presentation validation`, + { cause: error }, + ) + }) } finally { await page.close() } diff --git a/scripts/catalog-preview.test.mjs b/scripts/catalog-preview.test.mjs index afe9d814..45be5fd0 100644 --- a/scripts/catalog-preview.test.mjs +++ b/scripts/catalog-preview.test.mjs @@ -79,6 +79,18 @@ describe('catalog previews', () => { ) }) + it('does not treat semantic background IDs as CSS background paint', () => { + expect(() => + validateCatalogPreviewSvg( + portable().replace( + ' { expect(() => validateCatalogPreviewXml( @@ -102,6 +114,7 @@ describe('catalog previews', () => { '118-token-usage-calendar', '120-themed-interactive-area', '121-active-bar-dashboard', + '130-shadcn-radar-multiple', '80-echarts-axis-pointer', 'bar-horizontal-ranking', ]) @@ -111,6 +124,8 @@ describe('catalog previews', () => { expect(catalogMarginPreviewCaseIds).toEqual([ '115-definition-motion', '118-token-usage-calendar', + '128-shadcn-bar-multiple', + '132-shadcn-tooltip-advanced', '80-echarts-axis-pointer', '81-recharts-interactive-legend', '88-echarts-free-cursor', @@ -141,6 +156,9 @@ describe('catalog previews', () => { '123-active-donut-metric', '125-sales-funnel', '126-drillable-sunburst', + '129-shadcn-pie-donut-text', + '130-shadcn-radar-multiple', + '131-shadcn-radial-text', 'bar-horizontal-ranking', 'heatmap-labeled', ]) @@ -162,6 +180,13 @@ describe('catalog previews', () => { '115-definition-motion', ), ).not.toThrow() + expect(() => + validateCatalogPreviewPresentation( + 'label', + 'shadcn-collection-case', + { guides: true, legend: true, text: 'retain' }, + ), + ).not.toThrow() expect(() => validateCatalogPreviewPresentation( 'label', diff --git a/scripts/generate-shadcn-cases.mjs b/scripts/generate-shadcn-cases.mjs new file mode 100644 index 00000000..cf9dbb4d --- /dev/null +++ b/scripts/generate-shadcn-cases.mjs @@ -0,0 +1,351 @@ +import { mkdir, readFile, writeFile } from 'node:fs/promises' +import path from 'node:path' +import { format } from 'prettier' + +const root = path.resolve(import.meta.dirname, '..') +const catalogPath = path.join( + root, + 'benchmarks/conformance/shadcn/catalog.json', +) +const casesRoot = path.join(root, 'benchmarks/conformance/cases') +const coverageRoot = path.join(root, 'benchmarks/conformance') +const catalog = JSON.parse(await readFile(catalogPath, 'utf8')) +const handAuthoredCases = new Set([ + 'chart-bar-multiple', + 'chart-pie-donut-text', + 'chart-radar-multiple', + 'chart-radial-text', + 'chart-tooltip-advanced', +]) +let nextCaseNumber = + Math.max( + 126, + ...catalog.cases.map((entry) => + Number(entry.localCaseId?.match(/^(\d+)-/u)?.[1] ?? 0), + ), + ) + 1 +let generatedCount = 0 + +for (const entry of catalog.cases) { + if (!entry.localCaseId) { + entry.localCaseId = `${nextCaseNumber}-shadcn-${entry.name.replace(/^chart-/u, '')}` + nextCaseNumber += 1 + } + const caseNumber = Number(entry.localCaseId.match(/^(\d+)-/u)?.[1] ?? 0) + if (handAuthoredCases.has(entry.name)) continue + const id = entry.localCaseId + const directory = path.join(casesRoot, id) + generatedCount += 1 + await mkdir(directory, { recursive: true }) + await writeFile( + path.join(directory, 'case.json'), + `${JSON.stringify(metadata(entry, id, caseNumber), null, 2)}\n`, + ) + await writeFile( + path.join(directory, 'tanstack.ts'), + `import { createShadcnTanStackExample } from '../../shared/shadcn-catalog-tanstack'\n\nconst example = createShadcnTanStackExample('${entry.name}')\n\nexport const shadcnDefinition = example.definition\nexport const catalogCase = example.catalogCase\nexport const mount = example.mount\n`, + ) + await writeFile( + path.join(directory, 'recharts.ts'), + `import { createShadcnRechartsExample } from '../../shared/shadcn-catalog-recharts'\n\nconst example = createShadcnRechartsExample('${entry.name}')\n\nexport const mount = example.mount\n`, + ) +} + +await writeFile(catalogPath, `${JSON.stringify(catalog, null, 2)}\n`) +await syncDefinitionCoverage() +console.log(`Synchronized ${generatedCount} generated shadcn chart cases.`) + +async function syncDefinitionCoverage() { + const roadmapPath = path.join( + coverageRoot, + 'definition-coverage-roadmap.json', + ) + const roadmap = JSON.parse(await readFile(roadmapPath, 'utf8')) + const generatedEntries = [ + coverageEntry({ + family: 'dashboard', + localCaseId: '127-shadcn-dashboard', + name: 'dashboard', + }), + ...catalog.cases.map(coverageEntry), + ] + const generatedIds = new Set(generatedEntries.map((entry) => entry.id)) + const retainedCases = roadmap.cases.filter( + (entry) => !generatedIds.has(entry.id), + ) + const insertionIndex = + retainedCases.findIndex((entry) => entry.id === '126-drillable-sunburst') + + 1 + if (insertionIndex === 0) { + throw new Error('Could not locate catalog case 126-drillable-sunburst.') + } + retainedCases.splice(insertionIndex, 0, ...generatedEntries) + roadmap.cases = retainedCases + await writeFile(roadmapPath, `${JSON.stringify(roadmap, null, 2)}\n`) + + const counts = countCoverage(roadmap.cases) + const auditPath = path.join(coverageRoot, roadmap.audit) + let audit = await readFile(auditPath, 'utf8') + audit = replaceGeneratedRows( + audit, + generatedIds, + generatedEntries.map(auditRow).join('\n'), + '126-drillable-sunburst', + ) + audit = audit + .replace( + /Scope: all \d+ catalog directories\./u, + `Scope: all ${counts.total} catalog directories.`, + ) + .replace( + /Cases 119 through 126 were added afterward/u, + 'Cases 119 through 126 and the pinned shadcn collection were added afterward', + ) + .replace( + /(?:One hundred fourteen|\d+) of the \d+ cases present their visualization as a normal chart/u, + `${counts.normal} of the ${counts.total} cases present their visualization as a normal chart`, + ) + .replace( + /definition\. (?:Sixty-five|\d+) use the definition API/u, + `definition. ${counts.definitionNow} use the definition API`, + ) + .replace( + /\| Definition now\s+\|\s+\d+\s+\|/u, + `| Definition now | ${String(counts.definitionNow).padStart(3)} |`, + ) + .replace( + /\| \*\*Total\*\*\s+\| \*\*\d+\*\* \|/u, + `| **Total** | **${counts.total}** |`, + ) + .replace( + /All \d+ catalog directories now have one roadmap record and case-local/u, + `All ${counts.total} catalog directories now have one roadmap record and case-local`, + ) + .replace( + /All \d+ normal-definition cases are verified/u, + `All ${counts.normal} normal-definition cases are verified`, + ) + await writeFile(auditPath, await format(audit, { parser: 'markdown' })) + + const overviewPath = path.join(coverageRoot, roadmap.overview) + let overview = await readFile(overviewPath, 'utf8') + overview = replaceGeneratedRows( + overview, + generatedIds, + generatedEntries.map(overviewRow).join('\n'), + '126-drillable-sunburst', + ) + overview = overview + .replace( + /The catalog has \d+ case directories\. (?:One hundred fourteen|\d+) visualizations now use/u, + `The catalog has ${counts.total} case directories. ${counts.normal} visualizations now use`, + ) + .replace( + /\| Definition now\s+\|\s+\d+\s+\|/u, + `| Definition now | ${String(counts.definitionNow).padStart(3)} |`, + ) + .replace( + /\| \*\*Total\*\*\s+\| \*\*\d+\*\* \| \*\*\d+ normal definitions; 3 accepted boundaries\*\* \|/u, + `| **Total** | **${counts.total}** | **${counts.normal} normal definitions; 3 accepted boundaries** |`, + ) + .replace( + /compares all \d+ roadmap and audit IDs/u, + `compares all ${counts.total} roadmap and audit IDs`, + ) + await writeFile(overviewPath, await format(overview, { parser: 'markdown' })) +} + +function coverageEntry(entry) { + const id = entry.localCaseId + return { + id, + coverage: 'app-composed', + disposition: 'definition-now', + phase: 'phase-0', + status: 'verified', + capabilities: ['current-definition-api'], + evidence: [`cases/${id}/tanstack.ts`, `cases/${id}/case.json`], + work: [ + { + kind: 'definition-composition', + stage: 'definition-builder', + owner: 'charts', + coordinateSpace: 'none', + sources: ['benchmarks/conformance/shared/shadcn-catalog-tanstack.tsx'], + summary: `Compose the pinned shadcn ${entry.name} variant through the shared native ${entry.family} chart definition.`, + }, + { + kind: 'application-shell', + stage: 'post-render', + owner: 'application', + coordinateSpace: 'dom', + sources: ['benchmarks/conformance/shared/shadcn-chart-card.tsx'], + summary: + 'Share the canonical shadcn card dimensions, typography, footer, and theme tokens across renderer implementations.', + }, + ], + } +} + +function auditRow(entry) { + const label = entry.id.replace('-shadcn-', ' — shadcn ') + return `| [${label}](./cases/${entry.id}/tanstack.ts) | Definition now | Shared native TanStack Charts ${entry.id.split('-')[2]} definitions reproduce the pinned shadcn variant; the card remains application presentation. |` +} + +function overviewRow(entry) { + const metadata = catalog.cases.find( + (candidate) => candidate.localCaseId === entry.id, + ) + const title = metadata ? officialTitle(metadata.name) : 'Dashboard' + return `| [${entry.id} — ${title}](./cases/${entry.id}/tanstack.ts) | Definition now | \`current-definition-api\` | \`tanstack.ts\`, \`case.json\` |` +} + +function replaceGeneratedRows(source, generatedIds, rows, afterId) { + const retained = source + .split('\n') + .filter( + (line) => + ![...generatedIds].some((id) => line.includes(`./cases/${id}/`)), + ) + const index = retained.findIndex((line) => + line.includes(`./cases/${afterId}/`), + ) + if (index === -1) throw new Error(`Could not locate coverage row ${afterId}.`) + retained.splice(index + 1, 0, rows) + return retained.join('\n') +} + +function countCoverage(cases) { + const definitionNow = cases.filter( + (entry) => entry.disposition === 'definition-now', + ).length + const accepted = cases.filter( + (entry) => entry.status === 'accepted-boundary', + ).length + return { + total: cases.length, + normal: cases.length - accepted, + definitionNow, + } +} + +function metadata(entry, id, caseNumber) { + const title = officialTitle(entry.name) + return { + schemaVersion: 1, + referenceRenderer: 'recharts', + collections: ['shadcn'], + height: 600, + order: (caseNumber + 1) * 10, + id, + title: `shadcn ${title}`, + family: entry.family, + intent: `Reproduce shadcn/ui's ${entry.name} example with TanStack Charts.`, + support: 'native', + features: featureList(entry.name), + geometry: [geometry(entry.name, entry.family)], + source: { + title: `shadcn/ui ${entry.name}`, + url: `https://ui.shadcn.com/view/new-york-v4/${entry.name}`, + }, + ai: { + create: `Reproduce the official shadcn/ui ${entry.name} card and data with TanStack Charts.`, + maintain: `Preserve the pinned upstream data, ${entry.name.replace(/^chart-/u, '').replaceAll('-', ' ')} treatment, card dimensions, and light and dark tokens.`, + }, + } +} + +function featureList(name) { + const words = name.replace(/^chart-/u, '').split('-') + const family = words.shift() + const variant = words.join(' ') + return [ + `${family} chart`, + ...(variant === 'default' || variant === 'simple' ? [] : [variant]), + 'shadcn card shell', + ] +} + +function geometry(name, family) { + const variant = name.replace(`chart-${family}-`, '') + if (family === 'area') { + return { + role: 'area', + count: variant === 'stacked-expand' ? 3 : isAreaMultiple(variant) ? 2 : 1, + } + } + if (family === 'bar') { + return { + role: 'bar', + count: variant === 'multiple' || variant === 'stacked' ? 12 : 6, + } + } + if (family === 'line') { + return { + role: 'line', + count: variant === 'multiple' || variant === 'interactive' ? 2 : 1, + } + } + if (family === 'pie') return { role: 'arc', count: 5 } + if (family === 'radar') { + return { + role: 'radar', + count: variant === 'multiple' || variant === 'legend' ? 2 : 1, + } + } + if (family === 'radial') { + return { + role: 'bar', + count: variant === 'simple' || variant === 'text' ? 1 : 5, + rendererRoles: { recharts: 'arc', tanstack: 'arc' }, + } + } + return { role: 'bar', count: 10 } +} + +function isAreaMultiple(variant) { + return ( + variant === 'axes' || + variant === 'gradient' || + variant === 'icons' || + variant === 'interactive' || + variant === 'legend' || + variant === 'stacked' + ) +} + +function officialTitle(name) { + const overrides = { + 'chart-area-default': 'Area Chart', + 'chart-area-stacked-expand': 'Area Chart - Stacked Expanded', + 'chart-bar-default': 'Bar Chart', + 'chart-bar-label-custom': 'Bar Chart - Custom Label', + 'chart-bar-stacked': 'Bar Chart - Stacked + Legend', + 'chart-line-default': 'Line Chart', + 'chart-line-dots-custom': 'Line Chart - Custom Dots', + 'chart-line-label-custom': 'Line Chart - Custom Label', + 'chart-pie-simple': 'Pie Chart', + 'chart-pie-donut-text': 'Pie Chart - Donut with Text', + 'chart-pie-label-custom': 'Pie Chart - Custom Label', + 'chart-radar-default': 'Radar Chart', + 'chart-radar-grid-circle-fill': 'Radar Chart - Grid Circle Filled', + 'chart-radar-grid-circle-no-lines': 'Radar Chart - Grid Circle - No lines', + 'chart-radar-grid-fill': 'Radar Chart - Grid Filled', + 'chart-radar-label-custom': 'Radar Chart - Custom Label', + 'chart-radar-radius': 'Radar Chart - Radius Axis', + 'chart-radial-simple': 'Radial Chart', + 'chart-tooltip-indicator-line': 'Tooltip - Line Indicator', + 'chart-tooltip-indicator-none': 'Tooltip - No Indicator', + 'chart-tooltip-label-custom': 'Tooltip - Custom label', + 'chart-tooltip-label-none': 'Tooltip - No Label', + } + if (overrides[name]) return overrides[name] + const [, family, ...variant] = name.split('-') + const chartType = + family === 'tooltip' ? 'Tooltip' : `${titleCase(family)} Chart` + return `${chartType} - ${variant.map(titleCase).join(' ')}` +} + +function titleCase(value) { + return value.charAt(0).toUpperCase() + value.slice(1) +} diff --git a/scripts/measure-bundles.mjs b/scripts/measure-bundles.mjs index 2c89c779..8921fbde 100644 --- a/scripts/measure-bundles.mjs +++ b/scripts/measure-bundles.mjs @@ -91,6 +91,9 @@ const retainedInputGroups = { sceneMotionContract: [ /(?:^|\/)packages\/charts-core\/src\/scene-motion-internal\.ts$/u, ], + motionDefinition: [ + /(?:^|\/)packages\/charts-core\/src\/motion-definition\.ts$/u, + ], springRuntime: [/(?:^|\/)packages\/charts-core\/src\/spring\.ts$/u], focusGuide: [/(?:^|\/)packages\/charts-core\/src\/focus-guide\.ts$/u], focusMark: [/(?:^|\/)packages\/charts-core\/src\/focus-mark\.ts$/u], @@ -900,7 +903,7 @@ const entries = [ budgeted( 'Composite mark + static SVG', 'benchmarks/entries/charts-composite-mark.ts', - 26.45, + 26.5, { inputBoundary: { require: [ @@ -1195,7 +1198,7 @@ const entries = [ budgeted( 'Polar line + scatter composition + static SVG', 'benchmarks/entries/charts-polar-line-scatter-svg.ts', - 25.3, + 25.32, ), locked( 'Representative marks', @@ -1623,7 +1626,7 @@ const entries = [ lockedBudgeted( 'React compact-scale line consumer', 'benchmarks/entries/charts-react-compact-line.ts', - 27.05, + 27.2, { external: ['react', 'react/jsx-runtime', 'react-dom'], rendererBoundary: 'svg', @@ -1656,6 +1659,26 @@ const entries = [ 'compactBandKernel', 'compactOrdinal', 'reactTooltipBridge', + 'motionRuntime', + 'springRuntime', + 'transformRuntime', + 'd3Runtime', + ], + }, + }, + ), + budgeted( + 'Motion timing utilities', + 'benchmarks/entries/charts-motion-definition.ts', + 0.5, + { + inputBoundary: { + require: ['motionDefinition'], + forbid: [ + 'motionRuntime', + 'springRuntime', + 'tooltipExtension', + 'tooltipPortal', 'transformRuntime', 'd3Runtime', ], @@ -1665,7 +1688,7 @@ const entries = [ budgeted( 'Motion SVG renderer', 'benchmarks/entries/charts-motion-svg-renderer.ts', - 18.2, + 20.7, { rendererBoundary: 'svg', inputBoundary: { @@ -1771,7 +1794,7 @@ const entries = [ budgeted( 'Stats parity surface', 'benchmarks/entries/charts-stats-parity.ts', - 51.2, + 51.4, ), locked( 'Custom-scale line scene', @@ -1822,12 +1845,12 @@ const entries = [ budgeted( 'Direct D3 quadtree + TanStack DOM host', 'benchmarks/entries/charts-d3-quadtree-dom.ts', - 35.4, + 35.5, ), budgeted( 'Direct D3 Delaunay + TanStack DOM host', 'benchmarks/entries/charts-d3-delaunay-dom.ts', - 40.7, + 40.75, ), measured('D3 array numeric kernel', 'benchmarks/entries/d3-array-kernel.ts'), measured( @@ -2031,7 +2054,7 @@ const entries = [ budgeted( 'React Stats parity surface', 'benchmarks/entries/charts-react-stats-parity.tsx', - 52.1, + 52.3, { external: ['react', 'react/jsx-runtime', 'react-dom'] }, ), measured('Plot renderer integration', 'benchmarks/entries/plot-renderer.ts'), diff --git a/scripts/public-callback-contract.mjs b/scripts/public-callback-contract.mjs index d099f264..875ee1a2 100644 --- a/scripts/public-callback-contract.mjs +++ b/scripts/public-callback-contract.mjs @@ -42,8 +42,14 @@ const callbackInventory = { ], ['@tanstack/charts:src/band.ts:BandXOptions', 'color fill key x z'], ['@tanstack/charts:src/band.ts:BandYOptions', 'color fill key y z'], - ['@tanstack/charts:src/bar.ts:BarXOptions', 'color fill key x x1 x2 y z'], - ['@tanstack/charts:src/bar.ts:BarYOptions', 'color fill key x y y1 y2 z'], + [ + '@tanstack/charts:src/bar.ts:BarXOptions', + 'color fill key stroke strokeDasharray x x1 x2 y z', + ], + [ + '@tanstack/charts:src/bar.ts:BarYOptions', + 'color fill key stroke strokeDasharray x y y1 y2 z', + ], ['@tanstack/charts:src/box.ts:BoxOptions', 'key'], ['@tanstack/charts:src/box.ts:BoxXCallOptions', 'motion'], ['@tanstack/charts:src/box.ts:BoxXOptions', 'x y'], @@ -340,6 +346,7 @@ const callbackInventory = { ], ['@tanstack/charts:src/types.ts:ChartMarkStateValue', '$call'], ['@tanstack/charts:src/types.ts:ChartMotionDefinition', '$call'], + ['@tanstack/charts:src/types.ts:ChartMotionTiming', 'delay'], ['@tanstack/charts:src/types.ts:ChartMotionTweenTransition', 'easing'], ['@tanstack/charts:src/types.ts:ChartScale', 'resolve'], ['@tanstack/charts:src/types.ts:ChartScaleResolver', '$call'], diff --git a/scripts/shadcn-catalog.mjs b/scripts/shadcn-catalog.mjs new file mode 100644 index 00000000..abb95c5b --- /dev/null +++ b/scripts/shadcn-catalog.mjs @@ -0,0 +1,238 @@ +import { promises as fs } from 'node:fs' +import path from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' + +export const shadcnFamilyCounts = { + area: 10, + bar: 10, + line: 10, + pie: 11, + radar: 14, + radial: 6, + tooltip: 9, +} + +const rootDirectory = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '..', +) +const catalogPath = path.join( + rootDirectory, + 'benchmarks', + 'conformance', + 'shadcn', + 'catalog.json', +) +const casesDirectory = path.join( + rootDirectory, + 'benchmarks', + 'conformance', + 'cases', +) +const referenceDirectory = path.join( + rootDirectory, + 'benchmarks', + 'conformance', + 'shadcn', + 'reference', +) +const gitShaPattern = /^[a-f0-9]{40}$/u + +export function validateShadcnCatalog(catalog) { + assert(isRecord(catalog), 'shadcn catalog must be an object') + assert(catalog.schemaVersion === 1, 'shadcn catalog schemaVersion must be 1') + assert( + isRecord(catalog.upstream), + 'shadcn catalog upstream must be an object', + ) + assert( + catalog.upstream.repository === 'shadcn-ui/ui', + 'unexpected shadcn repository', + ) + assert( + gitShaPattern.test(catalog.upstream.commit), + 'shadcn commit must be pinned', + ) + assert( + typeof catalog.upstream.pathRoot === 'string', + 'shadcn pathRoot is required', + ) + assert( + typeof catalog.upstream.registryPath === 'string', + 'shadcn registryPath is required', + ) + assert( + gitShaPattern.test(catalog.upstream.registryBlobSha), + 'registry blob SHA is invalid', + ) + assert(Array.isArray(catalog.cases), 'shadcn cases must be an array') + assert( + catalog.cases.length === 70, + 'shadcn catalog must contain all 70 chart examples', + ) + + const names = new Set() + const paths = new Set() + const localCaseIds = new Set() + const familyCounts = {} + for (const entry of catalog.cases) { + assert(isRecord(entry), 'shadcn case must be an object') + assert( + /^chart-[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(entry.name), + 'invalid shadcn case name', + ) + assert( + entry.family === entry.name.split('-')[1], + `invalid family for ${entry.name}`, + ) + assert( + entry.path === `${catalog.upstream.pathRoot}/${entry.name}.tsx`, + `invalid path for ${entry.name}`, + ) + assert( + gitShaPattern.test(entry.blobSha), + `invalid blob SHA for ${entry.name}`, + ) + assert(!names.has(entry.name), `duplicate shadcn name ${entry.name}`) + assert(!paths.has(entry.path), `duplicate shadcn path ${entry.path}`) + names.add(entry.name) + paths.add(entry.path) + familyCounts[entry.family] = (familyCounts[entry.family] ?? 0) + 1 + if (entry.localCaseId !== undefined) { + assert( + typeof entry.localCaseId === 'string', + `invalid local case for ${entry.name}`, + ) + assert( + !localCaseIds.has(entry.localCaseId), + `duplicate local case ${entry.localCaseId}`, + ) + localCaseIds.add(entry.localCaseId) + } + } + + assert( + JSON.stringify(familyCounts) === JSON.stringify(shadcnFamilyCounts), + `unexpected shadcn family counts: ${JSON.stringify(familyCounts)}`, + ) + return { + caseCount: catalog.cases.length, + implementedCount: localCaseIds.size, + } +} + +export function compareShadcnCatalog(catalog, remoteTree, remoteCommit) { + validateShadcnCatalog(catalog) + const expected = new Map([ + [catalog.upstream.registryPath, catalog.upstream.registryBlobSha], + ...catalog.cases.map((entry) => [entry.path, entry.blobSha]), + ]) + const actual = new Map( + remoteTree + .filter((entry) => entry.type === 'blob' && expected.has(entry.path)) + .map((entry) => [entry.path, entry.sha]), + ) + const remoteCharts = remoteTree.filter( + (entry) => + entry.type === 'blob' && + entry.path.startsWith(`${catalog.upstream.pathRoot}/chart-`) && + entry.path.endsWith('.tsx'), + ) + const knownPaths = new Set(catalog.cases.map((entry) => entry.path)) + return { + pinnedCommit: catalog.upstream.commit, + remoteCommit, + changed: [...expected] + .filter(([entryPath, sha]) => actual.get(entryPath) !== sha) + .map(([entryPath]) => entryPath), + added: remoteCharts + .map((entry) => entry.path) + .filter((entryPath) => !knownPaths.has(entryPath)), + removed: catalog.cases + .map((entry) => entry.path) + .filter( + (entryPath) => + !remoteCharts.some((remote) => remote.path === entryPath), + ), + } +} + +export async function checkShadcnCatalog({ + remote = false, + fetchImpl = fetch, +} = {}) { + const catalog = JSON.parse(await fs.readFile(catalogPath, 'utf8')) + const summary = validateShadcnCatalog(catalog) + for (const entry of catalog.cases) { + if (!entry.localCaseId) continue + const metadataPath = path.join( + casesDirectory, + entry.localCaseId, + 'case.json', + ) + const metadata = JSON.parse(await fs.readFile(metadataPath, 'utf8')) + assert( + metadata.id === entry.localCaseId, + `missing local shadcn case ${entry.localCaseId}`, + ) + assert( + metadata.source?.url.endsWith(`/${entry.name}`), + `source URL mismatch for ${entry.localCaseId}`, + ) + await fs.access(path.join(referenceDirectory, `${entry.name}.png`)) + } + const referenceManifest = JSON.parse( + await fs.readFile(path.join(referenceDirectory, 'manifest.json'), 'utf8'), + ) + assert( + JSON.stringify(referenceManifest.cases) === + JSON.stringify(catalog.cases.map((entry) => entry.name)), + 'shadcn reference manifest must cover the pinned catalog in order', + ) + + if (!remote) { + console.log( + `Validated pinned shadcn inventory: ${summary.implementedCount}/${summary.caseCount} implemented.`, + ) + return summary + } + + const headers = { + Accept: 'application/vnd.github+json', + 'X-GitHub-Api-Version': '2022-11-28', + } + if (process.env.GITHUB_TOKEN) + headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}` + const response = await fetchImpl( + `https://api.github.com/repos/${catalog.upstream.repository}/git/trees/main?recursive=1`, + { headers }, + ) + if (!response.ok) + throw new Error(`GitHub tree request failed: ${response.status}`) + const payload = await response.json() + assert(Array.isArray(payload.tree), 'GitHub tree response is invalid') + const drift = compareShadcnCatalog(catalog, payload.tree, payload.sha) + const driftCount = + drift.changed.length + drift.added.length + drift.removed.length + if (driftCount > 0) { + console.log(JSON.stringify(drift, null, 2)) + throw new Error(`shadcn chart catalog drifted in ${driftCount} path(s)`) + } + console.log(`No shadcn chart drift at ${payload.sha}.`) + return drift +} + +function isRecord(value) { + return typeof value === 'object' && value !== null && !Array.isArray(value) +} + +function assert(condition, message) { + if (!condition) throw new TypeError(message) +} + +if ( + process.argv[1] && + import.meta.url === pathToFileURL(process.argv[1]).href +) { + await checkShadcnCatalog({ remote: process.argv.includes('--remote') }) +} diff --git a/scripts/shadcn-catalog.test.mjs b/scripts/shadcn-catalog.test.mjs new file mode 100644 index 00000000..95402f2e --- /dev/null +++ b/scripts/shadcn-catalog.test.mjs @@ -0,0 +1,61 @@ +import { readFile } from 'node:fs/promises' +import path from 'node:path' +import { describe, expect, it } from 'vitest' +import { + compareShadcnCatalog, + shadcnFamilyCounts, + validateShadcnCatalog, +} from './shadcn-catalog.mjs' + +const catalogPath = path.resolve( + import.meta.dirname, + '../benchmarks/conformance/shadcn/catalog.json', +) + +describe('shadcn catalog inventory', () => { + it('pins and maps every upstream example', async () => { + const catalog = JSON.parse(await readFile(catalogPath, 'utf8')) + expect(validateShadcnCatalog(catalog)).toEqual({ + caseCount: 70, + implementedCount: 70, + }) + expect( + Object.fromEntries( + Object.keys(shadcnFamilyCounts).map((family) => [ + family, + catalog.cases.filter((entry) => entry.family === family).length, + ]), + ), + ).toEqual(shadcnFamilyCounts) + }) + + it('reports source changes, additions, and removals by Git blob SHA', async () => { + const catalog = JSON.parse(await readFile(catalogPath, 'utf8')) + const tree = [ + { + path: catalog.upstream.registryPath, + sha: catalog.upstream.registryBlobSha, + type: 'blob', + }, + ...catalog.cases.slice(0, -1).map((entry, index) => ({ + path: entry.path, + sha: index === 0 ? '0'.repeat(40) : entry.blobSha, + type: 'blob', + })), + { + path: `${catalog.upstream.pathRoot}/chart-line-new.tsx`, + sha: '1'.repeat(40), + type: 'blob', + }, + ] + const drift = compareShadcnCatalog(catalog, tree, '2'.repeat(40)) + expect(drift.changed).toEqual([ + catalog.cases[0].path, + catalog.cases.at(-1).path, + ]) + expect(drift.added).toEqual([ + `${catalog.upstream.pathRoot}/chart-line-new.tsx`, + ]) + expect(drift.removed).toEqual([catalog.cases.at(-1).path]) + }) +}) diff --git a/scripts/shadcn-reference-screenshots.mjs b/scripts/shadcn-reference-screenshots.mjs new file mode 100644 index 00000000..8fcc481b --- /dev/null +++ b/scripts/shadcn-reference-screenshots.mjs @@ -0,0 +1,73 @@ +import { mkdir, readFile, writeFile } from 'node:fs/promises' +import path from 'node:path' +import { chromium } from 'playwright' + +const root = path.resolve(import.meta.dirname, '..') +const catalogPath = path.join( + root, + 'benchmarks/conformance/shadcn/catalog.json', +) +const outputDirectory = path.join( + root, + 'benchmarks/conformance/shadcn/reference', +) +const catalog = JSON.parse(await readFile(catalogPath, 'utf8')) +const requestedNames = optionValue('--case')?.split(',').filter(Boolean) +const entries = requestedNames + ? catalog.cases.filter((entry) => requestedNames.includes(entry.name)) + : catalog.cases + +await mkdir(outputDirectory, { recursive: true }) +const browser = await chromium.launch({ headless: true }) +const page = await browser.newPage({ + viewport: { width: 640, height: 900 }, + deviceScaleFactor: 1, + reducedMotion: 'reduce', +}) + +try { + for (const [index, entry] of entries.entries()) { + const url = `https://ui.shadcn.com/view/new-york-v4/${entry.name}` + await page.goto(url, { waitUntil: 'networkidle', timeout: 60_000 }) + await page.waitForTimeout(1_800) + await page.evaluate(async () => { + await document.fonts.ready + document.getAnimations().forEach((animation) => animation.finish()) + }) + const card = page.locator('[data-slot="card"]').first() + await card.waitFor({ state: 'visible' }) + await card.screenshot({ + path: path.join(outputDirectory, `${entry.name}.png`), + animations: 'disabled', + }) + process.stdout.write( + `\rCaptured ${String(index + 1).padStart(2)}/${entries.length}: ${entry.name}`, + ) + } + process.stdout.write('\n') + await writeFile( + path.join(outputDirectory, 'manifest.json'), + `${JSON.stringify( + { + schemaVersion: 1, + viewport: { width: 640, deviceScaleFactor: 1, theme: 'light' }, + source: 'https://ui.shadcn.com/view/new-york-v4/{name}', + capturedAt: new Date().toISOString(), + cases: entries.map((entry) => entry.name), + }, + null, + 2, + )}\n`, + ) +} finally { + await browser.close() +} + +function optionValue(name) { + const inline = process.argv.find((argument) => + argument.startsWith(`${name}=`), + ) + if (inline) return inline.slice(name.length + 1) + const index = process.argv.indexOf(name) + return index === -1 ? undefined : process.argv[index + 1] +} diff --git a/scripts/shadcn-visual-parity.mjs b/scripts/shadcn-visual-parity.mjs new file mode 100644 index 00000000..7d1abce0 --- /dev/null +++ b/scripts/shadcn-visual-parity.mjs @@ -0,0 +1,305 @@ +import { mkdir, readFile, writeFile } from 'node:fs/promises' +import { spawn } from 'node:child_process' +import { createServer } from 'node:net' +import path from 'node:path' +import pixelmatch from 'pixelmatch' +import { PNG } from 'pngjs' +import { chromium } from 'playwright' + +const root = path.resolve(import.meta.dirname, '..') +const catalog = JSON.parse( + await readFile( + path.join(root, 'benchmarks/conformance/shadcn/catalog.json'), + 'utf8', + ), +) +const explicitBaseUrl = optionValue('--url') +const requestedNames = optionValue('--case')?.split(',').filter(Boolean) +const minimumSimilarity = Number(optionValue('--minimum') ?? 0.9) +const minimumChartSimilarity = Number(optionValue('--minimum-chart') ?? 0.7) +const reportOnly = process.argv.includes('--report-only') +const entries = requestedNames + ? catalog.cases.filter((entry) => requestedNames.includes(entry.name)) + : catalog.cases +const referenceDirectory = path.join( + root, + 'benchmarks/conformance/shadcn/reference', +) +const outputDirectory = path.join(root, '.benchmark-output/shadcn') +const localDirectory = path.join(outputDirectory, 'local') +const diffDirectory = path.join(outputDirectory, 'diff') +await Promise.all([ + mkdir(localDirectory, { recursive: true }), + mkdir(diffDirectory, { recursive: true }), +]) + +const managedServer = explicitBaseUrl ? null : await startConformanceServer() +const baseUrl = explicitBaseUrl ?? managedServer.url +const browser = await chromium.launch({ headless: true }) +const results = [] + +try { + for (const [index, entry] of entries.entries()) { + const page = await browser.newPage({ + viewport: { width: 640, height: 900 }, + deviceScaleFactor: 1, + reducedMotion: 'reduce', + }) + try { + await page.goto(`${baseUrl}/embed/${entry.localCaseId}/`, { + waitUntil: 'domcontentloaded', + timeout: 60_000, + }) + await page.waitForTimeout(250) + await page.evaluate(async () => { + await document.fonts.ready + document.getAnimations().forEach((animation) => animation.finish()) + }) + const card = page.locator('.sc-card').first() + await card.waitFor({ state: 'visible' }) + const localPath = path.join(localDirectory, `${entry.name}.png`) + await card.screenshot({ path: localPath, animations: 'disabled' }) + const referencePath = path.join(referenceDirectory, `${entry.name}.png`) + const comparison = await comparePng(referencePath, localPath, entry) + const result = { + name: entry.name, + id: entry.localCaseId, + ...comparison, + pass: + comparison.similarity >= minimumSimilarity && + (comparison.chartSimilarity === null || + comparison.chartSimilarity >= minimumChartSimilarity), + } + results.push(result) + if (!result.pass) { + await writeFile( + path.join(diffDirectory, `${entry.name}.png`), + PNG.sync.write(comparison.diff), + ) + } + process.stdout.write( + `\rCompared ${String(index + 1).padStart(2)}/${entries.length}: ${entry.name} ${formatPercent(result.similarity)}${result.chartSimilarity === null ? '' : ` / chart ${formatPercent(result.chartSimilarity)}`}`, + ) + } finally { + await page.close() + } + } +} finally { + await browser.close() + await managedServer?.stop() + process.stdout.write('\n') +} + +const report = { + schemaVersion: 3, + minimumSimilarity, + minimumChartSimilarity, + source: baseUrl, + cases: results.map(({ diff: _diff, ...result }) => result), +} +await writeFile( + path.join(outputDirectory, 'report.json'), + `${JSON.stringify(report, null, 2)}\n`, +) +await writeFile( + path.join(outputDirectory, 'report.md'), + `${[ + '# shadcn visual parity', + '', + `Minimum: ${formatPercent(minimumSimilarity)}`, + `Chart-region foreground minimum: ${formatPercent(minimumChartSimilarity)}`, + '', + '| Case | Similarity | Chart foreground | Size | Result |', + '| --- | ---: | ---: | --- | --- |', + ...results + .sort((left, right) => left.similarity - right.similarity) + .map( + (result) => + `| ${result.name} | ${formatPercent(result.similarity)} | ${result.chartSimilarity === null ? '—' : formatPercent(result.chartSimilarity)} | ${result.referenceWidth}×${result.referenceHeight} / ${result.localWidth}×${result.localHeight} | ${result.pass ? 'pass' : 'fail'} |`, + ), + '', + ].join('\n')}\n`, +) + +const failed = results.filter((result) => !result.pass) +console.log( + `${results.length - failed.length}/${results.length} meet ${formatPercent(minimumSimilarity)}; mean ${formatPercent(results.reduce((total, result) => total + result.similarity, 0) / results.length)}.`, +) +if (failed.length && !reportOnly) process.exitCode = 1 + +async function comparePng(referencePath, localPath, entry) { + const reference = PNG.sync.read(await readFile(referencePath)) + const local = PNG.sync.read(await readFile(localPath)) + const width = Math.max(reference.width, local.width) + const height = Math.max(reference.height, local.height) + const paddedReference = padPng(reference, width, height) + const paddedLocal = padPng(local, width, height) + const diff = new PNG({ width, height }) + const differentPixels = pixelmatch( + paddedReference.data, + paddedLocal.data, + diff.data, + width, + height, + { threshold: 0.1, includeAA: false }, + ) + const chartSimilarity = compareChartForeground( + paddedReference, + paddedLocal, + entry.family, + ) + return { + similarity: 1 - differentPixels / (width * height), + chartSimilarity, + differentPixels, + referenceWidth: reference.width, + referenceHeight: reference.height, + localWidth: local.width, + localHeight: local.height, + diff, + } +} + +function compareChartForeground(reference, local, family) { + const square = family === 'pie' || family === 'radar' || family === 'radial' + const requestedSize = family === 'pie' ? 300 : square ? 250 : undefined + const width = Math.min( + requestedSize ?? 592, + reference.width - (square ? 0 : 48), + local.width - (square ? 0 : 48), + ) + const height = Math.min( + requestedSize ?? 340, + reference.height - 88, + local.height - 88, + ) + const x = Math.max(0, Math.floor((reference.width - width) / 2)) + const y = 88 + const referenceCrop = cropPng(reference, x, y, width, height) + const localCrop = cropPng(local, x, y, width, height) + const diff = new PNG({ width, height }) + const differentPixels = pixelmatch( + referenceCrop.data, + localCrop.data, + diff.data, + width, + height, + { threshold: 0.1, includeAA: false }, + ) + let foregroundPixels = 0 + for (let index = 0; index < referenceCrop.data.length; index += 4) { + const referenceDistance = + 765 - + referenceCrop.data[index] - + referenceCrop.data[index + 1] - + referenceCrop.data[index + 2] + const localDistance = + 765 - + localCrop.data[index] - + localCrop.data[index + 1] - + localCrop.data[index + 2] + if (referenceDistance > 15 || localDistance > 15) foregroundPixels += 1 + } + return foregroundPixels === 0 ? 1 : 1 - differentPixels / foregroundPixels +} + +function cropPng(source, x, y, width, height) { + const output = new PNG({ width, height }) + PNG.bitblt(source, output, x, y, width, height, 0, 0) + return output +} + +function padPng(source, width, height) { + if (source.width === width && source.height === height) return source + const output = new PNG({ width, height, fill: true }) + output.data.fill(255) + PNG.bitblt(source, output, 0, 0, source.width, source.height, 0, 0) + return output +} + +function formatPercent(value) { + return `${(value * 100).toFixed(1)}%` +} + +function optionValue(name) { + const inline = process.argv.find((argument) => + argument.startsWith(`${name}=`), + ) + if (inline) return inline.slice(name.length + 1) + const index = process.argv.indexOf(name) + return index === -1 ? undefined : process.argv[index + 1] +} + +async function startConformanceServer() { + const port = await availablePort() + const child = spawn( + 'pnpm', + [ + 'exec', + 'vite', + '--host', + '127.0.0.1', + '--port', + String(port), + '--strictPort', + ], + { + cwd: path.join(root, 'examples/conformance'), + stdio: ['ignore', 'pipe', 'pipe'], + }, + ) + let output = '' + child.stdout.on('data', (chunk) => { + output += chunk + }) + child.stderr.on('data', (chunk) => { + output += chunk + }) + const url = `http://127.0.0.1:${port}` + for (let attempt = 0; attempt < 100; attempt += 1) { + if (child.exitCode !== null) { + throw new Error(`Conformance server exited early:\n${output}`) + } + try { + const response = await fetch(url) + if (response.ok) { + return { + url, + stop: () => stopProcess(child), + } + } + } catch { + // Wait for Vite to bind the selected local port. + } + await new Promise((resolvePromise) => setTimeout(resolvePromise, 100)) + } + child.kill('SIGTERM') + throw new Error(`Timed out starting conformance server:\n${output}`) +} + +function availablePort() { + return new Promise((resolvePromise, reject) => { + const server = createServer() + server.once('error', reject) + server.listen(0, '127.0.0.1', () => { + const address = server.address() + if (!address || typeof address === 'string') { + server.close() + reject(new TypeError('Unable to allocate a local conformance port.')) + return + } + server.close((error) => { + if (error) reject(error) + else resolvePromise(address.port) + }) + }) + }) +} + +function stopProcess(child) { + if (child.exitCode !== null) return Promise.resolve() + return new Promise((resolvePromise) => { + child.once('exit', resolvePromise) + child.kill('SIGTERM') + }) +}