@opendata-ai/openchart-core 3.0.0 → 6.1.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.
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Per-chart-type encoding validation rules.
2
+ * Per-mark-type encoding validation rules.
3
3
  *
4
- * Defines which encoding channels are required vs optional for each chart type.
4
+ * Defines which encoding channels are required vs optional for each mark type.
5
5
  * The engine compiler uses these rules to validate specs at runtime (TypeScript
6
6
  * catches compile-time errors; these catch runtime JSON from Claude or APIs).
7
7
  */
8
8
 
9
- import type { ChartType, FieldType } from './spec';
9
+ import type { FieldType, MarkType } from './spec';
10
10
 
11
11
  // ---------------------------------------------------------------------------
12
12
  // Encoding rule types
@@ -24,13 +24,24 @@ export interface ChannelRule {
24
24
  export interface EncodingRule {
25
25
  x: ChannelRule;
26
26
  y: ChannelRule;
27
+ x2?: ChannelRule;
28
+ y2?: ChannelRule;
27
29
  color: ChannelRule;
28
30
  size: ChannelRule;
31
+ shape?: ChannelRule;
32
+ opacity?: ChannelRule;
33
+ strokeDash?: ChannelRule;
34
+ text?: ChannelRule;
35
+ tooltip?: ChannelRule;
36
+ href?: ChannelRule;
37
+ order?: ChannelRule;
38
+ theta?: ChannelRule;
39
+ radius?: ChannelRule;
29
40
  detail: ChannelRule;
30
41
  }
31
42
 
32
43
  // ---------------------------------------------------------------------------
33
- // Chart encoding rules
44
+ // Mark encoding rules
34
45
  // ---------------------------------------------------------------------------
35
46
 
36
47
  /** Helper to create a required channel rule. */
@@ -44,78 +55,148 @@ function optional(...types: FieldType[]): ChannelRule {
44
55
  }
45
56
 
46
57
  /**
47
- * Encoding rules per chart type.
58
+ * Encoding rules per mark type.
48
59
  *
49
60
  * Defines which channels are required and what field types they accept.
50
61
  * The compiler uses this map to validate user specs at runtime.
51
62
  *
52
63
  * Key design decisions:
53
64
  * - line/area: x is temporal/ordinal (the axis), y is quantitative (the value)
54
- * - bar: horizontal bars, so y is the category axis, x is the value
55
- * - column: vertical columns, so x is the category axis, y is the value
56
- * - pie/donut: no x axis; y is the value (quantitative), color is the category
57
- * - dot: y is the category, x is quantitative
58
- * - scatter: both axes are quantitative
65
+ * - bar: covers both horizontal and vertical orientations, so both axes accept
66
+ * all relevant types. The engine validates the combination later (one axis
67
+ * must be quantitative).
68
+ * - arc: no x axis; y is the value (quantitative), color is the category
69
+ * - circle: y is the category, x is quantitative
70
+ * - point: both axes are quantitative
71
+ * - text/rule: fully optional positioning
72
+ * - tick/rect: both axes required, any type
59
73
  */
60
- export const CHART_ENCODING_RULES: Record<ChartType, EncodingRule> = {
74
+ export const MARK_ENCODING_RULES: Record<MarkType, EncodingRule> = {
75
+ bar: {
76
+ x: required('quantitative', 'nominal', 'ordinal', 'temporal'),
77
+ y: required('quantitative', 'nominal', 'ordinal'),
78
+ x2: optional('quantitative'),
79
+ y2: optional('quantitative'),
80
+ color: optional('nominal', 'ordinal', 'quantitative'),
81
+ size: optional(),
82
+ opacity: optional('quantitative'),
83
+ tooltip: optional(),
84
+ href: optional(),
85
+ order: optional('quantitative', 'ordinal'),
86
+ detail: optional('nominal'),
87
+ },
61
88
  line: {
62
89
  x: required('temporal', 'ordinal'),
63
90
  y: required('quantitative'),
64
- color: optional('nominal', 'ordinal'),
65
- size: optional('quantitative'),
91
+ color: optional('nominal', 'ordinal', 'quantitative'),
92
+ size: optional(),
93
+ opacity: optional('quantitative'),
94
+ strokeDash: optional('nominal', 'ordinal'),
95
+ tooltip: optional(),
96
+ href: optional(),
97
+ order: optional('quantitative', 'ordinal'),
66
98
  detail: optional('nominal'),
67
99
  },
68
100
  area: {
69
101
  x: required('temporal', 'ordinal'),
70
102
  y: required('quantitative'),
71
- color: optional('nominal', 'ordinal'),
72
- size: optional('quantitative'),
103
+ y2: optional('quantitative'),
104
+ color: optional('nominal', 'ordinal', 'quantitative'),
105
+ size: optional(),
106
+ opacity: optional('quantitative'),
107
+ tooltip: optional(),
108
+ href: optional(),
109
+ order: optional('quantitative', 'ordinal'),
73
110
  detail: optional('nominal'),
74
111
  },
75
- bar: {
112
+ point: {
76
113
  x: required('quantitative'),
77
- y: required('nominal', 'ordinal'),
114
+ y: required('quantitative'),
78
115
  color: optional('nominal', 'ordinal', 'quantitative'),
79
116
  size: optional('quantitative'),
117
+ shape: optional('nominal', 'ordinal'),
118
+ opacity: optional('quantitative'),
119
+ tooltip: optional(),
120
+ href: optional(),
121
+ order: optional('quantitative', 'ordinal'),
80
122
  detail: optional('nominal'),
81
123
  },
82
- column: {
83
- x: required('nominal', 'ordinal', 'temporal'),
84
- y: required('quantitative'),
124
+ circle: {
125
+ x: required('quantitative'),
126
+ y: required('nominal', 'ordinal'),
85
127
  color: optional('nominal', 'ordinal', 'quantitative'),
86
128
  size: optional('quantitative'),
129
+ opacity: optional('quantitative'),
130
+ tooltip: optional(),
131
+ href: optional(),
132
+ order: optional('quantitative', 'ordinal'),
87
133
  detail: optional('nominal'),
88
134
  },
89
- pie: {
135
+ arc: {
90
136
  x: optional(),
91
137
  y: required('quantitative'),
92
138
  color: required('nominal', 'ordinal'),
93
- size: optional('quantitative'),
139
+ size: optional(),
140
+ opacity: optional('quantitative'),
141
+ tooltip: optional(),
142
+ href: optional(),
143
+ order: optional('quantitative', 'ordinal'),
144
+ theta: optional('quantitative'),
145
+ radius: optional('quantitative'),
94
146
  detail: optional('nominal'),
95
147
  },
96
- donut: {
148
+ text: {
97
149
  x: optional(),
98
- y: required('quantitative'),
99
- color: required('nominal', 'ordinal'),
150
+ y: optional(),
151
+ color: optional(),
100
152
  size: optional('quantitative'),
153
+ opacity: optional('quantitative'),
154
+ text: required(),
155
+ tooltip: optional(),
156
+ href: optional(),
101
157
  detail: optional('nominal'),
102
158
  },
103
- dot: {
104
- x: required('quantitative'),
105
- y: required('nominal', 'ordinal'),
106
- color: optional('nominal', 'ordinal'),
107
- size: optional('quantitative'),
159
+ rule: {
160
+ x: optional(),
161
+ y: optional(),
162
+ x2: optional(),
163
+ y2: optional(),
164
+ color: optional(),
165
+ size: optional(),
166
+ opacity: optional('quantitative'),
167
+ strokeDash: optional('nominal', 'ordinal'),
168
+ tooltip: optional(),
169
+ href: optional(),
108
170
  detail: optional('nominal'),
109
171
  },
110
- scatter: {
111
- x: required('quantitative'),
112
- y: required('quantitative'),
113
- color: optional('nominal', 'ordinal'),
114
- size: optional('quantitative'),
172
+ tick: {
173
+ x: required(),
174
+ y: required(),
175
+ color: optional(),
176
+ size: optional(),
177
+ opacity: optional('quantitative'),
178
+ tooltip: optional(),
179
+ href: optional(),
180
+ detail: optional('nominal'),
181
+ },
182
+ rect: {
183
+ x: required(),
184
+ y: required(),
185
+ x2: optional(),
186
+ y2: optional(),
187
+ color: optional(),
188
+ size: optional(),
189
+ opacity: optional('quantitative'),
190
+ tooltip: optional(),
191
+ href: optional(),
192
+ order: optional('quantitative', 'ordinal'),
115
193
  detail: optional('nominal'),
116
194
  },
117
195
  };
118
196
 
197
+ /** @deprecated Use MARK_ENCODING_RULES instead. */
198
+ export const CHART_ENCODING_RULES = MARK_ENCODING_RULES;
199
+
119
200
  // ---------------------------------------------------------------------------
120
201
  // Graph encoding rules
121
202
  // ---------------------------------------------------------------------------
@@ -14,6 +14,7 @@ export type {
14
14
  export {
15
15
  CHART_ENCODING_RULES,
16
16
  GRAPH_ENCODING_RULES,
17
+ MARK_ENCODING_RULES,
17
18
  } from './encoding';
18
19
  // Event types (chart interaction callbacks)
19
20
  export type {
@@ -59,6 +60,7 @@ export type {
59
60
  ResolvedChromeElement,
60
61
  ResolvedColumn,
61
62
  ResolvedLabel,
63
+ RuleMarkLayout,
62
64
  SortState,
63
65
  SparklineData,
64
66
  SparklineTableCell,
@@ -66,8 +68,10 @@ export type {
66
68
  TableCellBase,
67
69
  TableLayout,
68
70
  TableRow,
71
+ TextMarkLayout,
69
72
  TextStyle,
70
73
  TextTableCell,
74
+ TickMarkLayout,
71
75
  TooltipContent,
72
76
  TooltipField,
73
77
  } from './layout';
@@ -78,6 +82,10 @@ export type {
78
82
  AnnotationAnchor,
79
83
  AnnotationOffset,
80
84
  AxisConfig,
85
+ BinParams,
86
+ BinTransform,
87
+ CalculateExpression,
88
+ CalculateTransform,
81
89
  ChartSpec,
82
90
  ChartSpecOverride,
83
91
  ChartSpecWithoutData,
@@ -85,11 +93,16 @@ export type {
85
93
  Chrome,
86
94
  ChromeText,
87
95
  ChromeTextStyle,
96
+ Condition,
97
+ ConditionalValueDef,
88
98
  DarkMode,
89
99
  DataRow,
90
100
  Encoding,
91
101
  EncodingChannel,
102
+ FieldPredicate,
92
103
  FieldType,
104
+ FilterPredicate,
105
+ FilterTransform,
93
106
  GraphEdge,
94
107
  GraphEncoding,
95
108
  GraphEncodingChannel,
@@ -99,27 +112,46 @@ export type {
99
112
  GraphSpecWithoutData,
100
113
  LabelConfig,
101
114
  LabelDensity,
115
+ LayerSpec,
102
116
  LegendConfig,
117
+ LogicalAnd,
118
+ LogicalNot,
119
+ LogicalOr,
120
+ MarkDef,
121
+ MarkType,
103
122
  NodeOverride,
104
123
  RangeAnnotation,
105
124
  RefLineAnnotation,
125
+ ResolveConfig,
126
+ ResolveMode,
106
127
  ScaleConfig,
128
+ ScaleType,
107
129
  SeriesStyle,
108
130
  StoredVizSpec,
109
131
  TableSpec,
110
132
  TableSpecWithoutData,
111
133
  TextAnnotation,
112
134
  ThemeConfig,
135
+ TimeUnit,
136
+ TimeUnitTransform,
137
+ Transform,
113
138
  VizSpec,
114
139
  } from './spec';
115
140
  export {
116
141
  CHART_TYPES,
117
142
  isChartSpec,
143
+ isConditionalDef,
144
+ isEncodingChannel,
118
145
  isGraphSpec,
146
+ isLayerSpec,
119
147
  isRangeAnnotation,
120
148
  isRefLineAnnotation,
121
149
  isTableSpec,
122
150
  isTextAnnotation,
151
+ MARK_DISPLAY_NAMES,
152
+ MARK_TYPES,
153
+ resolveMarkDef,
154
+ resolveMarkType,
123
155
  } from './spec';
124
156
  // Table types
125
157
  export type {
@@ -148,6 +148,22 @@ export interface AxisLayout {
148
148
  start: Point;
149
149
  /** Axis line end position. */
150
150
  end: Point;
151
+ /** Axis orientation (which side it appears on). */
152
+ orient?: 'top' | 'bottom' | 'left' | 'right';
153
+ /** Whether to show the axis domain line. Defaults to true. */
154
+ domainLine?: boolean;
155
+ /** Whether to show tick marks. Defaults to true. */
156
+ tickMarks?: boolean;
157
+ /** Axis position offset in pixels. */
158
+ offset?: number;
159
+ /** Padding between axis title and axis in pixels. */
160
+ titlePadding?: number;
161
+ /** Padding between tick labels and axis in pixels. */
162
+ labelPadding?: number;
163
+ /** How overlapping labels should be handled. */
164
+ labelOverlap?: boolean | 'parity' | 'greedy';
165
+ /** Whether to flush labels to the axis edges. */
166
+ labelFlush?: boolean;
151
167
  }
152
168
 
153
169
  // ---------------------------------------------------------------------------
@@ -187,6 +203,16 @@ export interface LineMark {
187
203
  seriesKey?: string;
188
204
  /** Original data rows for this series. */
189
205
  data: Record<string, unknown>[];
206
+ /**
207
+ * Pixel-coordinate data points for spatial tooltip lookup (voronoi overlay).
208
+ * Each entry pairs a pixel position with its original data row.
209
+ */
210
+ dataPoints?: Array<{
211
+ x: number;
212
+ y: number;
213
+ datum: Record<string, unknown>;
214
+ tooltip?: TooltipContent;
215
+ }>;
190
216
  /** Resolved label after collision detection. */
191
217
  label?: ResolvedLabel;
192
218
  /** Accessibility attributes. */
@@ -219,6 +245,16 @@ export interface AreaMark {
219
245
  seriesKey?: string;
220
246
  /** Original data rows. */
221
247
  data: Record<string, unknown>[];
248
+ /**
249
+ * Pixel-coordinate data points for spatial tooltip lookup (voronoi overlay).
250
+ * Each entry pairs a pixel position with its original data row.
251
+ */
252
+ dataPoints?: Array<{
253
+ x: number;
254
+ y: number;
255
+ datum: Record<string, unknown>;
256
+ tooltip?: TooltipContent;
257
+ }>;
222
258
  /** Accessibility attributes. */
223
259
  aria: MarkAria;
224
260
  }
@@ -315,8 +351,102 @@ export interface PointMark {
315
351
  aria: MarkAria;
316
352
  }
317
353
 
354
+ /**
355
+ * Text mark: data-positioned text label.
356
+ * Used by text charts for showing values directly at data coordinates.
357
+ */
358
+ export interface TextMarkLayout {
359
+ type: 'textMark';
360
+ /** X position. */
361
+ x: number;
362
+ /** Y position. */
363
+ y: number;
364
+ /** Text content to display. */
365
+ text: string;
366
+ /** Fill color. */
367
+ fill: string;
368
+ /** Font size in pixels. */
369
+ fontSize: number;
370
+ /** Font weight. */
371
+ fontWeight?: number;
372
+ /** Font family override. */
373
+ fontFamily?: string;
374
+ /** Horizontal text alignment. */
375
+ textAnchor: 'start' | 'middle' | 'end';
376
+ /** Rotation angle in degrees. */
377
+ angle?: number;
378
+ /** Original data row. */
379
+ data: Record<string, unknown>;
380
+ /** Resolved label. */
381
+ label?: ResolvedLabel;
382
+ /** Accessibility attributes. */
383
+ aria: MarkAria;
384
+ }
385
+
386
+ /**
387
+ * Rule mark: a line segment between two points.
388
+ * Used for reference lines rendered as data marks.
389
+ */
390
+ export interface RuleMarkLayout {
391
+ type: 'rule';
392
+ /** Start x position. */
393
+ x1: number;
394
+ /** Start y position. */
395
+ y1: number;
396
+ /** End x position. */
397
+ x2: number;
398
+ /** End y position. */
399
+ y2: number;
400
+ /** Stroke color. */
401
+ stroke: string;
402
+ /** Stroke width in pixels. */
403
+ strokeWidth: number;
404
+ /** Stroke dash pattern (e.g. "4 2"). */
405
+ strokeDasharray?: string;
406
+ /** Opacity (0-1). */
407
+ opacity?: number;
408
+ /** Original data row. */
409
+ data: Record<string, unknown>;
410
+ /** Accessibility attributes. */
411
+ aria: MarkAria;
412
+ }
413
+
414
+ /**
415
+ * Tick mark: a short line segment at a data coordinate.
416
+ * Used for strip plots and rug plots.
417
+ */
418
+ export interface TickMarkLayout {
419
+ type: 'tick';
420
+ /** X position. */
421
+ x: number;
422
+ /** Y position. */
423
+ y: number;
424
+ /** Length of the tick mark in pixels. */
425
+ length: number;
426
+ /** Whether the tick is horizontal or vertical. */
427
+ orient: 'horizontal' | 'vertical';
428
+ /** Stroke color. */
429
+ stroke: string;
430
+ /** Stroke width in pixels. */
431
+ strokeWidth: number;
432
+ /** Opacity (0-1). */
433
+ opacity?: number;
434
+ /** Original data row. */
435
+ data: Record<string, unknown>;
436
+ /** Accessibility attributes. */
437
+ aria: MarkAria;
438
+ }
439
+
318
440
  /** Discriminated union of all mark types. */
319
- export type Mark = LineMark | AreaMark | RectMark | ArcMark | PointMark;
441
+ export type Mark =
442
+ | LineMark
443
+ | AreaMark
444
+ | RectMark
445
+ | ArcMark
446
+ | PointMark
447
+ | TextMarkLayout
448
+ | RuleMarkLayout
449
+ | TickMarkLayout;
320
450
 
321
451
  // ---------------------------------------------------------------------------
322
452
  // Labels