@opendata-ai/openchart-core 6.28.6 → 7.0.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/dist/index.d.ts +671 -111
- package/dist/index.js +163 -66
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/colors/__tests__/contrast.test.ts +2 -2
- package/src/colors/__tests__/palettes.test.ts +22 -2
- package/src/colors/index.ts +1 -0
- package/src/colors/palettes.ts +52 -20
- package/src/helpers/spec-builders.ts +3 -1
- package/src/layout/chrome.ts +91 -10
- package/src/styles/base.css +11 -1
- package/src/styles/chrome.css +127 -2
- package/src/styles/dark.css +27 -10
- package/src/styles/tokens.css +57 -14
- package/src/styles/tooltip.css +66 -22
- package/src/theme/__tests__/dark-mode.test.ts +53 -8
- package/src/theme/__tests__/defaults.test.ts +43 -17
- package/src/theme/dark-mode.ts +76 -16
- package/src/theme/defaults.ts +44 -30
- package/src/theme/index.ts +1 -1
- package/src/theme/resolve.ts +2 -0
- package/src/types/__tests__/spec.test.ts +85 -18
- package/src/types/index.ts +16 -0
- package/src/types/layout.ts +151 -5
- package/src/types/spec.ts +517 -85
- package/src/types/theme.ts +5 -0
package/src/types/spec.ts
CHANGED
|
@@ -147,10 +147,13 @@ export interface MarkDef {
|
|
|
147
147
|
* Show point markers on line/area marks.
|
|
148
148
|
* - true: filled circles at each data point
|
|
149
149
|
* - 'transparent': invisible hover targets (legacy behavior)
|
|
150
|
-
* - 'endpoints': show only first and last point per series
|
|
150
|
+
* - 'endpoints': show only first and last point per series (hollow dots)
|
|
151
|
+
* - 'last' / 'first': show only the last (or first) point — filled dot,
|
|
152
|
+
* no white halo. Useful for highlighting the latest reading on a line
|
|
153
|
+
* chart and the default for sparkline mode.
|
|
151
154
|
* - false: no point marks (default; uses voronoi overlay for tooltips)
|
|
152
155
|
*/
|
|
153
|
-
point?: boolean | 'transparent' | 'endpoints';
|
|
156
|
+
point?: boolean | 'transparent' | 'endpoints' | 'last' | 'first';
|
|
154
157
|
/**
|
|
155
158
|
* Curve interpolation for line/area marks.
|
|
156
159
|
* Maps to d3-shape curve factories.
|
|
@@ -245,6 +248,14 @@ export interface AxisConfig {
|
|
|
245
248
|
labelColor?: string;
|
|
246
249
|
/** Secondary data field to display alongside each tick label. Renders in lighter weight/color. Only effective on categorical y-axis labels (horizontal bar charts). */
|
|
247
250
|
labelField?: string;
|
|
251
|
+
/**
|
|
252
|
+
* Where tick labels render relative to the chart area. Editorial line/area
|
|
253
|
+
* y-axes default to `'inline'`: labels sit above their gridlines at the
|
|
254
|
+
* chart's left edge, with no left gutter, axis line, or tick marks. Other
|
|
255
|
+
* axis types default to `'gutter'` (the classic placement outside the chart
|
|
256
|
+
* area).
|
|
257
|
+
*/
|
|
258
|
+
tickPosition?: 'inline' | 'gutter';
|
|
248
259
|
}
|
|
249
260
|
|
|
250
261
|
/** Scale configuration for an encoding channel. */
|
|
@@ -301,10 +312,21 @@ export type ScaleType =
|
|
|
301
312
|
* Follows the Vega-Lite encoding model: field identifies the column,
|
|
302
313
|
* type determines how the engine interprets values, aggregate applies
|
|
303
314
|
* a transformation before encoding.
|
|
315
|
+
*
|
|
316
|
+
* @template TData - The shape of a data row. When `ChartSpec<TData>` is used
|
|
317
|
+
* with a typed data array, `field` is constrained to `keyof TData & string`,
|
|
318
|
+
* giving IDE autocomplete and compile-time typo detection. Defaults to `DataRow`
|
|
319
|
+
* which degrades to plain `string` (no constraint), keeping untyped specs working.
|
|
304
320
|
*/
|
|
305
|
-
export interface EncodingChannel {
|
|
306
|
-
/**
|
|
307
|
-
|
|
321
|
+
export interface EncodingChannel<TData extends DataRow = DataRow> {
|
|
322
|
+
/**
|
|
323
|
+
* Data field name (column in the data array).
|
|
324
|
+
*
|
|
325
|
+
* When using `ChartSpec<TData>` with a typed data array, this is constrained
|
|
326
|
+
* to the actual column names of `TData`. Typos fail at compile time and IDEs
|
|
327
|
+
* autocomplete your column names.
|
|
328
|
+
*/
|
|
329
|
+
field: keyof TData & string;
|
|
308
330
|
/**
|
|
309
331
|
* How to interpret the field values.
|
|
310
332
|
* - quantitative: continuous numbers (scale: linear)
|
|
@@ -321,18 +343,33 @@ export interface EncodingChannel {
|
|
|
321
343
|
scale?: ScaleConfig;
|
|
322
344
|
/**
|
|
323
345
|
* Stacking behavior for quantitative channels (Vega-Lite aligned).
|
|
324
|
-
*
|
|
346
|
+
*
|
|
347
|
+
* Vega-Lite accepts `'zero' | 'normalize' | 'center' | null`. OpenChart adds
|
|
348
|
+
* `true` (sugar for `'zero'`) and `false` (sugar for `null`) so callers can
|
|
349
|
+
* write `stack: true` / `stack: false` without learning the string forms.
|
|
350
|
+
* The string forms are still the canonical input — the boolean shorthand is
|
|
351
|
+
* normalized to the matching string before reaching layout.
|
|
352
|
+
*
|
|
353
|
+
* - undefined: chart-type default (see below)
|
|
354
|
+
* - true | 'zero': stack from zero baseline
|
|
325
355
|
* - 'normalize': stack and normalize to fraction of total (0-1 per category)
|
|
326
356
|
* - 'center': center stacks around zero (streamgraph style)
|
|
327
|
-
* - null | false: no stacking -- renders
|
|
357
|
+
* - null | false: no stacking -- renders overlap (area) or grouped/dodged (bar)
|
|
328
358
|
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
359
|
+
* **Defaults differ by chart type:**
|
|
360
|
+
* - **Bar**: defaults to stacked. Use `stack: null` for grouped (side-by-side) bars.
|
|
361
|
+
* - **Area**: defaults to overlap (v6 breaking change). Use `stack: 'zero'` (or `true`)
|
|
362
|
+
* to opt into stacked areas. Each overlapping series renders as a translucent
|
|
363
|
+
* gradient band anchored at the y-domain baseline.
|
|
364
|
+
* - **Line**: stacking is not applied (lines always overlap).
|
|
332
365
|
*
|
|
333
366
|
* @example
|
|
334
367
|
* // Side-by-side grouped bars (comparing 2018 vs 2022 wages by firm size):
|
|
335
368
|
* "x": { "field": "pay", "type": "quantitative", "stack": null }
|
|
369
|
+
*
|
|
370
|
+
* @example
|
|
371
|
+
* // Stacked area (opt-in; default is overlap):
|
|
372
|
+
* "y": { "field": "value", "type": "quantitative", "stack": "zero" }
|
|
336
373
|
*/
|
|
337
374
|
stack?: boolean | 'zero' | 'normalize' | 'center' | null;
|
|
338
375
|
/**
|
|
@@ -374,44 +411,97 @@ export interface EncodingChannel {
|
|
|
374
411
|
|
|
375
412
|
/**
|
|
376
413
|
* Encoding object mapping visual channels to data fields.
|
|
377
|
-
* Which channels are required depends on the mark type
|
|
378
|
-
*
|
|
414
|
+
* Which channels are required depends on the mark type — see the per-mark
|
|
415
|
+
* encoding interfaces (ArcEncoding, LineEncoding, etc.) and MARK_ENCODING_RULES
|
|
416
|
+
* in encoding.ts for the full requirements table.
|
|
417
|
+
*
|
|
418
|
+
* @template TData - Propagated from ChartSpec<TData>. Constrains `field` in
|
|
419
|
+
* every channel to `keyof TData & string` when a typed data row is provided.
|
|
379
420
|
*/
|
|
380
|
-
export interface Encoding {
|
|
381
|
-
/**
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
/**
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
/**
|
|
414
|
-
|
|
421
|
+
export interface Encoding<TData extends DataRow = DataRow> {
|
|
422
|
+
/**
|
|
423
|
+
* Horizontal position channel. Required for: bar, line, area, point, tick, rect, lollipop.
|
|
424
|
+
* Maps a field to the x-axis. Use `type: 'temporal'` for dates, `'nominal'` for categories,
|
|
425
|
+
* `'quantitative'` for numbers.
|
|
426
|
+
*/
|
|
427
|
+
x?: EncodingChannel<TData>;
|
|
428
|
+
/**
|
|
429
|
+
* Vertical position channel. Required for: bar, line, area, point, tick, rect, arc, lollipop.
|
|
430
|
+
* For arc marks, this is the value field (slice size). For all others it's the y-axis position.
|
|
431
|
+
*/
|
|
432
|
+
y?: EncodingChannel<TData>;
|
|
433
|
+
/**
|
|
434
|
+
* Color channel. Required for arc marks (determines pie/donut slice coloring).
|
|
435
|
+
* Optional for all other marks -- used for series differentiation on multi-series charts,
|
|
436
|
+
* or heatmap intensity. Accepts a conditional definition to apply colors based on data predicates.
|
|
437
|
+
*/
|
|
438
|
+
color?: EncodingChannel<TData> | ConditionalValueDef<TData>;
|
|
439
|
+
/**
|
|
440
|
+
* Size channel. Used by point/bubble charts to scale dot area by a quantitative field.
|
|
441
|
+
* Accepts a conditional definition to vary size based on data predicates.
|
|
442
|
+
*/
|
|
443
|
+
size?: EncodingChannel<TData> | ConditionalValueDef<TData>;
|
|
444
|
+
/**
|
|
445
|
+
* Detail channel. Groups data into multiple series without mapping to a visual property.
|
|
446
|
+
* Useful when you want separate lines per category but don't need the color to differ.
|
|
447
|
+
*/
|
|
448
|
+
detail?: EncodingChannel<TData>;
|
|
449
|
+
/**
|
|
450
|
+
* Secondary x position. Used with `x` to define a horizontal span (rect marks, error bars).
|
|
451
|
+
* Both `x` and `x2` must be quantitative.
|
|
452
|
+
*/
|
|
453
|
+
x2?: EncodingChannel<TData>;
|
|
454
|
+
/**
|
|
455
|
+
* Secondary y position. Used with `y` to define a vertical span (rect marks, error bands).
|
|
456
|
+
* Both `y` and `y2` must be quantitative.
|
|
457
|
+
*/
|
|
458
|
+
y2?: EncodingChannel<TData>;
|
|
459
|
+
/**
|
|
460
|
+
* Data-driven opacity (0-1 range). Accepts a conditional definition to vary opacity
|
|
461
|
+
* based on data predicates (e.g., highlight selected points).
|
|
462
|
+
*/
|
|
463
|
+
opacity?: EncodingChannel<TData> | ConditionalValueDef<TData>;
|
|
464
|
+
/**
|
|
465
|
+
* Point shape encoding. Valid values: 'circle', 'square', 'diamond', 'triangle-up',
|
|
466
|
+
* 'triangle-down', 'cross'. Used on point/scatter marks to differentiate series by shape.
|
|
467
|
+
*/
|
|
468
|
+
shape?: EncodingChannel<TData>;
|
|
469
|
+
/**
|
|
470
|
+
* Stroke dash pattern encoding. Maps a nominal field to different dash patterns
|
|
471
|
+
* on line marks. Useful when color alone doesn't distinguish series well.
|
|
472
|
+
*/
|
|
473
|
+
strokeDash?: EncodingChannel<TData>;
|
|
474
|
+
/** Rotation angle encoding for point marks. Maps a quantitative field to 0-360 degrees. */
|
|
475
|
+
angle?: EncodingChannel<TData>;
|
|
476
|
+
/**
|
|
477
|
+
* Text content for `text` marks. Required when mark is `'text'`.
|
|
478
|
+
* Not meaningful for other mark types.
|
|
479
|
+
*/
|
|
480
|
+
text?: EncodingChannel<TData>;
|
|
481
|
+
/**
|
|
482
|
+
* Tooltip field(s). Shown on hover. Can be a single channel or an array for
|
|
483
|
+
* multi-field tooltips. Independent of the x/y/color encoding.
|
|
484
|
+
*/
|
|
485
|
+
tooltip?: EncodingChannel<TData> | EncodingChannel<TData>[];
|
|
486
|
+
/** Hyperlink encoding. Maps a field containing URLs to clickable marks. */
|
|
487
|
+
href?: EncodingChannel<TData>;
|
|
488
|
+
/**
|
|
489
|
+
* Drawing order. Controls z-order and stacking sort order for bar/area marks.
|
|
490
|
+
* Lower values are drawn first (behind higher values).
|
|
491
|
+
*/
|
|
492
|
+
order?: EncodingChannel<TData>;
|
|
493
|
+
/**
|
|
494
|
+
* Angular position for arc marks (pie/donut).
|
|
495
|
+
* Optional -- defaults to the `y` channel value when omitted.
|
|
496
|
+
* Not used by any other mark type.
|
|
497
|
+
*/
|
|
498
|
+
theta?: EncodingChannel<TData>;
|
|
499
|
+
/**
|
|
500
|
+
* Radial distance from center for arc marks.
|
|
501
|
+
* Optional -- only meaningful on donut charts (controls inner radius boundary).
|
|
502
|
+
* Not used by any other mark type.
|
|
503
|
+
*/
|
|
504
|
+
radius?: EncodingChannel<TData>;
|
|
415
505
|
}
|
|
416
506
|
|
|
417
507
|
// ---------------------------------------------------------------------------
|
|
@@ -492,11 +582,18 @@ export interface ChromeText {
|
|
|
492
582
|
}
|
|
493
583
|
|
|
494
584
|
/**
|
|
495
|
-
* Editorial chrome elements: title, subtitle, source attribution, byline, footer.
|
|
585
|
+
* Editorial chrome elements: eyebrow, title, subtitle, source attribution, byline, footer.
|
|
496
586
|
* These are first-class structural elements, not string-only afterthoughts.
|
|
497
587
|
* Each element can be a simple string or a ChromeText object with style overrides.
|
|
498
588
|
*/
|
|
499
589
|
export interface Chrome {
|
|
590
|
+
/**
|
|
591
|
+
* Editorial kicker/category label rendered above the title. Typically
|
|
592
|
+
* uppercase, tracked, and tinted with the accent color (e.g.
|
|
593
|
+
* "Equities · Single Ticker"). Term follows IBM Carbon and Atlassian
|
|
594
|
+
* Design System conventions; not part of Vega-Lite's title model.
|
|
595
|
+
*/
|
|
596
|
+
eyebrow?: string | ChromeText;
|
|
500
597
|
/** Main title displayed above the visualization. */
|
|
501
598
|
title?: string | ChromeText;
|
|
502
599
|
/** Subtitle displayed below the title, typically providing context. */
|
|
@@ -507,6 +604,12 @@ export interface Chrome {
|
|
|
507
604
|
byline?: string | ChromeText;
|
|
508
605
|
/** Footer text, displayed at the very bottom. */
|
|
509
606
|
footer?: string | ChromeText;
|
|
607
|
+
/**
|
|
608
|
+
* Right-anchored brand block on the footer row, paired with a small accent
|
|
609
|
+
* dot to its left. Visually balances the source/byline left-anchored text.
|
|
610
|
+
* When set, suppresses the default `tryOpenData.ai` watermark for this chart.
|
|
611
|
+
*/
|
|
612
|
+
brand?: string | ChromeText;
|
|
510
613
|
}
|
|
511
614
|
|
|
512
615
|
// ---------------------------------------------------------------------------
|
|
@@ -524,6 +627,18 @@ export interface AnnotationOffset {
|
|
|
524
627
|
/** Anchor direction for annotation label placement relative to the data point. */
|
|
525
628
|
export type AnnotationAnchor = 'top' | 'bottom' | 'left' | 'right' | 'auto';
|
|
526
629
|
|
|
630
|
+
/** Style overrides for the dot marker drawn at the connector's data-point endpoint. */
|
|
631
|
+
export interface AnnotationDot {
|
|
632
|
+
/** Circle radius in pixels. Default 5. */
|
|
633
|
+
radius?: number;
|
|
634
|
+
/** Fill color. Defaults to theme background for an "open ring" look. */
|
|
635
|
+
fill?: string;
|
|
636
|
+
/** Stroke color. Defaults to theme text color. */
|
|
637
|
+
stroke?: string;
|
|
638
|
+
/** Stroke width in pixels. Default 2. */
|
|
639
|
+
strokeWidth?: number;
|
|
640
|
+
}
|
|
641
|
+
|
|
527
642
|
/** Base properties shared by all annotation types. */
|
|
528
643
|
interface AnnotationBase {
|
|
529
644
|
/** Stable identifier for selection and edit callbacks. When provided, edit events include this ID for reliable element matching. */
|
|
@@ -554,6 +669,18 @@ export interface TextAnnotation extends AnnotationBase {
|
|
|
554
669
|
y: string | number;
|
|
555
670
|
/** The annotation text. Required for text annotations. */
|
|
556
671
|
text: string;
|
|
672
|
+
/**
|
|
673
|
+
* Optional muted second-tone text rendered below the primary `text`.
|
|
674
|
+
* Used for supporting context (e.g. methodology, source). Newlines in
|
|
675
|
+
* `text` still produce multi-line primary; subtitle is a separate block.
|
|
676
|
+
*/
|
|
677
|
+
subtitle?: string;
|
|
678
|
+
/**
|
|
679
|
+
* Optional dot marker drawn at the connector's data-point endpoint.
|
|
680
|
+
* `true` enables the default open-ring style. Pass an object to override
|
|
681
|
+
* radius, fill, stroke, or strokeWidth.
|
|
682
|
+
*/
|
|
683
|
+
dot?: boolean | AnnotationDot;
|
|
557
684
|
/** Font size override. */
|
|
558
685
|
fontSize?: number;
|
|
559
686
|
/** Font weight override. */
|
|
@@ -565,10 +692,12 @@ export interface TextAnnotation extends AnnotationBase {
|
|
|
565
692
|
/**
|
|
566
693
|
* Connector from label to anchor point.
|
|
567
694
|
* - `true` (default): straight line
|
|
695
|
+
* - `'straight'`: straight line (alias of `true`)
|
|
568
696
|
* - `'curve'`: curved arrow with arrowhead
|
|
697
|
+
* - `'drop-line'`: vertical line through the data point's x; label sits beside the line and auto-flips to the opposite side if it would overflow the chart area
|
|
569
698
|
* - `false`: no connector
|
|
570
699
|
*/
|
|
571
|
-
connector?: boolean | 'curve';
|
|
700
|
+
connector?: boolean | 'straight' | 'curve' | 'drop-line';
|
|
572
701
|
/** Per-endpoint offsets for the connector line. Allows fine-tuning where the connector starts and ends. */
|
|
573
702
|
connectorOffset?: {
|
|
574
703
|
/** Offset for the label-end of the connector. */
|
|
@@ -745,6 +874,60 @@ export interface LegendConfig {
|
|
|
745
874
|
exclude?: string[];
|
|
746
875
|
}
|
|
747
876
|
|
|
877
|
+
/**
|
|
878
|
+
* Configuration for the endpoint labels column rendered at the chart's right edge
|
|
879
|
+
* for multi-series line/area charts. Each entry pairs the series name with its
|
|
880
|
+
* last formatted value, optionally anchored to the line by an open-circle marker.
|
|
881
|
+
*
|
|
882
|
+
* The column is independent of the traditional `legend` and the legacy
|
|
883
|
+
* end-of-line labels. Together with `legend.show`, the three suppression toggles
|
|
884
|
+
* follow this truth table for ≥2-series line/area charts:
|
|
885
|
+
*
|
|
886
|
+
* | `legend.show` | `endpointLabels` | Traditional legend | Endpoint column | End-of-line labels |
|
|
887
|
+
* |--|--|--|--|--|
|
|
888
|
+
* | unset | unset | hidden (auto-suppressed) | shown (default) | hidden |
|
|
889
|
+
* | true | unset | shown | shown | hidden |
|
|
890
|
+
* | unset | false | shown (auto-suppress revoked) | hidden | hidden |
|
|
891
|
+
* | false | false | hidden | hidden | shown (last-resort) |
|
|
892
|
+
* | true | false | shown | hidden | hidden |
|
|
893
|
+
* | false | true | hidden | shown | hidden |
|
|
894
|
+
* | true | true | shown | shown | hidden |
|
|
895
|
+
*
|
|
896
|
+
* Single-series charts: column is hidden by default (nothing to identify).
|
|
897
|
+
*
|
|
898
|
+
* The implementation is in `packages/engine/src/legend/suppression.ts` —
|
|
899
|
+
* `resolveSuppression` is the single source of truth. The table above is
|
|
900
|
+
* a user-facing mirror; if the two ever diverge, the engine wins. Tests
|
|
901
|
+
* in `legend/__tests__/suppression.test.ts` enforce every cell.
|
|
902
|
+
*/
|
|
903
|
+
export interface EndpointLabelsConfig {
|
|
904
|
+
/** Explicit on/off. When undefined, the chart auto-decides based on series count. */
|
|
905
|
+
show?: boolean;
|
|
906
|
+
/** Field to read the displayed value from. Defaults to `encoding.y.field`. */
|
|
907
|
+
valueField?: string;
|
|
908
|
+
/** d3-format string for the value. Defaults to `encoding.y.axis.format`. */
|
|
909
|
+
format?: string;
|
|
910
|
+
/** Max wrap width in pixels for long series names. Default 96. */
|
|
911
|
+
width?: number;
|
|
912
|
+
/** Render an open-circle marker on the line at the right edge. Default true. */
|
|
913
|
+
showMarker?: boolean;
|
|
914
|
+
/** Override the marker style. */
|
|
915
|
+
markerStyle?: {
|
|
916
|
+
fill?: string;
|
|
917
|
+
stroke?: string;
|
|
918
|
+
strokeWidth?: number;
|
|
919
|
+
radius?: number;
|
|
920
|
+
};
|
|
921
|
+
/**
|
|
922
|
+
* Render a thin leader line from the swatch row back to the line endpoint
|
|
923
|
+
* when collision-sweep displaces a label off its data point. Default false:
|
|
924
|
+
* the marker on the line plus the swatch in the column already pair label
|
|
925
|
+
* to data; the connector tends to add visual noise for small displacements.
|
|
926
|
+
* Opt in when labels collide hard and you need the explicit tie-back.
|
|
927
|
+
*/
|
|
928
|
+
showLeader?: boolean;
|
|
929
|
+
}
|
|
930
|
+
|
|
748
931
|
// ---------------------------------------------------------------------------
|
|
749
932
|
// Spec types (the top-level discriminated union)
|
|
750
933
|
// ---------------------------------------------------------------------------
|
|
@@ -858,33 +1041,174 @@ export interface ChartSpecOverride {
|
|
|
858
1041
|
}
|
|
859
1042
|
|
|
860
1043
|
/**
|
|
861
|
-
*
|
|
1044
|
+
* A KPI/metric cell rendered above the chart in a horizontal row.
|
|
862
1045
|
*
|
|
863
|
-
*
|
|
864
|
-
*
|
|
865
|
-
*
|
|
1046
|
+
* Used for editorial dashboards (e.g. "CLOSE $186.10 +1.4%") where the chart
|
|
1047
|
+
* is paired with summary statistics. Cells lay out evenly across the chart
|
|
1048
|
+
* width. Hidden in sparkline mode; auto-stripped when the container can't
|
|
1049
|
+
* fit the laid-out values.
|
|
866
1050
|
*/
|
|
867
|
-
export interface
|
|
868
|
-
/**
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
1051
|
+
export interface Metric {
|
|
1052
|
+
/** Uppercase eyebrow label, e.g. "CLOSE". */
|
|
1053
|
+
label: string;
|
|
1054
|
+
/** Primary numeric value, e.g. "$186.10". */
|
|
1055
|
+
value: string;
|
|
1056
|
+
/** Optional change indicator, e.g. "+1.4%". Rendered next to the value. */
|
|
1057
|
+
delta?: string;
|
|
1058
|
+
/** Tone for the delta. 'up' = positive (green), 'down' = negative (red). Default 'up'. */
|
|
1059
|
+
deltaTone?: 'up' | 'down';
|
|
1060
|
+
/** Optional secondary value (e.g. multiplier "10.3×"). Rendered after the delta. */
|
|
1061
|
+
secondary?: string;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
// ---------------------------------------------------------------------------
|
|
1065
|
+
// Per-mark encoding interfaces (Task 2)
|
|
1066
|
+
// Required channels derived directly from MARK_ENCODING_RULES in encoding.ts.
|
|
1067
|
+
// TypeScript types must stay in sync with those runtime rules.
|
|
1068
|
+
// ---------------------------------------------------------------------------
|
|
1069
|
+
|
|
1070
|
+
/**
|
|
1071
|
+
* Encoding for arc marks (pie/donut charts).
|
|
1072
|
+
* - `y`: required (quantitative — the slice value)
|
|
1073
|
+
* - `color`: required (nominal/ordinal — the category)
|
|
1074
|
+
* - `theta`: optional (defaults to `y` channel)
|
|
1075
|
+
* - `radius`: optional (donut inner radius)
|
|
1076
|
+
*/
|
|
1077
|
+
export interface ArcEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
|
|
1078
|
+
y: EncodingChannel<TData>;
|
|
1079
|
+
color: EncodingChannel<TData>;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
/**
|
|
1083
|
+
* Encoding for line marks.
|
|
1084
|
+
* - `x`: required (temporal or ordinal — the time/category axis)
|
|
1085
|
+
* - `y`: required (quantitative — the value axis)
|
|
1086
|
+
*/
|
|
1087
|
+
export interface LineEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
|
|
1088
|
+
x: EncodingChannel<TData>;
|
|
1089
|
+
y: EncodingChannel<TData>;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* Encoding for bar marks (vertical columns and horizontal bars).
|
|
1094
|
+
* - `x`: required
|
|
1095
|
+
* - `y`: required
|
|
1096
|
+
*/
|
|
1097
|
+
export interface BarEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
|
|
1098
|
+
x: EncodingChannel<TData>;
|
|
1099
|
+
y: EncodingChannel<TData>;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
/**
|
|
1103
|
+
* Encoding for area marks.
|
|
1104
|
+
* - `x`: required (temporal or ordinal)
|
|
1105
|
+
* - `y`: required (quantitative)
|
|
1106
|
+
*/
|
|
1107
|
+
export interface AreaEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
|
|
1108
|
+
x: EncodingChannel<TData>;
|
|
1109
|
+
y: EncodingChannel<TData>;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
/**
|
|
1113
|
+
* Encoding for point marks (scatter plots).
|
|
1114
|
+
* - `x`: required
|
|
1115
|
+
* - `y`: required
|
|
1116
|
+
*/
|
|
1117
|
+
export interface PointEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
|
|
1118
|
+
x: EncodingChannel<TData>;
|
|
1119
|
+
y: EncodingChannel<TData>;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* Encoding for circle marks (dot plots).
|
|
1124
|
+
* - `x`: required (quantitative)
|
|
1125
|
+
* - `y`: required (nominal/ordinal — the category axis)
|
|
1126
|
+
*/
|
|
1127
|
+
export interface CircleEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
|
|
1128
|
+
x: EncodingChannel<TData>;
|
|
1129
|
+
y: EncodingChannel<TData>;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/**
|
|
1133
|
+
* Encoding for lollipop marks.
|
|
1134
|
+
* - `x`: required (quantitative)
|
|
1135
|
+
* - `y`: required (nominal/ordinal)
|
|
1136
|
+
*/
|
|
1137
|
+
export interface LollipopEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
|
|
1138
|
+
x: EncodingChannel<TData>;
|
|
1139
|
+
y: EncodingChannel<TData>;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
/**
|
|
1143
|
+
* Encoding for text marks (data-positioned labels).
|
|
1144
|
+
* - `text`: required (the field to render as text)
|
|
1145
|
+
* - `x`, `y`: optional positioning
|
|
1146
|
+
*/
|
|
1147
|
+
export interface TextEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
|
|
1148
|
+
text: EncodingChannel<TData>;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
/**
|
|
1152
|
+
* Encoding for tick marks (strip/rug plots).
|
|
1153
|
+
* - `x`: required
|
|
1154
|
+
* - `y`: required
|
|
1155
|
+
*/
|
|
1156
|
+
export interface TickEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
|
|
1157
|
+
x: EncodingChannel<TData>;
|
|
1158
|
+
y: EncodingChannel<TData>;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/**
|
|
1162
|
+
* Encoding for rect marks (heatmaps, 2D binned plots).
|
|
1163
|
+
* - `x`: required
|
|
1164
|
+
* - `y`: required
|
|
1165
|
+
*/
|
|
1166
|
+
export interface RectEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
|
|
1167
|
+
x: EncodingChannel<TData>;
|
|
1168
|
+
y: EncodingChannel<TData>;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
// ---------------------------------------------------------------------------
|
|
1172
|
+
// Shared (non-mark-specific) ChartSpec properties
|
|
1173
|
+
// ---------------------------------------------------------------------------
|
|
1174
|
+
|
|
1175
|
+
/**
|
|
1176
|
+
* Properties shared across all ChartSpec mark variants.
|
|
1177
|
+
* Extracted to avoid repeating them in every union member.
|
|
1178
|
+
*
|
|
1179
|
+
* @internal
|
|
1180
|
+
*/
|
|
1181
|
+
interface BaseChartSpec<TData extends DataRow = DataRow> {
|
|
874
1182
|
/** Data array: each element is a row with field values. */
|
|
875
|
-
data:
|
|
1183
|
+
data: TData[];
|
|
876
1184
|
/** Data transforms applied in order before encoding (filter, bin, calculate, timeUnit). */
|
|
877
1185
|
transform?: Transform[];
|
|
878
|
-
/** Encoding mapping data fields to visual channels. */
|
|
879
|
-
encoding: Encoding;
|
|
880
1186
|
/** Editorial chrome (title, subtitle, source, etc.). */
|
|
881
1187
|
chrome?: Chrome;
|
|
1188
|
+
/**
|
|
1189
|
+
* KPI/metric cells rendered as a horizontal row between subtitle and chart
|
|
1190
|
+
* area. Each cell shows a label/value pair with optional delta and secondary
|
|
1191
|
+
* value. Hidden in sparkline mode and auto-stripped when the container is
|
|
1192
|
+
* too narrow or short, or when value text would overflow its cell.
|
|
1193
|
+
*/
|
|
1194
|
+
metrics?: Metric[];
|
|
882
1195
|
/** Data annotations (text callouts, highlighted ranges, reference lines). */
|
|
883
1196
|
annotations?: Annotation[];
|
|
884
1197
|
/** Label display configuration. `false` disables all labels, `true` uses defaults. */
|
|
885
1198
|
labels?: LabelSpec;
|
|
886
1199
|
/** Legend display configuration (position override). */
|
|
887
1200
|
legend?: LegendConfig;
|
|
1201
|
+
/**
|
|
1202
|
+
* Right-side endpoint labels column for multi-series line/area charts.
|
|
1203
|
+
*
|
|
1204
|
+
* - `true` or `EndpointLabelsConfig`: render the column.
|
|
1205
|
+
* - `false`: hide the column.
|
|
1206
|
+
* - omitted: auto-enable for multi-series line/area charts, hide otherwise.
|
|
1207
|
+
*
|
|
1208
|
+
* See {@link EndpointLabelsConfig} for the full suppression truth table that
|
|
1209
|
+
* relates this flag to `legend.show` and the legacy end-of-line labels.
|
|
1210
|
+
*/
|
|
1211
|
+
endpointLabels?: boolean | EndpointLabelsConfig;
|
|
888
1212
|
/** Whether the chart adapts to container width. Defaults to true. */
|
|
889
1213
|
responsive?: boolean;
|
|
890
1214
|
/** Theme configuration overrides. */
|
|
@@ -941,6 +1265,87 @@ export interface ChartSpec {
|
|
|
941
1265
|
zIndex?: number;
|
|
942
1266
|
}
|
|
943
1267
|
|
|
1268
|
+
/**
|
|
1269
|
+
* Chart specification: the primary input for standard chart types.
|
|
1270
|
+
*
|
|
1271
|
+
* Uses the Vega-Lite `mark` property instead of `type` to specify
|
|
1272
|
+
* the visualization mark. The mark can be a string shorthand or an
|
|
1273
|
+
* object with additional properties (interpolation, point markers, etc.).
|
|
1274
|
+
*
|
|
1275
|
+
* This is a discriminated union — the `mark` value determines which encoding
|
|
1276
|
+
* channels are required. TypeScript enforces required channels at compile time,
|
|
1277
|
+
* matching the runtime rules in `MARK_ENCODING_RULES`.
|
|
1278
|
+
*
|
|
1279
|
+
* @template TData - The shape of a single data row. When provided, `encoding.*.field`
|
|
1280
|
+
* values are constrained to `keyof TData` and IDEs autocomplete your column names.
|
|
1281
|
+
* Defaults to `DataRow` (no constraint) — existing untyped specs work unchanged.
|
|
1282
|
+
*
|
|
1283
|
+
* @example
|
|
1284
|
+
* // Typed: field autocomplete + typo detection
|
|
1285
|
+
* type SalesRow = { date: string; revenue: number; region: string };
|
|
1286
|
+
* const spec: ChartSpec<SalesRow> = {
|
|
1287
|
+
* mark: 'line',
|
|
1288
|
+
* data: rows,
|
|
1289
|
+
* encoding: {
|
|
1290
|
+
* x: { field: 'date', type: 'temporal' }, // autocompletes
|
|
1291
|
+
* y: { field: 'revenue', type: 'quantitative' }, // typos fail at compile time
|
|
1292
|
+
* }
|
|
1293
|
+
* };
|
|
1294
|
+
*
|
|
1295
|
+
* @example
|
|
1296
|
+
* // Untyped: works exactly as before (no migration needed)
|
|
1297
|
+
* const spec: ChartSpec = {
|
|
1298
|
+
* mark: 'bar',
|
|
1299
|
+
* data: myData,
|
|
1300
|
+
* encoding: { x: { field: 'category', type: 'nominal' }, y: { field: 'value', type: 'quantitative' } }
|
|
1301
|
+
* };
|
|
1302
|
+
*/
|
|
1303
|
+
export type ChartSpec<TData extends DataRow = DataRow> =
|
|
1304
|
+
| (BaseChartSpec<TData> & {
|
|
1305
|
+
mark: 'arc' | (MarkDef & { type: 'arc' });
|
|
1306
|
+
encoding: ArcEncoding<TData>;
|
|
1307
|
+
})
|
|
1308
|
+
| (BaseChartSpec<TData> & {
|
|
1309
|
+
mark: 'line' | (MarkDef & { type: 'line' });
|
|
1310
|
+
encoding: LineEncoding<TData>;
|
|
1311
|
+
})
|
|
1312
|
+
| (BaseChartSpec<TData> & {
|
|
1313
|
+
mark: 'bar' | (MarkDef & { type: 'bar' });
|
|
1314
|
+
encoding: BarEncoding<TData>;
|
|
1315
|
+
})
|
|
1316
|
+
| (BaseChartSpec<TData> & {
|
|
1317
|
+
mark: 'area' | (MarkDef & { type: 'area' });
|
|
1318
|
+
encoding: AreaEncoding<TData>;
|
|
1319
|
+
})
|
|
1320
|
+
| (BaseChartSpec<TData> & {
|
|
1321
|
+
mark: 'point' | (MarkDef & { type: 'point' });
|
|
1322
|
+
encoding: PointEncoding<TData>;
|
|
1323
|
+
})
|
|
1324
|
+
| (BaseChartSpec<TData> & {
|
|
1325
|
+
mark: 'circle' | (MarkDef & { type: 'circle' });
|
|
1326
|
+
encoding: CircleEncoding<TData>;
|
|
1327
|
+
})
|
|
1328
|
+
| (BaseChartSpec<TData> & {
|
|
1329
|
+
mark: 'lollipop' | (MarkDef & { type: 'lollipop' });
|
|
1330
|
+
encoding: LollipopEncoding<TData>;
|
|
1331
|
+
})
|
|
1332
|
+
| (BaseChartSpec<TData> & {
|
|
1333
|
+
mark: 'text' | (MarkDef & { type: 'text' });
|
|
1334
|
+
encoding: TextEncoding<TData>;
|
|
1335
|
+
})
|
|
1336
|
+
| (BaseChartSpec<TData> & {
|
|
1337
|
+
mark: 'tick' | (MarkDef & { type: 'tick' });
|
|
1338
|
+
encoding: TickEncoding<TData>;
|
|
1339
|
+
})
|
|
1340
|
+
| (BaseChartSpec<TData> & {
|
|
1341
|
+
mark: 'rect' | (MarkDef & { type: 'rect' });
|
|
1342
|
+
encoding: RectEncoding<TData>;
|
|
1343
|
+
})
|
|
1344
|
+
| (BaseChartSpec<TData> & {
|
|
1345
|
+
mark: 'rule' | (MarkDef & { type: 'rule' });
|
|
1346
|
+
encoding: Encoding<TData>;
|
|
1347
|
+
});
|
|
1348
|
+
|
|
944
1349
|
/**
|
|
945
1350
|
* Table specification: input for data table visualizations.
|
|
946
1351
|
*
|
|
@@ -1062,14 +1467,19 @@ export interface ResolveConfig {
|
|
|
1062
1467
|
* Each element in `layer` is either a ChartSpec or another LayerSpec (nested).
|
|
1063
1468
|
* Shared data, encoding, and transforms at the LayerSpec level are inherited
|
|
1064
1469
|
* by children that don't define their own.
|
|
1470
|
+
*
|
|
1471
|
+
* @template TData - The shape of a single data row. When all layers share the
|
|
1472
|
+
* same data shape, pass it here to get field autocomplete across the layer.
|
|
1473
|
+
* For layers with different data shapes per child, omit TData (defaults to
|
|
1474
|
+
* `DataRow`) — children can be independently typed.
|
|
1065
1475
|
*/
|
|
1066
|
-
export interface LayerSpec {
|
|
1476
|
+
export interface LayerSpec<TData extends DataRow = DataRow> {
|
|
1067
1477
|
/** Array of child layers (ChartSpec or nested LayerSpec). */
|
|
1068
|
-
layer: (ChartSpec | LayerSpec)[];
|
|
1478
|
+
layer: (ChartSpec<TData> | LayerSpec<TData>)[];
|
|
1069
1479
|
/** Shared data inherited by children without their own data. */
|
|
1070
|
-
data?:
|
|
1480
|
+
data?: TData[];
|
|
1071
1481
|
/** Shared encoding inherited by children (overridden per-channel by child). */
|
|
1072
|
-
encoding?: Encoding
|
|
1482
|
+
encoding?: Encoding<TData>;
|
|
1073
1483
|
/** Shared transforms. Parent transforms run before child transforms. */
|
|
1074
1484
|
transform?: Transform[];
|
|
1075
1485
|
/** Editorial chrome (title, subtitle, source, etc.). */
|
|
@@ -1297,8 +1707,11 @@ export type VizSpec =
|
|
|
1297
1707
|
| TileMapSpec
|
|
1298
1708
|
| BarListSpec;
|
|
1299
1709
|
|
|
1300
|
-
/**
|
|
1301
|
-
|
|
1710
|
+
/**
|
|
1711
|
+
* Chart spec without runtime data, for persistence/storage.
|
|
1712
|
+
* Generic: `ChartSpecWithoutData<MyRow>` constrains encoding field names.
|
|
1713
|
+
*/
|
|
1714
|
+
export type ChartSpecWithoutData<TData extends DataRow = DataRow> = Omit<ChartSpec<TData>, 'data'>;
|
|
1302
1715
|
/** Table spec without runtime data and columns, for persistence/storage. Columns can be auto-generated via dataTable(). */
|
|
1303
1716
|
export type TableSpecWithoutData = Omit<TableSpec, 'data' | 'columns'>;
|
|
1304
1717
|
/** Graph spec without runtime data, for persistence/storage. */
|
|
@@ -1346,7 +1759,12 @@ export interface RelativeTimeRef {
|
|
|
1346
1759
|
|
|
1347
1760
|
/** A predicate that tests a field value against a condition. */
|
|
1348
1761
|
export interface FieldPredicate {
|
|
1349
|
-
/**
|
|
1762
|
+
/**
|
|
1763
|
+
* Data field to test.
|
|
1764
|
+
* Note: FieldPredicate is used in transforms (FilterTransform) which operate
|
|
1765
|
+
* on the raw DataRow type — field is typed as string here since transforms
|
|
1766
|
+
* can reference computed/derived fields not present in the original TData.
|
|
1767
|
+
*/
|
|
1350
1768
|
field: string;
|
|
1351
1769
|
/** Equals comparison. */
|
|
1352
1770
|
equal?: unknown;
|
|
@@ -1509,14 +1927,23 @@ export type Transform =
|
|
|
1509
1927
|
/**
|
|
1510
1928
|
* A single condition with a test predicate and resulting value/field.
|
|
1511
1929
|
* When the test passes for a datum, the condition's value/field is used.
|
|
1930
|
+
*
|
|
1931
|
+
* @template TData - Propagated from ChartSpec<TData>. Constrains `field` to
|
|
1932
|
+
* `keyof TData & string` when a typed data row is provided.
|
|
1512
1933
|
*/
|
|
1513
|
-
export interface Condition {
|
|
1934
|
+
export interface Condition<TData extends DataRow = DataRow> {
|
|
1514
1935
|
/** Predicate to test against each datum. */
|
|
1515
1936
|
test: FilterPredicate;
|
|
1516
|
-
/**
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1937
|
+
/**
|
|
1938
|
+
* Static value to use when the condition is true.
|
|
1939
|
+
* Accepted values: CSS color string, opacity (0-1), size number, or boolean flag.
|
|
1940
|
+
*/
|
|
1941
|
+
value?: string | number | boolean | null;
|
|
1942
|
+
/**
|
|
1943
|
+
* Data field to use when the condition is true.
|
|
1944
|
+
* Constrained to column names of `TData` when using `ChartSpec<TData>`.
|
|
1945
|
+
*/
|
|
1946
|
+
field?: keyof TData & string;
|
|
1520
1947
|
/** Field type for the conditional field. */
|
|
1521
1948
|
type?: FieldType;
|
|
1522
1949
|
}
|
|
@@ -1524,12 +1951,17 @@ export interface Condition {
|
|
|
1524
1951
|
/**
|
|
1525
1952
|
* A conditional value definition for an encoding channel.
|
|
1526
1953
|
* Evaluates conditions in order, falling back to the default value.
|
|
1954
|
+
*
|
|
1955
|
+
* @template TData - Propagated from ChartSpec<TData>.
|
|
1527
1956
|
*/
|
|
1528
|
-
export interface ConditionalValueDef {
|
|
1957
|
+
export interface ConditionalValueDef<TData extends DataRow = DataRow> {
|
|
1529
1958
|
/** One or more conditions to evaluate. */
|
|
1530
|
-
condition: Condition | Condition[];
|
|
1531
|
-
/**
|
|
1532
|
-
|
|
1959
|
+
condition: Condition<TData> | Condition<TData>[];
|
|
1960
|
+
/**
|
|
1961
|
+
* Default value when no condition matches.
|
|
1962
|
+
* Accepted values: CSS color string, opacity (0-1), size number, or boolean flag.
|
|
1963
|
+
*/
|
|
1964
|
+
value?: string | number | boolean | null;
|
|
1533
1965
|
}
|
|
1534
1966
|
|
|
1535
1967
|
/**
|
|
@@ -1537,9 +1969,9 @@ export interface ConditionalValueDef {
|
|
|
1537
1969
|
* Use this to narrow `EncodingChannel | ConditionalValueDef` in encoding channels
|
|
1538
1970
|
* that support conditional encoding (color, size, opacity).
|
|
1539
1971
|
*/
|
|
1540
|
-
export function isEncodingChannel(
|
|
1541
|
-
def: EncodingChannel | ConditionalValueDef | undefined,
|
|
1542
|
-
): def is EncodingChannel {
|
|
1972
|
+
export function isEncodingChannel<TData extends DataRow = DataRow>(
|
|
1973
|
+
def: EncodingChannel<TData> | ConditionalValueDef<TData> | undefined,
|
|
1974
|
+
): def is EncodingChannel<TData> {
|
|
1543
1975
|
if (!def) return false;
|
|
1544
1976
|
return 'field' in def && !('condition' in def);
|
|
1545
1977
|
}
|
|
@@ -1547,9 +1979,9 @@ export function isEncodingChannel(
|
|
|
1547
1979
|
/**
|
|
1548
1980
|
* Check if a channel definition is a ConditionalValueDef.
|
|
1549
1981
|
*/
|
|
1550
|
-
export function isConditionalDef(
|
|
1551
|
-
def: EncodingChannel | ConditionalValueDef | undefined,
|
|
1552
|
-
): def is ConditionalValueDef {
|
|
1982
|
+
export function isConditionalDef<TData extends DataRow = DataRow>(
|
|
1983
|
+
def: EncodingChannel<TData> | ConditionalValueDef<TData> | undefined,
|
|
1984
|
+
): def is ConditionalValueDef<TData> {
|
|
1553
1985
|
if (!def) return false;
|
|
1554
1986
|
return 'condition' in def;
|
|
1555
1987
|
}
|