Chart frame
ProThe chassis every chart sits in (Candor's ChartFrame, the honesty chokepoint): title, legend, a chart/table toggle, and the states a chart can be in.
Preview
Install
With the Candor CLI
First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/chart-frame
Brings in: data, locale, skeleton, utils · npm: lucide-react@^1.67.0
Usage
"use client"
import { ChartFrame, type TableView } from "@/components/chart-frame"
import { formatValue, linear, seriesColor } from "@/components/scale"
// A custom plot in the shared chassis: p95 query time per table, as horizontal bars.
const rows: { table: string; p95: number }[] = [
{ table: "deployments", p95: 0.84 },
{ table: "audit_log", p95: 0.61 },
{ table: "projects", p95: 0.32 },
{ table: "users", p95: 0.18 },
]
const table: TableView = { columns: ["Table", "p95"], rows: rows.map((r) => [r.table, formatValue(r.p95, "duration", true)]) }
export default function Example() {
return (
<ChartFrame className="max-w-xl" title="Slowest tables" subtitle="p95 query time · Production, last 24 hours" table={table} height={140}>
{({ width, height }) => {
const label = 96
const x = linear(0, 1, 0, width - label - 48)
const band = height / rows.length
return (
<svg width={width} height={height} role="img" aria-label={rows.map((r) => `${r.table} ${formatValue(r.p95, "duration", true)}`).join(", ")}>
{rows.map((r, i) => (
<g key={r.table} transform={`translate(0, ${i * band})`}>
<text x={0} y={band / 2} dominantBaseline="middle" className="fill-muted-foreground text-xs">
{r.table}
</text>
<rect x={label} y={band / 2 - 8} width={x(r.p95)} height={16} rx={4} fill={seriesColor(0)} />
<text x={label + x(r.p95) + 6} y={band / 2} dominantBaseline="middle" className="fill-foreground text-xs tabular-nums">
{formatValue(r.p95, "duration")}
</text>
</g>
))}
</svg>
)
}}
</ChartFrame>
)
}<ChartFrame>
| Prop | Type | Default | Description |
|---|---|---|---|
| titlerequired | string | — | What the chart shows, above it. |
| subtitle | string | undefined | — | A line under the title, e.g. the period or unit. |
| state | ChartState | undefined | { kind: "ready" } | Loading, empty (with why), partial (with what's missing) or error. Defaults to ready. |
| legend | LegendItem[] | undefined | — | Series names and colours; shown only when there are two or more. |
| table | TableView | undefined | — | Every value as rows, behind a chart/table toggle. Without it there's no toggle. |
| height | number | undefined | 220 | The plot's height in pixels. Defaults to 220. |
| refreshing | boolean | undefined | false | Keeps the previous render at reduced opacity while new data loads. |
| defaultWidth | number | undefined | — | The width to draw at until the frame can be measured: on the server, and in test environments like jsdom that measure everything as 0. Left out, it comes from ChartSizeProvider, else 0 (the plot waits for a measurement). |
| childrenrequired | (size: { width: number; height: number }) => React.ReactNode | — | Renders the plot at the measured width. |
| className | string | undefined | — | Extra classes for the outer element. |
<ChartSizeProvider>
A default plot width for every chart inside: for server rendering, or tests whose DOM can't measure.
| Prop | Type | Default | Description |
|---|---|---|---|
| defaultWidthrequired | number | — | The width, in pixels, charts draw at until they can measure themselves. |
| childrenrequired | React.ReactNode | — | What goes inside. |
Strings
The fixed words, in English by default. Change any of them under chartFrame in <LocaleProvider strings={{ … }}>; the table is exported as chartFrameStrings.
| Key | English |
|---|---|
| view | View |
| chart | Chart |
| table | Table |
| legend | Legend |
Types
- LegendItem
{ label: string; color: string; shape?: "rect" | "line" | undefined }- TableView
{ columns: string[]; rows: (string | number)[][] }- TipState
- The pointer's position and what to show there.
{ x: number; y: number; title: string; rows: { label: string; value: string; color?: string | undefined; shape?: "rect" | "line" | undefined }[] } | null