@pond-ts/charts 0.31.2 → 0.32.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 +39 -1
- package/dist/AreaChart.d.ts +11 -5
- package/dist/AreaChart.js +23 -10
- package/dist/BandChart.d.ts +12 -5
- package/dist/BandChart.js +28 -10
- package/dist/BarChart.d.ts +17 -11
- package/dist/BarChart.js +12 -6
- package/dist/ChartContainer.d.ts +45 -2
- package/dist/ChartContainer.js +42 -1
- package/dist/ChartRow.js +16 -0
- package/dist/Layers.js +236 -96
- package/dist/YAxis.d.ts +16 -1
- package/dist/YAxis.js +22 -6
- package/dist/annotations.d.ts +110 -0
- package/dist/annotations.js +463 -0
- package/dist/chip.d.ts +23 -0
- package/dist/chip.js +43 -0
- package/dist/context.d.ts +116 -0
- package/dist/data.d.ts +34 -0
- package/dist/data.js +89 -14
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/theme.d.ts +18 -0
- package/dist/theme.js +13 -0
- package/package.json +3 -3
package/dist/context.d.ts
CHANGED
|
@@ -75,6 +75,13 @@ export interface ContainerFrame {
|
|
|
75
75
|
/** Show the cursor's time atop the in-chart readout (when a row's cursor draws
|
|
76
76
|
* one), formatted by {@link formatTime} to match the time axis. */
|
|
77
77
|
readonly cursorTime: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Whether the chart is in **annotation-edit mode** — suppresses the data cursor
|
|
80
|
+
* and makes editable annotations interactive (hovering reveals their handles +
|
|
81
|
+
* highlights them, dragging edits them). Set by the container's
|
|
82
|
+
* `editAnnotations` prop; annotations read it to switch from inert to interactive.
|
|
83
|
+
*/
|
|
84
|
+
readonly editAnnotations: boolean;
|
|
78
85
|
/** Format an epoch-ms instant the same way the time axis labels its ticks —
|
|
79
86
|
* shared by `<TimeAxis>` and the cursor-time readout. */
|
|
80
87
|
readonly formatTime: (epochMs: number) => string;
|
|
@@ -127,6 +134,105 @@ export interface ContainerFrame {
|
|
|
127
134
|
registerRow(key: symbol): () => void;
|
|
128
135
|
/** The first (topmost) row's key, or `null` before any row has registered. */
|
|
129
136
|
readonly firstRowKey: symbol | null;
|
|
137
|
+
/**
|
|
138
|
+
* Register an annotation (`<Region>`/`<Marker>`/`<Baseline>`) so the container
|
|
139
|
+
* can coordinate what a mark can't do in isolation: draw each mark's **guide
|
|
140
|
+
* line** across the *other* rows, resolve cross-region z-order, and serve
|
|
141
|
+
* **snap targets** to a drag. Keyed by the mark's per-instance slot key;
|
|
142
|
+
* unregister on unmount.
|
|
143
|
+
*/
|
|
144
|
+
registerAnnotation(key: symbol, spec: AnnotationSpec): void;
|
|
145
|
+
unregisterAnnotation(key: symbol): void;
|
|
146
|
+
/** Every registered annotation — read by each row to draw the *other* rows'
|
|
147
|
+
* guides, and by a drag to find snap targets. */
|
|
148
|
+
readonly annotations: readonly AnnotationSpec[];
|
|
149
|
+
/** Vertical lane (0 = top) for each top-flag label, by slot key — overlapping
|
|
150
|
+
* marker/region labels stack into lower lanes instead of colliding. A key absent
|
|
151
|
+
* from the map (or mapping to 0) sits in the top lane. */
|
|
152
|
+
readonly labelLanes: ReadonlyMap<symbol, number>;
|
|
153
|
+
/**
|
|
154
|
+
* The armed creation tool, or `null` (idle). Set by the consumer's toolbar;
|
|
155
|
+
* when non-null the plot captures a **create gesture** (draw a new mark) instead
|
|
156
|
+
* of panning, and fires {@link onCreate} on release. While armed, existing
|
|
157
|
+
* marks' edit handles stand down so the draw owns the surface.
|
|
158
|
+
*/
|
|
159
|
+
readonly creating: AnnotationKind | null;
|
|
160
|
+
/**
|
|
161
|
+
* Snap mode — the toolbar's "Snap". When on, a dragged mark snaps to other
|
|
162
|
+
* marks' **guidelines** (their x-positions) so spans align; off = free
|
|
163
|
+
* placement. Read by {@link snapToGuides}. (Data-sample snapping — landing on a
|
|
164
|
+
* clean `5:12` rather than `5:11:47` — is a future extension, not yet wired.)
|
|
165
|
+
*/
|
|
166
|
+
readonly snap: boolean;
|
|
167
|
+
/** Fired when a create gesture completes (on release). The consumer adds the
|
|
168
|
+
* mark, disarms ({@link creating} → `null`), and selects it. `undefined` ⇒
|
|
169
|
+
* creation is a no-op (the gesture still previews but commits nothing). */
|
|
170
|
+
readonly onCreate: ((spec: CreateSpec) => void) | undefined;
|
|
171
|
+
/** Fired when a mark is clicked (reports its `id`) or the plot is clicked empty
|
|
172
|
+
* (`null`) — the consumer updates its selection. A double-click on a region
|
|
173
|
+
* fires it too (the shortcut into a focused edit). */
|
|
174
|
+
readonly onSelectAnnotation: ((id: string | null) => void) | undefined;
|
|
175
|
+
/** Fired when the pointer enters a mark (reports its `id`) or leaves it (`null`),
|
|
176
|
+
* so the consumer can mirror hover out-of-band (e.g. a legend row). Pairs with a
|
|
177
|
+
* mark's controlled `hovered` prop to sync hover both ways. Works in any mode. */
|
|
178
|
+
readonly onHoverAnnotation: ((id: string | null) => void) | undefined;
|
|
179
|
+
/** Fired when a mark is **double-clicked** — the request to edit just that one.
|
|
180
|
+
* The consumer flips it into single-annotation edit (sets its `editing` prop),
|
|
181
|
+
* while the rest stay static. Distinct from {@link onSelectAnnotation} (single
|
|
182
|
+
* click = inspect-select). Works in any mode. */
|
|
183
|
+
readonly onEditAnnotation: ((id: string) => void) | undefined;
|
|
184
|
+
}
|
|
185
|
+
/** The kind of an annotation, and of a creation tool. */
|
|
186
|
+
export type AnnotationKind = 'region' | 'marker' | 'baseline';
|
|
187
|
+
/** What a completed create gesture reports to {@link ContainerFrame.onCreate} —
|
|
188
|
+
* the new mark's kind + position in axis units (+ the y-axis id for a baseline).
|
|
189
|
+
* (Which row a mark lands on is the consumer's call for now; multi-row routing is
|
|
190
|
+
* a follow-up.) */
|
|
191
|
+
export type CreateSpec = {
|
|
192
|
+
readonly kind: 'marker';
|
|
193
|
+
readonly at: number;
|
|
194
|
+
} | {
|
|
195
|
+
readonly kind: 'baseline';
|
|
196
|
+
readonly value: number;
|
|
197
|
+
readonly axis: string;
|
|
198
|
+
} | {
|
|
199
|
+
readonly kind: 'region';
|
|
200
|
+
readonly from: number;
|
|
201
|
+
readonly to: number;
|
|
202
|
+
};
|
|
203
|
+
/**
|
|
204
|
+
* A registered annotation as the container sees it — enough to draw its guide
|
|
205
|
+
* line on other rows, order it against other marks, and offer it as a snap target.
|
|
206
|
+
*/
|
|
207
|
+
export interface AnnotationSpec {
|
|
208
|
+
/** The mark's per-instance slot key — its identity in the registry. */
|
|
209
|
+
readonly key: symbol;
|
|
210
|
+
/** The consumer's stable id (its `id` prop), if any — what a click /
|
|
211
|
+
* double-click reports via {@link ContainerFrame.onSelectAnnotation}, so the
|
|
212
|
+
* consumer knows which mark to select. */
|
|
213
|
+
readonly id: string | undefined;
|
|
214
|
+
readonly kind: AnnotationKind;
|
|
215
|
+
/** The row it lives on (its `<ChartRow>`'s key), so a row skips its own marks
|
|
216
|
+
* when drawing guides. */
|
|
217
|
+
readonly rowKey: symbol;
|
|
218
|
+
/**
|
|
219
|
+
* Its vertical-guide x-position(s) in **axis units** (the shared x): a marker's
|
|
220
|
+
* `[at]`, a region's `[from, to]`. Empty for a baseline — a horizontal line
|
|
221
|
+
* casts no vertical guide.
|
|
222
|
+
*/
|
|
223
|
+
readonly xs: readonly number[];
|
|
224
|
+
/** Whether it's currently selected (controlled by the consumer). */
|
|
225
|
+
readonly selected: boolean;
|
|
226
|
+
/** Whether it's in single-annotation edit (the double-click target). The plot
|
|
227
|
+
* suppresses the data cursor while any mark is editing, as it does in global
|
|
228
|
+
* edit mode. */
|
|
229
|
+
readonly editing: boolean;
|
|
230
|
+
/** Whether it accepts hover + selection. A non-selectable region is skipped by
|
|
231
|
+
* the double-click hit-test (it's inert background context). */
|
|
232
|
+
readonly selectable: boolean;
|
|
233
|
+
/** The mark's resolved label text — used to pack overlapping top-flag labels
|
|
234
|
+
* (markers + regions) into stacked vertical lanes. */
|
|
235
|
+
readonly label: string;
|
|
130
236
|
}
|
|
131
237
|
/**
|
|
132
238
|
* A row's per-slot axis widths each side, **slot 0 nearest the plot** (so the
|
|
@@ -293,6 +399,9 @@ export interface AxisSpec {
|
|
|
293
399
|
/** Value formatting for the tick labels + the cursor readout ({@link AxisFormat}),
|
|
294
400
|
* or `undefined` for the scale's d3 default. */
|
|
295
401
|
readonly format: AxisFormat | undefined;
|
|
402
|
+
/** Explicit tick values (from `<YAxis ticks>`), driving BOTH the axis labels
|
|
403
|
+
* and the row's gridlines so they align; `undefined` auto-picks from the scale. */
|
|
404
|
+
readonly tickValues: readonly number[] | undefined;
|
|
296
405
|
/**
|
|
297
406
|
* Declaration position among the row's children, injected by `ChartRow`. The
|
|
298
407
|
* row sorts axes by this, so the **first declared** axis is the default
|
|
@@ -314,12 +423,19 @@ export interface RowFrame {
|
|
|
314
423
|
* against its scale) — used by both the tick labels and the cursor readout, so
|
|
315
424
|
* a value reads identically in both. */
|
|
316
425
|
readonly formats: ReadonlyMap<string, (value: number) => string>;
|
|
426
|
+
/** Explicit tick values per axis id (from {@link AxisSpec.tickValues}), for axes
|
|
427
|
+
* that set `<YAxis ticks>` — so `Layers` draws gridlines at the same positions
|
|
428
|
+
* the axis labels. Absent id ⇒ that axis auto-picks. */
|
|
429
|
+
readonly tickValues: ReadonlyMap<string, readonly number[]>;
|
|
317
430
|
/** This row's cursor-mode override, or `undefined` to inherit the container's
|
|
318
431
|
* default ({@link ContainerFrame.cursor}). */
|
|
319
432
|
readonly cursor: CursorMode | undefined;
|
|
320
433
|
/** Whether this is the first (topmost) row — the shared cursor-time chip shows
|
|
321
434
|
* here only, not repeated on every row. Derived from {@link ContainerFrame.firstRowKey}. */
|
|
322
435
|
readonly isFirstRow: boolean;
|
|
436
|
+
/** This row's per-instance key — annotations register it so the container can
|
|
437
|
+
* draw a mark's guide on the *other* rows (a row skips its own marks). */
|
|
438
|
+
readonly rowKey: symbol;
|
|
323
439
|
/** The axis a layer uses when it names none (the first declared, or implicit). */
|
|
324
440
|
readonly defaultAxisId: string;
|
|
325
441
|
/**
|
package/dist/data.d.ts
CHANGED
|
@@ -118,6 +118,20 @@ export declare function fromValueSeries<VS extends ValueSeriesSchema>(series: Va
|
|
|
118
118
|
* @throws TypeError if `lower` or `upper` is not a numeric column.
|
|
119
119
|
*/
|
|
120
120
|
export declare function bandFromTimeSeries<S extends SeriesSchema>(series: TimeSeries<S>, lower: string, upper: string): BandSeries;
|
|
121
|
+
/**
|
|
122
|
+
* Build a {@link BandSeries} from a pond `ValueSeries` — the value-axis sibling
|
|
123
|
+
* of {@link bandFromTimeSeries}. The x axis is the series' monotonic value axis
|
|
124
|
+
* (`axisValues()`, e.g. cumulative distance) instead of time; `lower`/`upper`
|
|
125
|
+
* name the two numeric edge columns (typically `rollingByColumn` percentiles).
|
|
126
|
+
* The resulting `BandSeries` is identical in shape — the chart draws it exactly
|
|
127
|
+
* as a time band, only the x scale differs (a value scale rather than
|
|
128
|
+
* `scaleTime`). A sample with either edge missing reads as a gap in the fill
|
|
129
|
+
* (same contract as {@link bandFromTimeSeries}).
|
|
130
|
+
*
|
|
131
|
+
* @throws RangeError if `lower` or `upper` does not exist.
|
|
132
|
+
* @throws TypeError if `lower` or `upper` is not a numeric column.
|
|
133
|
+
*/
|
|
134
|
+
export declare function bandFromValueSeries<VS extends ValueSeriesSchema>(series: ValueSeries<VS>, lower: string, upper: string): BandSeries;
|
|
121
135
|
/**
|
|
122
136
|
* Build a {@link BoxSeries} from a pond `TimeSeries` — five numeric quantile
|
|
123
137
|
* columns (`lower`/`q1`/`median`/`q3`/`upper`) sharing the series' interval time
|
|
@@ -151,4 +165,24 @@ export declare function boxFromTimeSeries<S extends SeriesSchema>(series: TimeSe
|
|
|
151
165
|
* @throws TypeError if `column` is not a numeric column.
|
|
152
166
|
*/
|
|
153
167
|
export declare function barsFromTimeSeries<S extends SeriesSchema>(series: TimeSeries<S>, column: string): BarSeries;
|
|
168
|
+
/**
|
|
169
|
+
* Build a {@link BarSeries} from a pond `ValueSeries` — the value-axis sibling
|
|
170
|
+
* of {@link barsFromTimeSeries}. A `ValueSeries` is **point-keyed** on its value
|
|
171
|
+
* axis (one axis value per row, no per-row span), so — exactly like the
|
|
172
|
+
* point-keyed `time` case of {@link barsFromTimeSeries} — each bar is centred on
|
|
173
|
+
* its axis value and synthesises a span from **neighbour spacing**: it reaches
|
|
174
|
+
* halfway to each neighbour (a Voronoi cell on the value axis). The first / last
|
|
175
|
+
* bars mirror their single adjacent gap; a lone point (length 1) keeps zero
|
|
176
|
+
* width and falls back to the renderer's `minWidth`.
|
|
177
|
+
*
|
|
178
|
+
* For evenly-spaced contiguous keys (e.g. uniform splits centred on their
|
|
179
|
+
* midpoints) the cell boundaries land exactly on the segment boundaries; for
|
|
180
|
+
* unevenly-spaced keys a boundary sits at the midpoint between adjacent centres
|
|
181
|
+
* (a slight drift from a true segment edge — fine for the bar look; key an
|
|
182
|
+
* interval/timeRange `TimeSeries` instead if exact edges matter).
|
|
183
|
+
*
|
|
184
|
+
* @throws RangeError if `column` does not exist.
|
|
185
|
+
* @throws TypeError if `column` is not a numeric column.
|
|
186
|
+
*/
|
|
187
|
+
export declare function barsFromValueSeries<VS extends ValueSeriesSchema>(series: ValueSeries<VS>, column: string): BarSeries;
|
|
154
188
|
//# sourceMappingURL=data.d.ts.map
|
package/dist/data.js
CHANGED
|
@@ -33,6 +33,31 @@ function readNumericColumn(series, column) {
|
|
|
33
33
|
}
|
|
34
34
|
return out;
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Read a numeric column from a `ValueSeries` into a `Float64Array`, missing
|
|
38
|
+
* cells as `NaN` — the value-axis sibling of {@link readNumericColumn}, sharing
|
|
39
|
+
* its per-element `read(i)` rationale (the bulk reader is tree-shaken away in
|
|
40
|
+
* bundled browser builds).
|
|
41
|
+
*
|
|
42
|
+
* @throws RangeError if `column` does not exist.
|
|
43
|
+
* @throws TypeError if `column` is not a numeric column.
|
|
44
|
+
*/
|
|
45
|
+
function readValueColumn(series, column) {
|
|
46
|
+
const col = series.column(column);
|
|
47
|
+
if (col === undefined) {
|
|
48
|
+
throw new RangeError(`unknown column '${column}'`);
|
|
49
|
+
}
|
|
50
|
+
if (col.kind !== 'number') {
|
|
51
|
+
throw new TypeError(`column '${column}' must be numeric (got '${col.kind}')`);
|
|
52
|
+
}
|
|
53
|
+
const length = series.length;
|
|
54
|
+
const out = new Float64Array(length);
|
|
55
|
+
for (let i = 0; i < length; i += 1) {
|
|
56
|
+
const v = col.read(i);
|
|
57
|
+
out[i] = v === undefined ? NaN : v;
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
36
61
|
/** The key column's `begin` buffer aligned to the logical length (zero-copy). */
|
|
37
62
|
function timeAxis(series) {
|
|
38
63
|
// `begin` may carry trailing capacity beyond the logical length; subarray so
|
|
@@ -78,21 +103,12 @@ export function fromTimeSeries(series, column) {
|
|
|
78
103
|
* @throws TypeError if `column` is not a numeric column.
|
|
79
104
|
*/
|
|
80
105
|
export function fromValueSeries(series, column) {
|
|
81
|
-
const col = series.column(column);
|
|
82
|
-
if (col === undefined) {
|
|
83
|
-
throw new RangeError(`unknown column '${column}'`);
|
|
84
|
-
}
|
|
85
|
-
if (col.kind !== 'number') {
|
|
86
|
-
throw new TypeError(`column '${column}' must be numeric (got '${col.kind}')`);
|
|
87
|
-
}
|
|
88
|
-
const length = series.length;
|
|
89
|
-
const y = new Float64Array(length);
|
|
90
|
-
for (let i = 0; i < length; i += 1) {
|
|
91
|
-
const v = col.read(i);
|
|
92
|
-
y[i] = v === undefined ? NaN : v;
|
|
93
|
-
}
|
|
94
106
|
// axisValues() is the key buffer already trimmed to length (zero-copy).
|
|
95
|
-
return {
|
|
107
|
+
return {
|
|
108
|
+
x: series.axisValues(),
|
|
109
|
+
y: readValueColumn(series, column),
|
|
110
|
+
length: series.length,
|
|
111
|
+
};
|
|
96
112
|
}
|
|
97
113
|
/**
|
|
98
114
|
* Build a {@link BandSeries} from a pond `TimeSeries` — two numeric columns for
|
|
@@ -111,6 +127,27 @@ export function bandFromTimeSeries(series, lower, upper) {
|
|
|
111
127
|
length: series.length,
|
|
112
128
|
};
|
|
113
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Build a {@link BandSeries} from a pond `ValueSeries` — the value-axis sibling
|
|
132
|
+
* of {@link bandFromTimeSeries}. The x axis is the series' monotonic value axis
|
|
133
|
+
* (`axisValues()`, e.g. cumulative distance) instead of time; `lower`/`upper`
|
|
134
|
+
* name the two numeric edge columns (typically `rollingByColumn` percentiles).
|
|
135
|
+
* The resulting `BandSeries` is identical in shape — the chart draws it exactly
|
|
136
|
+
* as a time band, only the x scale differs (a value scale rather than
|
|
137
|
+
* `scaleTime`). A sample with either edge missing reads as a gap in the fill
|
|
138
|
+
* (same contract as {@link bandFromTimeSeries}).
|
|
139
|
+
*
|
|
140
|
+
* @throws RangeError if `lower` or `upper` does not exist.
|
|
141
|
+
* @throws TypeError if `lower` or `upper` is not a numeric column.
|
|
142
|
+
*/
|
|
143
|
+
export function bandFromValueSeries(series, lower, upper) {
|
|
144
|
+
return {
|
|
145
|
+
x: series.axisValues(),
|
|
146
|
+
lower: readValueColumn(series, lower),
|
|
147
|
+
upper: readValueColumn(series, upper),
|
|
148
|
+
length: series.length,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
114
151
|
/**
|
|
115
152
|
* Build a {@link BoxSeries} from a pond `TimeSeries` — five numeric quantile
|
|
116
153
|
* columns (`lower`/`q1`/`median`/`q3`/`upper`) sharing the series' interval time
|
|
@@ -194,4 +231,42 @@ export function barsFromTimeSeries(series, column) {
|
|
|
194
231
|
}
|
|
195
232
|
return { begin, end, y, length: n };
|
|
196
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* Build a {@link BarSeries} from a pond `ValueSeries` — the value-axis sibling
|
|
236
|
+
* of {@link barsFromTimeSeries}. A `ValueSeries` is **point-keyed** on its value
|
|
237
|
+
* axis (one axis value per row, no per-row span), so — exactly like the
|
|
238
|
+
* point-keyed `time` case of {@link barsFromTimeSeries} — each bar is centred on
|
|
239
|
+
* its axis value and synthesises a span from **neighbour spacing**: it reaches
|
|
240
|
+
* halfway to each neighbour (a Voronoi cell on the value axis). The first / last
|
|
241
|
+
* bars mirror their single adjacent gap; a lone point (length 1) keeps zero
|
|
242
|
+
* width and falls back to the renderer's `minWidth`.
|
|
243
|
+
*
|
|
244
|
+
* For evenly-spaced contiguous keys (e.g. uniform splits centred on their
|
|
245
|
+
* midpoints) the cell boundaries land exactly on the segment boundaries; for
|
|
246
|
+
* unevenly-spaced keys a boundary sits at the midpoint between adjacent centres
|
|
247
|
+
* (a slight drift from a true segment edge — fine for the bar look; key an
|
|
248
|
+
* interval/timeRange `TimeSeries` instead if exact edges matter).
|
|
249
|
+
*
|
|
250
|
+
* @throws RangeError if `column` does not exist.
|
|
251
|
+
* @throws TypeError if `column` is not a numeric column.
|
|
252
|
+
*/
|
|
253
|
+
export function barsFromValueSeries(series, column) {
|
|
254
|
+
const y = readValueColumn(series, column);
|
|
255
|
+
const n = series.length;
|
|
256
|
+
// axisValues() is the monotonic key buffer (zero-copy) — must not be mutated,
|
|
257
|
+
// so synthesise the spans into fresh buffers.
|
|
258
|
+
const ax = series.axisValues();
|
|
259
|
+
const begin = new Float64Array(n);
|
|
260
|
+
const end = new Float64Array(n);
|
|
261
|
+
for (let i = 0; i < n; i += 1) {
|
|
262
|
+
const x = ax[i];
|
|
263
|
+
// Half-gap to the previous neighbour (mirror the next gap at the left edge).
|
|
264
|
+
const prevGap = i > 0 ? x - ax[i - 1] : i + 1 < n ? ax[i + 1] - x : 0;
|
|
265
|
+
// Half-gap to the next neighbour (mirror the previous gap at the right edge).
|
|
266
|
+
const nextGap = i + 1 < n ? ax[i + 1] - x : i > 0 ? x - ax[i - 1] : 0;
|
|
267
|
+
begin[i] = x - prevGap / 2;
|
|
268
|
+
end[i] = x + nextGap / 2;
|
|
269
|
+
}
|
|
270
|
+
return { begin, end, y, length: n };
|
|
271
|
+
}
|
|
197
272
|
//# sourceMappingURL=data.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -42,6 +42,9 @@ export { BoxPlot } from './BoxPlot.js';
|
|
|
42
42
|
export type { BoxPlotProps } from './BoxPlot.js';
|
|
43
43
|
export { BarChart } from './BarChart.js';
|
|
44
44
|
export type { BarChartProps } from './BarChart.js';
|
|
45
|
+
export { Region, Baseline, Marker } from './annotations.js';
|
|
46
|
+
export type { RegionProps, BaselineProps, MarkerProps } from './annotations.js';
|
|
47
|
+
export type { AnnotationKind, CreateSpec } from './context.js';
|
|
45
48
|
export { fromTimeSeries, bandFromTimeSeries, boxFromTimeSeries, barsFromTimeSeries, } from './data.js';
|
|
46
49
|
export type { ChartSeries, BandSeries, BoxSeries, BoxColumns, BarSeries, } from './data.js';
|
|
47
50
|
export type { RadiusEncoding, ColorEncoding } from './encoding.js';
|
package/dist/index.js
CHANGED
|
@@ -29,6 +29,9 @@ export { AreaChart } from './AreaChart.js';
|
|
|
29
29
|
export { ScatterChart } from './ScatterChart.js';
|
|
30
30
|
export { BoxPlot } from './BoxPlot.js';
|
|
31
31
|
export { BarChart } from './BarChart.js';
|
|
32
|
+
// Annotations — user-authored marks in the turquoise register (distinct from the
|
|
33
|
+
// data): a shaded span, a horizontal value line, a vertical x line.
|
|
34
|
+
export { Region, Baseline, Marker } from './annotations.js';
|
|
32
35
|
export { fromTimeSeries, bandFromTimeSeries, boxFromTimeSeries, barsFromTimeSeries, } from './data.js';
|
|
33
36
|
export { defaultTheme, estelaTheme } from './theme.js';
|
|
34
37
|
//# sourceMappingURL=index.js.map
|
package/dist/theme.d.ts
CHANGED
|
@@ -115,6 +115,24 @@ export interface ChartTheme {
|
|
|
115
115
|
readonly gap?: {
|
|
116
116
|
readonly connectorOpacity: number;
|
|
117
117
|
};
|
|
118
|
+
/**
|
|
119
|
+
* The **annotation register** — the styling for *user-authored* marks
|
|
120
|
+
* (`<Region>` / `<Baseline>` / `<Marker>`), deliberately a distinct hue from
|
|
121
|
+
* the data's `line`/`area`/… so a mark you place never reads as data (the
|
|
122
|
+
* "data stays foam, marks are turquoise" rule). `color` is the shared register
|
|
123
|
+
* hue (lines, region edges + fill, handles, label text); the region fill draws
|
|
124
|
+
* at `fillOpacity`. **Luminosity encodes depth** — brighter reads as forward,
|
|
125
|
+
* dimmer as further back. `depth` is the three-level ramp: `[0]` = level 1
|
|
126
|
+
* (forward / brightest — a selected mark, or an edit-mode line), `[1]` = level 2
|
|
127
|
+
* (mid — a hovered mark, or an edit-mode region body), `[2]` = level 3 (back —
|
|
128
|
+
* the resting, backgrounded state). Cross-row guides draw fainter still (a
|
|
129
|
+
* notional level 4, set in `Layers`). Falls back to a built-in turquoise.
|
|
130
|
+
*/
|
|
131
|
+
readonly annotation?: {
|
|
132
|
+
readonly color: string;
|
|
133
|
+
readonly fillOpacity: number;
|
|
134
|
+
readonly depth: readonly [number, number, number];
|
|
135
|
+
};
|
|
118
136
|
}
|
|
119
137
|
/** A resolved line style: stroke colour + width (px). */
|
|
120
138
|
export interface LineStyle {
|
package/dist/theme.js
CHANGED
|
@@ -107,6 +107,12 @@ export const defaultTheme = {
|
|
|
107
107
|
cursor: '#64748b',
|
|
108
108
|
chip: { background: '#ffffff' },
|
|
109
109
|
gap: { connectorOpacity: 0.5 },
|
|
110
|
+
// Teal marks register — distinct from the blue data, reads on the light ground.
|
|
111
|
+
annotation: {
|
|
112
|
+
color: '#0d9488',
|
|
113
|
+
fillOpacity: 0.1,
|
|
114
|
+
depth: [1, 0.7, 0.4],
|
|
115
|
+
},
|
|
110
116
|
};
|
|
111
117
|
/**
|
|
112
118
|
* The estela theme — estela's real `@estela/ui` palette as *one theme*, on its
|
|
@@ -228,5 +234,12 @@ export const estelaTheme = {
|
|
|
228
234
|
cursor: '#7FE2D2', // --es-reef (bright tracker on the dark ground)
|
|
229
235
|
chip: { background: '#0B4E58' }, // --es-deep (panel behind readout text)
|
|
230
236
|
gap: { connectorOpacity: 0.5 },
|
|
237
|
+
// Marks register: --es-reef, estela's bright attention turquoise (the same hue
|
|
238
|
+
// as the cursor / selected highlight) — distinct from the foam (white) data.
|
|
239
|
+
annotation: {
|
|
240
|
+
color: '#7FE2D2', // --es-reef
|
|
241
|
+
fillOpacity: 0.1,
|
|
242
|
+
depth: [1, 0.7, 0.4],
|
|
243
|
+
},
|
|
231
244
|
};
|
|
232
245
|
//# sourceMappingURL=theme.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pond-ts/charts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Canvas-rendered, streaming-first time-series charts for pond-ts",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"perf": "PERF_BENCH=1 playwright test perf.spec.ts --workers=1"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@pond-ts/react": "^0.
|
|
42
|
-
"pond-ts": "^0.
|
|
41
|
+
"@pond-ts/react": "^0.32.0",
|
|
42
|
+
"pond-ts": "^0.32.0",
|
|
43
43
|
"react": "^18.0.0 || ^19.0.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|