@weasel-js/plot 0.0.0-stage → 1.15.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.
@@ -0,0 +1,679 @@
1
+ import { Path } from '@weasel-js/geom';
2
+ import { ResolvedTheme } from '@weasel-js/theme';
3
+ import { AddNodeSpec, RectPose, Scene, NodeId, RenderLayer, InvocationCtx, Action, Contribution, Paint } from '@weasel-js/core';
4
+ import { ReactElement } from 'react';
5
+
6
+ /** What every positional scale answers, and what an axis is drawn from: where a value sits along its range, the
7
+ * values an axis marks, and how one reads. */
8
+ interface Scale<T> {
9
+ readonly kind: 'linear' | 'log' | 'band' | 'time';
10
+ /** Where the scale's output runs, in drawing units: start to end, either way round. */
11
+ readonly range: readonly [number, number];
12
+ /** Where `value` sits along the range. A band scale answers its band's center. */
13
+ position(value: T): number;
14
+ /** The values an axis marks, about `count` of them where the scale chooses. */
15
+ ticks(count?: number): T[];
16
+ /** `value` as an axis label reads it. */
17
+ format(value: T): string;
18
+ }
19
+ /** A scale over numbers that maps both ways. */
20
+ interface ContinuousScale extends Scale<number> {
21
+ readonly kind: 'linear' | 'log' | 'time';
22
+ readonly domain: readonly [number, number];
23
+ /** `value` along the range; the same as {@link Scale.position}. */
24
+ map(value: number): number;
25
+ /** The value at `at` along the range. */
26
+ invert(at: number): number;
27
+ }
28
+
29
+ /** Options for {@link linearScale}. */
30
+ interface LinearScaleOptions {
31
+ domain: readonly [number, number];
32
+ range: readonly [number, number];
33
+ /** Widen the domain out to whole tick steps for about this many ticks. Default no widening. */
34
+ nice?: number;
35
+ }
36
+ /** A scale that maps numbers proportionally, as a value axis does. */
37
+ interface LinearScale extends ContinuousScale {
38
+ readonly kind: 'linear';
39
+ /** The distance between ticks when about `count` are asked for. */
40
+ tickStep(count?: number): number;
41
+ }
42
+ /** The round step, 1, 2 or 5 times a power of ten, that cuts `lo`..`hi` into about `count` pieces. */
43
+ declare function tickStep(lo: number, hi: number, count?: number): number;
44
+ /** Every multiple of `step` from `lo` to `hi`, inclusive. */
45
+ declare function stepTicks(lo: number, hi: number, step: number): number[];
46
+ /** `value` written as plainly as a grid of `step` allows: no trailing zeros, no exponent. */
47
+ declare function formatNumber(value: number, step: number): string;
48
+ /** `domain` widened to whole steps of a tick grid of about `count` ticks. */
49
+ declare function niceDomain(domain: readonly [number, number], count?: number): [number, number];
50
+ /** A linear scale from `domain` onto `range`. */
51
+ declare function linearScale(opts: LinearScaleOptions): LinearScale;
52
+
53
+ /** Options for {@link logScale}. */
54
+ interface LogScaleOptions {
55
+ /** Both ends above zero. */
56
+ domain: readonly [number, number];
57
+ range: readonly [number, number];
58
+ /** Default 10. */
59
+ base?: number;
60
+ }
61
+ /** A scale that maps the logarithm of a value proportionally. */
62
+ interface LogScale extends ContinuousScale {
63
+ readonly kind: 'log';
64
+ readonly base: number;
65
+ }
66
+ /** A logarithmic scale from `domain`, whose ends must both be above zero, onto `range`. */
67
+ declare function logScale(opts: LogScaleOptions): LogScale;
68
+
69
+ /** Options for {@link bandScale}. */
70
+ interface BandScaleOptions {
71
+ /** The categories, one band each, in order. */
72
+ domain: readonly string[];
73
+ range: readonly [number, number];
74
+ /** The share of each step left empty between bands, 0 to 1. Default 0.2. */
75
+ paddingInner?: number;
76
+ /** The room before the first band and after the last, in steps. Default half of `paddingInner`. */
77
+ paddingOuter?: number;
78
+ }
79
+ /** A scale that cuts its range into equal bands, one per category, as a bar chart's category axis does. */
80
+ interface BandScale extends Scale<string> {
81
+ readonly kind: 'band';
82
+ readonly domain: readonly string[];
83
+ /** The width of each band. */
84
+ readonly bandwidth: number;
85
+ /** From one band's start to the next's. */
86
+ readonly step: number;
87
+ /** Where `key`'s band starts, or `NaN` for a key not in the domain. */
88
+ start(key: string): number;
89
+ /** The key whose band is nearest `at`; `undefined` for an empty domain. */
90
+ invert(at: number): string | undefined;
91
+ }
92
+ /** A band scale over `domain`. */
93
+ declare function bandScale(opts: BandScaleOptions): BandScale;
94
+
95
+ /** Options for {@link timeScale}. */
96
+ interface TimeScaleOptions {
97
+ /** Milliseconds since the epoch, as `Date.getTime` gives them. */
98
+ domain: readonly [number, number];
99
+ range: readonly [number, number];
100
+ }
101
+ /** A scale over instants, whose ticks fall on whole seconds, minutes, hours, days, months or years, in UTC. */
102
+ interface TimeScale extends ContinuousScale {
103
+ readonly kind: 'time';
104
+ }
105
+ type Unit = 'second' | 'minute' | 'hour' | 'day' | 'month' | 'year';
106
+ /** `t` written to the precision of `unit`. */
107
+ declare function formatInstant(t: number, unit: Unit): string;
108
+ /** A time scale from `domain`, in milliseconds since the epoch, onto `range`. */
109
+ declare function timeScale(opts: TimeScaleOptions): TimeScale;
110
+
111
+ /** Options for {@link ordinalScale}. */
112
+ interface OrdinalScaleOptions<T> {
113
+ /** The keys, in order. */
114
+ domain: readonly string[];
115
+ /** What each key maps to, in domain order, wrapping when there are fewer than keys. */
116
+ range: readonly T[];
117
+ }
118
+ /** A scale from keys to values in turn, as a series' colors are chosen. */
119
+ interface OrdinalScale<T> {
120
+ readonly kind: 'ordinal';
121
+ readonly domain: readonly string[];
122
+ readonly range: readonly T[];
123
+ /** `key`'s value; a key not in the domain takes the next value after the last, and joins the domain. */
124
+ map(key: string): T;
125
+ /** The keys a legend lists. */
126
+ ticks(): string[];
127
+ format(key: string): string;
128
+ }
129
+ /** An ordinal scale from `domain` to `range`. Throws on an empty range. */
130
+ declare function ordinalScale<T>(opts: OrdinalScaleOptions<T>): OrdinalScale<T>;
131
+
132
+ /** The kinds of mark a chart is drawn with. */
133
+ type MarkKind = 'bar' | 'span' | 'line' | 'area' | 'point' | 'arc' | 'ribbon' | 'rule' | 'text';
134
+ /** The value a mark stands for, as plain fields: a series and index, a category, a value. */
135
+ type PlotDatum = Readonly<Record<string, string | number | boolean>>;
136
+ /** What a mark's scene data says about it: its kind, and the datum it draws. */
137
+ interface PlotMarkTrait {
138
+ kind: MarkKind;
139
+ datum?: PlotDatum;
140
+ }
141
+ /** A solid paint, as the kit's painters read one. */
142
+ interface SolidFill {
143
+ paint: {
144
+ color: string;
145
+ opacity?: number;
146
+ };
147
+ }
148
+ /** A solid stroke, as the kit's painters read one. */
149
+ interface SolidStroke {
150
+ paint: {
151
+ color: string;
152
+ };
153
+ width: number;
154
+ dash?: number[];
155
+ cap?: 'butt' | 'round' | 'square';
156
+ join?: 'miter' | 'round' | 'bevel';
157
+ }
158
+ /**
159
+ * A mark's scene data: the trait under `plotMark`, and the fields the kit's
160
+ * painters draw it from — `path` for a shape, a line or a region, `text` for
161
+ * a label.
162
+ */
163
+ interface PlotMarkData {
164
+ plotMark: PlotMarkTrait;
165
+ path?: Path;
166
+ fill?: SolidFill;
167
+ stroke?: SolidStroke;
168
+ text?: string;
169
+ style?: {
170
+ fontFamily: string;
171
+ fontSize: number;
172
+ align?: 'left' | 'center' | 'right';
173
+ fontWeight?: number;
174
+ };
175
+ verticalAlign?: 'top' | 'center' | 'bottom';
176
+ }
177
+ /** A box, in the plot's own coordinates: from its root's corner. */
178
+ interface PlotBox {
179
+ x: number;
180
+ y: number;
181
+ width: number;
182
+ height: number;
183
+ /** Radians about the box's center. */
184
+ rotation?: number;
185
+ }
186
+ /** One mark, ready to be a scene node: a key unique within its plot, where it is drawn, and its data. */
187
+ interface PlotMark {
188
+ key: string;
189
+ pose: PlotBox;
190
+ data: PlotMarkData;
191
+ }
192
+ /** A point in the plot's coordinates. */
193
+ interface PlotPoint {
194
+ x: number;
195
+ y: number;
196
+ }
197
+
198
+ /** Options for {@link barMark}. */
199
+ interface BarMarkOptions {
200
+ key: string;
201
+ /** Where the bar stands along the category axis: its left edge, or its top for a horizontal bar. */
202
+ x: number;
203
+ /** The bar's breadth across the category axis. */
204
+ width: number;
205
+ /** The baseline the bar rises from, and the end it reaches, along the value axis. */
206
+ from: number;
207
+ to: number;
208
+ color: string;
209
+ datum?: PlotDatum;
210
+ /** Default `'vertical'`: `x` and `width` run along x. */
211
+ orient?: 'vertical' | 'horizontal';
212
+ }
213
+ /** A bar from `from` to `to`, either way round. */
214
+ declare function barMark(opts: BarMarkOptions): PlotMark;
215
+ /** Options for {@link spanMark}. */
216
+ interface SpanMarkOptions {
217
+ key: string;
218
+ /** Start and end along x. */
219
+ x0: number;
220
+ x1: number;
221
+ /** Its row: top and height. */
222
+ y: number;
223
+ height: number;
224
+ color: string;
225
+ opacity?: number;
226
+ datum?: PlotDatum;
227
+ }
228
+ /** A span across x, as a task's bar on a timeline is. */
229
+ declare function spanMark(opts: SpanMarkOptions): PlotMark;
230
+ /** Options for {@link pointMark}. */
231
+ interface PointMarkOptions {
232
+ key: string;
233
+ x: number;
234
+ y: number;
235
+ /** Default 3. */
236
+ radius?: number;
237
+ color: string;
238
+ datum?: PlotDatum;
239
+ }
240
+ /** A dot centered on `x`, `y`. */
241
+ declare function pointMark(opts: PointMarkOptions): PlotMark;
242
+
243
+ /** Options for {@link lineMark}. */
244
+ interface LineMarkOptions {
245
+ key: string;
246
+ /** At least one. */
247
+ points: readonly PlotPoint[];
248
+ color: string;
249
+ /** Default 2. */
250
+ width?: number;
251
+ dash?: number[];
252
+ datum?: PlotDatum;
253
+ }
254
+ /** A line through `points`, in order. */
255
+ declare function lineMark(opts: LineMarkOptions): PlotMark;
256
+ /** Options for {@link ruleMark}. */
257
+ interface RuleMarkOptions {
258
+ key: string;
259
+ from: PlotPoint;
260
+ to: PlotPoint;
261
+ color: string;
262
+ /** Default 1. */
263
+ width?: number;
264
+ dash?: number[];
265
+ datum?: PlotDatum;
266
+ }
267
+ /** A straight rule from `from` to `to`: an axis line, a tick, a grid line, a marker. */
268
+ declare function ruleMark(opts: RuleMarkOptions): PlotMark;
269
+ /** Options for {@link areaMark}. */
270
+ interface AreaMarkOptions {
271
+ key: string;
272
+ /** The upper edge, in order. */
273
+ top: readonly PlotPoint[];
274
+ /** The lower edge, in the same order as `top`. Default a baseline at `baseline` under each top point. */
275
+ bottom?: readonly PlotPoint[];
276
+ baseline?: number;
277
+ color: string;
278
+ /** Default 0.35. */
279
+ opacity?: number;
280
+ datum?: PlotDatum;
281
+ }
282
+ /** The region between two edges, as an area chart or a radar's polygon fills it. */
283
+ declare function areaMark(opts: AreaMarkOptions): PlotMark;
284
+ /** Options for {@link arcMark}. */
285
+ interface ArcMarkOptions {
286
+ key: string;
287
+ center: PlotPoint;
288
+ /** 0 for a pie slice. */
289
+ innerRadius: number;
290
+ outerRadius: number;
291
+ /** Radians clockwise from twelve o'clock. */
292
+ startAngle: number;
293
+ endAngle: number;
294
+ color: string;
295
+ datum?: PlotDatum;
296
+ }
297
+ /** A slice of a ring, as a pie or a donut is cut. */
298
+ declare function arcMark(opts: ArcMarkOptions): PlotMark;
299
+ /** Options for {@link ribbonMark}. */
300
+ interface RibbonMarkOptions {
301
+ key: string;
302
+ /** The end it leaves: an x, and the span along y it leaves across. */
303
+ from: {
304
+ x: number;
305
+ y0: number;
306
+ y1: number;
307
+ };
308
+ to: {
309
+ x: number;
310
+ y0: number;
311
+ y1: number;
312
+ };
313
+ color: string;
314
+ /** Default 0.45. */
315
+ opacity?: number;
316
+ datum?: PlotDatum;
317
+ }
318
+ /** A band curving from one span to another, as a flow between two nodes is drawn. */
319
+ declare function ribbonMark(opts: RibbonMarkOptions): PlotMark;
320
+
321
+ /** The face a chart's text is set in. */
322
+ interface PlotFont {
323
+ fontFamily: string;
324
+ fontSize: number;
325
+ }
326
+ /** The colors a chart is drawn in, by role, and its face. Plain strings, so a scene can keep it. */
327
+ interface PlotTheme {
328
+ /** Titles, labels and values. */
329
+ text: string;
330
+ /** Axis rules and ticks. */
331
+ axis: string;
332
+ /** Grid lines behind the marks. */
333
+ grid: string;
334
+ /** The colors series take in turn. Never empty. */
335
+ series: readonly string[];
336
+ font: PlotFont;
337
+ }
338
+ /** A plot theme from a resolved weasel theme: text in `fg`, axes in `border-strong`, grid in `border`, series in
339
+ * its swatches. */
340
+ declare function plotThemeFrom(resolved: Readonly<Record<string, string>> | ResolvedTheme, font?: PlotFont): PlotTheme;
341
+ /** 12px sans-serif. */
342
+ declare const PLOT_FONT: PlotFont;
343
+ /** The weasel theme in its dark mode. */
344
+ declare const PLOT_THEME: PlotTheme;
345
+ /** The `i`th series' color, wrapping. */
346
+ declare const seriesColor: (theme: PlotTheme, i: number) => string;
347
+
348
+ /** How wide and tall one line of text is set in `font`. */
349
+ type PlotMeasure = (text: string, font: PlotFont) => {
350
+ width: number;
351
+ height: number;
352
+ };
353
+ /** Measures as the kit's text renderer lays text out, from the registered face's metrics; a line is 1.2 sizes tall. */
354
+ declare const textMeasure: PlotMeasure;
355
+
356
+ /** Marks drawn as text: titles, tick labels, values. */
357
+
358
+ /** Options for {@link textMark}. */
359
+ interface TextMarkOptions {
360
+ key: string;
361
+ text: string;
362
+ /** The anchor point. */
363
+ x: number;
364
+ y: number;
365
+ /** Which part of the text stands on `x`. Default `'middle'`. */
366
+ anchor?: 'start' | 'middle' | 'end';
367
+ /** Which part of the text stands on `y`. Default `'middle'`. */
368
+ baseline?: 'top' | 'middle' | 'bottom';
369
+ font: PlotFont;
370
+ color: string;
371
+ measure: PlotMeasure;
372
+ /** Turns the text a quarter turn counterclockwise about the anchor, as a vertical axis's title reads. */
373
+ vertical?: boolean;
374
+ bold?: boolean;
375
+ datum?: PlotDatum;
376
+ }
377
+ /** One line of text, its box measured with `measure` and placed by its anchor. */
378
+ declare function textMark(opts: TextMarkOptions): PlotMark;
379
+
380
+ /** Which side of the plot an axis runs along. Its scale's range runs along x for top and bottom, along y otherwise. */
381
+ type AxisSide = 'top' | 'bottom' | 'left' | 'right';
382
+ /** Options for {@link axisMarks} and {@link axisRoom}. */
383
+ interface AxisOptions<T> {
384
+ scale: Scale<T>;
385
+ side: AxisSide;
386
+ /** Where the axis line stands across its scale: a y for top and bottom, an x for left and right. */
387
+ at: number;
388
+ /** Draw a grid line at each tick, from the axis across to here. */
389
+ gridTo?: number;
390
+ title?: string;
391
+ /** How many ticks to ask the scale for. */
392
+ ticks?: number;
393
+ /** Prefixes every mark's key. Default `axis:<side>`. */
394
+ key?: string;
395
+ theme: PlotTheme;
396
+ measure: PlotMeasure;
397
+ }
398
+ /** How far an axis reaches out from its line: ticks, labels and title. */
399
+ declare function axisRoom<T>(opts: AxisOptions<T>): number;
400
+ /** Every mark one axis is drawn with. Keys: `<key>:rule`, `<key>:tick:<i>`, `<key>:label:<i>`, `<key>:grid:<i>`, `<key>:title`. */
401
+ declare function axisMarks<T>(opts: AxisOptions<T>): PlotMark[];
402
+
403
+ /** A category axis: one band per label, in order; with none, bands numbered from 1. */
404
+ interface XyBandAxis {
405
+ kind: 'band';
406
+ title?: string;
407
+ categories: readonly string[];
408
+ }
409
+ /** A numeric axis from `min` to `max`, the series' values spread evenly across it. */
410
+ interface XyRangeAxis {
411
+ kind: 'range';
412
+ title?: string;
413
+ min: number;
414
+ max: number;
415
+ }
416
+ /** The value axis. A missing end fits the series' values, zero included. */
417
+ interface XyValueAxis {
418
+ title?: string;
419
+ min?: number;
420
+ max?: number;
421
+ }
422
+ /** One series: a value per category, drawn as bars or as a line. */
423
+ interface XySeries {
424
+ kind: 'bar' | 'line';
425
+ name?: string;
426
+ values: readonly number[];
427
+ }
428
+ /** A bar and line chart over categories or a numeric range. */
429
+ interface XyChartData {
430
+ kind: 'xychart';
431
+ title?: string;
432
+ /** Default `'vertical'`: categories along x, values up y. */
433
+ orientation?: 'vertical' | 'horizontal';
434
+ /** Absent, bands numbered from 1, one per value of the longest series. */
435
+ x?: XyBandAxis | XyRangeAxis;
436
+ y?: XyValueAxis;
437
+ series: readonly XySeries[];
438
+ }
439
+
440
+ /** Chart specs, and the one table that compiles each kind to marks. */
441
+
442
+ /** A chart, as data: one spec per kind, told apart by `kind`. */
443
+ type PlotData = XyChartData;
444
+ /** The kinds of chart {@link PlotData} holds. */
445
+ type PlotKind = PlotData['kind'];
446
+ /** What a kind's compiler is given and gives back: its spec laid out in a box of `size`, as marks. */
447
+ type PlotCompiler<D extends PlotData> = (data: D, size: {
448
+ width: number;
449
+ height: number;
450
+ }, theme: PlotTheme, measure: PlotMeasure) => {
451
+ marks: PlotMark[];
452
+ };
453
+ /** `data` laid out in a box of `size`, as the marks it is drawn with, in drawing order. */
454
+ declare function compilePlot(data: PlotData, size: {
455
+ width: number;
456
+ height: number;
457
+ }, theme: PlotTheme, measure: PlotMeasure): PlotMark[];
458
+
459
+ /** Where an xychart's parts landed: the plot area, the value scale, and each value index's place across it. */
460
+ interface XyLayout {
461
+ orientation: 'vertical' | 'horizontal';
462
+ /** The area the marks are drawn in, inside the axes. */
463
+ plot: {
464
+ x: number;
465
+ y: number;
466
+ width: number;
467
+ height: number;
468
+ };
469
+ /** Values along y for a vertical chart, along x for a horizontal one. */
470
+ value: LinearScale;
471
+ /** Each value index's center along the category axis. */
472
+ slots: readonly number[];
473
+ /** How much of the category axis each index's bars share. */
474
+ slotWidth: number;
475
+ }
476
+ /** How many values the longest series holds. */
477
+ declare const xyLength: (data: XyChartData) => number;
478
+ /** The value axis's domain: its own ends, and where one is missing, the values' reach with zero in it, made round. */
479
+ declare function xyValueDomain(data: XyChartData): [number, number];
480
+ /** Lays out an xychart in a box of `size`: its title, axes, bars, lines and points. */
481
+ declare function compileXyChart(data: XyChartData, size: {
482
+ width: number;
483
+ height: number;
484
+ }, theme: PlotTheme, measure: PlotMeasure): {
485
+ marks: PlotMark[];
486
+ layout: XyLayout;
487
+ };
488
+
489
+ /** `data` with series `series`'s value at `index` set to `value`. Throws on an index the series does not hold. */
490
+ declare function setXyValue(data: XyChartData, series: number, index: number, value: number): XyChartData;
491
+ /** The value a point stands at along `layout`'s value axis, rounded to a tenth of its tick step. */
492
+ declare function xyValueAt(layout: XyLayout, at: {
493
+ x: number;
494
+ y: number;
495
+ }): number;
496
+
497
+ /**
498
+ * A plot in a scene: one root node holding the spec, its marks derived
499
+ * children rebuilt from it. An edit replaces the spec and rebuilds the marks
500
+ * as one undo entry.
501
+ */
502
+
503
+ /** What a plot's root node keeps: the spec, and the theme its marks were drawn in. */
504
+ interface PlotRootTrait {
505
+ data: PlotData;
506
+ theme: PlotTheme;
507
+ }
508
+ /** A plot root's scene data. */
509
+ interface PlotRootData {
510
+ plot: PlotRootTrait;
511
+ }
512
+ /** The data of any node a plot puts in a scene. */
513
+ type PlotSceneData = PlotRootData | PlotMarkData;
514
+ /** A scene node spec, as {@link plotSpecs} returns them. */
515
+ type PlotSpec<TLayer extends string = 'main'> = AddNodeSpec<PlotSceneData, TLayer, RectPose>;
516
+ /** Options for {@link plotSpecs}. */
517
+ interface PlotSpecsOptions<TLayer extends string = 'main'> {
518
+ /** The root's id. Default `'plot'`. */
519
+ id?: string;
520
+ /** The box the plot fills. */
521
+ box: {
522
+ x: number;
523
+ y: number;
524
+ width: number;
525
+ height: number;
526
+ };
527
+ /** Default `'main'`. */
528
+ layer?: TLayer;
529
+ /** Default {@link PLOT_THEME}. */
530
+ theme?: PlotTheme;
531
+ /** Default {@link textMeasure}. */
532
+ measure?: PlotMeasure;
533
+ }
534
+ /** The trait a plot root's data carries, or `null` for any other node. */
535
+ declare function plotRootOf(node: {
536
+ data: unknown;
537
+ } | undefined): PlotRootTrait | null;
538
+ /** The trait a plot mark's data carries, or `null` for any other node. */
539
+ declare function plotMarkOf(node: {
540
+ data: unknown;
541
+ } | undefined): PlotMarkTrait | null;
542
+ /** The id of the mark keyed `key` under the root `rootId`. */
543
+ declare const plotMarkId: (rootId: string, key: string) => string;
544
+ /** `marks` as children of the root `rootId`, in its frame. */
545
+ declare function markSpecs<TLayer extends string>(rootId: string, layer: TLayer, marks: readonly PlotMark[]): PlotSpec<TLayer>[];
546
+ /** The nodes a plot of `data` is: its root, a group filling `box`, then each mark under it, in drawing order. */
547
+ declare function plotSpecs<TLayer extends string = 'main'>(data: PlotData, opts: PlotSpecsOptions<TLayer>): PlotSpec<TLayer>[];
548
+ /** Options for {@link replacePlotData}. */
549
+ interface ReplacePlotOptions {
550
+ /** The undo entry's label. Default `'Edit chart'`. */
551
+ label?: string;
552
+ /** Draw in this theme from now on. Default the one the root keeps. */
553
+ theme?: PlotTheme;
554
+ /** Default {@link textMeasure}. */
555
+ measure?: PlotMeasure;
556
+ }
557
+ /** Replaces the spec of the plot rooted at `rootId` with `data` and rebuilds its marks, as one undo entry. */
558
+ declare function replacePlotData<TData, TLayer extends string, TPose>(scene: Scene<TData, TLayer, TPose>, rootId: string, data: PlotData, opts?: ReplacePlotOptions): void;
559
+ /** Every plot root in `scene`, in render order. */
560
+ declare function plotRootsIn(scene: Scene<unknown, string, unknown>): NodeId[];
561
+
562
+ /** What each chart kind lets a drag edit: which marks carry a handle, and the spec a drag of one proposes. */
563
+
564
+ /** A handle a press can take: where it stands in the plot's coordinates, and the cursor over it. */
565
+ interface PlotHandle {
566
+ at: PlotPoint;
567
+ cursor: 'ns-resize' | 'ew-resize' | 'move';
568
+ }
569
+ /** One chart kind's edits. */
570
+ interface PlotEditor<D extends PlotData> {
571
+ /** The handle on a mark, or `null` for a mark a drag does not edit. */
572
+ handle(data: D, mark: {
573
+ trait: PlotMarkTrait;
574
+ pose: PlotBox;
575
+ }): PlotHandle | null;
576
+ /** A drag that took the handle on `mark`: the spec it proposes with the pointer at a point in the plot's coordinates.
577
+ * `null` when the mark has nothing to edit. */
578
+ drag(data: D, mark: PlotMarkTrait, size: {
579
+ width: number;
580
+ height: number;
581
+ }, theme: PlotTheme, measure: PlotMeasure): ((at: PlotPoint) => D) | null;
582
+ }
583
+ /** The edits `data`'s kind allows, or `null` for a kind with none. */
584
+ declare const plotEditorOf: (data: PlotData) => PlotEditor<PlotData> | null;
585
+
586
+ /**
587
+ * What makes a plot's editable marks grabbable: a hit-only point region at
588
+ * each handle its kind's editor names, composed into a layer whose id is the
589
+ * kind a press reports.
590
+ */
591
+
592
+ /** The layer id the handles are composed into. */
593
+ declare const PLOT_HANDLE_LAYER_ID = "plot-handles";
594
+ /** The affordance kind a press on a handle reports. */
595
+ declare const PLOT_HANDLE_AFFORDANCE_KIND: "layer:plot-handles";
596
+ /** What a press on a handle hands the drag, as the hit's `payload`. */
597
+ interface PlotHandleScratch {
598
+ rootId: string;
599
+ markId: string;
600
+ /** The handle, in world coordinates. */
601
+ at: PlotPoint;
602
+ }
603
+ /** A press's scratch, read back off the hit an action was handed. */
604
+ declare function plotHandleScratchOf(hit: {
605
+ payload?: unknown;
606
+ } | null | undefined): PlotHandleScratch | null;
607
+ type AnyScene = Scene<unknown, string, unknown>;
608
+ /** Where the plot root `rootId`'s corner stands in world coordinates. */
609
+ declare function plotOrigin(scene: AnyScene, rootId: string): PlotPoint;
610
+ /** Every handle in `scene` an edit may take, in world coordinates, in render order. */
611
+ declare function plotHandles(scene: AnyScene): Iterable<PlotHandleScratch & PlotHandle>;
612
+ /** The handle layer, for `CanvasExtensionApi.registerLayer`. */
613
+ declare function plotHandleLayer(scene: Scene<unknown, string, unknown>): RenderLayer<unknown>;
614
+
615
+ /**
616
+ * Dragging a plot's handle to edit the value behind it. The handle layer
617
+ * claims the press; this action previews every mark the proposed spec
618
+ * redraws, and commits the spec as one edit of the plot's root.
619
+ */
620
+
621
+ /** The action that drags a plot's handle. */
622
+ declare const PLOT_DRAG_ACTION_ID = "plot.dragValue";
623
+ /** A plot edited: the root, and the spec it now holds. */
624
+ interface PlotEdit {
625
+ rootId: string;
626
+ data: PlotData;
627
+ }
628
+ /** Options for {@link createPlotDragAction}. */
629
+ interface PlotDragOptions {
630
+ id?: string;
631
+ /** Default {@link textMeasure}. */
632
+ measure?: PlotMeasure;
633
+ /** Defaults to {@link replacePlotData} against `deps.scene`, labeled "Edit value". */
634
+ commit?: (edit: PlotEdit, ctx: InvocationCtx) => void;
635
+ }
636
+ /** The default commit: the spec into the root, its marks rebuilt, as one history entry, "Edit value". */
637
+ declare function commitPlotToScene(edit: PlotEdit, ctx: InvocationCtx, measure?: PlotMeasure): void;
638
+ /** The `plot.dragValue` action: an ongoing drag that moves one value of a plot, previewing the marks it redraws. */
639
+ declare function createPlotDragAction(opts?: PlotDragOptions): Action;
640
+
641
+ /** The action id that absorbs a press on a handle, so it reaches neither marquee nor pan. */
642
+ declare const GRAB_PLOT_HANDLE_ACTION_ID = "plot.grabHandle";
643
+ /**
644
+ * Both halves of editing a scene's plots by dragging: the handle layer to
645
+ * `registerLayer`, and the contribution to pass as `ambient`, which binds a
646
+ * drag on a handle to `plot.dragValue`. One without the other fails quietly:
647
+ * the layer alone claims presses that start nothing, the contribution alone
648
+ * binds a kind nothing reports.
649
+ */
650
+ declare function plotEditing(opts: PlotDragOptions & {
651
+ scene: Scene<unknown, string, unknown>;
652
+ }): {
653
+ layer: RenderLayer<unknown>;
654
+ contribution: Contribution;
655
+ };
656
+
657
+ /** Props for {@link PlotView}. */
658
+ interface PlotViewProps {
659
+ /** The chart. A new value redraws it, with no undo entry. */
660
+ data: PlotData;
661
+ /** Hears each edit a drag commits, with the spec it leaves. Undo and redo in the view report too. */
662
+ onChange?: (data: PlotData) => void;
663
+ width: number;
664
+ height: number;
665
+ /** Default the weasel theme's dark mode. */
666
+ theme?: PlotTheme;
667
+ measure?: PlotMeasure;
668
+ background?: Paint;
669
+ className?: string;
670
+ 'aria-label'?: string;
671
+ }
672
+ /**
673
+ * A chart on its own canvas, edited by dragging its marks: a bar's end or a
674
+ * line's point moves its value. Each drag is one undo entry. Text paints only
675
+ * in a registered font family, so register the theme's (`registerCanvasFont`).
676
+ */
677
+ declare function PlotView(props: PlotViewProps): ReactElement;
678
+
679
+ export { type ArcMarkOptions, type AreaMarkOptions, type AxisOptions, type AxisSide, type BandScale, type BandScaleOptions, type BarMarkOptions, type ContinuousScale, GRAB_PLOT_HANDLE_ACTION_ID, type LineMarkOptions, type LinearScale, type LinearScaleOptions, type LogScale, type LogScaleOptions, type MarkKind, type OrdinalScale, type OrdinalScaleOptions, PLOT_DRAG_ACTION_ID, PLOT_FONT, PLOT_HANDLE_AFFORDANCE_KIND, PLOT_HANDLE_LAYER_ID, PLOT_THEME, type PlotBox, type PlotCompiler, type PlotData, type PlotDatum, type PlotDragOptions, type PlotEdit, type PlotEditor, type PlotFont, type PlotHandle, type PlotHandleScratch, type PlotKind, type PlotMark, type PlotMarkData, type PlotMarkTrait, type PlotMeasure, type PlotPoint, type PlotRootData, type PlotRootTrait, type PlotSceneData, type PlotSpec, type PlotSpecsOptions, type PlotTheme, PlotView, type PlotViewProps, type PointMarkOptions, type ReplacePlotOptions, type RibbonMarkOptions, type RuleMarkOptions, type Scale, type SolidFill, type SolidStroke, type SpanMarkOptions, type TextMarkOptions, type TimeScale, type TimeScaleOptions, type XyBandAxis, type XyChartData, type XyLayout, type XyRangeAxis, type XySeries, type XyValueAxis, arcMark, areaMark, axisMarks, axisRoom, bandScale, barMark, commitPlotToScene, compilePlot, compileXyChart, createPlotDragAction, formatInstant, formatNumber, lineMark, linearScale, logScale, markSpecs, niceDomain, ordinalScale, plotEditing, plotEditorOf, plotHandleLayer, plotHandleScratchOf, plotHandles, plotMarkId, plotMarkOf, plotOrigin, plotRootOf, plotRootsIn, plotSpecs, plotThemeFrom, pointMark, replacePlotData, ribbonMark, ruleMark, seriesColor, setXyValue, spanMark, stepTicks, textMark, textMeasure, tickStep, timeScale, xyLength, xyValueAt, xyValueDomain };