@opendata-ai/openchart-core 7.11.0 → 8.0.0-rc.10
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 +1348 -132
- package/dist/index.js +556 -48
- package/dist/index.js.map +1 -1
- package/dist/styles.css +2 -1
- package/dist/styles.css.map +1 -0
- package/package.json +9 -3
- package/schema/chart.schema.json +9086 -0
- package/schema/table.schema.json +861 -0
- package/schema/vizspec.schema.json +17922 -0
- package/src/accessibility/alt-text.ts +39 -1
- package/src/accessibility/aria.ts +4 -2
- package/src/colors/__tests__/colorblind.test.ts +12 -0
- package/src/colors/colorblind.ts +11 -2
- package/src/colors/contrast.ts +11 -0
- package/src/colors/index.ts +9 -1
- package/src/colors/palettes.ts +47 -0
- package/src/helpers/__tests__/deep-merge.test.ts +54 -0
- package/src/helpers/__tests__/spec-builders.test.ts +3 -3
- package/src/helpers/deep-merge.ts +29 -0
- package/src/helpers/spec-builders.ts +7 -7
- package/src/index.ts +29 -1
- package/src/labels/collision.ts +1 -0
- package/src/layout/chrome.ts +24 -0
- package/src/layout/index.ts +1 -1
- package/src/locale/__tests__/format.test.ts +347 -11
- package/src/locale/format.ts +243 -18
- package/src/locale/index.ts +10 -1
- package/src/responsive/breakpoints.ts +7 -2
- package/src/responsive/index.ts +2 -0
- package/src/responsive/metrics.ts +77 -10
- package/src/schema/__tests__/generated-artifacts.test.ts +42 -0
- package/src/schema/__tests__/schema.test.ts +179 -0
- package/src/styles/__tests__/reduced-motion-selectors.test.ts +28 -0
- package/src/styles/animation.css +211 -168
- package/src/styles/base.css +89 -62
- package/src/styles/chrome.css +173 -179
- package/src/styles/dark.css +38 -35
- package/src/styles/graph.css +116 -111
- package/src/styles/index.css +5 -0
- package/src/styles/keyframes.css +130 -99
- package/src/styles/legend.css +14 -12
- package/src/styles/reduced-motion.css +61 -40
- package/src/styles/series-search.css +153 -0
- package/src/styles/sparkline.css +14 -12
- package/src/styles/table-animation.css +82 -80
- package/src/styles/table.css +314 -302
- package/src/styles/token-definitions.ts +199 -0
- package/src/styles/tokens.css +132 -128
- package/src/styles/tooltip.css +109 -115
- package/src/styles/you-draw-it.css +126 -0
- package/src/types/__tests__/encoding.test.ts +86 -17
- package/src/types/__tests__/spec.test.ts +16 -12
- package/src/types/encoding.ts +69 -25
- package/src/types/events.ts +107 -13
- package/src/types/index.ts +42 -3
- package/src/types/layout.ts +484 -11
- package/src/types/schema-roots.ts +32 -0
- package/src/types/spec.ts +718 -87
- package/src/types/table.ts +3 -2
package/src/types/layout.ts
CHANGED
|
@@ -127,6 +127,12 @@ export interface AxisTick {
|
|
|
127
127
|
position: number;
|
|
128
128
|
/** Formatted label string for display. */
|
|
129
129
|
label: string;
|
|
130
|
+
/**
|
|
131
|
+
* The untruncated label, set only when `label` was shortened with an ellipsis
|
|
132
|
+
* to fit the rotated-tick reservation. Renderers should surface this as the
|
|
133
|
+
* tick's accessible/tooltip text so the full category name is never lost.
|
|
134
|
+
*/
|
|
135
|
+
fullLabel?: string;
|
|
130
136
|
/** Secondary label text from axis.labelField. */
|
|
131
137
|
subtitle?: string;
|
|
132
138
|
}
|
|
@@ -190,6 +196,25 @@ export interface AxisLayout {
|
|
|
190
196
|
// Marks (data visual elements)
|
|
191
197
|
// ---------------------------------------------------------------------------
|
|
192
198
|
|
|
199
|
+
/** The four built-in fill pattern shapes for series reinforcement. */
|
|
200
|
+
export type FillPatternType = 'diagonal' | 'dot' | 'crosshatch' | 'vertical';
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* A resolved per-mark fill pattern (engine output for `mark.fillPattern: 'auto'`).
|
|
204
|
+
*
|
|
205
|
+
* The renderer builds one SVG `<pattern>` per unique (type, base, line)
|
|
206
|
+
* combination: a tile of `base` color overlaid with `line`-colored pattern
|
|
207
|
+
* geometry. Marks referencing the same resolved pattern share one def.
|
|
208
|
+
*/
|
|
209
|
+
export interface ResolvedFillPattern {
|
|
210
|
+
/** Pattern shape assigned to this mark's series. */
|
|
211
|
+
type: FillPatternType;
|
|
212
|
+
/** Base tile color: the series' solid color. */
|
|
213
|
+
base: string;
|
|
214
|
+
/** Pattern line/dot color, contrast-picked against `base`. */
|
|
215
|
+
line: string;
|
|
216
|
+
}
|
|
217
|
+
|
|
193
218
|
/** Accessibility attributes for a mark. */
|
|
194
219
|
export interface MarkAria {
|
|
195
220
|
/** ARIA label for the mark. Optional when `decorative: true` — decorative
|
|
@@ -277,6 +302,8 @@ export interface AreaMark {
|
|
|
277
302
|
interpolate?: string;
|
|
278
303
|
/** Fill color or gradient. */
|
|
279
304
|
fill: string | GradientDef;
|
|
305
|
+
/** Resolved fill pattern. Present only when `mark.fillPattern: 'auto'` assigned one. */
|
|
306
|
+
pattern?: ResolvedFillPattern;
|
|
280
307
|
/** Fill opacity. */
|
|
281
308
|
fillOpacity: number;
|
|
282
309
|
/** Optional stroke for the top boundary. */
|
|
@@ -323,6 +350,8 @@ export interface RectMark {
|
|
|
323
350
|
height: number;
|
|
324
351
|
/** Fill color or gradient. */
|
|
325
352
|
fill: string | GradientDef;
|
|
353
|
+
/** Resolved fill pattern. Present only when `mark.fillPattern: 'auto'` assigned one. */
|
|
354
|
+
pattern?: ResolvedFillPattern;
|
|
326
355
|
/** Stroke color. */
|
|
327
356
|
stroke?: string;
|
|
328
357
|
/** Stroke width. */
|
|
@@ -376,6 +405,8 @@ export interface ArcMark {
|
|
|
376
405
|
endAngle: number;
|
|
377
406
|
/** Fill color or gradient. */
|
|
378
407
|
fill: string | GradientDef;
|
|
408
|
+
/** Resolved fill pattern. Present only when `mark.fillPattern: 'auto'` assigned one. */
|
|
409
|
+
pattern?: ResolvedFillPattern;
|
|
379
410
|
/** Stroke color (usually white for slice separation). */
|
|
380
411
|
stroke: string;
|
|
381
412
|
/** Stroke width. */
|
|
@@ -434,10 +465,14 @@ export interface TextMarkLayout {
|
|
|
434
465
|
type: 'textMark';
|
|
435
466
|
/** Stable identity key for data-update transitions. */
|
|
436
467
|
key?: string;
|
|
437
|
-
/** X position. */
|
|
468
|
+
/** X position, including any `dx` offset. */
|
|
438
469
|
x: number;
|
|
439
|
-
/** Y position. */
|
|
470
|
+
/** Y position, including any `dy` offset. */
|
|
440
471
|
y: number;
|
|
472
|
+
/** Data-space anchor before `dx` was applied. Set only when the mark is offset. */
|
|
473
|
+
anchorX?: number;
|
|
474
|
+
/** Data-space anchor before `dy` was applied. Set only when the mark is offset. */
|
|
475
|
+
anchorY?: number;
|
|
441
476
|
/** Text content to display. */
|
|
442
477
|
text: string;
|
|
443
478
|
/** Fill color. */
|
|
@@ -450,6 +485,11 @@ export interface TextMarkLayout {
|
|
|
450
485
|
fontFamily?: string;
|
|
451
486
|
/** Horizontal text alignment. */
|
|
452
487
|
textAnchor: 'start' | 'middle' | 'end';
|
|
488
|
+
/**
|
|
489
|
+
* Vertical baseline. Omit for the SVG default (alphabetic), which the calendar
|
|
490
|
+
* and parliament marks rely on — they hand-compute baseline-relative offsets.
|
|
491
|
+
*/
|
|
492
|
+
dominantBaseline?: 'auto' | 'hanging' | 'central' | 'text-after-edge';
|
|
453
493
|
/** Rotation angle in degrees. */
|
|
454
494
|
angle?: number;
|
|
455
495
|
/** Original data row. */
|
|
@@ -570,10 +610,19 @@ export interface ResolvedLabel {
|
|
|
570
610
|
to: Point;
|
|
571
611
|
/** Actual data point the connector is calling out. Renderer uses this for the endpoint marker. */
|
|
572
612
|
endpoint?: Point;
|
|
613
|
+
/** Author's from-end nudge, re-applied on every geometry rebuild (mirrors `endpoint` for `to`). */
|
|
614
|
+
fromOffset?: { dx: number; dy: number };
|
|
573
615
|
/** Connector line color. */
|
|
574
616
|
stroke: string;
|
|
575
617
|
/** Connector style: straight line, curved arrow, or vertical drop-line through the data point. */
|
|
576
618
|
style: 'straight' | 'curve' | 'drop-line';
|
|
619
|
+
/** Whether to draw an arrowhead at the data-point end. */
|
|
620
|
+
arrow: boolean;
|
|
621
|
+
/**
|
|
622
|
+
* Which edge of the label block the connector leaves from. Renderers use it
|
|
623
|
+
* to shape the curve's control point. Internal; not part of the spec.
|
|
624
|
+
*/
|
|
625
|
+
exit?: 'horizontal' | 'vertical';
|
|
577
626
|
};
|
|
578
627
|
/** Background color behind the label text. */
|
|
579
628
|
background?: string;
|
|
@@ -595,6 +644,16 @@ export interface ResolvedAnnotation {
|
|
|
595
644
|
type: 'text' | 'range' | 'refline';
|
|
596
645
|
/** Stable identifier from the spec annotation, for selection/edit callbacks. */
|
|
597
646
|
id?: string;
|
|
647
|
+
/**
|
|
648
|
+
* Index of the originating annotation in `spec.annotations`.
|
|
649
|
+
*
|
|
650
|
+
* The resolved array is a *filtered* view of the spec array -- an annotation
|
|
651
|
+
* whose position falls outside the scale domain resolves to nothing and is
|
|
652
|
+
* dropped -- so a resolved array index is NOT a spec index. Anything that has
|
|
653
|
+
* to reach back to the authored annotation (thinning's priority/responsive
|
|
654
|
+
* lookup, faceted footnote numbering) must key on this.
|
|
655
|
+
*/
|
|
656
|
+
specIndex?: number;
|
|
598
657
|
/** Label text (if any). */
|
|
599
658
|
label?: ResolvedLabel;
|
|
600
659
|
/** For range: the highlighted rectangle in pixel coordinates. */
|
|
@@ -614,8 +673,9 @@ export interface ResolvedAnnotation {
|
|
|
614
673
|
/** Z-index for render ordering. Higher values render on top. */
|
|
615
674
|
zIndex?: number;
|
|
616
675
|
/**
|
|
617
|
-
* For text annotations:
|
|
618
|
-
* data
|
|
676
|
+
* For text annotations: the endpoint marker drawn at the data point.
|
|
677
|
+
* Coordinates sit on the data point itself (plus any `connectorOffset.to`),
|
|
678
|
+
* not on the pulled-back `label.connector.to`.
|
|
619
679
|
*/
|
|
620
680
|
dot?: {
|
|
621
681
|
x: number;
|
|
@@ -671,10 +731,25 @@ export interface LegendEntry {
|
|
|
671
731
|
overflow?: boolean;
|
|
672
732
|
}
|
|
673
733
|
|
|
734
|
+
/** The encoding channel a legend keys. */
|
|
735
|
+
export type LegendChannel = 'color' | 'size';
|
|
736
|
+
|
|
674
737
|
/** Base legend layout fields shared by all legend types. */
|
|
675
738
|
export interface BaseLegendLayout {
|
|
676
|
-
/**
|
|
677
|
-
|
|
739
|
+
/**
|
|
740
|
+
* Which encoding channel this legend keys. Omitted means `'color'` (every
|
|
741
|
+
* legend was a color legend before size legends existed).
|
|
742
|
+
*
|
|
743
|
+
* This is the identity that makes plural legends safe: click-to-toggle-series
|
|
744
|
+
* must fire for color entries and never for size circles, and selection/drag
|
|
745
|
+
* must be able to tell two legends apart. Without it, `.oc-legend` and
|
|
746
|
+
* `data-legend-index` are ambiguous across legends and the DOM queries that
|
|
747
|
+
* read them silently pick whichever comes first.
|
|
748
|
+
*/
|
|
749
|
+
channel?: LegendChannel;
|
|
750
|
+
/** Where the legend is positioned relative to the chart area. `'top-left'`
|
|
751
|
+
* is a map-only overlay position (floats inside the map area). */
|
|
752
|
+
position: 'top' | 'top-left' | 'right' | 'bottom' | 'bottom-right' | 'inline';
|
|
678
753
|
/** Bounding box for the legend (pixel coordinates). */
|
|
679
754
|
bounds: Rect;
|
|
680
755
|
/** Entry label style. */
|
|
@@ -733,8 +808,103 @@ export interface GradientLegendLayout extends BaseLegendLayout {
|
|
|
733
808
|
maxLabel: string;
|
|
734
809
|
}
|
|
735
810
|
|
|
736
|
-
/**
|
|
737
|
-
export
|
|
811
|
+
/** A positioned value label on a continuous/binned color legend. */
|
|
812
|
+
export interface ContinuousLegendTick {
|
|
813
|
+
/** The data value this label marks. */
|
|
814
|
+
value: number;
|
|
815
|
+
/** Formatted label text (channel format applied). */
|
|
816
|
+
label: string;
|
|
817
|
+
/** Label x position (pixel coordinates). */
|
|
818
|
+
x: number;
|
|
819
|
+
/** Horizontal text anchor for the label. */
|
|
820
|
+
anchor: 'start' | 'middle' | 'end';
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/** A discrete class swatch in a binned continuous legend. */
|
|
824
|
+
export interface ContinuousLegendBin {
|
|
825
|
+
/** Swatch x position (pixel coordinates). */
|
|
826
|
+
x: number;
|
|
827
|
+
/** Swatch width in pixels. */
|
|
828
|
+
width: number;
|
|
829
|
+
/** Fill color. */
|
|
830
|
+
color: string;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Continuous color legend for quantitative color scales on charts.
|
|
835
|
+
*
|
|
836
|
+
* Two rendering modes, resolved from the color channel's scale type:
|
|
837
|
+
* - `gradient`: a single gradient-filled bar with min/max labels (plus a
|
|
838
|
+
* midpoint label at the neutral value for diverging ramps).
|
|
839
|
+
* - `binned`: a contiguous swatch row (quantile/quantize/threshold scales)
|
|
840
|
+
* with boundary value labels between swatches at the class breaks.
|
|
841
|
+
*/
|
|
842
|
+
export interface ContinuousLegendLayout extends BaseLegendLayout {
|
|
843
|
+
/** Discriminant for legend type. */
|
|
844
|
+
type: 'continuous';
|
|
845
|
+
/** Rendering mode: gradient bar or binned swatch row. */
|
|
846
|
+
mode: 'gradient' | 'binned';
|
|
847
|
+
/** The color bar rectangle (pixel coordinates). */
|
|
848
|
+
bar: Rect;
|
|
849
|
+
/** Gradient color stops (empty in binned mode). */
|
|
850
|
+
colorStops: GradientColorStop[];
|
|
851
|
+
/** Class swatches, left to right (empty in gradient mode). */
|
|
852
|
+
bins: ContinuousLegendBin[];
|
|
853
|
+
/** Value labels rendered below the bar. */
|
|
854
|
+
ticks: ContinuousLegendTick[];
|
|
855
|
+
/** Text baseline y for the value labels (pixel coordinates). */
|
|
856
|
+
labelY: number;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/**
|
|
860
|
+
* One graduated circle in a size legend: a representative value from the size
|
|
861
|
+
* scale's domain, drawn at the radius that value actually resolves to.
|
|
862
|
+
*/
|
|
863
|
+
export interface SizeLegendCircle {
|
|
864
|
+
/** The data value this circle stands for. */
|
|
865
|
+
value: number;
|
|
866
|
+
/** Formatted label text. */
|
|
867
|
+
label: string;
|
|
868
|
+
/** Radius in pixels -- the *same* scale the marks use, never a re-derivation. */
|
|
869
|
+
radius: number;
|
|
870
|
+
/** Center x, relative to the legend bounds. */
|
|
871
|
+
cx: number;
|
|
872
|
+
/** Center y, relative to the legend bounds. Circles sit on a common baseline. */
|
|
873
|
+
cy: number;
|
|
874
|
+
/** Label baseline y, relative to the legend bounds. */
|
|
875
|
+
labelY: number;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* Size legend: graduated circles keying a quantitative `size` encoding.
|
|
880
|
+
*
|
|
881
|
+
* The circles are drawn nested (concentric, sharing a bottom edge) rather than
|
|
882
|
+
* side by side -- the newsroom convention, and it makes the *ratio* between
|
|
883
|
+
* magnitudes legible instead of just their order.
|
|
884
|
+
*
|
|
885
|
+
* The keyed values come from the scale's DOMAIN, not the data extent. The size
|
|
886
|
+
* scale clamps, so with an explicit `scale.domain` every datum past `domain[1]`
|
|
887
|
+
* renders at max radius; keying the data extent would imply the biggest circle
|
|
888
|
+
* equals the biggest datum when it may not.
|
|
889
|
+
*/
|
|
890
|
+
export interface SizeLegendLayout extends BaseLegendLayout {
|
|
891
|
+
/** Discriminant for legend type. */
|
|
892
|
+
type: 'size';
|
|
893
|
+
/** Graduated circles, smallest first. */
|
|
894
|
+
circles: SizeLegendCircle[];
|
|
895
|
+
/** Stroke color for the circle outlines. */
|
|
896
|
+
stroke: string;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
* Resolved legend layout: categorical (swatches), gradient (tilemap bar),
|
|
901
|
+
* continuous (chart color bar), or size (graduated circles).
|
|
902
|
+
*/
|
|
903
|
+
export type LegendLayout =
|
|
904
|
+
| CategoricalLegendLayout
|
|
905
|
+
| GradientLegendLayout
|
|
906
|
+
| ContinuousLegendLayout
|
|
907
|
+
| SizeLegendLayout;
|
|
738
908
|
|
|
739
909
|
// ---------------------------------------------------------------------------
|
|
740
910
|
// Endpoint labels (right-side per-series column for line/area charts)
|
|
@@ -826,7 +996,7 @@ export interface TooltipContent {
|
|
|
826
996
|
|
|
827
997
|
/** Accessibility metadata for the entire visualization. */
|
|
828
998
|
export interface A11yMetadata {
|
|
829
|
-
/**
|
|
999
|
+
/** Alt text describing the visualization: the author's `a11y.description` / `description` when set, auto-generated otherwise. */
|
|
830
1000
|
altText: string;
|
|
831
1001
|
/** Tabular data fallback for screen readers. Each inner array is a row. */
|
|
832
1002
|
dataTableFallback: unknown[][];
|
|
@@ -834,6 +1004,13 @@ export interface A11yMetadata {
|
|
|
834
1004
|
role: string;
|
|
835
1005
|
/** Whether the visualization is keyboard-navigable. */
|
|
836
1006
|
keyboardNavigable: boolean;
|
|
1007
|
+
/**
|
|
1008
|
+
* Hide the visualization from assistive technology (`aria-hidden="true"`,
|
|
1009
|
+
* no screen-reader table, no keyboard mark navigation). Optional because
|
|
1010
|
+
* only chart compilation sets it, and only on author opt-out via
|
|
1011
|
+
* `a11y.hidden`.
|
|
1012
|
+
*/
|
|
1013
|
+
hidden?: boolean;
|
|
837
1014
|
}
|
|
838
1015
|
|
|
839
1016
|
// ---------------------------------------------------------------------------
|
|
@@ -900,6 +1077,108 @@ export interface ResolvedMetricBar {
|
|
|
900
1077
|
cells: ResolvedMetricCell[];
|
|
901
1078
|
}
|
|
902
1079
|
|
|
1080
|
+
// ---------------------------------------------------------------------------
|
|
1081
|
+
// Series search (resolved)
|
|
1082
|
+
// ---------------------------------------------------------------------------
|
|
1083
|
+
|
|
1084
|
+
/**
|
|
1085
|
+
* Reserved band for the series search input (`seriesSearch`). The band is
|
|
1086
|
+
* empty SVG space between the chrome/metrics block and the top legend; the
|
|
1087
|
+
* vanilla adapter overlays an absolutely-positioned DOM combobox aligned to
|
|
1088
|
+
* this rect, so mounting it never changes the container's size.
|
|
1089
|
+
*/
|
|
1090
|
+
export interface ResolvedSeriesSearch {
|
|
1091
|
+
/** Band left x in layout coordinates. */
|
|
1092
|
+
x: number;
|
|
1093
|
+
/** Band top y in layout coordinates. */
|
|
1094
|
+
y: number;
|
|
1095
|
+
/** Band width. */
|
|
1096
|
+
width: number;
|
|
1097
|
+
/** Input row height (the reservation adds a gap below this). */
|
|
1098
|
+
height: number;
|
|
1099
|
+
/** Resolved input placeholder text. */
|
|
1100
|
+
placeholder: string;
|
|
1101
|
+
/** Distinct color-channel values available for search, in data order. */
|
|
1102
|
+
values: string[];
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
// ---------------------------------------------------------------------------
|
|
1106
|
+
// You draw it (resolved)
|
|
1107
|
+
// ---------------------------------------------------------------------------
|
|
1108
|
+
|
|
1109
|
+
/**
|
|
1110
|
+
* One x-sample the reader's drawing snaps to: a pixel x position paired with
|
|
1111
|
+
* its data-x value. `xValue` is the value reported by `onReveal`; it falls
|
|
1112
|
+
* back to the pixel x when the data value can't be recovered.
|
|
1113
|
+
*/
|
|
1114
|
+
export interface YouDrawItSample {
|
|
1115
|
+
/** Pixel x position within the chart area. */
|
|
1116
|
+
px: number;
|
|
1117
|
+
/** Data-coordinate x value at this sample (from the target line's data). */
|
|
1118
|
+
xValue: string | number;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* Linear y pixel-to-data anchors, so the vanilla layer can map a drawn pixel
|
|
1123
|
+
* y back to a data value for `onReveal` without holding the scale object.
|
|
1124
|
+
* Present only when the y scale is invertible (continuous scales).
|
|
1125
|
+
*/
|
|
1126
|
+
export interface YouDrawItYInvert {
|
|
1127
|
+
/** Top pixel of the drawing area. */
|
|
1128
|
+
topPixel: number;
|
|
1129
|
+
/** Bottom pixel of the drawing area. */
|
|
1130
|
+
bottomPixel: number;
|
|
1131
|
+
/** Data value at the top pixel. */
|
|
1132
|
+
topData: number;
|
|
1133
|
+
/** Data value at the bottom pixel. */
|
|
1134
|
+
bottomData: number;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* Scale inversion anchors serialized from the engine so the vanilla layer
|
|
1139
|
+
* can convert pixel positions to data-space values without holding d3
|
|
1140
|
+
* scale objects. Generalizes the YouDrawItYInvert pattern to both axes.
|
|
1141
|
+
*/
|
|
1142
|
+
export interface ScaleInvert {
|
|
1143
|
+
/** For continuous scales: two reference pixel-to-data anchor points. */
|
|
1144
|
+
topPixel: number;
|
|
1145
|
+
bottomPixel: number;
|
|
1146
|
+
topData: number;
|
|
1147
|
+
bottomData: number;
|
|
1148
|
+
/** Scale type so the vanilla layer applies the correct inverse transform. */
|
|
1149
|
+
scaleType?: 'linear' | 'log' | 'pow' | 'sqrt' | 'symlog' | 'time';
|
|
1150
|
+
/** For band/point scales: ordered domain values and their pixel centers. */
|
|
1151
|
+
domain?: (string | number)[];
|
|
1152
|
+
positions?: number[];
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
/**
|
|
1156
|
+
* Resolved geometry and config for the "you draw it" interactive format
|
|
1157
|
+
* (`youDrawIt`). The vanilla adapter uses this to render the hatched drawing
|
|
1158
|
+
* region, capture pointer input, and mask/reveal the real line. Present only
|
|
1159
|
+
* when the spec enables `youDrawIt` on a valid single-series line chart.
|
|
1160
|
+
*/
|
|
1161
|
+
export interface ResolvedYouDrawIt {
|
|
1162
|
+
/** Pixel x position of `from` within the chart area (drawing starts here). */
|
|
1163
|
+
fromX: number;
|
|
1164
|
+
/** The chart drawing area, for clamping pointer input and sizing the hatch region. */
|
|
1165
|
+
area: Rect;
|
|
1166
|
+
/** X samples (pixel + data value) at or after `from`, ascending by pixel x. Drawing snaps to these. */
|
|
1167
|
+
samples: YouDrawItSample[];
|
|
1168
|
+
/** Resolved prompt text for the hatched region. */
|
|
1169
|
+
prompt: string;
|
|
1170
|
+
/** Resolved skip-to-reveal button label. */
|
|
1171
|
+
revealLabel: string;
|
|
1172
|
+
/** Stroke color of the target line (the reader's guess uses a distinct pen style, but shares the palette for continuity). */
|
|
1173
|
+
lineColor: string;
|
|
1174
|
+
/** seriesKey of the target line mark, so the vanilla layer can find its DOM element. Undefined for a single-series line with no color encoding. */
|
|
1175
|
+
targetSeriesKey?: string;
|
|
1176
|
+
/** Pixel-to-data y anchors for reporting the guess in data coordinates. Absent when the y scale isn't invertible. */
|
|
1177
|
+
yInvert?: YouDrawItYInvert;
|
|
1178
|
+
/** Optional comparison line, resolved to pixel points parallel to the target line's points. */
|
|
1179
|
+
comparisonPoints?: Point[];
|
|
1180
|
+
}
|
|
1181
|
+
|
|
903
1182
|
// ---------------------------------------------------------------------------
|
|
904
1183
|
// ChartLayout (the main engine output for charts)
|
|
905
1184
|
// ---------------------------------------------------------------------------
|
|
@@ -921,7 +1200,14 @@ export interface FacetPanelLayout {
|
|
|
921
1200
|
/** Per-panel annotations. */
|
|
922
1201
|
annotations: ResolvedAnnotation[];
|
|
923
1202
|
/** Panel header label (facet value) positioned above the panel. */
|
|
924
|
-
header: {
|
|
1203
|
+
header: {
|
|
1204
|
+
text: string;
|
|
1205
|
+
x: number;
|
|
1206
|
+
y: number;
|
|
1207
|
+
fontSize: number;
|
|
1208
|
+
fontWeight: number;
|
|
1209
|
+
textAnchor?: 'start' | 'middle' | 'end';
|
|
1210
|
+
};
|
|
925
1211
|
}
|
|
926
1212
|
|
|
927
1213
|
/** Layout for a faceted (small-multiples) chart. */
|
|
@@ -934,6 +1220,8 @@ export interface FacetLayout {
|
|
|
934
1220
|
columns: number;
|
|
935
1221
|
/** Whether scales are shared across panels. */
|
|
936
1222
|
sharedScales: boolean;
|
|
1223
|
+
/** How panels are arranged: 'row' (vertical stack), 'column' (horizontal), 'wrap' (grid). */
|
|
1224
|
+
direction: 'row' | 'column' | 'wrap';
|
|
937
1225
|
}
|
|
938
1226
|
|
|
939
1227
|
/**
|
|
@@ -951,6 +1239,10 @@ export interface ChartLayout {
|
|
|
951
1239
|
chrome: ResolvedChrome;
|
|
952
1240
|
/** Resolved KPI metric bar. Present only when spec.metrics is supplied and fits. */
|
|
953
1241
|
metrics?: ResolvedMetricBar;
|
|
1242
|
+
/** Reserved series-search band. Present only when spec.seriesSearch is enabled. */
|
|
1243
|
+
seriesSearch?: ResolvedSeriesSearch;
|
|
1244
|
+
/** Resolved "you draw it" config. Present only when spec.youDrawIt is enabled and valid. */
|
|
1245
|
+
youDrawIt?: ResolvedYouDrawIt;
|
|
954
1246
|
/** Resolved axis layouts. */
|
|
955
1247
|
axes: {
|
|
956
1248
|
x?: AxisLayout;
|
|
@@ -962,8 +1254,22 @@ export interface ChartLayout {
|
|
|
962
1254
|
marks: Mark[];
|
|
963
1255
|
/** Resolved annotations with pixel positions. */
|
|
964
1256
|
annotations: ResolvedAnnotation[];
|
|
965
|
-
/**
|
|
1257
|
+
/**
|
|
1258
|
+
* Primary legend (the color legend). Kept as a singular slot: it is what every
|
|
1259
|
+
* existing consumer reads, and a chart's legend is a color legend unless it
|
|
1260
|
+
* says otherwise.
|
|
1261
|
+
*/
|
|
966
1262
|
legend: LegendLayout;
|
|
1263
|
+
/**
|
|
1264
|
+
* Every legend on the chart, in render order -- the color legend plus, when a
|
|
1265
|
+
* quantitative `size` channel is encoded, a size legend.
|
|
1266
|
+
*
|
|
1267
|
+
* `legend` is `legends[0]` when a color legend exists. Consumers that reserve
|
|
1268
|
+
* space or hit-test **must** iterate this array: a bubble chart keys both
|
|
1269
|
+
* continent (color) and population (size), and reserving margin for only the
|
|
1270
|
+
* first leaves the second drawing on top of the plot.
|
|
1271
|
+
*/
|
|
1272
|
+
legends: LegendLayout[];
|
|
967
1273
|
/**
|
|
968
1274
|
* Right-side endpoint labels column for multi-series line/area charts.
|
|
969
1275
|
* Empty `entries` means the column is suppressed (single-series, opt-out, or
|
|
@@ -988,6 +1294,10 @@ export interface ChartLayout {
|
|
|
988
1294
|
crosshair: boolean;
|
|
989
1295
|
/** Real text measurement function from the adapter (for accurate SVG text wrapping). */
|
|
990
1296
|
measureText?: MeasureTextFn;
|
|
1297
|
+
/** Scale inversion anchors for the x axis. Used by anchor drag to convert pixel to data-space. */
|
|
1298
|
+
xInvert?: ScaleInvert;
|
|
1299
|
+
/** Scale inversion anchors for the y axis. Used by anchor drag to convert pixel to data-space. */
|
|
1300
|
+
yInvert?: ScaleInvert;
|
|
991
1301
|
/** Faceted (small-multiples) layout. When present, renderers iterate panels instead of using top-level marks/axes. */
|
|
992
1302
|
facet?: FacetLayout;
|
|
993
1303
|
}
|
|
@@ -1429,6 +1739,139 @@ export interface TileMapLayout {
|
|
|
1429
1739
|
measureText: MeasureTextFn;
|
|
1430
1740
|
}
|
|
1431
1741
|
|
|
1742
|
+
// ---------------------------------------------------------------------------
|
|
1743
|
+
// MapLayout (engine output for map visualizations)
|
|
1744
|
+
// ---------------------------------------------------------------------------
|
|
1745
|
+
|
|
1746
|
+
/** A structured compile warning with a code and context. */
|
|
1747
|
+
export interface CompileWarning {
|
|
1748
|
+
code: string;
|
|
1749
|
+
message: string;
|
|
1750
|
+
context?: Record<string, unknown>;
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
/** Resolved focus target for map camera framing. */
|
|
1754
|
+
export interface MapFocusLayout {
|
|
1755
|
+
target: { x: number; y: number; width: number; height: number; padding: number };
|
|
1756
|
+
ids: Array<string | number>;
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
/** A resolved map feature mark with computed SVG path and fill. */
|
|
1760
|
+
export interface MapFeatureMark {
|
|
1761
|
+
type: 'map-feature';
|
|
1762
|
+
/** SVG path `d` attribute for this feature's shape. */
|
|
1763
|
+
path: string;
|
|
1764
|
+
/** CSS fill color. */
|
|
1765
|
+
fill: string;
|
|
1766
|
+
/** CSS stroke color (border). */
|
|
1767
|
+
stroke: string;
|
|
1768
|
+
/** Stroke width in px. */
|
|
1769
|
+
strokeWidth: number;
|
|
1770
|
+
/** Feature ID (from TopoJSON). */
|
|
1771
|
+
id: string | number;
|
|
1772
|
+
/** Feature name (from properties, if available). */
|
|
1773
|
+
name?: string;
|
|
1774
|
+
/** Original joined data row (null if no data matched this feature). */
|
|
1775
|
+
data: Record<string, unknown> | null;
|
|
1776
|
+
/** Accessibility attributes. */
|
|
1777
|
+
aria: MarkAria;
|
|
1778
|
+
/** Index for stagger animation ordering. */
|
|
1779
|
+
animationIndex: number;
|
|
1780
|
+
/** Bounding box in map-local coordinates. */
|
|
1781
|
+
bounds: { x: number; y: number; width: number; height: number };
|
|
1782
|
+
/** Centroid in map-local coordinates. */
|
|
1783
|
+
centroid: [number, number];
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
/** A resolved map point mark with projected coordinates and visual properties. */
|
|
1787
|
+
export interface MapPointMark {
|
|
1788
|
+
type: 'map-point';
|
|
1789
|
+
/** Projected center x in map-local coordinates. */
|
|
1790
|
+
cx: number;
|
|
1791
|
+
/** Projected center y in map-local coordinates. */
|
|
1792
|
+
cy: number;
|
|
1793
|
+
/** Radius in pixels (base radius before camera counter-scaling). */
|
|
1794
|
+
r: number;
|
|
1795
|
+
/** CSS fill color. */
|
|
1796
|
+
fill: string;
|
|
1797
|
+
/** CSS stroke color. */
|
|
1798
|
+
stroke: string;
|
|
1799
|
+
/** Stroke width in px. */
|
|
1800
|
+
strokeWidth: number;
|
|
1801
|
+
/** Fill opacity (0-1). */
|
|
1802
|
+
fillOpacity: number;
|
|
1803
|
+
/** Stable key for events. */
|
|
1804
|
+
key: string;
|
|
1805
|
+
/** Original data row. */
|
|
1806
|
+
data: Record<string, unknown>;
|
|
1807
|
+
/** Accessibility attributes. */
|
|
1808
|
+
aria: MarkAria;
|
|
1809
|
+
/** Index for stagger animation ordering. */
|
|
1810
|
+
animationIndex: number;
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
/** Border paths for the map (interior borders and coastline/outline). */
|
|
1814
|
+
export interface MapBorders {
|
|
1815
|
+
/** SVG path `d` for interior borders between features. */
|
|
1816
|
+
interiorPath: string;
|
|
1817
|
+
/** SVG path `d` for the outer boundary / coastline. */
|
|
1818
|
+
outlinePath: string;
|
|
1819
|
+
/** Stroke color for interior borders. */
|
|
1820
|
+
interiorStroke: string;
|
|
1821
|
+
/** Stroke color for outer boundary. */
|
|
1822
|
+
outlineStroke: string;
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
/**
|
|
1826
|
+
* MapLayout: the complete engine output for map visualizations.
|
|
1827
|
+
*
|
|
1828
|
+
* Contains everything an adapter needs to render the map: dimensions,
|
|
1829
|
+
* chrome, feature paths, borders, legend, tooltip descriptors, and
|
|
1830
|
+
* accessibility metadata.
|
|
1831
|
+
*/
|
|
1832
|
+
export interface MapLayout {
|
|
1833
|
+
/** The map drawing area (after chrome and legend are subtracted). */
|
|
1834
|
+
area: Rect;
|
|
1835
|
+
/** Resolved chrome text elements with positions and styles. */
|
|
1836
|
+
chrome: ResolvedChrome;
|
|
1837
|
+
/** Resolved feature marks with paths, fills, and joined data. */
|
|
1838
|
+
features: MapFeatureMark[];
|
|
1839
|
+
/** Border paths (interior mesh and outline). */
|
|
1840
|
+
borders: MapBorders;
|
|
1841
|
+
/** Resolved point marks for the symbol layer (empty array if no points layer). */
|
|
1842
|
+
pointMarks: MapPointMark[];
|
|
1843
|
+
/** Categorical legend for the point color channel (null if no point color). */
|
|
1844
|
+
pointCategoricalLegend: CategoricalLegendLayout | null;
|
|
1845
|
+
/** Continuous legend for the point color channel (null if no point color). */
|
|
1846
|
+
pointContinuousLegend: ContinuousLegendLayout | null;
|
|
1847
|
+
/** Continuous color legend for quantitative mode (null if hidden or categorical). */
|
|
1848
|
+
continuousLegend: ContinuousLegendLayout | null;
|
|
1849
|
+
/** Categorical swatch legend for nominal mode (null if hidden or quantitative). */
|
|
1850
|
+
categoricalLegend: CategoricalLegendLayout | null;
|
|
1851
|
+
/** Tooltip content descriptors keyed by feature ID. */
|
|
1852
|
+
tooltipDescriptors: Map<string, TooltipContent>;
|
|
1853
|
+
/** Accessibility metadata for the map. */
|
|
1854
|
+
a11y: A11yMetadata;
|
|
1855
|
+
/** Resolved theme. */
|
|
1856
|
+
theme: ResolvedTheme;
|
|
1857
|
+
/** Total SVG width in pixels. */
|
|
1858
|
+
width: number;
|
|
1859
|
+
/** Total SVG height in pixels. */
|
|
1860
|
+
height: number;
|
|
1861
|
+
/** Resolved animation config (undefined if animation is disabled). */
|
|
1862
|
+
animation: ResolvedAnimation | undefined;
|
|
1863
|
+
/** Whether to render the watermark. */
|
|
1864
|
+
watermark: boolean;
|
|
1865
|
+
/** Text measurement function for the rendering adapter. */
|
|
1866
|
+
measureText: MeasureTextFn;
|
|
1867
|
+
/** Structured compile warnings (unmatched keys, missing data, winding issues). */
|
|
1868
|
+
warnings: CompileWarning[];
|
|
1869
|
+
/** The (width, height) passed to the projection's fitSize -- camera fits against this, not the SVG viewBox. */
|
|
1870
|
+
mapSize: { width: number; height: number };
|
|
1871
|
+
/** Resolved focus target for camera framing (null if no focus). */
|
|
1872
|
+
focus: MapFocusLayout | null;
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1432
1875
|
// ---------------------------------------------------------------------------
|
|
1433
1876
|
// BarListLayout (engine output for bar list visualizations)
|
|
1434
1877
|
// ---------------------------------------------------------------------------
|
|
@@ -1529,6 +1972,24 @@ export interface CompileOptions {
|
|
|
1529
1972
|
darkMode?: boolean;
|
|
1530
1973
|
/** Whether to show the tryOpenData.ai watermark. Defaults to true. */
|
|
1531
1974
|
watermark?: boolean;
|
|
1975
|
+
/**
|
|
1976
|
+
* Enable development-time diagnostics (WCAG contrast warnings via
|
|
1977
|
+
* `console.warn`). The engine is isomorphic, so it never sniffs
|
|
1978
|
+
* `process.env`; the host decides what "dev" means and passes this flag.
|
|
1979
|
+
* Warnings are advisory only and never throw. Defaults to false.
|
|
1980
|
+
*/
|
|
1981
|
+
dev?: boolean;
|
|
1982
|
+
/**
|
|
1983
|
+
* Sink for advisory spec warnings (deprecations, did-you-mean suggestions,
|
|
1984
|
+
* unknown scheme names, overplotting/beeswarm budget notes, etc.). The engine
|
|
1985
|
+
* is isomorphic and side-effect-free by default: rather than writing to the
|
|
1986
|
+
* global `console`, it routes every advisory warning through this callback so
|
|
1987
|
+
* a host can collect, reroute, or silence them (e.g. suppress under SSR, fail
|
|
1988
|
+
* a test on any warning, surface them in a dev overlay). When omitted, the
|
|
1989
|
+
* engine defaults to `console.warn`. Never called with fatal errors, which are
|
|
1990
|
+
* thrown instead.
|
|
1991
|
+
*/
|
|
1992
|
+
onWarn?: (message: string) => void;
|
|
1532
1993
|
/**
|
|
1533
1994
|
* Real text measurement function provided by the adapter.
|
|
1534
1995
|
* Uses a hidden canvas or DOM element for accurate text dimensions.
|
|
@@ -1537,6 +1998,18 @@ export interface CompileOptions {
|
|
|
1537
1998
|
measureText?: MeasureTextFn;
|
|
1538
1999
|
/** Extra pixels to reserve on the right margin for a secondary y-axis. Set by compileLayer when resolve.scale.y is 'independent'. */
|
|
1539
2000
|
rightAxisReserve?: number;
|
|
2001
|
+
/**
|
|
2002
|
+
* Extra pixels to reserve below the plot for the auto-thinning footnote list.
|
|
2003
|
+
* Chicken-and-egg: thinning needs the chart area to decide what to demote, but
|
|
2004
|
+
* the footnotes it produces need space carved out of that same area. compileChart
|
|
2005
|
+
* resolves it by compiling once to learn the footnote count, then recompiling with
|
|
2006
|
+
* this set. Callers should not set it; it is internal to that second pass.
|
|
2007
|
+
*/
|
|
2008
|
+
footnoteReserve?: number;
|
|
2009
|
+
/** Recursion depth of the footnote-reserve convergence pass. Internal to compileChart. */
|
|
2010
|
+
footnotePass?: number;
|
|
2011
|
+
/** Set after facet height growth to prevent unbounded recursion. Internal to compileFaceted. */
|
|
2012
|
+
facetHeightGrown?: boolean;
|
|
1540
2013
|
/**
|
|
1541
2014
|
* Use this chart drawing area instead of computing one from chrome, axes,
|
|
1542
2015
|
* and legend reservations. Set by compileLayer when compiling leaf layers so
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concrete (non-generic) type aliases used as JSON Schema generation roots.
|
|
3
|
+
*
|
|
4
|
+
* `ts-json-schema-generator` cannot instantiate generic type parameters, so the
|
|
5
|
+
* schema generator (`scripts/generate-schema.mjs`) points at these fixed
|
|
6
|
+
* aliases instead of the generic `ChartSpec<TData>` / `TableSpec` directly.
|
|
7
|
+
* They resolve every generic to its `DataRow` default, which is the shape an
|
|
8
|
+
* LLM actually emits (untyped rows).
|
|
9
|
+
*
|
|
10
|
+
* These aliases exist only to shape schema output. They are not re-exported
|
|
11
|
+
* from the package barrel and add no runtime surface.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { ChartSpec, TableSpec, VizSpec } from './spec';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Root for the full published schema (`vizspec.schema.json`). The complete
|
|
18
|
+
* discriminated union of every openchart spec kind.
|
|
19
|
+
*/
|
|
20
|
+
export type VizSpecSchema = VizSpec;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Root for the ChartSpec subset schema (`chart.schema.json`). Covers all 16
|
|
24
|
+
* mark types. This is the LLM chart workhorse and the primary tool-use surface.
|
|
25
|
+
*/
|
|
26
|
+
export type ChartSpecSchema = ChartSpec;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Root for the TableSpec subset schema (`table.schema.json`). The data-table
|
|
30
|
+
* visualization spec.
|
|
31
|
+
*/
|
|
32
|
+
export type TableSpecSchema = TableSpec;
|