Chart frame

Pro

The 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.

Markdown

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

TSX · The example above
"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>

PropTypeDefaultDescription
titlerequiredstring—What the chart shows, above it.
subtitlestring | undefined—A line under the title, e.g. the period or unit.
stateChartState | undefined{ kind: "ready" }Loading, empty (with why), partial (with what's missing) or error. Defaults to ready.
legendLegendItem[] | undefined—Series names and colours; shown only when there are two or more.
tableTableView | undefined—Every value as rows, behind a chart/table toggle. Without it there's no toggle.
heightnumber | undefined220The plot's height in pixels. Defaults to 220.
refreshingboolean | undefinedfalseKeeps the previous render at reduced opacity while new data loads.
defaultWidthnumber | 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.
classNamestring | 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.

PropTypeDefaultDescription
defaultWidthrequirednumber—The width, in pixels, charts draw at until they can measure themselves.
childrenrequiredReact.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.

KeyEnglish
viewView
chartChart
tableTable
legendLegend

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