@lilt-ui/charts 0.1.0 → 0.3.0
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/CHANGELOG.md +69 -0
- package/README.md +120 -0
- package/dist/analysis-BJRmXHOB.js +62 -0
- package/dist/cards/area-chart-card.d.ts +10 -1
- package/dist/cards/box-plot-card.d.ts +30 -0
- package/dist/cards/cartesian-card.d.ts +86 -6
- package/dist/cards/chart-card.d.ts +12 -1
- package/dist/cards/line-chart-card.d.ts +10 -1
- package/dist/cards/range-chart-card.d.ts +28 -0
- package/dist/cartesian-card-Dcd0ZnQL.js +7004 -0
- package/dist/chart-context.d.ts +18 -0
- package/dist/chart-data-CCwLQeXa.js +44 -0
- package/dist/chart.d.ts +3 -1
- package/dist/data.js +3 -2
- package/dist/engine/box.d.ts +18 -0
- package/dist/engine/domains.d.ts +2 -0
- package/dist/engine/geometry.d.ts +2 -1
- package/dist/engine/normalize.d.ts +2 -4
- package/dist/engine/ranges.d.ts +59 -0
- package/dist/finance/candle-geometry.d.ts +55 -0
- package/dist/finance/candles.d.ts +18 -0
- package/dist/finance/cards/candlestick-chart-card.d.ts +41 -0
- package/dist/finance/cards/depth-chart-card.d.ts +32 -0
- package/dist/finance/cards/indicator-chart-card.d.ts +52 -0
- package/dist/finance/cards/pane.d.ts +24 -0
- package/dist/finance/cards/portfolio-chart-card.d.ts +22 -0
- package/dist/finance/cards/price-chart-card.d.ts +27 -0
- package/dist/finance/index.d.ts +17 -0
- package/dist/finance/indicators.d.ts +65 -0
- package/dist/finance/order-book.d.ts +26 -0
- package/dist/finance.d.ts +2 -0
- package/dist/finance.js +1158 -0
- package/dist/index.d.ts +14 -4
- package/dist/index.js +2239 -8793
- package/dist/interaction/axis-cursor.d.ts +12 -2
- package/dist/interaction/chart-sync.d.ts +11 -1
- package/dist/interaction/field-values.d.ts +12 -0
- package/dist/interaction/inspection-layer.d.ts +10 -1
- package/dist/interaction/pin-glyphs.d.ts +8 -0
- package/dist/interaction/pin-marker.d.ts +9 -1
- package/dist/marks/contract.d.ts +49 -0
- package/dist/marks/use-range-mark.d.ts +19 -0
- package/dist/model/cartesian-model.d.ts +3 -3
- package/dist/primitives/annotations.d.ts +1 -1
- package/dist/primitives/axis-segments.d.ts +23 -0
- package/dist/primitives/box-plot.d.ts +4 -0
- package/dist/primitives/error-bar.d.ts +4 -0
- package/dist/primitives/interval-band.d.ts +2 -2
- package/dist/primitives/range-bar.d.ts +4 -0
- package/dist/report-context-Cz0AYdV2.js +895 -0
- package/dist/styles.css +1 -1
- package/dist/types.d.ts +114 -16
- package/dist/watch/board/board-state.d.ts +74 -0
- package/dist/watch/board/board-toast.d.ts +19 -0
- package/dist/watch/board/chart-board.d.ts +97 -0
- package/dist/watch/board/report-context.d.ts +3 -0
- package/dist/watch/engine/describe.d.ts +9 -0
- package/dist/watch/engine/explain.d.ts +48 -0
- package/dist/watch/engine/normal.d.ts +114 -0
- package/dist/watch/engine/robust.d.ts +7 -0
- package/dist/watch/engine/rules.d.ts +22 -0
- package/dist/watch/engine/status.d.ts +39 -0
- package/dist/watch/index.d.ts +14 -0
- package/dist/watch/normal-layer.d.ts +18 -0
- package/dist/watch.d.ts +2 -0
- package/dist/watch.js +628 -0
- package/package.json +16 -2
- package/dist/analysis-BLLWEKBw.js +0 -105
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
Charts that know what normal looks like, dashboards that arrange themselves around what needs attention, pins shared across linked charts, and segmented axes.
|
|
6
|
+
|
|
7
|
+
### Watch
|
|
8
|
+
|
|
9
|
+
- `normal` on Line and unstacked Area cards watches one series. Layers are off unless set: `limits`, `around` another series with a `tolerance`, `learn` from its own history by `week`, `day`, or `all`, `given` similar conditions, `when` to skip rows such as maintenance, `rules` across series, and `history` to learn from rows the card does not draw.
|
|
10
|
+
- The card draws the normal band, recolors unusual stretches, keeps limits on screen, and says "Unusually low since Tue 2 PM". With `breakdown`, an unusual reading says which segment caused it: "98% of the drop is Mobile Safari".
|
|
11
|
+
- `onNormalChange` reports whether the series is unusual, how badly, why, and since when.
|
|
12
|
+
- `ChartBoard` and `BoardItem`, from `@lilt-ui/charts/watch`, rank cards by what needs attention. Each card carries its state in its glass tab and on its edge: Needs attention with Acknowledge, Seen, Back to normal, or Pinned. Nothing moves while the viewer points at or works inside the board.
|
|
13
|
+
- `toast` alerts the viewer to cards that turn unusual off screen, with View; `memory` marks incidents new since the last visit; `onEvent` and its `show()` feed your own alerts.
|
|
14
|
+
- The engine is exported for custom UI: `evaluateNormal`, `trackNormalStatus`, `explainReadings`, `explainDeviation`, `describeFlag`, `describeExplanation`, `flatline`, `jump`, `boardReducer`, and `rankBoard`.
|
|
15
|
+
- `CardFrameContext` lets a container give the card inside it a tab, an edge tone, and a handle.
|
|
16
|
+
|
|
17
|
+
### Axes
|
|
18
|
+
|
|
19
|
+
- `axis="segmented"` draws rounded segments between ticks instead of one continuous line, with y labels beside them; the segment under the pointer lights up.
|
|
20
|
+
- `axis` takes one preset per axis, e.g. `{ x: 'minimal', y: 'segmented' }`; an axis left out is `minimal`.
|
|
21
|
+
- A segmented axis takes a `gradient` to read as a range: `true` fades the first series' color from faint to full, or pass colors low to high, e.g. `['var(--lilt-positive)', 'var(--lilt-negative)']`.
|
|
22
|
+
|
|
23
|
+
### Pins
|
|
24
|
+
|
|
25
|
+
- A click on a linked card pins every card in its `sync` group. The clicked card shows the pin, the others show a ghost of it that names where it came from, and any of them releases it.
|
|
26
|
+
- Alt-click (Option-click) pins one card only, and Alt+Enter does the same from the keyboard; linked cards keep following the pointer. `model.actions.pin(x, { local: true })` does it in code.
|
|
27
|
+
- Pins travel between points with the crosshair's quick spring, and the pin glyph sits centered in its marker.
|
|
28
|
+
|
|
29
|
+
## 0.2.0
|
|
30
|
+
|
|
31
|
+
Finance charts, range charts, and series that carry more than one value.
|
|
32
|
+
|
|
33
|
+
### Breaking
|
|
34
|
+
|
|
35
|
+
- A series' `interval` is replaced by `fields`: named companion values in the series' unit, such as a forecast's low and high, quartiles, or a candle's open, high, and low. Fields fit the y axis, appear in hover, and range marks read them by ID.
|
|
36
|
+
- `IntervalBand` now names the fields it draws: `<IntervalBand series="revenue" lower="low" upper="high" />`.
|
|
37
|
+
- Tooltip content (`ChartTooltipContext.series[]`) has `fields` instead of `interval`.
|
|
38
|
+
|
|
39
|
+
Cards are unchanged: `forecast={{ from, lower, upper }}` works as before. See "Upgrading from 0.1" in the README.
|
|
40
|
+
|
|
41
|
+
### Finance, from `@lilt-ui/charts/finance`
|
|
42
|
+
|
|
43
|
+
- `CandlestickChartCard`: candles, hollow candles, OHLC bars, or a price area colored by direction, with an optional linked volume pane, a log scale, and live mode. Hover reads the candle's open, high, and low beside its date.
|
|
44
|
+
- `IndicatorChartCard`: simple or exponential averages and Bollinger bands over the price, with RSI, MACD, or volume panes underneath that move together on hover. Each pane reads its own value.
|
|
45
|
+
- `PriceChartCard`: a ticker with its latest price, change, and change in money; `versus` rebases other instruments to percent change on one axis.
|
|
46
|
+
- `DepthChartCard`: how much could trade at each price on the buy and sell side, with the gap between the best prices and the total resting on each side.
|
|
47
|
+
- `OrderBook`: resting orders as an accessible table, each level with a bar sized by the total behind it; changed levels highlight briefly.
|
|
48
|
+
- `PortfolioChartCard`: what an account is worth next to what went into it, with how far it sits below its best value in a linked pane.
|
|
49
|
+
- `Candles`, a primitive that draws any number of candles into a handful of paths.
|
|
50
|
+
- Helpers: `sma`, `ema`, `rsi` (Wilder), `macd`, `bollinger`, `drawdown`, `rebase`, and `depthLevels`.
|
|
51
|
+
- `--lilt-candle-up` and `--lilt-candle-down` tokens, following `--lilt-positive` and `--lilt-negative`.
|
|
52
|
+
|
|
53
|
+
### Charts
|
|
54
|
+
|
|
55
|
+
- `RangeChartCard`: floating bars from low to high, or error bars around a value.
|
|
56
|
+
- `BoxPlotCard`: quartiles, a median, Tukey whiskers, and outliers computed from raw samples. `summarizeBox` returns the same statistics.
|
|
57
|
+
- Primitives: `RangeBar`, `ErrorBar`, and `BoxPlot`.
|
|
58
|
+
- `series.colorAt(row)` colors one bar, candle, range, or point, such as a falling day in red.
|
|
59
|
+
- `y.scale: 'log'` spaces values by ratio, with ticks on 1, 2, and 5.
|
|
60
|
+
- `ReferenceBand axis="y"` shades a range of values, such as RSI 30–70.
|
|
61
|
+
- `ChartPlot slots` gives every observation a bar-width slot, so a line pane lines up with bars or candles beside it.
|
|
62
|
+
|
|
63
|
+
### Fixes
|
|
64
|
+
|
|
65
|
+
- A card `valueFormat` with more than one minimum fraction digit, such as two-decimal prices, no longer throws.
|
|
66
|
+
|
|
67
|
+
## 0.1.0
|
|
68
|
+
|
|
69
|
+
The first release: Area, Line, Bar, Combo, Horizontal bar, Stat, Radial, Progress, Activity ring, Funnel, Heatmap, Scatter, Sankey, Radar, and Slope cards, with linked hover, targets, forecasts, and range comparison.
|
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
<p align="center">
|
|
15
15
|
<a href="https://liltui.vercel.app"><strong>Documentation</strong></a> ·
|
|
16
16
|
<a href="https://liltui.vercel.app/charts/area">Charts</a> ·
|
|
17
|
+
<a href="https://liltui.vercel.app/finance/candlestick">Finance</a> ·
|
|
18
|
+
<a href="https://liltui.vercel.app/watch">Watch</a> ·
|
|
17
19
|
<a href="https://liltui.vercel.app/llms.txt">llms.txt</a>
|
|
18
20
|
</p>
|
|
19
21
|
|
|
@@ -82,6 +84,107 @@ That's the whole card: the headline, the change chip, value tiles, and hover all
|
|
|
82
84
|
| **Rankings** | Horizontal bar, with shares and an "Other" row |
|
|
83
85
|
| **Flows and change** | Funnel, Sankey, and Slope |
|
|
84
86
|
| **Patterns** | Heatmap, Scatter, and Radar |
|
|
87
|
+
| **Spread** | Range (floating or error bars) and Box plot, from raw samples |
|
|
88
|
+
| **Markets** | Candlestick, Indicators, Price, Depth, Order book, and Portfolio |
|
|
89
|
+
|
|
90
|
+
## Finance charts
|
|
91
|
+
|
|
92
|
+
Candlesticks, indicators, and order books ship from their own entry point, so apps that never draw a candle never load one:
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
'use client';
|
|
96
|
+
|
|
97
|
+
import { CandlestickChartCard } from '@lilt-ui/charts/finance';
|
|
98
|
+
|
|
99
|
+
const candles = [
|
|
100
|
+
{
|
|
101
|
+
date: new Date('2026-08-20'),
|
|
102
|
+
open: 243.1,
|
|
103
|
+
high: 246.4,
|
|
104
|
+
low: 241.8,
|
|
105
|
+
close: 245.2,
|
|
106
|
+
volume: 2410000,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
date: new Date('2026-08-21'),
|
|
110
|
+
open: 245.2,
|
|
111
|
+
high: 247.0,
|
|
112
|
+
low: 243.9,
|
|
113
|
+
close: 244.6,
|
|
114
|
+
volume: 1980000,
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
export function MarketCard() {
|
|
119
|
+
return (
|
|
120
|
+
<CandlestickChartCard
|
|
121
|
+
title="SOL · USDC"
|
|
122
|
+
data={candles}
|
|
123
|
+
x="date"
|
|
124
|
+
open="open"
|
|
125
|
+
high="high"
|
|
126
|
+
low="low"
|
|
127
|
+
close="close"
|
|
128
|
+
volume="volume"
|
|
129
|
+
valueFormat={{ style: 'currency', currency: 'USD' }}
|
|
130
|
+
/>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
| Card | What it shows |
|
|
136
|
+
| ---------------------- | ------------------------------------------------------------------------------ |
|
|
137
|
+
| `CandlestickChartCard` | Candles, hollow candles, OHLC bars, or a price area, with a linked volume pane |
|
|
138
|
+
| `IndicatorChartCard` | Moving averages or Bollinger bands over the price; RSI, MACD, or volume panes |
|
|
139
|
+
| `PriceChartCard` | A ticker with its latest price and change, or several instruments rebased |
|
|
140
|
+
| `DepthChartCard` | How much could trade at each price, buying and selling, and the gap between |
|
|
141
|
+
| `OrderBook` | Resting orders level by level, the best prices meeting in the middle |
|
|
142
|
+
| `PortfolioChartCard` | What an account is worth next to what went in, and its fall from its best |
|
|
143
|
+
|
|
144
|
+
Candles draw into a handful of paths however many there are, so years of daily history stay fast. `scale="log"` spaces long histories by ratio, and `live` follows new rows as they arrive. The indicator math (`sma`, `ema`, `rsi`, `macd`, `bollinger`, `drawdown`, `depthLevels`) is exported too, for your own layouts.
|
|
145
|
+
|
|
146
|
+
## Watch
|
|
147
|
+
|
|
148
|
+
Tell a chart what normal looks like, and it says when something isn't:
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
<LineChartCard
|
|
152
|
+
title="Orders"
|
|
153
|
+
data={lastTwoDays}
|
|
154
|
+
x="hour"
|
|
155
|
+
series={[{ key: 'orders' }]}
|
|
156
|
+
normal={{
|
|
157
|
+
history: earlierWeeks, // learn from weeks you don't draw
|
|
158
|
+
learn: 'week', // same hour, same weekday
|
|
159
|
+
limits: { min: 10 }, // a line you set
|
|
160
|
+
when: (hour) => !hour.maintenance, // never judge downtime
|
|
161
|
+
breakdown: (hour) => hour.devices, // say which device caused it
|
|
162
|
+
}}
|
|
163
|
+
/>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The card draws a calm band where the series should be, recolors unusual stretches, and says "Unusually low since Tue 2 PM". Hover an unusual reading and it says why: "98% of the drop is Mobile Safari". Every layer is off until you set it.
|
|
167
|
+
|
|
168
|
+
Wrap cards in a board and the dashboard arranges itself around what needs attention:
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
import { BoardItem, ChartBoard } from '@lilt-ui/charts/watch';
|
|
172
|
+
|
|
173
|
+
<ChartBoard aria-label="Store health" toast>
|
|
174
|
+
<BoardItem id="orders" title="Orders">
|
|
175
|
+
<AreaChartCard
|
|
176
|
+
title="Orders"
|
|
177
|
+
data={rows}
|
|
178
|
+
x="hour"
|
|
179
|
+
series={[{ key: 'orders' }]}
|
|
180
|
+
normal={{ learn: 'week' }}
|
|
181
|
+
/>
|
|
182
|
+
</BoardItem>
|
|
183
|
+
{/* …more cards */}
|
|
184
|
+
</ChartBoard>;
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Cards that need attention rise, what you've seen calms down, and what clears settles back. Nothing moves while you're reading, and a toast finds you when something breaks off screen.
|
|
85
188
|
|
|
86
189
|
## Built in, one prop away
|
|
87
190
|
|
|
@@ -89,6 +192,7 @@ That's the whole card: the headline, the change chip, value tiles, and hover all
|
|
|
89
192
|
- **Targets and forecasts.** `target` draws a goal line and counts the points that reach it. `forecast` marks projected rows and keeps them out of the headline.
|
|
90
193
|
- **Pin and compare.** Click to pin a point. Add `compare` and drag across a chart to see what changed.
|
|
91
194
|
- **Honest data.** `null` is a gap, never a zero, and missing values say so.
|
|
195
|
+
- **Watch.** `normal` flags what's unusual and says why; a `ChartBoard` raises what needs attention.
|
|
92
196
|
- **Calm by default.** Keyboard navigation, screen reader labels, and motion that respects reduced-motion settings.
|
|
93
197
|
|
|
94
198
|
## Fits your design system
|
|
@@ -102,6 +206,22 @@ Every Lilt style lives in the `lilt` cascade layer, so your CSS wins without spe
|
|
|
102
206
|
@import 'tailwindcss';
|
|
103
207
|
```
|
|
104
208
|
|
|
209
|
+
## Upgrading from 0.1
|
|
210
|
+
|
|
211
|
+
0.2 replaces a series' `interval` with named `fields`, which carry any companion values: a forecast's low and high, quartiles, or a candle's open, high, and low. `IntervalBand` now names the fields it reads:
|
|
212
|
+
|
|
213
|
+
```tsx
|
|
214
|
+
// 0.1
|
|
215
|
+
{ id: 'revenue', accessor: (row) => row.revenue, interval: { lower: (row) => row.low, upper: (row) => row.high, meaning: 'Forecast' } }
|
|
216
|
+
<IntervalBand series="revenue" />
|
|
217
|
+
|
|
218
|
+
// 0.2
|
|
219
|
+
{ id: 'revenue', accessor: (row) => row.revenue, fields: { low: { label: 'Low', accessor: (row) => row.low }, high: { label: 'High', accessor: (row) => row.high } } }
|
|
220
|
+
<IntervalBand series="revenue" lower="low" upper="high" />
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Cards are unchanged: `forecast={{ from, lower, upper }}` works as before. Tooltip content reads `series[].fields` instead of `series[].interval`.
|
|
224
|
+
|
|
105
225
|
## License
|
|
106
226
|
|
|
107
227
|
MIT © Lilt UI
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
//#region src/engine/ranking.ts
|
|
2
|
+
function e(e, t, n) {
|
|
3
|
+
let r = /* @__PURE__ */ new Set();
|
|
4
|
+
return e.map((e, i) => {
|
|
5
|
+
let a = t.id(e), o = t.label(e), s = n(e);
|
|
6
|
+
if (typeof a != "string" || !a.trim()) throw Error(`Invalid category at row ${i + 1}: a non-empty string ID is required.`);
|
|
7
|
+
if (r.has(a)) throw Error(`Duplicate category ID "${a}".`);
|
|
8
|
+
if (typeof o != "string" || !o.trim()) throw Error(`Invalid label for category "${a}".`);
|
|
9
|
+
if (s !== null && (typeof s != "number" || !Number.isFinite(s))) throw Error(`Invalid value for category "${a}": expected a finite number or null.`);
|
|
10
|
+
return r.add(a), {
|
|
11
|
+
id: a,
|
|
12
|
+
label: o,
|
|
13
|
+
datum: e,
|
|
14
|
+
value: s
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function t(e, t) {
|
|
19
|
+
return t === "input" ? [...e] : [...e].sort((e, n) => {
|
|
20
|
+
if (e.value === null && n.value !== null) return 1;
|
|
21
|
+
if (n.value === null && e.value !== null) return -1;
|
|
22
|
+
let r = (e.value ?? 0) - (n.value ?? 0);
|
|
23
|
+
return r ? r * (t === "descending" ? -1 : 1) : e.id < n.id ? -1 : +(e.id > n.id);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function n(e) {
|
|
27
|
+
let t = 0, n = 0;
|
|
28
|
+
for (let r of e) r.value !== null && (t = Math.min(t, r.value), n = Math.max(n, r.value));
|
|
29
|
+
return t === n ? [0, 1] : [t, n];
|
|
30
|
+
}
|
|
31
|
+
function r(e, t) {
|
|
32
|
+
let n = (e) => (e - t[0]) / (t[1] - t[0]) * 100, r = n(0), i = e === null ? r : n(e);
|
|
33
|
+
return {
|
|
34
|
+
zero: r,
|
|
35
|
+
left: Math.min(r, i),
|
|
36
|
+
width: Math.abs(i - r)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/engine/analysis.ts
|
|
41
|
+
function i(e, t) {
|
|
42
|
+
if (e !== null && (!Number.isFinite(e) || e < 0)) throw Error(`${t} must be a non-negative finite number or null.`);
|
|
43
|
+
}
|
|
44
|
+
function a(t) {
|
|
45
|
+
let n = e(t.data, t.category, t.value), r = Infinity;
|
|
46
|
+
for (let e of n) if (i(e.value, `Stage ${e.id}`), e.value !== null) {
|
|
47
|
+
if (e.value > r) throw Error(`Stage ${e.id} exceeds a preceding stage; cumulative funnel counts must not increase.`);
|
|
48
|
+
r = e.value;
|
|
49
|
+
}
|
|
50
|
+
let a = n[0]?.value;
|
|
51
|
+
return n.map((e, t) => {
|
|
52
|
+
let r = n[t - 1]?.value;
|
|
53
|
+
return {
|
|
54
|
+
...e,
|
|
55
|
+
share: e.value !== null && a != null && a > 0 ? e.value / a : null,
|
|
56
|
+
conversion: e.value !== null && r != null && r > 0 ? e.value / r : null,
|
|
57
|
+
drop: e.value !== null && r != null ? r - e.value : null
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
export { n as a, r as i, e as n, t as r, a as t };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReactElement } from 'react';
|
|
2
|
-
import { CartesianCardProps } from './cartesian-card';
|
|
2
|
+
import { CardNormal, CartesianCardProps } from './cartesian-card';
|
|
3
3
|
import { NumericKey } from './keys';
|
|
4
|
+
import { NormalReport } from '../watch/board/board-state';
|
|
4
5
|
export type { CardRange, CardSeries } from './cartesian-card';
|
|
5
6
|
export interface AreaChartCardProps<Row, Key extends NumericKey<Row>> extends CartesianCardProps<Row, Key> {
|
|
6
7
|
/**
|
|
@@ -11,6 +12,14 @@ export interface AreaChartCardProps<Row, Key extends NumericKey<Row>> extends Ca
|
|
|
11
12
|
stack?: boolean | 'percent';
|
|
12
13
|
/** `smooth` uses a monotone curve; `linear` draws straight segments. */
|
|
13
14
|
curve?: 'smooth' | 'linear';
|
|
15
|
+
/**
|
|
16
|
+
* Watch one series against what is normal for it: a calm band where it is expected, a ring on
|
|
17
|
+
* each unusual reading, and a header note while it stays unusual. Every layer is off unless
|
|
18
|
+
* set. Unstacked cards only.
|
|
19
|
+
*/
|
|
20
|
+
normal?: CardNormal<Row, NoInfer<Key>>;
|
|
21
|
+
/** Called when the watched series' status changes, such as when an incident starts or clears. */
|
|
22
|
+
onNormalChange?: (report: NormalReport | null) => void;
|
|
14
23
|
}
|
|
15
24
|
/**
|
|
16
25
|
* A complete area chart card: headline, delta, period, plot, and value tiles.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
import { BoxSummary } from '../engine/box';
|
|
3
|
+
import { CartesianCardProps } from './cartesian-card';
|
|
4
|
+
import { KeysOfType } from './keys';
|
|
5
|
+
/** One box, with its x value and the row it came from. */
|
|
6
|
+
interface BoxRow<Row> {
|
|
7
|
+
x: string | number | Date;
|
|
8
|
+
median: number | null;
|
|
9
|
+
box: BoxSummary | null;
|
|
10
|
+
source: Row;
|
|
11
|
+
}
|
|
12
|
+
type SharedProps<Row> = Omit<CartesianCardProps<BoxRow<Row>, 'median'>, 'series' | 'data' | 'ranges' | 'x' | 'headlineSeries' | 'pillSeries' | 'target' | 'forecast' | 'bleed' | 'tiles' | 'aggregate'>;
|
|
13
|
+
export interface BoxPlotCardProps<Row> extends SharedProps<Row> {
|
|
14
|
+
data?: readonly Row[];
|
|
15
|
+
/** One box per row, placed by this field: labels, numbers, or dates. */
|
|
16
|
+
x: KeysOfType<Row, string | number | Date>;
|
|
17
|
+
/** Field holding each row's raw samples; Lilt computes the quartiles, whiskers and outliers. */
|
|
18
|
+
samples: KeysOfType<Row, readonly number[]>;
|
|
19
|
+
/** Names the series in hover. Defaults to "Median". */
|
|
20
|
+
label?: string;
|
|
21
|
+
color?: string;
|
|
22
|
+
/** Draw samples beyond the whiskers as dots. Defaults to true. */
|
|
23
|
+
outliers?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Distributions side by side: quartile boxes, a median line, Tukey whiskers and outliers,
|
|
27
|
+
* computed from each row's raw samples. The headline is the median of every sample.
|
|
28
|
+
*/
|
|
29
|
+
export declare function BoxPlotCard<Row>({ data, x, samples, label, color, outliers, headline, ...props }: BoxPlotCardProps<Row>): ReactElement;
|
|
30
|
+
export {};
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { CSSProperties, ReactElement, ReactNode } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { ChartController, ChartMargins, ChartTooltipContext, NumericXConfig, TimeXConfig, ChartAxis, ChartBarAppearance, ChartBackground, ChartCurve, ChartMotion, ChartPalette, ChartHoverReadout, ChartHoverStyle, ChartSeries, ChartTooltipIndicator, ChartStyle, ChartSurface } from '../types';
|
|
3
3
|
import { NumericKey, ResolvedX, XKey, XKind } from './keys';
|
|
4
4
|
import { CardAggregate } from './format';
|
|
5
5
|
import { AnimatedNumberVariant } from '../motion/animated-number';
|
|
6
|
+
import { NormalSpec } from '../watch/engine/normal';
|
|
7
|
+
import { NormalBreakdown } from '../watch/engine/explain';
|
|
8
|
+
import { NormalReport } from '../watch/board/board-state';
|
|
6
9
|
export interface CardSeries<Key extends string> {
|
|
7
10
|
/** A numeric field of each row. */
|
|
8
11
|
key: Key;
|
|
@@ -31,6 +34,24 @@ export interface CardForecast<Row> {
|
|
|
31
34
|
/** Names the projected region and the hover caption. Defaults to "Forecast". */
|
|
32
35
|
label?: string;
|
|
33
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* What normal looks like for one series of a line or unstacked area card. Every layer is off
|
|
39
|
+
* unless set; see `NormalSpec`. Projected rows are never judged.
|
|
40
|
+
*/
|
|
41
|
+
export interface CardNormal<Row, Key extends string = string> extends NormalSpec<Row> {
|
|
42
|
+
/** The watched series. Defaults to the headline series, then the first solid series. */
|
|
43
|
+
series?: Key;
|
|
44
|
+
/**
|
|
45
|
+
* Earlier rows to learn from without drawing them, such as the weeks before the day on screen.
|
|
46
|
+
* They come before `data`, in the same shape.
|
|
47
|
+
*/
|
|
48
|
+
history?: readonly Row[];
|
|
49
|
+
/**
|
|
50
|
+
* Split a reading into the segments that add up to it, such as orders by device, so an unusual
|
|
51
|
+
* reading can say which segment caused it: "91% of the drop is Mobile Safari".
|
|
52
|
+
*/
|
|
53
|
+
breakdown?: NormalBreakdown<Row>;
|
|
54
|
+
}
|
|
34
55
|
export interface CardRange<Row> {
|
|
35
56
|
id: string;
|
|
36
57
|
label: string;
|
|
@@ -76,8 +97,11 @@ export interface CartesianCardProps<Row, Key extends NumericKey<Row>> {
|
|
|
76
97
|
formatX?: (value: string | number | Date) => string;
|
|
77
98
|
/** Override x inference. Numbers default to `number`; use `time` for timestamps. */
|
|
78
99
|
xType?: XKind;
|
|
79
|
-
/**
|
|
80
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Axis preset: `minimal` (default), `dots`, `inline`, `ruler`, `classic`, or `segmented`, for
|
|
102
|
+
* both axes or one per axis, e.g. `{ x: 'minimal', y: { style: 'segmented', gradient: true } }`.
|
|
103
|
+
*/
|
|
104
|
+
axis?: ChartAxis;
|
|
81
105
|
/** Texture behind the plot: `dots` (default), `grid`, `lines`, or `none`. */
|
|
82
106
|
background?: ChartBackground;
|
|
83
107
|
/** Brighten the background around the hovered point. Defaults to true. */
|
|
@@ -143,7 +167,8 @@ export interface CartesianCardProps<Row, Key extends NumericKey<Row>> {
|
|
|
143
167
|
/**
|
|
144
168
|
* Link hover and pins with every card that uses the same name. Cards match by x: labels
|
|
145
169
|
* exactly, dates and numbers to the nearest row. Every linked card shows its hover marks,
|
|
146
|
-
* pills, and headline at that point
|
|
170
|
+
* pills, and headline at that point. A click pins every linked card: the clicked card shows the
|
|
171
|
+
* pin and the others a ghost of it, and any of them releases it. Alt-click pins one card only.
|
|
147
172
|
*/
|
|
148
173
|
sync?: string;
|
|
149
174
|
loading?: boolean;
|
|
@@ -191,16 +216,71 @@ export type CardShape = {
|
|
|
191
216
|
/** Curve and point markers of lines drawn over the bars. */
|
|
192
217
|
curve?: ChartCurve;
|
|
193
218
|
points?: boolean;
|
|
194
|
-
};
|
|
219
|
+
} | CardMarks;
|
|
220
|
+
/**
|
|
221
|
+
* Column marks drawn from each series' fields, such as candles or range bars. Cards built on
|
|
222
|
+
* this shape keep every Cartesian card behaviour: headline, ranges, hover sync and tiles.
|
|
223
|
+
*/
|
|
224
|
+
export interface CardMarks {
|
|
225
|
+
kind: 'marks';
|
|
226
|
+
/** A stable description of everything below, so memoized series only rebuild when it changes. */
|
|
227
|
+
id: string;
|
|
228
|
+
/** Descriptor options for a series by key, such as its fields, colors, curve, or area. */
|
|
229
|
+
series?: (key: string) => Partial<ChartSeries<never>> | undefined;
|
|
230
|
+
/**
|
|
231
|
+
* The marks for the drawn series, as elements or fragments of them (not wrapped in a
|
|
232
|
+
* component), so the plot can find column marks. Dashed references still draw as lines.
|
|
233
|
+
*/
|
|
234
|
+
draw: (ids: readonly string[]) => ReactNode;
|
|
235
|
+
includeZero: boolean;
|
|
236
|
+
scale?: 'linear' | 'log';
|
|
237
|
+
curve?: ChartCurve;
|
|
238
|
+
/** Values the y axis must reach, such as outliers beyond the fields. */
|
|
239
|
+
include?: readonly number[];
|
|
240
|
+
/** Slot every observation like a bar, so linked bar panes line up. Column marks imply it. */
|
|
241
|
+
slots?: boolean;
|
|
242
|
+
/** Label the x axis here. Turn off when a pane below carries it. Defaults to true. */
|
|
243
|
+
xAxis?: boolean;
|
|
244
|
+
/** Extra text after the hovered x in the headline caption, e.g. open, high and low. */
|
|
245
|
+
describe?: (inspection: ChartTooltipContext) => string | null;
|
|
246
|
+
}
|
|
247
|
+
/** Panes drawn under the main plot, linked to it: same data, x, margins and crosshair. */
|
|
248
|
+
export interface CardPaneContext<Row> {
|
|
249
|
+
data: readonly Row[];
|
|
250
|
+
x: TimeXConfig<Row> | NumericXConfig<Row>;
|
|
251
|
+
controller: ChartController;
|
|
252
|
+
margins: ChartMargins;
|
|
253
|
+
/** The main plot's `axisInset` and `slots`, so a pane's observations sit under its own. */
|
|
254
|
+
axisInset: number;
|
|
255
|
+
slots: boolean;
|
|
256
|
+
axis: ChartAxis;
|
|
257
|
+
loading: boolean;
|
|
258
|
+
motion?: ChartMotion;
|
|
259
|
+
hoverStyle: ChartHoverStyle;
|
|
260
|
+
formatValue: (value: number) => string;
|
|
261
|
+
formatAxis: (value: number) => string;
|
|
262
|
+
}
|
|
195
263
|
/** Matches the default `--lilt-card-padding`. */
|
|
196
264
|
export declare const CARD_PADDING = 20;
|
|
197
265
|
/**
|
|
198
266
|
* The shared card body: headline, delta, period, plot, and value tiles, with hover sync and
|
|
199
267
|
* range comparison. Area, Line, and Bar cards differ only in their `shape`.
|
|
200
268
|
*/
|
|
201
|
-
export declare function CartesianCard<Row, Key extends NumericKey<Row>>({ shape, title, data: suppliedData, x, series: seriesInput, headline: suppliedHeadline, aggregate: suppliedAggregate, headlineSeries, delta: suppliedDelta, deltaTone, range, ranges, defaultRange, tiles, height, valueFormat, locale, formatValue: suppliedFormatValue, formatAxisValue: suppliedFormatAxisValue, formatX, xType, compare, target: suppliedTarget, forecast, sync, axis, hover, hoverStyle, background, spotlight, surface, badge, numberStyle, palette, bleed, axisInset, pillSeries, pillValue, pillPosition, tooltipIndicator, loading, motion, className, style, }: Omit<CartesianCardProps<Row, Key>, 'series'> & {
|
|
269
|
+
export declare function CartesianCard<Row, Key extends NumericKey<Row>>({ shape, title, data: suppliedData, x, series: seriesInput, headline: suppliedHeadline, aggregate: suppliedAggregate, headlineSeries, delta: suppliedDelta, deltaTone, range, ranges, defaultRange, tiles, height, valueFormat, locale, formatValue: suppliedFormatValue, formatAxisValue: suppliedFormatAxisValue, formatX, xType, compare, target: suppliedTarget, forecast, sync, axis, hover, hoverStyle, background, spotlight, surface, badge, numberStyle, palette, bleed, axisInset, pillSeries, pillValue, pillPosition, tooltipIndicator, loading, motion, className, style, panes, live, heading, restCaption, normal, onNormalChange, }: Omit<CartesianCardProps<Row, Key>, 'series'> & {
|
|
202
270
|
series: readonly CoreSeries<Key>[];
|
|
203
271
|
shape: CardShape;
|
|
272
|
+
/** Linked panes under the plot, such as volume or indicators. */
|
|
273
|
+
panes?: (context: CardPaneContext<Row>) => ReactNode;
|
|
274
|
+
/** Follow the newest observation as rows arrive. */
|
|
275
|
+
live?: boolean;
|
|
276
|
+
/** Richer title content, such as a ticker and its name; `title` still labels the chart. */
|
|
277
|
+
heading?: ReactNode;
|
|
278
|
+
/** Text beside the delta at rest, such as an absolute change, from the active data. */
|
|
279
|
+
restCaption?: (data: readonly Row[]) => ReactNode;
|
|
280
|
+
/** Watch one series against its normal. Line and unstacked area cards only. */
|
|
281
|
+
normal?: CardNormal<Row, NoInfer<Key>>;
|
|
282
|
+
/** Called when the watched series' status changes. */
|
|
283
|
+
onNormalChange?: (report: NormalReport | null) => void;
|
|
204
284
|
}): ReactElement;
|
|
205
285
|
/** The plot position where projections begin: a label's row, or the date or number itself. */
|
|
206
286
|
export declare function forecastPosition<Row>(data: readonly Row[], x: string, resolved: ResolvedX<Row>, from: string | number): number | null;
|
|
@@ -20,8 +20,19 @@ export interface ChartCardProps {
|
|
|
20
20
|
style?: CSSProperties | ChartStyle;
|
|
21
21
|
'aria-label'?: string;
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* What a container such as a watch board adds to the card directly inside it: a tab that takes
|
|
25
|
+
* the badge's place, a tone for the card's edge, and a control that floats on the top edge.
|
|
26
|
+
*/
|
|
27
|
+
export interface CardFrame {
|
|
28
|
+
badge?: ReactNode;
|
|
29
|
+
tone?: 'warning' | 'critical' | null;
|
|
30
|
+
handle?: ReactNode;
|
|
31
|
+
}
|
|
32
|
+
/** Set by a container around one card. Cards nested inside that card do not see it. */
|
|
33
|
+
export declare const CardFrameContext: import('react').Context<CardFrame | null>;
|
|
23
34
|
/** The card surface every Lilt card shares: padding, radius, border, and theme tokens. */
|
|
24
|
-
export declare function ChartCard({ children, surface, palette, motion, numberStyle, badge, className, style, 'aria-label': ariaLabel, }: ChartCardProps): ReactElement;
|
|
35
|
+
export declare function ChartCard({ children, surface, palette, motion, numberStyle, badge: ownBadge, className, style, 'aria-label': ariaLabel, }: ChartCardProps): ReactElement;
|
|
25
36
|
export interface CardBadgeProps {
|
|
26
37
|
children?: ReactNode;
|
|
27
38
|
className?: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReactElement } from 'react';
|
|
2
|
-
import { CartesianCardProps } from './cartesian-card';
|
|
2
|
+
import { CardNormal, CartesianCardProps } from './cartesian-card';
|
|
3
3
|
import { NumericKey } from './keys';
|
|
4
|
+
import { NormalReport } from '../watch/board/board-state';
|
|
4
5
|
export interface LineChartCardProps<Row, Key extends NumericKey<Row>> extends CartesianCardProps<Row, Key> {
|
|
5
6
|
/** `smooth` uses a monotone curve, `linear` straight segments, `step` holds each value flat. */
|
|
6
7
|
curve?: 'smooth' | 'linear' | 'step';
|
|
@@ -11,6 +12,14 @@ export interface LineChartCardProps<Row, Key extends NumericKey<Row>> extends Ca
|
|
|
11
12
|
* zero when the absolute level matters.
|
|
12
13
|
*/
|
|
13
14
|
baseline?: 'auto' | 'zero';
|
|
15
|
+
/**
|
|
16
|
+
* Watch one series against what is normal for it: a calm band where it is expected, a ring on
|
|
17
|
+
* each unusual reading, and a header note while it stays unusual. Every layer is off unless
|
|
18
|
+
* set. Unstacked cards only.
|
|
19
|
+
*/
|
|
20
|
+
normal?: CardNormal<Row, NoInfer<Key>>;
|
|
21
|
+
/** Called when the watched series' status changes, such as when an incident starts or clears. */
|
|
22
|
+
onNormalChange?: (report: NormalReport | null) => void;
|
|
14
23
|
}
|
|
15
24
|
/**
|
|
16
25
|
* A complete line chart card: headline, delta, period, plot, and value tiles.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
import { CartesianCardProps } from './cartesian-card';
|
|
3
|
+
import { NumericKey } from './keys';
|
|
4
|
+
type SharedProps<Row, Key extends NumericKey<Row>> = Omit<CartesianCardProps<Row, Key>, 'series' | 'headlineSeries' | 'pillSeries' | 'target' | 'forecast' | 'bleed' | 'tiles'>;
|
|
5
|
+
export interface RangeChartCardProps<Row, Key extends NumericKey<Row>> extends SharedProps<Row, Key> {
|
|
6
|
+
/** Field holding the low end of each range. */
|
|
7
|
+
low: Key;
|
|
8
|
+
/** Field holding the high end of each range. */
|
|
9
|
+
high: Key;
|
|
10
|
+
/**
|
|
11
|
+
* Field holding each measured value, such as a mean inside its uncertainty. It drives the
|
|
12
|
+
* headline and pills. Defaults to `high`.
|
|
13
|
+
*/
|
|
14
|
+
value?: Key;
|
|
15
|
+
/** Names the series in hover and tiles. Defaults to the value key. */
|
|
16
|
+
label?: string;
|
|
17
|
+
color?: string;
|
|
18
|
+
/** `bar` (default) floats a bar from low to high; `error` draws whiskers around the value. */
|
|
19
|
+
display?: 'bar' | 'error';
|
|
20
|
+
/** Per-series stat tile under the plot. Defaults to false. */
|
|
21
|
+
tiles?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Ranges per observation: floating bars from low to high, or error bars around a value. The
|
|
25
|
+
* headline summarizes the values (the mean by default) and hover reads the range's ends.
|
|
26
|
+
*/
|
|
27
|
+
export declare function RangeChartCard<Row, const Key extends NumericKey<Row>>({ low, high, value, label, color, display, aggregate, tiles, ...props }: RangeChartCardProps<Row, Key>): ReactElement;
|
|
28
|
+
export {};
|