@opendata-ai/openchart-core 2.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.
Files changed (51) hide show
  1. package/README.md +130 -0
  2. package/dist/index.d.ts +2030 -0
  3. package/dist/index.js +1176 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/styles.css +757 -0
  6. package/package.json +61 -0
  7. package/src/accessibility/__tests__/alt-text.test.ts +110 -0
  8. package/src/accessibility/__tests__/aria.test.ts +125 -0
  9. package/src/accessibility/alt-text.ts +120 -0
  10. package/src/accessibility/aria.ts +73 -0
  11. package/src/accessibility/index.ts +6 -0
  12. package/src/colors/__tests__/colorblind.test.ts +63 -0
  13. package/src/colors/__tests__/contrast.test.ts +71 -0
  14. package/src/colors/__tests__/palettes.test.ts +54 -0
  15. package/src/colors/colorblind.ts +122 -0
  16. package/src/colors/contrast.ts +94 -0
  17. package/src/colors/index.ts +27 -0
  18. package/src/colors/palettes.ts +118 -0
  19. package/src/helpers/__tests__/spec-builders.test.ts +336 -0
  20. package/src/helpers/spec-builders.ts +410 -0
  21. package/src/index.ts +129 -0
  22. package/src/labels/__tests__/collision.test.ts +197 -0
  23. package/src/labels/collision.ts +154 -0
  24. package/src/labels/index.ts +6 -0
  25. package/src/layout/__tests__/chrome.test.ts +114 -0
  26. package/src/layout/__tests__/text-measure.test.ts +49 -0
  27. package/src/layout/chrome.ts +223 -0
  28. package/src/layout/index.ts +6 -0
  29. package/src/layout/text-measure.ts +54 -0
  30. package/src/locale/__tests__/format.test.ts +90 -0
  31. package/src/locale/format.ts +132 -0
  32. package/src/locale/index.ts +6 -0
  33. package/src/responsive/__tests__/breakpoints.test.ts +58 -0
  34. package/src/responsive/breakpoints.ts +92 -0
  35. package/src/responsive/index.ts +18 -0
  36. package/src/styles/viz.css +757 -0
  37. package/src/theme/__tests__/dark-mode.test.ts +68 -0
  38. package/src/theme/__tests__/defaults.test.ts +47 -0
  39. package/src/theme/__tests__/resolve.test.ts +61 -0
  40. package/src/theme/dark-mode.ts +123 -0
  41. package/src/theme/defaults.ts +85 -0
  42. package/src/theme/index.ts +7 -0
  43. package/src/theme/resolve.ts +190 -0
  44. package/src/types/__tests__/spec.test.ts +387 -0
  45. package/src/types/encoding.ts +144 -0
  46. package/src/types/events.ts +96 -0
  47. package/src/types/index.ts +141 -0
  48. package/src/types/layout.ts +794 -0
  49. package/src/types/spec.ts +563 -0
  50. package/src/types/table.ts +105 -0
  51. package/src/types/theme.ts +159 -0
@@ -0,0 +1,563 @@
1
+ /**
2
+ * Spec types: the user-facing input contract.
3
+ *
4
+ * These types define what a user (or Claude) writes to describe a visualization.
5
+ * The engine validates, normalizes, and compiles specs into layout objects.
6
+ *
7
+ * Encoding vocabulary follows Vega-Lite conventions (field/type/aggregate)
8
+ * with editorial extensions for chrome, annotations, responsive, and dark mode.
9
+ */
10
+
11
+ // Re-import for use in LegendConfig (avoids circular by importing from sibling)
12
+ import type { LegendPosition } from '../responsive/breakpoints';
13
+ import type { ColumnConfig } from './table';
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Chart type union
17
+ // ---------------------------------------------------------------------------
18
+
19
+ /** Supported chart types. Graph is separate since it uses nodes/edges, not data + encoding. */
20
+ export type ChartType = 'line' | 'area' | 'bar' | 'column' | 'pie' | 'donut' | 'dot' | 'scatter';
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Encoding
24
+ // ---------------------------------------------------------------------------
25
+
26
+ /** Data field type, following Vega-Lite conventions. */
27
+ export type FieldType = 'quantitative' | 'temporal' | 'nominal' | 'ordinal';
28
+
29
+ /** Aggregate function applied to a field before encoding. */
30
+ export type AggregateOp = 'count' | 'sum' | 'mean' | 'median' | 'min' | 'max';
31
+
32
+ /** Axis configuration for an encoding channel. */
33
+ export interface AxisConfig {
34
+ /** Axis label text. If omitted, the field name is used. */
35
+ label?: string;
36
+ /** Number format string (d3-format). e.g. ",.0f" for comma-separated integers. */
37
+ format?: string;
38
+ /** Override tick count. Engine picks a sensible default if omitted. */
39
+ tickCount?: number;
40
+ /** Whether to show gridlines for this axis. */
41
+ grid?: boolean;
42
+ }
43
+
44
+ /** Scale configuration for an encoding channel. */
45
+ export interface ScaleConfig {
46
+ /** Explicit domain override. Auto-derived from data if omitted. */
47
+ domain?: [number, number] | string[];
48
+ /** Scale type override. Usually inferred from field type. */
49
+ type?: 'linear' | 'log' | 'time' | 'band' | 'point' | 'ordinal';
50
+ /** Whether to nice-ify the domain for clean tick values. Defaults to true. */
51
+ nice?: boolean;
52
+ /** Whether the domain should include zero. Defaults to true for quantitative. */
53
+ zero?: boolean;
54
+ }
55
+
56
+ /**
57
+ * A single encoding channel mapping a data field to a visual property.
58
+ *
59
+ * Follows the Vega-Lite encoding model: field identifies the column,
60
+ * type determines how the engine interprets values, aggregate applies
61
+ * a transformation before encoding.
62
+ */
63
+ export interface EncodingChannel {
64
+ /** Data field name (column in the data array). */
65
+ field: string;
66
+ /**
67
+ * How to interpret the field values.
68
+ * - quantitative: continuous numbers (scale: linear)
69
+ * - temporal: dates/times (scale: time)
70
+ * - nominal: unordered categories (scale: ordinal)
71
+ * - ordinal: ordered categories (scale: ordinal)
72
+ */
73
+ type: FieldType;
74
+ /** Optional aggregate to apply before encoding. */
75
+ aggregate?: AggregateOp;
76
+ /** Axis configuration. Only relevant for x and y channels. */
77
+ axis?: AxisConfig;
78
+ /** Scale configuration. */
79
+ scale?: ScaleConfig;
80
+ }
81
+
82
+ /**
83
+ * Encoding object mapping visual channels to data fields.
84
+ * Which channels are required depends on the chart type.
85
+ * See ChartEncodingRules in encoding.ts for per-type requirements.
86
+ */
87
+ export interface Encoding {
88
+ /** Horizontal position channel. */
89
+ x?: EncodingChannel;
90
+ /** Vertical position channel. */
91
+ y?: EncodingChannel;
92
+ /** Color channel (series differentiation or heatmap). */
93
+ color?: EncodingChannel;
94
+ /** Size channel (bubble charts, dot plots). */
95
+ size?: EncodingChannel;
96
+ /** Detail channel (group without encoding to a visual property). */
97
+ detail?: EncodingChannel;
98
+ }
99
+
100
+ // ---------------------------------------------------------------------------
101
+ // Graph-specific encoding
102
+ // ---------------------------------------------------------------------------
103
+
104
+ /** Encoding channel for graph nodes and edges. Same structure as EncodingChannel. */
105
+ export interface GraphEncodingChannel {
106
+ /** Data field name on the node/edge object. */
107
+ field: string;
108
+ /** How to interpret the field values. */
109
+ type?: FieldType;
110
+ }
111
+
112
+ /** Graph-specific encoding mapping visual properties to node/edge data fields. */
113
+ export interface GraphEncoding {
114
+ /** Color mapping for nodes. */
115
+ nodeColor?: GraphEncodingChannel;
116
+ /** Size mapping for nodes. */
117
+ nodeSize?: GraphEncodingChannel;
118
+ /** Color mapping for edges. */
119
+ edgeColor?: GraphEncodingChannel;
120
+ /** Width mapping for edges. */
121
+ edgeWidth?: GraphEncodingChannel;
122
+ /** Label field for nodes. */
123
+ nodeLabel?: GraphEncodingChannel;
124
+ }
125
+
126
+ /** Layout algorithm for graph visualization. */
127
+ export interface GraphLayoutConfig {
128
+ /** Layout algorithm type. */
129
+ type: 'force' | 'radial' | 'hierarchical';
130
+ /** Optional clustering configuration. */
131
+ clustering?: {
132
+ /** Field to group nodes by for cluster forces. */
133
+ field: string;
134
+ };
135
+ /** Charge strength for force layout. Negative values create repulsion. */
136
+ chargeStrength?: number;
137
+ /** Target distance between linked nodes. */
138
+ linkDistance?: number;
139
+ }
140
+
141
+ // ---------------------------------------------------------------------------
142
+ // Chrome (editorial text elements)
143
+ // ---------------------------------------------------------------------------
144
+
145
+ /** Style overrides for a chrome text element. */
146
+ export interface ChromeTextStyle {
147
+ /** Font size in pixels. */
148
+ fontSize?: number;
149
+ /** Font weight (400 = normal, 600 = semibold, 700 = bold). */
150
+ fontWeight?: number;
151
+ /** Font family override. */
152
+ fontFamily?: string;
153
+ /** Text color (CSS color string). */
154
+ color?: string;
155
+ }
156
+
157
+ /** A chrome text element with optional style overrides. */
158
+ export interface ChromeText {
159
+ /** The text content to display. */
160
+ text: string;
161
+ /** Optional style overrides. Theme defaults are used for any omitted property. */
162
+ style?: ChromeTextStyle;
163
+ /** Pixel offset for fine-tuning position. */
164
+ offset?: AnnotationOffset;
165
+ }
166
+
167
+ /**
168
+ * Editorial chrome elements: title, subtitle, source attribution, byline, footer.
169
+ * These are first-class structural elements, not string-only afterthoughts.
170
+ * Each element can be a simple string or a ChromeText object with style overrides.
171
+ */
172
+ export interface Chrome {
173
+ /** Main title displayed above the visualization. */
174
+ title?: string | ChromeText;
175
+ /** Subtitle displayed below the title, typically providing context. */
176
+ subtitle?: string | ChromeText;
177
+ /** Data source attribution, displayed below the chart area. */
178
+ source?: string | ChromeText;
179
+ /** Author or organization byline. */
180
+ byline?: string | ChromeText;
181
+ /** Footer text, displayed at the very bottom. */
182
+ footer?: string | ChromeText;
183
+ }
184
+
185
+ // ---------------------------------------------------------------------------
186
+ // Annotations
187
+ // ---------------------------------------------------------------------------
188
+
189
+ /** Pixel offset for fine-grained annotation positioning. */
190
+ export interface AnnotationOffset {
191
+ /** Horizontal pixel offset. */
192
+ dx?: number;
193
+ /** Vertical pixel offset. */
194
+ dy?: number;
195
+ }
196
+
197
+ /** Anchor direction for annotation label placement relative to the data point. */
198
+ export type AnnotationAnchor = 'top' | 'bottom' | 'left' | 'right' | 'auto';
199
+
200
+ /** Base properties shared by all annotation types. */
201
+ interface AnnotationBase {
202
+ /** Human-readable label for the annotation. */
203
+ label?: string;
204
+ /** Fill color for the annotation element. */
205
+ fill?: string;
206
+ /** Stroke color for the annotation element. */
207
+ stroke?: string;
208
+ /** Opacity from 0 to 1. */
209
+ opacity?: number;
210
+ /** Z-index for render ordering. Higher values render on top. */
211
+ zIndex?: number;
212
+ }
213
+
214
+ /**
215
+ * Text annotation positioned at a data coordinate.
216
+ * Shows a callout label at a specific point in the chart.
217
+ */
218
+ export interface TextAnnotation extends AnnotationBase {
219
+ type: 'text';
220
+ /** X-axis data value or position. */
221
+ x: string | number;
222
+ /** Y-axis data value or position. */
223
+ y: string | number;
224
+ /** The annotation text. Required for text annotations. */
225
+ text: string;
226
+ /** Font size override. */
227
+ fontSize?: number;
228
+ /** Font weight override. */
229
+ fontWeight?: number;
230
+ /** Pixel offset from the computed position. */
231
+ offset?: AnnotationOffset;
232
+ /** Anchor direction for label placement relative to the data point. */
233
+ anchor?: AnnotationAnchor;
234
+ /**
235
+ * Connector from label to anchor point.
236
+ * - `true` (default): straight line
237
+ * - `'curve'`: curved arrow with arrowhead
238
+ * - `false`: no connector
239
+ */
240
+ connector?: boolean | 'curve';
241
+ /** Per-endpoint offsets for the connector line. Allows fine-tuning where the connector starts and ends. */
242
+ connectorOffset?: {
243
+ /** Offset for the label-end of the connector. */
244
+ from?: AnnotationOffset;
245
+ /** Offset for the data-point-end of the connector. */
246
+ to?: AnnotationOffset;
247
+ };
248
+ /** Background color behind the text. Useful for readability over chart lines. */
249
+ background?: string;
250
+ }
251
+
252
+ /**
253
+ * Range annotation highlighting a region of the chart.
254
+ * Defined by x1/x2 (vertical band) or y1/y2 (horizontal band) or both (rectangle).
255
+ */
256
+ export interface RangeAnnotation extends AnnotationBase {
257
+ type: 'range';
258
+ /** Start of the range on the x-axis. */
259
+ x1?: string | number;
260
+ /** End of the range on the x-axis. */
261
+ x2?: string | number;
262
+ /** Start of the range on the y-axis. */
263
+ y1?: string | number;
264
+ /** End of the range on the y-axis. */
265
+ y2?: string | number;
266
+ /** Pixel offset for the range label. */
267
+ labelOffset?: AnnotationOffset;
268
+ /** Anchor direction for the range label. */
269
+ labelAnchor?: AnnotationAnchor;
270
+ }
271
+
272
+ /**
273
+ * Reference line annotation: a horizontal or vertical line at a data value.
274
+ * Useful for baselines (zero), targets, or thresholds.
275
+ */
276
+ export interface RefLineAnnotation extends AnnotationBase {
277
+ type: 'refline';
278
+ /** X-axis value for a vertical reference line. */
279
+ x?: string | number;
280
+ /** Y-axis value for a horizontal reference line. */
281
+ y?: string | number;
282
+ /** Line style. */
283
+ style?: 'solid' | 'dashed' | 'dotted';
284
+ /** Line width in pixels. */
285
+ strokeWidth?: number;
286
+ /** Pixel offset for the reference line label. */
287
+ labelOffset?: AnnotationOffset;
288
+ /** Anchor direction for the reference line label. */
289
+ labelAnchor?: AnnotationAnchor;
290
+ }
291
+
292
+ /** Discriminated union of all annotation types. */
293
+ export type Annotation = TextAnnotation | RangeAnnotation | RefLineAnnotation;
294
+
295
+ // ---------------------------------------------------------------------------
296
+ // Theme + Dark Mode
297
+ // ---------------------------------------------------------------------------
298
+
299
+ /**
300
+ * Dark mode behavior.
301
+ * - "auto": respect system preference (prefers-color-scheme)
302
+ * - "force": always render in dark mode
303
+ * - "off": always render in light mode (default)
304
+ */
305
+ export type DarkMode = 'auto' | 'force' | 'off';
306
+
307
+ /**
308
+ * User-facing theme configuration for overriding defaults.
309
+ * All fields are optional. The engine deep-merges these onto the default theme.
310
+ */
311
+ export interface ThemeConfig {
312
+ /** Color palette overrides. */
313
+ colors?: {
314
+ /** Categorical palette for nominal data (array of CSS color strings). */
315
+ categorical?: string[];
316
+ /** Sequential palettes keyed by name. Each is an array of color stops. */
317
+ sequential?: Record<string, string[]>;
318
+ /** Diverging palettes keyed by name. Each is an array of color stops with a neutral midpoint. */
319
+ diverging?: Record<string, string[]>;
320
+ /** Background color. */
321
+ background?: string;
322
+ /** Default text color. */
323
+ text?: string;
324
+ /** Gridline color. */
325
+ gridline?: string;
326
+ /** Axis line and tick color. */
327
+ axis?: string;
328
+ };
329
+ /** Font overrides. */
330
+ fonts?: {
331
+ /** Primary font family. */
332
+ family?: string;
333
+ /** Monospace font family (for tabular numbers). */
334
+ mono?: string;
335
+ };
336
+ /** Spacing overrides in pixels. */
337
+ spacing?: {
338
+ /** Padding inside the chart container. */
339
+ padding?: number;
340
+ /** Gap between chrome elements (title to subtitle, etc.). */
341
+ chromeGap?: number;
342
+ };
343
+ /** Border radius for chart container and tooltips. */
344
+ borderRadius?: number;
345
+ }
346
+
347
+ // ---------------------------------------------------------------------------
348
+ // Label configuration
349
+ // ---------------------------------------------------------------------------
350
+
351
+ /**
352
+ * Label density mode controlling how many data labels are shown.
353
+ * - 'all': show every label, skip collision detection
354
+ * - 'auto': show labels with collision detection (default)
355
+ * - 'endpoints': show only first and last per series (useful for line charts)
356
+ * - 'none': hide all labels (rely on tooltips and legend)
357
+ */
358
+ export type LabelDensity = 'all' | 'auto' | 'endpoints' | 'none';
359
+
360
+ /** Label display configuration for chart data labels. */
361
+ export interface LabelConfig {
362
+ /** How many labels to show. Defaults to 'auto'. */
363
+ density?: LabelDensity;
364
+ /** Number format override for label values (d3-format string, e.g. ",.0f"). */
365
+ format?: string;
366
+ /** Per-series pixel offsets for fine-tuning label positions, keyed by series name. */
367
+ offsets?: Record<string, AnnotationOffset>;
368
+ }
369
+
370
+ // ---------------------------------------------------------------------------
371
+ // Legend configuration
372
+ // ---------------------------------------------------------------------------
373
+
374
+ /** Legend display configuration. Overrides the responsive-default position. */
375
+ export interface LegendConfig {
376
+ /** Override the legend position. If omitted, the responsive strategy decides. */
377
+ position?: LegendPosition;
378
+ /** Pixel offset for fine-tuning legend position. */
379
+ offset?: AnnotationOffset;
380
+ }
381
+
382
+ // ---------------------------------------------------------------------------
383
+ // Spec types (the top-level discriminated union)
384
+ // ---------------------------------------------------------------------------
385
+
386
+ /** Data row: a plain object with string keys. */
387
+ export type DataRow = Record<string, unknown>;
388
+
389
+ /**
390
+ * Chart specification: the primary input for standard chart types.
391
+ *
392
+ * Combines a chart type with data, encoding channels, editorial chrome,
393
+ * annotations, and configuration. The engine validates, normalizes, and
394
+ * compiles this into a ChartLayout.
395
+ */
396
+ export interface ChartSpec {
397
+ /** The chart type to render. */
398
+ type: ChartType;
399
+ /** Data array: each element is a row with field values. */
400
+ data: DataRow[];
401
+ /** Encoding mapping data fields to visual channels. */
402
+ encoding: Encoding;
403
+ /** Editorial chrome (title, subtitle, source, etc.). */
404
+ chrome?: Chrome;
405
+ /** Data annotations (text callouts, highlighted ranges, reference lines). */
406
+ annotations?: Annotation[];
407
+ /** Label display configuration (density, format). */
408
+ labels?: LabelConfig;
409
+ /** Legend display configuration (position override). */
410
+ legend?: LegendConfig;
411
+ /** Whether the chart adapts to container width. Defaults to true. */
412
+ responsive?: boolean;
413
+ /** Theme configuration overrides. */
414
+ theme?: ThemeConfig;
415
+ /** Dark mode behavior. Defaults to "off". */
416
+ darkMode?: DarkMode;
417
+ }
418
+
419
+ /**
420
+ * Table specification: input for data table visualizations.
421
+ *
422
+ * Tables are a visualization type, not just an HTML grid. They support
423
+ * heatmap coloring, inline sparklines, sorted columns, search, and pagination.
424
+ */
425
+ export interface TableSpec {
426
+ /** Discriminant: always "table". */
427
+ type: 'table';
428
+ /** Data array: each element is a row. */
429
+ data: DataRow[];
430
+ /** Column definitions controlling display, sorting, formatting, and mini-charts. */
431
+ columns: ColumnConfig[];
432
+ /** Optional field to use as a unique row identifier. */
433
+ rowKey?: string;
434
+ /** Editorial chrome. */
435
+ chrome?: Chrome;
436
+ /** Theme configuration overrides. */
437
+ theme?: ThemeConfig;
438
+ /** Dark mode behavior. */
439
+ darkMode?: DarkMode;
440
+ /** Enable client-side search/filter. */
441
+ search?: boolean;
442
+ /** Pagination configuration. True for defaults, or an object with pageSize. */
443
+ pagination?: boolean | { pageSize: number };
444
+ /** Whether to stick the first column during horizontal scroll. */
445
+ stickyFirstColumn?: boolean;
446
+ /** Compact mode: reduced padding and font sizes. */
447
+ compact?: boolean;
448
+ /** Whether the table adapts to container width. Defaults to true. */
449
+ responsive?: boolean;
450
+ }
451
+
452
+ /** Graph node: must have an id, plus arbitrary data fields. */
453
+ export interface GraphNode {
454
+ /** Unique identifier for the node. */
455
+ id: string;
456
+ /** Arbitrary data fields. */
457
+ [key: string]: unknown;
458
+ }
459
+
460
+ /** Graph edge: connects two nodes by id. */
461
+ export interface GraphEdge {
462
+ /** Source node id. */
463
+ source: string;
464
+ /** Target node id. */
465
+ target: string;
466
+ /** Arbitrary data fields (weight, type, confidence, etc.). */
467
+ [key: string]: unknown;
468
+ }
469
+
470
+ /**
471
+ * Graph specification: input for network/relationship visualizations.
472
+ *
473
+ * Uses a nodes + edges data model instead of the flat data + encoding model
474
+ * used by chart types. The graph type is defined here for forward compatibility
475
+ * but rendering is deferred to a future phase.
476
+ */
477
+ export interface GraphSpec {
478
+ /** Discriminant: always "graph". */
479
+ type: 'graph';
480
+ /** Node array. Each node must have an id field. */
481
+ nodes: GraphNode[];
482
+ /** Edge array. Each edge connects source and target node ids. */
483
+ edges: GraphEdge[];
484
+ /** Graph-specific encoding mapping visual properties to node/edge fields. */
485
+ encoding?: GraphEncoding;
486
+ /** Layout algorithm configuration. */
487
+ layout?: GraphLayoutConfig;
488
+ /** Editorial chrome. */
489
+ chrome?: Chrome;
490
+ /** Annotations. */
491
+ annotations?: Annotation[];
492
+ /** Theme configuration overrides. */
493
+ theme?: ThemeConfig;
494
+ /** Dark mode behavior. */
495
+ darkMode?: DarkMode;
496
+ }
497
+
498
+ /**
499
+ * Top-level visualization spec: discriminated union on the `type` field.
500
+ *
501
+ * This is the primary API contract. Users (and Claude) write VizSpec objects,
502
+ * the engine validates and compiles them into layout objects for rendering.
503
+ */
504
+ export type VizSpec = ChartSpec | TableSpec | GraphSpec;
505
+
506
+ /** Chart spec without runtime data, for persistence/storage. */
507
+ export type ChartSpecWithoutData = Omit<ChartSpec, 'data'>;
508
+ /** Table spec without runtime data and columns, for persistence/storage. Columns can be auto-generated via dataTable(). */
509
+ export type TableSpecWithoutData = Omit<TableSpec, 'data' | 'columns'>;
510
+ /** Graph spec without runtime data, for persistence/storage. */
511
+ export type GraphSpecWithoutData = Omit<GraphSpec, 'nodes' | 'edges'>;
512
+ /** Union of data-stripped spec types for persistence/storage. */
513
+ export type StoredVizSpec = ChartSpecWithoutData | TableSpecWithoutData | GraphSpecWithoutData;
514
+
515
+ // ---------------------------------------------------------------------------
516
+ // Type guards
517
+ // ---------------------------------------------------------------------------
518
+
519
+ /** All valid chart type strings for runtime checking. */
520
+ export const CHART_TYPES: ReadonlySet<string> = new Set<ChartType>([
521
+ 'line',
522
+ 'area',
523
+ 'bar',
524
+ 'column',
525
+ 'pie',
526
+ 'donut',
527
+ 'dot',
528
+ 'scatter',
529
+ ]);
530
+
531
+ /** Check if a spec is a ChartSpec (any standard chart type). */
532
+ export function isChartSpec(spec: VizSpec): spec is ChartSpec {
533
+ return CHART_TYPES.has(spec.type);
534
+ }
535
+
536
+ /** Check if a spec is a TableSpec. */
537
+ export function isTableSpec(spec: VizSpec): spec is TableSpec {
538
+ return spec.type === 'table';
539
+ }
540
+
541
+ /** Check if a spec is a GraphSpec. */
542
+ export function isGraphSpec(spec: VizSpec): spec is GraphSpec {
543
+ return spec.type === 'graph';
544
+ }
545
+
546
+ // ---------------------------------------------------------------------------
547
+ // Annotation type guards
548
+ // ---------------------------------------------------------------------------
549
+
550
+ /** Check if an annotation is a TextAnnotation. */
551
+ export function isTextAnnotation(annotation: Annotation): annotation is TextAnnotation {
552
+ return annotation.type === 'text';
553
+ }
554
+
555
+ /** Check if an annotation is a RangeAnnotation. */
556
+ export function isRangeAnnotation(annotation: Annotation): annotation is RangeAnnotation {
557
+ return annotation.type === 'range';
558
+ }
559
+
560
+ /** Check if an annotation is a RefLineAnnotation. */
561
+ export function isRefLineAnnotation(annotation: Annotation): annotation is RefLineAnnotation {
562
+ return annotation.type === 'refline';
563
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Table column configuration types.
3
+ *
4
+ * These types define how individual columns in a TableSpec are displayed,
5
+ * formatted, and enhanced with visual features like heatmaps, bars,
6
+ * sparklines, images, and category colors.
7
+ */
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // Column visual feature configs
11
+ // ---------------------------------------------------------------------------
12
+
13
+ /** Heatmap coloring configuration for a table column. */
14
+ export interface HeatmapColumnConfig {
15
+ /**
16
+ * Color palette name or array of color stops.
17
+ * Uses sequential palette from theme if a string name is provided.
18
+ */
19
+ palette?: string | string[];
20
+ /** Explicit domain [min, max] for the color scale. Auto-derived from data if omitted. */
21
+ domain?: [number, number];
22
+ /**
23
+ * Use a different field's values for coloring while displaying this column's values.
24
+ * Useful for coloring a label column based on a numeric column.
25
+ */
26
+ colorByField?: string;
27
+ }
28
+
29
+ /** Inline bar configuration for a table column. */
30
+ export interface BarColumnConfig {
31
+ /** Maximum value for the bar scale. Auto-derived from data if omitted. */
32
+ maxValue?: number;
33
+ /** Bar fill color. Uses the first categorical palette color if omitted. */
34
+ color?: string;
35
+ }
36
+
37
+ /** Inline sparkline configuration for a table column. */
38
+ export interface SparklineColumnConfig {
39
+ /** Sparkline chart type. Defaults to "line". */
40
+ type?: 'line' | 'bar' | 'column';
41
+ /** Field containing the array of values to plot. */
42
+ valuesField?: string;
43
+ /** Sparkline color. Uses the first categorical palette color if omitted. */
44
+ color?: string;
45
+ }
46
+
47
+ /** Image cell configuration for a table column. */
48
+ export interface ImageColumnConfig {
49
+ /** Image width in pixels. Defaults to 24. */
50
+ width?: number;
51
+ /** Image height in pixels. Defaults to 24. */
52
+ height?: number;
53
+ /** Whether to apply border-radius for rounded/circular images. */
54
+ rounded?: boolean;
55
+ }
56
+
57
+ /** Category color mapping for a table column. Maps category values to CSS colors. */
58
+ export type CategoryColorsConfig = Record<string, string>;
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // Column config
62
+ // ---------------------------------------------------------------------------
63
+
64
+ /**
65
+ * Configuration for a single table column.
66
+ *
67
+ * At minimum, `key` identifies the data field. All other properties
68
+ * control display, sorting, formatting, and visual features.
69
+ *
70
+ * Only one visual feature (heatmap, bar, sparkline, image, flag, categoryColors)
71
+ * should be active per column. If multiple are provided, the engine picks
72
+ * the first one in this precedence order: sparkline > bar > heatmap > image > flag > categoryColors.
73
+ */
74
+ export interface ColumnConfig {
75
+ /** Data field key (must match a key in the data rows). */
76
+ key: string;
77
+ /** Display label for the column header. Defaults to the key if omitted. */
78
+ label?: string;
79
+ /** Whether this column is sortable. Defaults to true. */
80
+ sortable?: boolean;
81
+ /** Text alignment in the column. Defaults to "left" for text, "right" for numbers. */
82
+ align?: 'left' | 'center' | 'right';
83
+ /** Explicit column width (CSS value like "200px" or "20%"). Auto-sized if omitted. */
84
+ width?: string;
85
+ /**
86
+ * Number/date format string (d3-format or d3-time-format).
87
+ * Applied to the raw value before display. e.g. ",.0f" or "%Y-%m-%d".
88
+ */
89
+ format?: string;
90
+
91
+ // Visual features (pick at most one per column)
92
+
93
+ /** Heatmap: color the cell background based on numeric value. */
94
+ heatmap?: HeatmapColumnConfig;
95
+ /** Inline bar: render a proportional bar in the cell. */
96
+ bar?: BarColumnConfig;
97
+ /** Sparkline: render a mini line/bar chart in the cell. */
98
+ sparkline?: SparklineColumnConfig;
99
+ /** Image: render the cell value as an image URL. */
100
+ image?: ImageColumnConfig;
101
+ /** Flag: render the cell value as a country flag emoji or image. */
102
+ flag?: boolean;
103
+ /** Category colors: color-code the cell based on its categorical value. */
104
+ categoryColors?: CategoryColorsConfig;
105
+ }