@opendata-ai/openchart-engine 1.2.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 (85) hide show
  1. package/dist/index.d.ts +366 -0
  2. package/dist/index.js +4227 -0
  3. package/dist/index.js.map +1 -0
  4. package/package.json +62 -0
  5. package/src/__test-fixtures__/specs.ts +124 -0
  6. package/src/__tests__/axes.test.ts +114 -0
  7. package/src/__tests__/compile-chart.test.ts +337 -0
  8. package/src/__tests__/dimensions.test.ts +151 -0
  9. package/src/__tests__/legend.test.ts +113 -0
  10. package/src/__tests__/scales.test.ts +109 -0
  11. package/src/annotations/__tests__/compute.test.ts +454 -0
  12. package/src/annotations/compute.ts +603 -0
  13. package/src/charts/__tests__/registry.test.ts +110 -0
  14. package/src/charts/bar/__tests__/compute.test.ts +294 -0
  15. package/src/charts/bar/__tests__/labels.test.ts +75 -0
  16. package/src/charts/bar/compute.ts +205 -0
  17. package/src/charts/bar/index.ts +33 -0
  18. package/src/charts/bar/labels.ts +132 -0
  19. package/src/charts/column/__tests__/compute.test.ts +277 -0
  20. package/src/charts/column/compute.ts +282 -0
  21. package/src/charts/column/index.ts +33 -0
  22. package/src/charts/column/labels.ts +108 -0
  23. package/src/charts/dot/__tests__/compute.test.ts +344 -0
  24. package/src/charts/dot/compute.ts +257 -0
  25. package/src/charts/dot/index.ts +46 -0
  26. package/src/charts/dot/labels.ts +97 -0
  27. package/src/charts/line/__tests__/compute.test.ts +437 -0
  28. package/src/charts/line/__tests__/labels.test.ts +93 -0
  29. package/src/charts/line/area.ts +288 -0
  30. package/src/charts/line/compute.ts +177 -0
  31. package/src/charts/line/index.ts +68 -0
  32. package/src/charts/line/labels.ts +144 -0
  33. package/src/charts/pie/__tests__/compute.test.ts +276 -0
  34. package/src/charts/pie/compute.ts +234 -0
  35. package/src/charts/pie/index.ts +49 -0
  36. package/src/charts/pie/labels.ts +142 -0
  37. package/src/charts/registry.ts +64 -0
  38. package/src/charts/scatter/__tests__/compute.test.ts +304 -0
  39. package/src/charts/scatter/__tests__/trendline.test.ts +191 -0
  40. package/src/charts/scatter/compute.ts +124 -0
  41. package/src/charts/scatter/index.ts +41 -0
  42. package/src/charts/scatter/trendline.ts +100 -0
  43. package/src/charts/utils.ts +120 -0
  44. package/src/compile.ts +368 -0
  45. package/src/compiler/__tests__/compile.test.ts +87 -0
  46. package/src/compiler/__tests__/normalize.test.ts +210 -0
  47. package/src/compiler/__tests__/validate.test.ts +440 -0
  48. package/src/compiler/index.ts +47 -0
  49. package/src/compiler/normalize.ts +269 -0
  50. package/src/compiler/types.ts +148 -0
  51. package/src/compiler/validate.ts +581 -0
  52. package/src/graphs/__tests__/community.test.ts +228 -0
  53. package/src/graphs/__tests__/compile-graph.test.ts +315 -0
  54. package/src/graphs/__tests__/encoding.test.ts +314 -0
  55. package/src/graphs/community.ts +92 -0
  56. package/src/graphs/compile-graph.ts +291 -0
  57. package/src/graphs/encoding.ts +302 -0
  58. package/src/graphs/types.ts +98 -0
  59. package/src/index.ts +74 -0
  60. package/src/layout/axes.ts +194 -0
  61. package/src/layout/dimensions.ts +199 -0
  62. package/src/layout/gridlines.ts +84 -0
  63. package/src/layout/scales.ts +426 -0
  64. package/src/legend/compute.ts +186 -0
  65. package/src/tables/__tests__/bar-column.test.ts +147 -0
  66. package/src/tables/__tests__/category-colors.test.ts +153 -0
  67. package/src/tables/__tests__/compile-table.test.ts +208 -0
  68. package/src/tables/__tests__/format-cells.test.ts +126 -0
  69. package/src/tables/__tests__/heatmap.test.ts +124 -0
  70. package/src/tables/__tests__/pagination.test.ts +78 -0
  71. package/src/tables/__tests__/search.test.ts +94 -0
  72. package/src/tables/__tests__/sort.test.ts +107 -0
  73. package/src/tables/__tests__/sparkline.test.ts +122 -0
  74. package/src/tables/bar-column.ts +94 -0
  75. package/src/tables/category-colors.ts +67 -0
  76. package/src/tables/compile-table.ts +420 -0
  77. package/src/tables/format-cells.ts +110 -0
  78. package/src/tables/heatmap.ts +121 -0
  79. package/src/tables/pagination.ts +46 -0
  80. package/src/tables/search.ts +66 -0
  81. package/src/tables/sort.ts +69 -0
  82. package/src/tables/sparkline.ts +113 -0
  83. package/src/tables/utils.ts +16 -0
  84. package/src/tooltips/__tests__/compute.test.ts +328 -0
  85. package/src/tooltips/compute.ts +231 -0
@@ -0,0 +1,328 @@
1
+ import type { ArcMark, Mark, PointMark, Rect, RectMark } from '@opendata-ai/openchart-core';
2
+ import { describe, expect, it } from 'vitest';
3
+ import type { NormalizedChartSpec } from '../../compiler/types';
4
+ import { computeTooltipDescriptors } from '../compute';
5
+
6
+ // ---------------------------------------------------------------------------
7
+ // Fixtures
8
+ // ---------------------------------------------------------------------------
9
+
10
+ const _chartArea: Rect = { x: 50, y: 20, width: 500, height: 300 };
11
+
12
+ function makeLineSpec(): NormalizedChartSpec {
13
+ return {
14
+ type: 'line',
15
+ data: [
16
+ { date: '2020-01-01', value: 10, country: 'US' },
17
+ { date: '2021-01-01', value: 40, country: 'US' },
18
+ { date: '2020-01-01', value: 15, country: 'UK' },
19
+ { date: '2021-01-01', value: 35, country: 'UK' },
20
+ ],
21
+ encoding: {
22
+ x: { field: 'date', type: 'temporal' },
23
+ y: { field: 'value', type: 'quantitative' },
24
+ color: { field: 'country', type: 'nominal' },
25
+ },
26
+ chrome: {},
27
+ annotations: [],
28
+ responsive: true,
29
+ theme: {},
30
+ darkMode: 'off',
31
+ labels: { density: 'auto', format: '' },
32
+ };
33
+ }
34
+
35
+ function makeBarSpec(): NormalizedChartSpec {
36
+ return {
37
+ type: 'bar',
38
+ data: [
39
+ { category: 'A', value: 100 },
40
+ { category: 'B', value: 200 },
41
+ { category: 'C', value: 150 },
42
+ ],
43
+ encoding: {
44
+ x: { field: 'value', type: 'quantitative' },
45
+ y: { field: 'category', type: 'nominal' },
46
+ },
47
+ chrome: {},
48
+ annotations: [],
49
+ responsive: true,
50
+ theme: {},
51
+ darkMode: 'off',
52
+ labels: { density: 'auto', format: '' },
53
+ };
54
+ }
55
+
56
+ function makePieSpec(): NormalizedChartSpec {
57
+ return {
58
+ type: 'pie',
59
+ data: [
60
+ { segment: 'Alpha', amount: 30 },
61
+ { segment: 'Beta', amount: 50 },
62
+ { segment: 'Gamma', amount: 20 },
63
+ ],
64
+ encoding: {
65
+ y: { field: 'amount', type: 'quantitative' },
66
+ color: { field: 'segment', type: 'nominal' },
67
+ },
68
+ chrome: {},
69
+ annotations: [],
70
+ responsive: true,
71
+ theme: {},
72
+ darkMode: 'off',
73
+ labels: { density: 'auto', format: '' },
74
+ };
75
+ }
76
+
77
+ // ---------------------------------------------------------------------------
78
+ // Tests
79
+ // ---------------------------------------------------------------------------
80
+
81
+ describe('computeTooltipDescriptors', () => {
82
+ describe('point marks (from line charts)', () => {
83
+ it('generates tooltip for each point mark', () => {
84
+ const spec = makeLineSpec();
85
+ const pointMarks: PointMark[] = [
86
+ {
87
+ type: 'point',
88
+ cx: 100,
89
+ cy: 200,
90
+ r: 3,
91
+ fill: '#1b7fa3',
92
+ stroke: '#fff',
93
+ strokeWidth: 1.5,
94
+ fillOpacity: 0,
95
+ data: { date: '2020-01-01', value: 10, country: 'US' },
96
+ aria: { label: 'point' },
97
+ },
98
+ {
99
+ type: 'point',
100
+ cx: 300,
101
+ cy: 100,
102
+ r: 3,
103
+ fill: '#c44e52',
104
+ stroke: '#fff',
105
+ strokeWidth: 1.5,
106
+ fillOpacity: 0,
107
+ data: { date: '2021-01-01', value: 40, country: 'US' },
108
+ aria: { label: 'point' },
109
+ },
110
+ ];
111
+
112
+ const descriptors = computeTooltipDescriptors(spec, pointMarks);
113
+
114
+ expect(descriptors.size).toBe(2);
115
+ expect(descriptors.has('point-0')).toBe(true);
116
+ expect(descriptors.has('point-1')).toBe(true);
117
+ });
118
+
119
+ it('tooltip includes formatted values', () => {
120
+ const spec = makeLineSpec();
121
+ const pointMarks: PointMark[] = [
122
+ {
123
+ type: 'point',
124
+ cx: 100,
125
+ cy: 200,
126
+ r: 3,
127
+ fill: '#1b7fa3',
128
+ stroke: '#fff',
129
+ strokeWidth: 1.5,
130
+ data: { date: '2020-01-01', value: 1500, country: 'US' },
131
+ aria: { label: 'point' },
132
+ },
133
+ ];
134
+
135
+ const descriptors = computeTooltipDescriptors(spec, pointMarks);
136
+ const content = descriptors.get('point-0')!;
137
+
138
+ // Should have fields for y (value) and x (date)
139
+ expect(content.fields.length).toBeGreaterThanOrEqual(1);
140
+
141
+ // The value field should be formatted with commas
142
+ const valueField = content.fields.find((f) => f.label === 'value');
143
+ expect(valueField).toBeDefined();
144
+ expect(valueField!.value).toBe('1,500');
145
+ });
146
+
147
+ it('tooltip title uses temporal x-axis value', () => {
148
+ const spec = makeLineSpec();
149
+ const pointMarks: PointMark[] = [
150
+ {
151
+ type: 'point',
152
+ cx: 100,
153
+ cy: 200,
154
+ r: 3,
155
+ fill: '#1b7fa3',
156
+ stroke: '#fff',
157
+ strokeWidth: 1.5,
158
+ data: { date: '2020-01-01', value: 10, country: 'US' },
159
+ aria: { label: 'point' },
160
+ },
161
+ ];
162
+
163
+ const descriptors = computeTooltipDescriptors(spec, pointMarks);
164
+ const content = descriptors.get('point-0')!;
165
+
166
+ // Title should be the date value
167
+ expect(content.title).toBeDefined();
168
+ expect(content.title!.length).toBeGreaterThan(0);
169
+ });
170
+ });
171
+
172
+ describe('rect marks (from bar charts)', () => {
173
+ it('generates tooltip for each rect mark', () => {
174
+ const spec = makeBarSpec();
175
+ const rectMarks: RectMark[] = [
176
+ {
177
+ type: 'rect',
178
+ x: 50,
179
+ y: 30,
180
+ width: 200,
181
+ height: 40,
182
+ fill: '#1b7fa3',
183
+ data: { category: 'A', value: 100 },
184
+ aria: { label: 'bar' },
185
+ },
186
+ {
187
+ type: 'rect',
188
+ x: 50,
189
+ y: 80,
190
+ width: 400,
191
+ height: 40,
192
+ fill: '#1b7fa3',
193
+ data: { category: 'B', value: 200 },
194
+ aria: { label: 'bar' },
195
+ },
196
+ ];
197
+
198
+ const descriptors = computeTooltipDescriptors(spec, rectMarks);
199
+
200
+ expect(descriptors.size).toBe(2);
201
+ expect(descriptors.has('rect-0')).toBe(true);
202
+ expect(descriptors.has('rect-1')).toBe(true);
203
+ });
204
+
205
+ it('rect tooltip includes category as title', () => {
206
+ const spec = makeBarSpec();
207
+ const rectMarks: RectMark[] = [
208
+ {
209
+ type: 'rect',
210
+ x: 50,
211
+ y: 30,
212
+ width: 200,
213
+ height: 40,
214
+ fill: '#1b7fa3',
215
+ data: { category: 'A', value: 100 },
216
+ aria: { label: 'bar' },
217
+ },
218
+ ];
219
+
220
+ const descriptors = computeTooltipDescriptors(spec, rectMarks);
221
+ const content = descriptors.get('rect-0')!;
222
+
223
+ // Nominal y-axis = category as title
224
+ expect(content.title).toBe('A');
225
+ });
226
+ });
227
+
228
+ describe('arc marks (from pie charts)', () => {
229
+ it('generates tooltip for each arc mark', () => {
230
+ const spec = makePieSpec();
231
+ const arcMarks: ArcMark[] = [
232
+ {
233
+ type: 'arc',
234
+ path: 'M0,0L10,0A10,10,0,0,1,0,10Z',
235
+ centroid: { x: 5, y: 5 },
236
+ innerRadius: 0,
237
+ outerRadius: 100,
238
+ startAngle: 0,
239
+ endAngle: 1.5,
240
+ fill: '#1b7fa3',
241
+ stroke: '#fff',
242
+ strokeWidth: 1,
243
+ data: { segment: 'Alpha', amount: 30 },
244
+ aria: { label: 'arc' },
245
+ },
246
+ ];
247
+
248
+ const descriptors = computeTooltipDescriptors(spec, arcMarks);
249
+
250
+ expect(descriptors.size).toBe(1);
251
+ expect(descriptors.has('arc-0')).toBe(true);
252
+ });
253
+
254
+ it('arc tooltip uses color field as title', () => {
255
+ const spec = makePieSpec();
256
+ const arcMarks: ArcMark[] = [
257
+ {
258
+ type: 'arc',
259
+ path: 'M0,0',
260
+ centroid: { x: 5, y: 5 },
261
+ innerRadius: 0,
262
+ outerRadius: 100,
263
+ startAngle: 0,
264
+ endAngle: 1.5,
265
+ fill: '#1b7fa3',
266
+ stroke: '#fff',
267
+ strokeWidth: 1,
268
+ data: { segment: 'Alpha', amount: 30 },
269
+ aria: { label: 'arc' },
270
+ },
271
+ ];
272
+
273
+ const descriptors = computeTooltipDescriptors(spec, arcMarks);
274
+ const content = descriptors.get('arc-0')!;
275
+
276
+ expect(content.title).toBe('Alpha');
277
+ });
278
+ });
279
+
280
+ describe('line and area marks', () => {
281
+ it('line marks do not generate tooltips (points do instead)', () => {
282
+ const spec = makeLineSpec();
283
+ const marks: Mark[] = [
284
+ {
285
+ type: 'line',
286
+ points: [
287
+ { x: 100, y: 200 },
288
+ { x: 300, y: 100 },
289
+ ],
290
+ stroke: '#1b7fa3',
291
+ strokeWidth: 2,
292
+ data: [{ date: '2020-01-01', value: 10 }],
293
+ aria: { label: 'line' },
294
+ },
295
+ ];
296
+
297
+ const descriptors = computeTooltipDescriptors(spec, marks);
298
+ expect(descriptors.size).toBe(0);
299
+ });
300
+
301
+ it('area marks do not generate tooltips (points do instead)', () => {
302
+ const spec = makeLineSpec();
303
+ const marks: Mark[] = [
304
+ {
305
+ type: 'area',
306
+ topPoints: [{ x: 100, y: 200 }],
307
+ bottomPoints: [{ x: 100, y: 300 }],
308
+ path: 'M0,0',
309
+ fill: '#1b7fa3',
310
+ fillOpacity: 0.3,
311
+ data: [{ date: '2020-01-01', value: 10 }],
312
+ aria: { label: 'area' },
313
+ },
314
+ ];
315
+
316
+ const descriptors = computeTooltipDescriptors(spec, marks);
317
+ expect(descriptors.size).toBe(0);
318
+ });
319
+ });
320
+
321
+ describe('empty data', () => {
322
+ it('returns empty map for no marks', () => {
323
+ const spec = makeLineSpec();
324
+ const descriptors = computeTooltipDescriptors(spec, []);
325
+ expect(descriptors.size).toBe(0);
326
+ });
327
+ });
328
+ });
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Tooltip descriptor computation.
3
+ *
4
+ * Generates a Map of mark-id -> TooltipContent from the spec encoding and marks.
5
+ * Each mark gets a tooltip that shows relevant field values formatted for display.
6
+ * The mark-id keys match the data-mark-id attributes set by the SVG renderer.
7
+ */
8
+
9
+ import type {
10
+ ArcMark,
11
+ AreaMark,
12
+ DataRow,
13
+ Encoding,
14
+ LineMark,
15
+ Mark,
16
+ PointMark,
17
+ RectMark,
18
+ TooltipContent,
19
+ TooltipField,
20
+ } from '@opendata-ai/openchart-core';
21
+ import { formatDate, formatNumber } from '@opendata-ai/openchart-core';
22
+ import { format as d3Format } from 'd3-format';
23
+
24
+ import type { NormalizedChartSpec } from '../compiler/types';
25
+
26
+ // ---------------------------------------------------------------------------
27
+ // Helpers
28
+ // ---------------------------------------------------------------------------
29
+
30
+ /** Format a raw data value for tooltip display. */
31
+ function formatValue(value: unknown, fieldType?: string, format?: string): string {
32
+ if (value == null) return '';
33
+
34
+ if (fieldType === 'temporal' || value instanceof Date) {
35
+ return formatDate(value as Date | string | number);
36
+ }
37
+
38
+ if (typeof value === 'number') {
39
+ if (format) {
40
+ try {
41
+ return d3Format(format)(value);
42
+ } catch {
43
+ return formatNumber(value);
44
+ }
45
+ }
46
+ return formatNumber(value);
47
+ }
48
+
49
+ return String(value);
50
+ }
51
+
52
+ /** Build tooltip fields from a data row based on the spec encoding. */
53
+ function buildFields(row: DataRow, encoding: Encoding, color?: string): TooltipField[] {
54
+ const fields: TooltipField[] = [];
55
+
56
+ // Y-axis value (the "main" value in most charts)
57
+ if (encoding.y) {
58
+ fields.push({
59
+ label: encoding.y.axis?.label ?? encoding.y.field,
60
+ value: formatValue(row[encoding.y.field], encoding.y.type, encoding.y.axis?.format),
61
+ color,
62
+ });
63
+ }
64
+
65
+ // X-axis value (often the category or date)
66
+ if (encoding.x) {
67
+ fields.push({
68
+ label: encoding.x.axis?.label ?? encoding.x.field,
69
+ value: formatValue(row[encoding.x.field], encoding.x.type, encoding.x.axis?.format),
70
+ });
71
+ }
72
+
73
+ // Size (for scatter/bubble)
74
+ if (encoding.size) {
75
+ fields.push({
76
+ label: encoding.size.axis?.label ?? encoding.size.field,
77
+ value: formatValue(row[encoding.size.field], encoding.size.type, encoding.size.axis?.format),
78
+ });
79
+ }
80
+
81
+ return fields;
82
+ }
83
+
84
+ /** Determine the title for a tooltip based on encoding. */
85
+ function getTooltipTitle(row: DataRow, encoding: Encoding): string | undefined {
86
+ // For charts with a temporal x-axis, use the date as the title
87
+ if (encoding.x?.type === 'temporal') {
88
+ return formatValue(row[encoding.x.field], 'temporal');
89
+ }
90
+
91
+ // For nominal x, use the category
92
+ if (encoding.x?.type === 'nominal' || encoding.x?.type === 'ordinal') {
93
+ return String(row[encoding.x.field] ?? '');
94
+ }
95
+
96
+ // For nominal y (e.g. horizontal bar charts), use the category
97
+ if (encoding.y?.type === 'nominal' || encoding.y?.type === 'ordinal') {
98
+ return String(row[encoding.y.field] ?? '');
99
+ }
100
+
101
+ // For color-encoded series, use the series name
102
+ if (encoding.color) {
103
+ return String(row[encoding.color.field] ?? '');
104
+ }
105
+
106
+ return undefined;
107
+ }
108
+
109
+ // ---------------------------------------------------------------------------
110
+ // Per-mark-type tooltip generation
111
+ // ---------------------------------------------------------------------------
112
+
113
+ function tooltipsForLine(
114
+ _mark: LineMark,
115
+ _encoding: Encoding,
116
+ _markIndex: number,
117
+ ): Array<[string, TooltipContent]> {
118
+ // Line marks themselves don't get individual tooltips.
119
+ // The point marks at each data point handle that.
120
+ return [];
121
+ }
122
+
123
+ function tooltipsForPoint(
124
+ mark: PointMark,
125
+ encoding: Encoding,
126
+ markIndex: number,
127
+ ): Array<[string, TooltipContent]> {
128
+ const title = getTooltipTitle(mark.data, encoding);
129
+ const fields = buildFields(mark.data, encoding, mark.fill);
130
+
131
+ return [[`point-${markIndex}`, { title, fields }]];
132
+ }
133
+
134
+ function tooltipsForRect(
135
+ mark: RectMark,
136
+ encoding: Encoding,
137
+ markIndex: number,
138
+ ): Array<[string, TooltipContent]> {
139
+ const title = getTooltipTitle(mark.data, encoding);
140
+ const fields = buildFields(mark.data, encoding, mark.fill);
141
+
142
+ return [[`rect-${markIndex}`, { title, fields }]];
143
+ }
144
+
145
+ function tooltipsForArc(
146
+ mark: ArcMark,
147
+ encoding: Encoding,
148
+ markIndex: number,
149
+ ): Array<[string, TooltipContent]> {
150
+ const row = mark.data;
151
+ const fields: TooltipField[] = [];
152
+
153
+ // For pie/donut, show the category and its value
154
+ if (encoding.color) {
155
+ const categoryName = String(row[encoding.color.field] ?? '');
156
+ if (encoding.y) {
157
+ fields.push({
158
+ label: categoryName,
159
+ value: formatValue(row[encoding.y.field], encoding.y.type, encoding.y.axis?.format),
160
+ color: mark.fill,
161
+ });
162
+ }
163
+ } else if (encoding.y) {
164
+ fields.push({
165
+ label: encoding.y.field,
166
+ value: formatValue(row[encoding.y.field], encoding.y.type, encoding.y.axis?.format),
167
+ color: mark.fill,
168
+ });
169
+ }
170
+
171
+ const title = encoding.color ? String(row[encoding.color.field] ?? '') : undefined;
172
+
173
+ return [[`arc-${markIndex}`, { title, fields }]];
174
+ }
175
+
176
+ function tooltipsForArea(
177
+ _mark: AreaMark,
178
+ _encoding: Encoding,
179
+ _markIndex: number,
180
+ ): Array<[string, TooltipContent]> {
181
+ // Area marks are background fills; point marks on top handle tooltips.
182
+ return [];
183
+ }
184
+
185
+ // ---------------------------------------------------------------------------
186
+ // Public API
187
+ // ---------------------------------------------------------------------------
188
+
189
+ /**
190
+ * Compute tooltip descriptors for all marks in the layout.
191
+ *
192
+ * Returns a Map keyed by data-mark-id (matching the SVG attribute)
193
+ * to TooltipContent objects. The vanilla adapter uses this to show
194
+ * tooltips on hover/tap/keyboard focus.
195
+ */
196
+ export function computeTooltipDescriptors(
197
+ spec: NormalizedChartSpec,
198
+ marks: Mark[],
199
+ ): Map<string, TooltipContent> {
200
+ const encoding = spec.encoding as Encoding;
201
+ const descriptors = new Map<string, TooltipContent>();
202
+
203
+ for (let i = 0; i < marks.length; i++) {
204
+ const mark = marks[i];
205
+ let entries: Array<[string, TooltipContent]> = [];
206
+
207
+ switch (mark.type) {
208
+ case 'line':
209
+ entries = tooltipsForLine(mark, encoding, i);
210
+ break;
211
+ case 'area':
212
+ entries = tooltipsForArea(mark, encoding, i);
213
+ break;
214
+ case 'point':
215
+ entries = tooltipsForPoint(mark, encoding, i);
216
+ break;
217
+ case 'rect':
218
+ entries = tooltipsForRect(mark, encoding, i);
219
+ break;
220
+ case 'arc':
221
+ entries = tooltipsForArc(mark, encoding, i);
222
+ break;
223
+ }
224
+
225
+ for (const [key, content] of entries) {
226
+ descriptors.set(key, content);
227
+ }
228
+ }
229
+
230
+ return descriptors;
231
+ }