@invana/charts 0.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +148 -0
- package/dist/index.cjs +1737 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +681 -0
- package/dist/index.d.ts +681 -0
- package/dist/index.js +1701 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# @invana/charts
|
|
2
|
+
|
|
3
|
+
Every chart Invana draws: the time series on the Plan and Model pages, the marks that sit inside
|
|
4
|
+
a table row, and the provisional charts that moved here from `@invana/ui`.
|
|
5
|
+
|
|
6
|
+
This package exists because the time series are built on [uPlot](https://github.com/leeoniya/uPlot),
|
|
7
|
+
an external library. Following the kit's placement rule, anything that needs an external JS
|
|
8
|
+
library gets its own package, so a consumer that only wants a button never pays for a chart
|
|
9
|
+
engine. The dependency points one way: `@invana/charts` imports from `@invana/ui`, and
|
|
10
|
+
`@invana/ui` neither depends on nor re-exports it.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm add @invana/charts
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Peer dependencies: `@invana/ui`, `@invana/styling`, `@invana/tables`, `react`, `react-dom`.
|
|
17
|
+
`uplot` ships as a direct dependency.
|
|
18
|
+
|
|
19
|
+
## Setup: Tailwind v4 import order
|
|
20
|
+
|
|
21
|
+
The charts use kit tokens and Tailwind utilities, so your stylesheet must compile the
|
|
22
|
+
`@invana/styling` theme and scan this package for classes:
|
|
23
|
+
|
|
24
|
+
```css
|
|
25
|
+
@import "tailwindcss";
|
|
26
|
+
|
|
27
|
+
@source "../node_modules/@invana/ui/dist/**/*.js";
|
|
28
|
+
@source "../node_modules/@invana/tables/dist/**/*.js";
|
|
29
|
+
@source "../node_modules/@invana/charts/dist/**/*.js";
|
|
30
|
+
|
|
31
|
+
@import "@invana/styling/index.css";
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
1. `tailwindcss` comes first.
|
|
35
|
+
2. Then your `@source` globs, with this package among them.
|
|
36
|
+
3. Then `@invana/styling`.
|
|
37
|
+
|
|
38
|
+
Importing `@invana/styling` is what gives you `--color-data-1…8`, the status tokens and the
|
|
39
|
+
`--text-*` type ladder. The charts read all three. No uPlot stylesheet is needed: the few rules
|
|
40
|
+
uPlot needs are installed once at runtime.
|
|
41
|
+
|
|
42
|
+
## What's in it
|
|
43
|
+
|
|
44
|
+
| Component | Engine | For |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `LineChart` | uPlot | One measure over time, such as work p50 or p95 a day. It can carry event `marks` and a `reference` line. |
|
|
47
|
+
| `StackedBarChartV` | uPlot | Counts a day split into parts: runs served and failed, or queries by caller. |
|
|
48
|
+
| `StackedAreaChart` | uPlot | Totals over time by part, with stepped paths, such as records by model or by type. |
|
|
49
|
+
| `Sparkline` | SVG | The shape of a series inside a row. |
|
|
50
|
+
| `InlineMeter` | DOM | A share as a bar plus its value, in a table cell. |
|
|
51
|
+
| `SegmentedBar`, `SegmentedBarLegend` | DOM | One row's total split into categories, with one legend per table. |
|
|
52
|
+
| `BarChartH`, `BarChartV`, `DivergingBar`, `HeatStrip` | DOM/SVG | Provisional: moved as-is and not yet used by a design. |
|
|
53
|
+
|
|
54
|
+
`MetricTile`, `MetricGrid`, `Legend` and `Progress` stay in `@invana/ui`, and the charts here
|
|
55
|
+
compose them.
|
|
56
|
+
|
|
57
|
+
### Why some charts use uPlot and others DOM/SVG
|
|
58
|
+
|
|
59
|
+
- **uPlot, for the time series.** A 30-to-90-day series with a crosshair, hover, marks and a
|
|
60
|
+
redraw on every resize or theme change is what a canvas engine is for. uPlot is small and
|
|
61
|
+
fast, and owns scales, hit-testing and the cursor. Everything read as text stays DOM,
|
|
62
|
+
composed from `@invana/ui`:
|
|
63
|
+
- period ticks
|
|
64
|
+
- the tooltip (`Card` + `Eyebrow`)
|
|
65
|
+
- the legend (`Legend`)
|
|
66
|
+
- the table view (`DataTable`)
|
|
67
|
+
|
|
68
|
+
So chart text sets in the same type as the panel around it and never scales with the canvas.
|
|
69
|
+
- **DOM/SVG, for marks in a row.** A sparkline, meter or segmented bar repeats dozens of times
|
|
70
|
+
down a list. Each is a handful of elements, so a canvas per row would cost more than it
|
|
71
|
+
draws, and it would not inherit the row's type, colour or selection state.
|
|
72
|
+
|
|
73
|
+
## Usage
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import { LineChart, StackedBarChartV, StackedAreaChart } from '@invana/charts';
|
|
77
|
+
|
|
78
|
+
<LineChart
|
|
79
|
+
values={p95} // (number | null)[] — null breaks the line
|
|
80
|
+
labels={days}
|
|
81
|
+
format={(v) => `${v.toFixed(1)}s`}
|
|
82
|
+
marks={[{ index: 17, label: 'import' }]}
|
|
83
|
+
reference={{ value: 1.4, label: 'Graph p95' }}
|
|
84
|
+
/>;
|
|
85
|
+
|
|
86
|
+
<StackedBarChartV
|
|
87
|
+
data={days.map((label, i) => ({ label, values: { served: served[i], failed: failed[i] } }))}
|
|
88
|
+
series={[
|
|
89
|
+
{ key: 'served', label: 'served', color: 'var(--color-success)' },
|
|
90
|
+
{ key: 'failed', label: 'failed', color: 'var(--color-destructive)' },
|
|
91
|
+
]}
|
|
92
|
+
/>;
|
|
93
|
+
|
|
94
|
+
<StackedAreaChart
|
|
95
|
+
data={growth}
|
|
96
|
+
series={models} // fixed order; colour follows the model, not its rank
|
|
97
|
+
marks={writes}
|
|
98
|
+
markLegend="a write — an import or a stitch commit"
|
|
99
|
+
/>;
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Colours are always tokens (`var(--color-data-3)`, `var(--color-success)`), never literal
|
|
103
|
+
values. The status tokens are only for series that *are* a status.
|
|
104
|
+
|
|
105
|
+
## The chart base
|
|
106
|
+
|
|
107
|
+
Every uPlot chart sits on one internal frame, which does the following:
|
|
108
|
+
|
|
109
|
+
- **Sizes to its container** with a `ResizeObserver` and draws at its real width, so axis text
|
|
110
|
+
is never stretched.
|
|
111
|
+
- **Resolves tokens** with `getComputedStyle`, since a canvas cannot read `var()`. It resolves:
|
|
112
|
+
- `--color-data-1…8`
|
|
113
|
+
- `--color-success`, `--color-warning`, `--color-destructive`
|
|
114
|
+
- `--color-foreground`, `--color-muted-foreground`, `--color-border`, `--color-card`
|
|
115
|
+
- the `--text-*` sizes
|
|
116
|
+
- **Redraws on a theme, mode or density change** without a reload. A `MutationObserver` watches
|
|
117
|
+
`class`, `style`, `data-theme` and `data-density` on `<html>`, and a listener catches the OS
|
|
118
|
+
colour scheme flipping under a `system` theme.
|
|
119
|
+
- **Shares its parts** between the charts:
|
|
120
|
+
- a hover tooltip that reads the nearest period (arrow keys do the same on focus)
|
|
121
|
+
- event marks: a dashed rule with its label at the top, because a mark is an event, not a value
|
|
122
|
+
- a reference line: a dashed rule with its label to the right of the plot
|
|
123
|
+
- a hover target the size of the whole period's slot, not the painted mark
|
|
124
|
+
- **Is accessible.** It gives an `aria-label` summary, plus a "View as table" toggle that shows
|
|
125
|
+
the same data in `@invana/tables`' `DataTable`.
|
|
126
|
+
- **Keeps the legend inside the chart.** The legend and toggle are in the chart's own flow, so a
|
|
127
|
+
parent never clips them. `height` is the plot's height; ticks and legend sit below it.
|
|
128
|
+
|
|
129
|
+
## Floors
|
|
130
|
+
|
|
131
|
+
These hold on every chart, so a thin window never draws a false picture:
|
|
132
|
+
|
|
133
|
+
- **A count axis never tops out below 5.** One run on a quiet day is a short column, not a
|
|
134
|
+
full-height one.
|
|
135
|
+
- **Axes step cleanly.** A ceiling is 2–4 steps of 1, 2 or 5 × 10ⁿ, so a busiest day of 51
|
|
136
|
+
reads 0 · 20 · 40 · 60. Line charts keep headroom above their highest value.
|
|
137
|
+
- **Bars are at most 24px wide**, with a 4px rounded data end, a square baseline end, and a 2px
|
|
138
|
+
surface gap between stacked segments.
|
|
139
|
+
- **A single point draws a point**, centred on an axis from zero, not on an edge.
|
|
140
|
+
- **A null period breaks the line** rather than dropping it to zero, because *nothing ran* is not
|
|
141
|
+
*it took no time*. A measured day between two gaps draws as a point.
|
|
142
|
+
- **An all-zero window shows that it is empty**, not a chart of zeros pretending to be data.
|
|
143
|
+
- **Direct labels are dropped rather than stacked when they would collide.** The legend is
|
|
144
|
+
always there for two or more series.
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT © Ravi Raja Merugu
|