# Overview

Pure SVG charts with no extra dependency. Every chart reads colors from the theme tokens, so light and dark mode work out of the box.

The family: `LineChart`, `AreaChart`, `BarChart`, `BarSegment`, `Histogram`, `RangeBarChart`, `CandlestickChart`, `PieChart`, `DonutChart`, `RadarChart`, `ScatterChart` and `Sparkline`. Shared pieces are `useChart`, `ChartLegend`, `CartesianGrid` and `ChartAxis`.

Group: Charts

## Import

```tsx
import { Chart } from "@indoku/ui"

// <Chart.LineChart />, <Chart.BarChart />, Chart.useChart(), ...
```

## Anatomy

```text
Chart.LineChart | AreaChart | BarChart | ...
  CartesianGrid (grid lines)
  ChartAxis (bottom / left / right)
  ChartLegend (interactive, driven by useChart)
```

## Examples

### Installation

Charts ship inside @indoku/ui. Only React and Emotion are required.

```tsx
function ChartInstall() {
  const sales = [120, 180, 150, 220, 260, 240]
  return <Chart.LineChart categories={["Jan", "Feb", "Mar", "Apr", "May", "Jun"]} series={[{ name: "Sales", values: sales }]} />
}
```

### useChart

Shared tokens, number formatting and legend highlight.

```tsx
function ChartUseChart() {
  const series = [
    { name: "Revenue", values: [120, 180, 150, 220, 260, 240] },
    { name: "Cost", color: "red", values: [80, 90, 100, 110, 120, 130] },
  ]
  const chart = Chart.useChart({ series, format: (v) => `$${v}k` })
  return (
    <div>
      <Chart.LineChart categories={["Jan", "Feb", "Mar", "Apr", "May", "Jun"]} series={series} highlight={chart.active} yTickFormat={chart.format} />
      <Chart.ChartLegend items={series.map((s, i) => ({ name: s.name, color: chart.colors[i] }))} chart={chart} />
    </div>
  )
}
```

### Sorting

sortChartData and compactFormat helpers.

```tsx
function ChartSorted() {
  const data = [
    { name: "Organic", value: 45 },
    { name: "Direct", value: 25 },
    { name: "Referral", value: 18 },
    { name: "Social", value: 12 },
  ]
  const sorted = Chart.sortChartData(data, (d) => d.value, "desc")
  return <Chart.BarChart categories={sorted.map((d) => d.name)} series={[{ name: "Visits", values: sorted.map((d) => d.value) }]} yTickFormat={Chart.compactFormat} />
}
```

### Bar: grouped

```tsx
function BarGrouped() {
  return (
    <Chart.BarChart
      legend
      categories={["Q1", "Q2", "Q3", "Q4"]}
      series={[
        { name: "2025", values: [40, 65, 52, 90] },
        { name: "2026", values: [55, 70, 80, 110] },
      ]}
    />
  )
}
```

### Bar: stacked

```tsx
function BarStacked() {
  return (
    <Chart.BarChart
      mode="stacked"
      legend
      categories={["Mon", "Tue", "Wed", "Thu", "Fri"]}
      series={[
        { name: "Web", values: [30, 40, 35, 50, 45] },
        { name: "iOS", values: [20, 25, 30, 22, 28] },
        { name: "Android", values: [15, 18, 20, 25, 30] },
      ]}
    />
  )
}
```

### Bar: percent

```tsx
function BarPercent() {
  return (
    <Chart.BarChart
      mode="percent"
      legend
      categories={["A", "B", "C", "D"]}
      series={[
        { name: "Passed", values: [60, 45, 80, 70] },
        { name: "Failed", values: [40, 55, 20, 30] },
      ]}
    />
  )
}
```

### Bar segment

One segmented bar for composition.

```tsx
function BarSegmentDemo() {
  return (
    <Chart.BarSegment
      data={[
        { name: "Design", value: 420 },
        { name: "Engineering", value: 860 },
        { name: "Marketing", value: 310 },
      ]}
    />
  )
}
```

### Histogram

```tsx
function HistogramDemo() {
  const values = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 7, 8, 8, 9]
  return <Chart.Histogram values={values} bins={5} />
}
```

### Range bar

```tsx
function RangeBarDemo() {
  return (
    <Chart.RangeBarChart
      data={[
        { label: "Mon", min: 18, max: 27 },
        { label: "Tue", min: 20, max: 30 },
        { label: "Wed", min: 17, max: 25 },
        { label: "Thu", min: 21, max: 33 },
      ]}
    />
  )
}
```

### Candlestick

```tsx
function CandlestickDemo() {
  return (
    <Chart.CandlestickChart
      data={[
        { label: "1", open: 100, high: 112, low: 96, close: 108 },
        { label: "2", open: 108, high: 115, low: 104, close: 106 },
        { label: "3", open: 106, high: 110, low: 98, close: 101 },
        { label: "4", open: 101, high: 120, low: 99, close: 118 },
        { label: "5", open: 118, high: 124, low: 112, close: 114 },
      ]}
    />
  )
}
```

### Line: dashed series

```tsx
function LineDashed() {
  return (
    <Chart.LineChart
      legend
      categories={["Jan", "Feb", "Mar", "Apr", "May"]}
      series={[
        { name: "Actual", values: [10, 25, 18, 32, 40] },
        { name: "Forecast", color: "orange", dashed: true, values: [12, 22, 24, 30, 38] },
      ]}
    />
  )
}
```

### Line: gradient area

```tsx
function LineGradient() {
  return <Chart.LineChart area gradient showDots categories={["Jan", "Feb", "Mar", "Apr", "May", "Jun"]} series={[{ name: "Users", values: [30, 45, 38, 62, 70, 91] }]} />
}
```

### Line: biaxial

Per-series `axis` with `rightTickFormat`.

```tsx
function LineBiaxial() {
  return (
    <Chart.LineChart
      legend
      categories={["Jan", "Feb", "Mar", "Apr", "May"]}
      rightTickFormat={(v) => `${v}%`}
      series={[
        { name: "Revenue", values: [120, 150, 170, 160, 210] },
        { name: "Conversion", color: "green", axis: "right", values: [2.1, 2.4, 3.0, 2.8, 3.6] },
      ]}
    />
  )
}
```

### Line: connect nulls + series labels

```tsx
function LineConnectNulls() {
  return <Chart.LineChart connectNulls seriesLabels categories={["Jan", "Feb", "Mar", "Apr", "May", "Jun"]} series={[{ name: "Signups", values: [10, 20, null, 35, null, 50] }]} />
}
```

### Area: gradient fill

```tsx
function AreaGradient() {
  return <Chart.AreaChart categories={["Jan", "Feb", "Mar", "Apr", "May", "Jun"]} series={[{ name: "Traffic", values: [20, 35, 30, 50, 65, 60] }]} />
}
```

### Area: percent

```tsx
function AreaPercent() {
  return (
    <Chart.AreaChart
      mode="percent"
      legend
      categories={["Jan", "Feb", "Mar", "Apr", "May"]}
      series={[
        { name: "Free", values: [50, 45, 40, 35, 30] },
        { name: "Pro", values: [30, 35, 40, 45, 50] },
        { name: "Team", values: [20, 20, 20, 20, 20] },
      ]}
    />
  )
}
```

### Area: reference area and dots

```tsx
function AreaReference() {
  return (
    <Chart.AreaChart
      showDots
      referenceArea={{ from: 2, to: 3, label: "Promo" }}
      categories={["Jan", "Feb", "Mar", "Apr", "May", "Jun"]}
      series={[{ name: "Orders", values: [12, 18, 40, 52, 30, 28] }]}
    />
  )
}
```

### Pie: padding and detached segment

```tsx
function PieDemo() {
  return (
    <Chart.PieChart
      legend
      padAngle={2}
      detached={[0]}
      data={[
        { name: "Organic", value: 45 },
        { name: "Direct", value: 25 },
        { name: "Referral", value: 18 },
        { name: "Social", value: 12 },
      ]}
    />
  )
}
```

### Donut: radial text

```tsx
function DonutDemo() {
  return (
    <Chart.DonutChart
      legend
      radialText={{ label: "100", sublabel: "visits" }}
      data={[
        { name: "Organic", value: 45 },
        { name: "Direct", value: 25 },
        { name: "Referral", value: 18 },
        { name: "Social", value: 12 },
      ]}
    />
  )
}
```

### Radar: filled grid

```tsx
function RadarDemo() {
  return (
    <Chart.RadarChart
      legend
      grid="filled"
      axes={["Speed", "Power", "Range", "Armor", "Stealth"]}
      series={[
        { name: "Alpha", values: [8, 6, 7, 5, 9] },
        { name: "Beta", color: "orange", values: [5, 9, 6, 8, 4] },
      ]}
    />
  )
}
```

### Radar: circle grid

```tsx
function RadarCircle() {
  return <Chart.RadarChart grid="circle" axes={["HTML", "CSS", "JS", "TS", "Node"]} series={[{ name: "Skills", values: [9, 8, 9, 7, 6] }]} />
}
```

### Scatter: groups and trend lines

```tsx
function ScatterDemo() {
  return (
    <Chart.ScatterChart
      trendLine
      legend
      series={[
        { name: "Group A", points: [{ x: 1, y: 2 }, { x: 2, y: 3.5 }, { x: 3, y: 3 }, { x: 4, y: 5.2 }, { x: 5, y: 6 }] },
        { name: "Group B", color: "orange", points: [{ x: 1, y: 5 }, { x: 2, y: 4.2 }, { x: 3, y: 3.8 }, { x: 4, y: 2.5 }, { x: 5, y: 2 }] },
      ]}
    />
  )
}
```

### Scatter: connected dots

```tsx
function ScatterConnected() {
  return <Chart.ScatterChart connect dotSize={5} series={[{ name: "Path", points: [{ x: 0, y: 0 }, { x: 2, y: 3 }, { x: 4, y: 1 }, { x: 6, y: 5 }, { x: 8, y: 4 }] }]} />
}
```

### Sparkline

Plain, trend-colored with area, and interactive tooltip.

```tsx
function SparklineDemo() {
  return (
    <div style={{ display: "flex", gap: 24, alignItems: "center" }}>
      <Chart.Sparkline values={[3, 5, 4, 7, 6, 9, 12]} />
      <Chart.Sparkline values={[12, 10, 11, 8, 7, 5, 4]} color="trend" area />
      <Chart.Sparkline values={[4, 8, 6, 10, 9, 14]} interactive width={160} height={40} />
    </div>
  )
}
```

### Grid, axis and reference lines

```tsx
function GridAxisDemo() {
  return (
    <Chart.LineChart
      showGrid
      categories={["Mon", "Tue", "Wed", "Thu", "Fri"]}
      xTickFormat={(label) => label.slice(0, 1)}
      yTickFormat={(v) => `${v} ms`}
      referenceLines={[{ value: 60, label: "SLA", color: "red.500", dashed: true }]}
      series={[{ name: "Latency", values: [42, 55, 48, 63, 51] }]}
    />
  )
}
```

## Props

### ChartLegend

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `items` * | `{ name: string; color: string }[]` |  |  |
| `chart` * | `ChartApi` |  |  |

### CartesianGrid

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `x0` * | `number` |  |  |
| `x1` * | `number` |  |  |
| `y0` * | `number` |  |  |
| `y1` * | `number` |  |  |
| `horizontal` | `number[]` |  | Posisi y (px) tiap garis horizontal. |
| `vertical` | `number[]` |  | Posisi x (px) tiap garis vertikal. |

### ChartAxis

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `orientation` * | `"bottom" \| "left" \| "right"` |  |  |
| `at` * | `number` |  | Posisi garis sumbu (px): y untuk bottom, x untuk left/right. |
| `ticks` * | `{ pos: number; label: string }[]` |  |  |

### LineChart

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `series` * | `ChartSeries[]` |  |  |
| `categories` | `string[]` |  | Label for each data point (x axis), used for the point's aria-label. |
| `area` | `boolean` | `false` | Fill the area under the line (area chart). |
| `gradient` | `boolean` |  | Isi area pakai gradient (memudar ke bawah). |
| `showDots` | `boolean` |  |  |
| `dashed` | `boolean` |  | Semua garis putus-putus (atau per series lewat `series[i].dashed`). |
| `connectNulls` | `boolean` |  | Sambung garis melewati nilai `null`. |
| `seriesLabels` | `boolean` |  | Tampilkan nama series di ujung garis. |
| `rightTickFormat` | `ChartFormat` |  |  |

### AreaChart

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `series` * | `ChartSeries[]` |  |  |
| `categories` | `string[]` |  |  |
| `mode` | `"overlap" \| "stacked" \| "percent"` |  | "overlap" (default), "stacked" (ditumpuk) atau "percent" (ditumpuk, 0-100%). |
| `gradient` | `boolean` | `true` | Isi pakai gradient. |
| `showDots` | `boolean` |  | Tanda titik di tiap data. |
| `referenceArea` | `{ from: number; to: number; label?: string }` |  | Blok warna di antara dua index kategori. |

### BarChart

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `series` * | `ChartSeries[]` |  |  |
| `categories` | `string[]` |  |  |
| `mode` | `"grouped" \| "stacked" \| "percent"` |  | "grouped" (default), "stacked" atau "percent" (ditumpuk, 0-100%). |
| `gap` | `number` | `0.3` | Gap between category groups, 0–1 (share of the slot width). |
| `joined` | `boolean` |  | Hilangkan jarak antar bar (histogram). |

### Histogram

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `values` * | `number[]` |  |  |
| `bins` | `number` | `akar jumlah data (aturan Sturges sederhana)` | Jumlah bin. |
| `color` | `string` |  |  |
| `name` | `string` |  |  |

### RangeBarChart

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `data` * | `RangeDatum[]` |  |  |
| `color` | `string` |  |  |
| `gap` | `number` |  |  |

### CandlestickChart

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `data` * | `CandleDatum[]` |  |  |
| `upColor` | `string` |  | Warna naik / turun (token warna). |
| `downColor` | `string` |  |  |

### BarSegment

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `data` * | `BarSegmentDatum[]` |  |  |
| `barHeight` | `number` | `12` | Tinggi bar (px). |
| `showLegend` | `boolean` | `true` | Tampilkan legend dengan nilai & persentase. |
| `format` | `ChartFormat` |  |  |

### PieChart

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `data` * | `PieDatum[]` |  |  |
| `size` | `number` |  |  |
| `innerRadiusRatio` | `number` | `0` | `0` = full pie, e.g. `0.6` = donut with a 60% radius hole. |
| `padAngle` | `number` | `0` | Celah antar segmen (derajat). |
| `detached` | `number[]` |  | Index segmen yang digeser keluar. |
| `detachDistance` | `number` | `8` | Jarak geser segmen (px). |
| `radialText` | `{ label: string; sublabel?: string }` |  | Teks di tengah (donut): `label` besar + `sublabel` kecil. |
| `legend` | `boolean` |  | Legend interaktif. |

### RadialText

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `x` * | `number` |  |  |
| `y` * | `number` |  |  |
| `label` * | `string` |  |  |
| `sublabel` | `string` |  |  |

### RadarChart

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `axes` * | `string[]` |  | Nama tiap sumbu (minimal 3). |
| `series` * | `ChartSeries[]` |  |  |
| `size` | `number` |  |  |
| `levels` | `number` | `4` | Jumlah lingkaran/poligon grid. |
| `grid` | `"polygon" \| "circle" \| "filled"` |  | Bentuk grid: "polygon" (default), "circle" atau "filled" (selang-seling terisi). |
| `pointLabels` | `boolean` | `true` | Label di tiap sumbu. |
| `max` | `number` | `nilai terbesar data` | Nilai maksimum sumbu. |
| `legend` | `boolean` |  |  |
| `highlight` | `number \| null` |  |  |

### ScatterChart

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `series` * | `ScatterSeries[]` |  | Satu atau lebih grup data. |
| `trendLine` | `boolean` |  | Garis tren regresi linear per grup. |
| `connect` | `boolean` |  | Sambungkan titik berurutan dengan garis. |
| `dotSize` | `number` | `4` | Radius titik. |
| `referenceLines` | `ReferenceLineSpec[]` |  |  |

### Sparkline

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `values` * | `number[]` |  |  |
| `width` | `number` |  |  |
| `height` | `number` |  |  |
| `color` | `string` |  | Token warna, atau "trend" (hijau kalau naik, merah kalau turun). |
| `area` | `boolean` |  | Isi area di bawah garis. |
| `interactive` | `boolean` | `false` | Tampilkan nilai titik yang di-hover (tooltip). |
| `format` | `ChartFormat` |  |  |

`*` = required. Style props and HTML attributes are accepted too.

## Accessibility

- Charts are decorative SVG; pair them with a table or text summary for screen readers.
- Legend items are focusable and highlight their series on focus.

## Notes

- `null` values create gaps unless `connectNulls` is set.
- Series colors accept a palette name (`blue`) or a full token (`blue.500`).

## See also

- Plot 2D: https://ui.indoku.org/docs/plot-2d
- Data Table: https://ui.indoku.org/docs/data-table
