@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.
- package/dist/index.d.ts +495 -46
- package/dist/index.js +157 -56
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/accessibility/__tests__/alt-text.test.ts +4 -4
- package/src/accessibility/alt-text.ts +13 -16
- package/src/helpers/__tests__/spec-builders.test.ts +8 -6
- package/src/helpers/spec-builders.ts +9 -8
- package/src/types/__tests__/encoding.test.ts +267 -0
- package/src/types/__tests__/spec.test.ts +61 -22
- package/src/types/encoding.ts +116 -35
- package/src/types/index.ts +32 -0
- package/src/types/layout.ts +131 -1
- package/src/types/spec.ts +492 -45
package/src/types/encoding.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Per-
|
|
2
|
+
* Per-mark-type encoding validation rules.
|
|
3
3
|
*
|
|
4
|
-
* Defines which encoding channels are required vs optional for each
|
|
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 {
|
|
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
|
-
//
|
|
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
|
|
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:
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* -
|
|
58
|
-
* -
|
|
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
|
|
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(
|
|
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
|
-
|
|
72
|
-
|
|
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
|
-
|
|
112
|
+
point: {
|
|
76
113
|
x: required('quantitative'),
|
|
77
|
-
y: required('
|
|
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
|
-
|
|
83
|
-
x: required('
|
|
84
|
-
y: required('
|
|
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
|
-
|
|
135
|
+
arc: {
|
|
90
136
|
x: optional(),
|
|
91
137
|
y: required('quantitative'),
|
|
92
138
|
color: required('nominal', 'ordinal'),
|
|
93
|
-
size: optional(
|
|
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
|
-
|
|
148
|
+
text: {
|
|
97
149
|
x: optional(),
|
|
98
|
-
y:
|
|
99
|
-
color:
|
|
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
|
-
|
|
104
|
-
x:
|
|
105
|
-
y:
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
111
|
-
x: required(
|
|
112
|
-
y: required(
|
|
113
|
-
color: optional(
|
|
114
|
-
size: optional(
|
|
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
|
// ---------------------------------------------------------------------------
|
package/src/types/index.ts
CHANGED
|
@@ -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 {
|
package/src/types/layout.ts
CHANGED
|
@@ -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 =
|
|
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
|