@lilt-ui/charts 0.2.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +45 -0
  3. package/dist/cards/area-chart-card.d.ts +10 -1
  4. package/dist/cards/cartesian-card.d.ts +35 -6
  5. package/dist/cards/chart-card.d.ts +12 -1
  6. package/dist/cards/line-chart-card.d.ts +10 -1
  7. package/dist/{cartesian-card-BDZs0v6E.js → cartesian-card-Dcd0ZnQL.js} +2745 -2976
  8. package/dist/chart.d.ts +1 -1
  9. package/dist/finance/cards/depth-chart-card.d.ts +2 -2
  10. package/dist/finance.js +220 -219
  11. package/dist/index.d.ts +5 -4
  12. package/dist/index.js +1635 -1631
  13. package/dist/interaction/axis-cursor.d.ts +12 -2
  14. package/dist/interaction/chart-sync.d.ts +11 -1
  15. package/dist/interaction/inspection-layer.d.ts +6 -0
  16. package/dist/interaction/pin-glyphs.d.ts +8 -0
  17. package/dist/interaction/pin-marker.d.ts +9 -1
  18. package/dist/model/cartesian-model.d.ts +3 -3
  19. package/dist/primitives/axis-segments.d.ts +23 -0
  20. package/dist/report-context-Cz0AYdV2.js +895 -0
  21. package/dist/styles.css +1 -1
  22. package/dist/types.d.ts +34 -3
  23. package/dist/watch/board/board-state.d.ts +74 -0
  24. package/dist/watch/board/board-toast.d.ts +19 -0
  25. package/dist/watch/board/chart-board.d.ts +97 -0
  26. package/dist/watch/board/report-context.d.ts +3 -0
  27. package/dist/watch/engine/describe.d.ts +9 -0
  28. package/dist/watch/engine/explain.d.ts +48 -0
  29. package/dist/watch/engine/normal.d.ts +114 -0
  30. package/dist/watch/engine/robust.d.ts +7 -0
  31. package/dist/watch/engine/rules.d.ts +22 -0
  32. package/dist/watch/engine/status.d.ts +39 -0
  33. package/dist/watch/index.d.ts +14 -0
  34. package/dist/watch/normal-layer.d.ts +18 -0
  35. package/dist/watch.d.ts +2 -0
  36. package/dist/watch.js +628 -0
  37. package/package.json +6 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
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
+
3
29
  ## 0.2.0
4
30
 
5
31
  Finance charts, range charts, and series that carry more than one value.
package/README.md CHANGED
@@ -15,6 +15,7 @@
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
17
  <a href="https://liltui.vercel.app/finance/candlestick">Finance</a> ·
18
+ <a href="https://liltui.vercel.app/watch">Watch</a> ·
18
19
  <a href="https://liltui.vercel.app/llms.txt">llms.txt</a>
19
20
  </p>
20
21
 
@@ -142,12 +143,56 @@ export function MarketCard() {
142
143
 
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.
144
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.
188
+
145
189
  ## Built in, one prop away
146
190
 
147
191
  - **Linked hover.** Give cards the same `sync` name and they read the same moment together.
148
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.
149
193
  - **Pin and compare.** Click to pin a point. Add `compare` and drag across a chart to see what changed.
150
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.
151
196
  - **Calm by default.** Keyboard navigation, screen reader labels, and motion that respects reduced-motion settings.
152
197
 
153
198
  ## Fits your design system
@@ -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.
@@ -1,8 +1,11 @@
1
1
  import { CSSProperties, ReactElement, ReactNode } from 'react';
2
- import { ChartController, ChartMargins, ChartTooltipContext, NumericXConfig, TimeXConfig, ChartAxisStyle, ChartBarAppearance, ChartBackground, ChartCurve, ChartMotion, ChartPalette, ChartHoverReadout, ChartHoverStyle, ChartSeries, ChartTooltipIndicator, ChartStyle, ChartSurface } from '../types';
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
- /** Axis preset: `minimal` (default), `dots`, `inline`, `ruler`, or `classic`. */
80
- axis?: ChartAxisStyle;
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; only the card that owns a pin shows its release control.
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;
@@ -228,7 +253,7 @@ export interface CardPaneContext<Row> {
228
253
  /** The main plot's `axisInset` and `slots`, so a pane's observations sit under its own. */
229
254
  axisInset: number;
230
255
  slots: boolean;
231
- axis: ChartAxisStyle;
256
+ axis: ChartAxis;
232
257
  loading: boolean;
233
258
  motion?: ChartMotion;
234
259
  hoverStyle: ChartHoverStyle;
@@ -241,7 +266,7 @@ export declare const CARD_PADDING = 20;
241
266
  * The shared card body: headline, delta, period, plot, and value tiles, with hover sync and
242
267
  * range comparison. Area, Line, and Bar cards differ only in their `shape`.
243
268
  */
244
- 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, }: 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'> & {
245
270
  series: readonly CoreSeries<Key>[];
246
271
  shape: CardShape;
247
272
  /** Linked panes under the plot, such as volume or indicators. */
@@ -252,6 +277,10 @@ export declare function CartesianCard<Row, Key extends NumericKey<Row>>({ shape,
252
277
  heading?: ReactNode;
253
278
  /** Text beside the delta at rest, such as an absolute change, from the active data. */
254
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;
255
284
  }): ReactElement;
256
285
  /** The plot position where projections begin: a label's row, or the date or number itself. */
257
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.