@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 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