@fluentui/react-charts 9.3.12 → 9.3.14

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 (55) hide show
  1. package/CHANGELOG.md +25 -2
  2. package/dist/index.d.ts +859 -0
  3. package/lib/VegaDeclarativeChart.js +1 -0
  4. package/lib/VegaDeclarativeChart.js.map +1 -0
  5. package/lib/components/LineChart/LineChart.js +47 -7
  6. package/lib/components/LineChart/LineChart.js.map +1 -1
  7. package/lib/components/ScatterChart/ScatterChart.js +12 -12
  8. package/lib/components/ScatterChart/ScatterChart.js.map +1 -1
  9. package/lib/components/VegaDeclarativeChart/VegaDeclarativeChart.js +405 -0
  10. package/lib/components/VegaDeclarativeChart/VegaDeclarativeChart.js.map +1 -0
  11. package/lib/components/VegaDeclarativeChart/VegaDeclarativeChartHooks.js +20 -0
  12. package/lib/components/VegaDeclarativeChart/VegaDeclarativeChartHooks.js.map +1 -0
  13. package/lib/components/VegaDeclarativeChart/VegaLiteColorAdapter.js +415 -0
  14. package/lib/components/VegaDeclarativeChart/VegaLiteColorAdapter.js.map +1 -0
  15. package/lib/components/VegaDeclarativeChart/VegaLiteExpressionEvaluator.js +537 -0
  16. package/lib/components/VegaDeclarativeChart/VegaLiteExpressionEvaluator.js.map +1 -0
  17. package/lib/components/VegaDeclarativeChart/VegaLiteSchemaAdapter.js +3279 -0
  18. package/lib/components/VegaDeclarativeChart/VegaLiteSchemaAdapter.js.map +1 -0
  19. package/lib/components/VegaDeclarativeChart/VegaLiteTypes.js +28 -0
  20. package/lib/components/VegaDeclarativeChart/VegaLiteTypes.js.map +1 -0
  21. package/lib/components/VegaDeclarativeChart/index.js +1 -0
  22. package/lib/components/VegaDeclarativeChart/index.js.map +1 -0
  23. package/lib/components/VerticalStackedBarChart/VerticalStackedBarChart.js +5 -2
  24. package/lib/components/VerticalStackedBarChart/VerticalStackedBarChart.js.map +1 -1
  25. package/lib/index.js +1 -0
  26. package/lib/index.js.map +1 -1
  27. package/lib/utilities/utilities.js +41 -0
  28. package/lib/utilities/utilities.js.map +1 -1
  29. package/lib-commonjs/VegaDeclarativeChart.js +6 -0
  30. package/lib-commonjs/VegaDeclarativeChart.js.map +1 -0
  31. package/lib-commonjs/components/LineChart/LineChart.js +46 -6
  32. package/lib-commonjs/components/LineChart/LineChart.js.map +1 -1
  33. package/lib-commonjs/components/ScatterChart/ScatterChart.js +11 -11
  34. package/lib-commonjs/components/ScatterChart/ScatterChart.js.map +1 -1
  35. package/lib-commonjs/components/VegaDeclarativeChart/VegaDeclarativeChart.js +274 -0
  36. package/lib-commonjs/components/VegaDeclarativeChart/VegaDeclarativeChart.js.map +1 -0
  37. package/lib-commonjs/components/VegaDeclarativeChart/VegaDeclarativeChartHooks.js +35 -0
  38. package/lib-commonjs/components/VegaDeclarativeChart/VegaDeclarativeChartHooks.js.map +1 -0
  39. package/lib-commonjs/components/VegaDeclarativeChart/VegaLiteColorAdapter.js +412 -0
  40. package/lib-commonjs/components/VegaDeclarativeChart/VegaLiteColorAdapter.js.map +1 -0
  41. package/lib-commonjs/components/VegaDeclarativeChart/VegaLiteExpressionEvaluator.js +533 -0
  42. package/lib-commonjs/components/VegaDeclarativeChart/VegaLiteExpressionEvaluator.js.map +1 -0
  43. package/lib-commonjs/components/VegaDeclarativeChart/VegaLiteSchemaAdapter.js +3214 -0
  44. package/lib-commonjs/components/VegaDeclarativeChart/VegaLiteSchemaAdapter.js.map +1 -0
  45. package/lib-commonjs/components/VegaDeclarativeChart/VegaLiteTypes.js +31 -0
  46. package/lib-commonjs/components/VegaDeclarativeChart/VegaLiteTypes.js.map +1 -0
  47. package/lib-commonjs/components/VegaDeclarativeChart/index.js +6 -0
  48. package/lib-commonjs/components/VegaDeclarativeChart/index.js.map +1 -0
  49. package/lib-commonjs/components/VerticalStackedBarChart/VerticalStackedBarChart.js +4 -1
  50. package/lib-commonjs/components/VerticalStackedBarChart/VerticalStackedBarChart.js.map +1 -1
  51. package/lib-commonjs/index.js +1 -0
  52. package/lib-commonjs/index.js.map +1 -1
  53. package/lib-commonjs/utilities/utilities.js +33 -0
  54. package/lib-commonjs/utilities/utilities.js.map +1 -1
  55. package/package.json +3 -3
@@ -0,0 +1,3214 @@
1
+ // Using custom VegaLiteTypes for internal adapter logic
2
+ // For public API, VegaDeclarativeChart accepts vega-lite's TopLevelSpec
3
+ "use strict";
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ function _export(target, all) {
8
+ for(var name in all)Object.defineProperty(target, name, {
9
+ enumerable: true,
10
+ get: all[name]
11
+ });
12
+ }
13
+ _export(exports, {
14
+ autoCorrectEncodingTypes: function() {
15
+ return autoCorrectEncodingTypes;
16
+ },
17
+ getChartType: function() {
18
+ return getChartType;
19
+ },
20
+ getMarkType: function() {
21
+ return getMarkType;
22
+ },
23
+ getVegaLiteLegendsProps: function() {
24
+ return getVegaLiteLegendsProps;
25
+ },
26
+ getVegaLiteTitles: function() {
27
+ return getVegaLiteTitles;
28
+ },
29
+ transformVegaLiteToAreaChartProps: function() {
30
+ return transformVegaLiteToAreaChartProps;
31
+ },
32
+ transformVegaLiteToDonutChartProps: function() {
33
+ return transformVegaLiteToDonutChartProps;
34
+ },
35
+ transformVegaLiteToGroupedVerticalBarChartProps: function() {
36
+ return transformVegaLiteToGroupedVerticalBarChartProps;
37
+ },
38
+ transformVegaLiteToHeatMapChartProps: function() {
39
+ return transformVegaLiteToHeatMapChartProps;
40
+ },
41
+ transformVegaLiteToHistogramProps: function() {
42
+ return transformVegaLiteToHistogramProps;
43
+ },
44
+ transformVegaLiteToHorizontalBarChartProps: function() {
45
+ return transformVegaLiteToHorizontalBarChartProps;
46
+ },
47
+ transformVegaLiteToLineChartProps: function() {
48
+ return transformVegaLiteToLineChartProps;
49
+ },
50
+ transformVegaLiteToPolarChartProps: function() {
51
+ return transformVegaLiteToPolarChartProps;
52
+ },
53
+ transformVegaLiteToScatterChartProps: function() {
54
+ return transformVegaLiteToScatterChartProps;
55
+ },
56
+ transformVegaLiteToVerticalBarChartProps: function() {
57
+ return transformVegaLiteToVerticalBarChartProps;
58
+ },
59
+ transformVegaLiteToVerticalStackedBarChartProps: function() {
60
+ return transformVegaLiteToVerticalStackedBarChartProps;
61
+ }
62
+ });
63
+ const _VegaLiteColorAdapter = require("./VegaLiteColorAdapter");
64
+ const _VegaLiteExpressionEvaluator = require("./VegaLiteExpressionEvaluator");
65
+ const _d3array = require("d3-array");
66
+ const _d3format = require("d3-format");
67
+ const _chartutilities = require("@fluentui/chart-utilities");
68
+ /**
69
+ * Vega-Lite to Fluent Charts adapter for line/point charts.
70
+ *
71
+ * Transforms Vega-Lite JSON specifications into Fluent LineChart props.
72
+ * Supports basic line charts with temporal/quantitative axes and color-encoded series.
73
+ *
74
+ * TODO: Future enhancements
75
+ * - Multi-view layouts (facet, concat, repeat)
76
+ * - Selection interactions
77
+ * - Remote data loading (url)
78
+ * - Transform pipeline (filter, aggregate, calculate)
79
+ * - Conditional encodings
80
+ * - Additional mark types (area, bar, etc.)
81
+ * - Tooltip customization
82
+ */ /**
83
+ * Default configuration values for VegaLite charts
84
+ */ const DEFAULT_CHART_HEIGHT = 350;
85
+ const DEFAULT_MAX_BAR_WIDTH = 50;
86
+ const DEFAULT_TRUNCATE_CHARS = 20;
87
+ /**
88
+ * Determines if a spec is a layered specification
89
+ */ function isLayerSpec(spec) {
90
+ return Array.isArray(spec.layer) && spec.layer.length > 0;
91
+ }
92
+ /**
93
+ * Determines if a spec is a single unit specification
94
+ */ function isUnitSpec(spec) {
95
+ return spec.mark !== undefined && spec.encoding !== undefined;
96
+ }
97
+ function getMarkType(mark) {
98
+ if (!mark) {
99
+ return undefined;
100
+ }
101
+ return typeof mark === 'string' ? mark : mark.type;
102
+ }
103
+ /**
104
+ * Resolves the color for a legend label using the priority chain:
105
+ * 1. Explicit color value from encoding
106
+ * 2. Mark-level color
107
+ * 3. Cached color from the shared color map
108
+ * 4. New color via local index for deterministic per-chart assignment
109
+ */ function resolveColor(legend, index, colorValue, markColor, colorMap, colorScheme, colorRange, isDarkTheme) {
110
+ var _colorMap_current, _colorMap_current1;
111
+ if (colorValue) {
112
+ return colorValue;
113
+ }
114
+ if (markColor) {
115
+ return markColor;
116
+ }
117
+ // Check colorMap cache first for cross-chart consistency
118
+ if ((_colorMap_current = colorMap.current) === null || _colorMap_current === void 0 ? void 0 : _colorMap_current.has(legend)) {
119
+ return colorMap.current.get(legend);
120
+ }
121
+ // Use local index (not colorMap.size) for deterministic per-chart color assignment
122
+ const color = (0, _VegaLiteColorAdapter.getVegaColor)(index, colorScheme, colorRange, isDarkTheme);
123
+ (_colorMap_current1 = colorMap.current) === null || _colorMap_current1 === void 0 ? void 0 : _colorMap_current1.set(legend, color);
124
+ return color;
125
+ }
126
+ /**
127
+ * Extracts inline data values from a Vega-Lite data specification
128
+ * TODO: Add support for URL-based data loading
129
+ * TODO: Add support for named dataset resolution
130
+ * TODO: Add support for data format parsing (csv, tsv)
131
+ */ function extractDataValues(data) {
132
+ if (!data) {
133
+ return [];
134
+ }
135
+ if (data.values && Array.isArray(data.values)) {
136
+ return data.values;
137
+ }
138
+ // TODO: Handle data.url - load remote data
139
+ if (data.url) {
140
+ // Remote data URLs are not yet supported
141
+ return [];
142
+ }
143
+ // TODO: Handle data.name - resolve named datasets
144
+ if (data.name) {
145
+ // Named datasets are not yet supported
146
+ return [];
147
+ }
148
+ return [];
149
+ }
150
+ /**
151
+ * Applies a fold transform to convert wide-format data to long-format
152
+ * The fold transform unpivots specified fields into key-value pairs
153
+ *
154
+ * @param data - Array of data records in wide format
155
+ * @param foldFields - Array of field names to fold
156
+ * @param asFields - [keyName, valueName] for the new columns (defaults to ['key', 'value'])
157
+ * @returns Array of data records in long format
158
+ */ function applyFoldTransform(data, foldFields, asFields = [
159
+ 'key',
160
+ 'value'
161
+ ]) {
162
+ const [keyField, valueField] = asFields;
163
+ const result = [];
164
+ for (const row of data){
165
+ // Create a base row without the fields being folded
166
+ const baseRow = {};
167
+ for (const [key, value] of Object.entries(row)){
168
+ if (!foldFields.includes(key)) {
169
+ baseRow[key] = value;
170
+ }
171
+ }
172
+ // Create a new row for each folded field
173
+ for (const field of foldFields){
174
+ if (field in row) {
175
+ result.push({
176
+ ...baseRow,
177
+ [keyField]: field,
178
+ [valueField]: row[field]
179
+ });
180
+ }
181
+ }
182
+ }
183
+ return result;
184
+ }
185
+ /**
186
+ * Applies transforms from a Vega-Lite spec to data
187
+ * Currently supports: fold transform
188
+ *
189
+ * @param data - Array of data records
190
+ * @param transforms - Array of Vega-Lite transform specifications
191
+ * @returns Transformed data array
192
+ */ function applyTransforms(data, transforms) {
193
+ if (!transforms || transforms.length === 0) {
194
+ return data;
195
+ }
196
+ let result = data;
197
+ for (const transform of transforms){
198
+ // Handle fold transform
199
+ if ('fold' in transform && Array.isArray(transform.fold)) {
200
+ const foldFields = transform.fold;
201
+ const asFields = transform.as || [
202
+ 'key',
203
+ 'value'
204
+ ];
205
+ result = applyFoldTransform(result, foldFields, asFields);
206
+ }
207
+ // Handle filter transform
208
+ if ('filter' in transform) {
209
+ const filterExpr = transform.filter;
210
+ if (typeof filterExpr === 'string') {
211
+ result = result.filter((row)=>{
212
+ try {
213
+ return (0, _VegaLiteExpressionEvaluator.safeEvaluateExpression)(filterExpr, row);
214
+ } catch {
215
+ return true;
216
+ }
217
+ });
218
+ }
219
+ }
220
+ // Handle calculate transform
221
+ if ('calculate' in transform && 'as' in transform) {
222
+ const expr = transform.calculate;
223
+ const asField = transform.as;
224
+ result = result.map((row)=>{
225
+ try {
226
+ const value = (0, _VegaLiteExpressionEvaluator.safeEvaluateExpression)(expr, row);
227
+ return {
228
+ ...row,
229
+ [asField]: value
230
+ };
231
+ } catch {
232
+ return row;
233
+ }
234
+ });
235
+ }
236
+ // Handle aggregate transform
237
+ if ('aggregate' in transform && Array.isArray(transform.aggregate)) {
238
+ const aggSpecs = transform.aggregate;
239
+ const groupby = transform.groupby || [];
240
+ const groups = new Map();
241
+ result.forEach((row)=>{
242
+ const key = groupby.map((g)=>String(row[g])).join('|');
243
+ if (!groups.has(key)) {
244
+ groups.set(key, []);
245
+ }
246
+ groups.get(key).push(row);
247
+ });
248
+ result = Array.from(groups.entries()).map(([key, rows])=>{
249
+ const baseRow = {};
250
+ groupby.forEach((g, i)=>{
251
+ baseRow[g] = rows[0][g];
252
+ });
253
+ aggSpecs.forEach((spec)=>{
254
+ const values = spec.field ? rows.map((r)=>Number(r[spec.field])).filter((v)=>!isNaN(v)) : [];
255
+ switch(spec.op){
256
+ case 'count':
257
+ baseRow[spec.as] = rows.length;
258
+ break;
259
+ case 'sum':
260
+ baseRow[spec.as] = (0, _d3array.sum)(values);
261
+ break;
262
+ case 'mean':
263
+ case 'average':
264
+ var _d3Mean;
265
+ baseRow[spec.as] = (_d3Mean = (0, _d3array.mean)(values)) !== null && _d3Mean !== void 0 ? _d3Mean : 0;
266
+ break;
267
+ case 'min':
268
+ var _d3Min;
269
+ baseRow[spec.as] = (_d3Min = (0, _d3array.min)(values)) !== null && _d3Min !== void 0 ? _d3Min : 0;
270
+ break;
271
+ case 'max':
272
+ var _d3Max;
273
+ baseRow[spec.as] = (_d3Max = (0, _d3array.max)(values)) !== null && _d3Max !== void 0 ? _d3Max : 0;
274
+ break;
275
+ default:
276
+ baseRow[spec.as] = rows.length;
277
+ }
278
+ });
279
+ return baseRow;
280
+ });
281
+ }
282
+ // Handle window transform
283
+ if ('window' in transform && Array.isArray(transform.window)) {
284
+ const windowOps = transform.window;
285
+ const sortFields = transform.sort || [];
286
+ const groupby = transform.groupby || [];
287
+ // Group data
288
+ const groups = new Map();
289
+ result.forEach((row)=>{
290
+ const key = groupby.length > 0 ? groupby.map((g)=>String(row[g])).join('|') : '__all__';
291
+ if (!groups.has(key)) {
292
+ groups.set(key, []);
293
+ }
294
+ groups.get(key).push(row);
295
+ });
296
+ const newResult = [];
297
+ groups.forEach((rows)=>{
298
+ // Sort within group
299
+ if (sortFields.length > 0) {
300
+ rows.sort((a, b)=>{
301
+ for (const sf of sortFields){
302
+ const va = Number(a[sf.field]) || 0;
303
+ const vb = Number(b[sf.field]) || 0;
304
+ const cmp = sf.order === 'descending' ? vb - va : va - vb;
305
+ if (cmp !== 0) {
306
+ return cmp;
307
+ }
308
+ }
309
+ return 0;
310
+ });
311
+ }
312
+ let runningSum = 0;
313
+ rows.forEach((row, idx)=>{
314
+ const newRow = {
315
+ ...row
316
+ };
317
+ windowOps.forEach((op)=>{
318
+ switch(op.op){
319
+ case 'sum':
320
+ runningSum += Number(row[op.field]) || 0;
321
+ newRow[op.as] = runningSum;
322
+ break;
323
+ case 'rank':
324
+ newRow[op.as] = idx + 1;
325
+ break;
326
+ case 'row_number':
327
+ newRow[op.as] = idx + 1;
328
+ break;
329
+ case 'count':
330
+ newRow[op.as] = idx + 1;
331
+ break;
332
+ default:
333
+ newRow[op.as] = idx + 1;
334
+ }
335
+ });
336
+ newResult.push(newRow);
337
+ });
338
+ });
339
+ result = newResult;
340
+ }
341
+ // Handle joinaggregate transform
342
+ if ('joinaggregate' in transform && Array.isArray(transform.joinaggregate)) {
343
+ const aggSpecs = transform.joinaggregate;
344
+ const groupby = transform.groupby || [];
345
+ // Compute aggregates
346
+ const groups = new Map();
347
+ result.forEach((row)=>{
348
+ const key = groupby.length > 0 ? groupby.map((g)=>String(row[g])).join('|') : '__all__';
349
+ if (!groups.has(key)) {
350
+ groups.set(key, []);
351
+ }
352
+ groups.get(key).push(row);
353
+ });
354
+ const aggResults = new Map();
355
+ groups.forEach((rows, key)=>{
356
+ const aggs = {};
357
+ aggSpecs.forEach((spec)=>{
358
+ const values = spec.field ? rows.map((r)=>Number(r[spec.field])).filter((v)=>!isNaN(v)) : [];
359
+ switch(spec.op){
360
+ case 'mean':
361
+ case 'average':
362
+ var _d3Mean;
363
+ aggs[spec.as] = (_d3Mean = (0, _d3array.mean)(values)) !== null && _d3Mean !== void 0 ? _d3Mean : 0;
364
+ break;
365
+ case 'sum':
366
+ aggs[spec.as] = (0, _d3array.sum)(values);
367
+ break;
368
+ case 'count':
369
+ aggs[spec.as] = rows.length;
370
+ break;
371
+ case 'min':
372
+ var _d3Min;
373
+ aggs[spec.as] = (_d3Min = (0, _d3array.min)(values)) !== null && _d3Min !== void 0 ? _d3Min : 0;
374
+ break;
375
+ case 'max':
376
+ var _d3Max;
377
+ aggs[spec.as] = (_d3Max = (0, _d3array.max)(values)) !== null && _d3Max !== void 0 ? _d3Max : 0;
378
+ break;
379
+ default:
380
+ aggs[spec.as] = rows.length;
381
+ }
382
+ });
383
+ aggResults.set(key, aggs);
384
+ });
385
+ // Join back: add aggregate values to each row
386
+ result = result.map((row)=>{
387
+ const key = groupby.length > 0 ? groupby.map((g)=>String(row[g])).join('|') : '__all__';
388
+ return {
389
+ ...row,
390
+ ...aggResults.get(key) || {}
391
+ };
392
+ });
393
+ }
394
+ // Handle regression transform (simple linear regression)
395
+ if ('regression' in transform && 'on' in transform) {
396
+ const yField = transform.regression;
397
+ const xField = transform.on;
398
+ const points = result.map((r)=>({
399
+ x: Number(r[xField]),
400
+ y: Number(r[yField])
401
+ })).filter((p)=>!isNaN(p.x) && !isNaN(p.y));
402
+ if (points.length >= 2) {
403
+ const n = points.length;
404
+ const sumX = (0, _d3array.sum)(points.map((p)=>p.x));
405
+ const sumY = (0, _d3array.sum)(points.map((p)=>p.y));
406
+ const sumXY = (0, _d3array.sum)(points.map((p)=>p.x * p.y));
407
+ const sumX2 = (0, _d3array.sum)(points.map((p)=>p.x * p.x));
408
+ const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
409
+ const intercept = (sumY - slope * sumX) / n;
410
+ var _d3Min;
411
+ const xMin = (_d3Min = (0, _d3array.min)(points.map((p)=>p.x))) !== null && _d3Min !== void 0 ? _d3Min : 0;
412
+ var _d3Max;
413
+ const xMax = (_d3Max = (0, _d3array.max)(points.map((p)=>p.x))) !== null && _d3Max !== void 0 ? _d3Max : 0;
414
+ result = [
415
+ {
416
+ [xField]: xMin,
417
+ [yField]: slope * xMin + intercept
418
+ },
419
+ {
420
+ [xField]: xMax,
421
+ [yField]: slope * xMax + intercept
422
+ }
423
+ ];
424
+ }
425
+ }
426
+ // Handle loess transform (simplified: moving average approximation)
427
+ if ('loess' in transform && 'on' in transform) {
428
+ const yField = transform.loess;
429
+ const xField = transform.on;
430
+ const sorted = [
431
+ ...result
432
+ ].filter((r)=>!isNaN(Number(r[xField])) && !isNaN(Number(r[yField]))).sort((a, b)=>Number(a[xField]) - Number(b[xField]));
433
+ const windowSize = Math.max(3, Math.floor(sorted.length / 4));
434
+ result = sorted.map((row, i)=>{
435
+ const start = Math.max(0, i - Math.floor(windowSize / 2));
436
+ const end = Math.min(sorted.length, start + windowSize);
437
+ const windowSlice = sorted.slice(start, end);
438
+ var _d3Mean;
439
+ const avgY = (_d3Mean = (0, _d3array.mean)(windowSlice.map((r)=>Number(r[yField])))) !== null && _d3Mean !== void 0 ? _d3Mean : Number(row[yField]);
440
+ return {
441
+ [xField]: row[xField],
442
+ [yField]: avgY
443
+ };
444
+ });
445
+ }
446
+ // Handle density transform (simplified: histogram-based density estimation)
447
+ if ('density' in transform) {
448
+ const field = transform.density;
449
+ const groupby = transform.groupby || [];
450
+ const groups = new Map();
451
+ result.forEach((row)=>{
452
+ const key = groupby.length > 0 ? groupby.map((g)=>String(row[g])).join('|') : '__all__';
453
+ if (!groups.has(key)) {
454
+ groups.set(key, []);
455
+ }
456
+ groups.get(key).push(Number(row[field]));
457
+ });
458
+ const densityResult = [];
459
+ groups.forEach((values, key)=>{
460
+ var _d3Min;
461
+ const min = (_d3Min = (0, _d3array.min)(values)) !== null && _d3Min !== void 0 ? _d3Min : 0;
462
+ var _d3Max;
463
+ const max = (_d3Max = (0, _d3array.max)(values)) !== null && _d3Max !== void 0 ? _d3Max : 0;
464
+ const range = max - min || 1;
465
+ const bins = 20;
466
+ const bandwidth = range / bins;
467
+ const groupFields = {};
468
+ if (groupby.length > 0) {
469
+ const sampleRow = result.find((r)=>groupby.map((g)=>String(r[g])).join('|') === key);
470
+ groupby.forEach((g)=>{
471
+ groupFields[g] = sampleRow === null || sampleRow === void 0 ? void 0 : sampleRow[g];
472
+ });
473
+ }
474
+ for(let i = 0; i <= bins; i++){
475
+ const x = min + i / bins * range;
476
+ const count = values.filter((v)=>Math.abs(v - x) < bandwidth).length;
477
+ const density = count / (values.length * bandwidth);
478
+ densityResult.push({
479
+ value: x,
480
+ density,
481
+ ...groupFields
482
+ });
483
+ }
484
+ });
485
+ result = densityResult;
486
+ }
487
+ // Handle quantile transform
488
+ if ('quantile' in transform) {
489
+ const field = transform.quantile;
490
+ const probs = transform.probs || [
491
+ 0.25,
492
+ 0.5,
493
+ 0.75
494
+ ];
495
+ const values = result.map((r)=>Number(r[field])).filter((v)=>!isNaN(v)).sort((a, b)=>a - b);
496
+ if (values.length > 0) {
497
+ result = probs.map((p)=>{
498
+ const idx = Math.min(Math.floor(p * values.length), values.length - 1);
499
+ return {
500
+ prob: String(p),
501
+ value: values[idx]
502
+ };
503
+ });
504
+ }
505
+ }
506
+ // Handle impute transform (fill missing values)
507
+ if ('impute' in transform && 'key' in transform) {
508
+ const field = transform.impute;
509
+ const keyField = transform.key;
510
+ const method = transform.method || 'value';
511
+ var _transform_value;
512
+ const fillValue = (_transform_value = transform.value) !== null && _transform_value !== void 0 ? _transform_value : 0;
513
+ const existingKeys = new Set(result.map((r)=>r[keyField]));
514
+ const allKeyValues = result.map((r)=>Number(r[keyField])).filter((v)=>!isNaN(v));
515
+ if (allKeyValues.length > 0) {
516
+ var _d3Min1;
517
+ const minKey = (_d3Min1 = (0, _d3array.min)(allKeyValues)) !== null && _d3Min1 !== void 0 ? _d3Min1 : 0;
518
+ var _d3Max1;
519
+ const maxKey = (_d3Max1 = (0, _d3array.max)(allKeyValues)) !== null && _d3Max1 !== void 0 ? _d3Max1 : 0;
520
+ for(let k = minKey; k <= maxKey; k++){
521
+ if (!existingKeys.has(k)) {
522
+ const imputed = {
523
+ [keyField]: k
524
+ };
525
+ imputed[field] = method === 'value' ? fillValue : 0;
526
+ result.push(imputed);
527
+ }
528
+ }
529
+ result.sort((a, b)=>Number(a[keyField]) - Number(b[keyField]));
530
+ }
531
+ }
532
+ // Handle lookup transform (join with secondary dataset)
533
+ if ('lookup' in transform && 'from' in transform) {
534
+ var _fromSpec_data;
535
+ const lookupField = transform.lookup;
536
+ const fromSpec = transform.from;
537
+ if (((_fromSpec_data = fromSpec.data) === null || _fromSpec_data === void 0 ? void 0 : _fromSpec_data.values) && fromSpec.key && fromSpec.fields) {
538
+ const lookupMap = new Map();
539
+ fromSpec.data.values.forEach((row)=>{
540
+ lookupMap.set(String(row[fromSpec.key]), row);
541
+ });
542
+ result = result.map((row)=>{
543
+ const lookupRow = lookupMap.get(String(row[lookupField]));
544
+ if (lookupRow) {
545
+ const extra = {};
546
+ fromSpec.fields.forEach((f)=>{
547
+ extra[f] = lookupRow[f];
548
+ });
549
+ return {
550
+ ...row,
551
+ ...extra
552
+ };
553
+ }
554
+ return row;
555
+ });
556
+ }
557
+ }
558
+ }
559
+ return result;
560
+ }
561
+ /**
562
+ * Normalizes a Vega-Lite spec into an array of unit specs with resolved data and encoding
563
+ * Handles both single-view and layered specifications
564
+ */ function normalizeSpec(spec) {
565
+ if (isLayerSpec(spec)) {
566
+ // Layered spec: merge shared data and encoding with each layer
567
+ const sharedData = spec.data;
568
+ const sharedEncoding = spec.encoding;
569
+ return spec.layer.map((layer)=>({
570
+ ...layer,
571
+ data: layer.data || sharedData,
572
+ encoding: {
573
+ ...sharedEncoding,
574
+ ...layer.encoding
575
+ }
576
+ }));
577
+ }
578
+ if (isUnitSpec(spec)) {
579
+ // Single unit spec
580
+ return [
581
+ {
582
+ mark: spec.mark,
583
+ encoding: spec.encoding,
584
+ data: spec.data
585
+ }
586
+ ];
587
+ }
588
+ // Unsupported spec structure
589
+ return [];
590
+ }
591
+ /**
592
+ * Parses a value to a Date if it's temporal, otherwise returns as number or string
593
+ */ function parseValue(value, isTemporalType) {
594
+ if (value === null || value === undefined) {
595
+ return '';
596
+ }
597
+ if (isTemporalType) {
598
+ // Try parsing as date
599
+ const dateValue = new Date(value);
600
+ if (!isNaN(dateValue.getTime())) {
601
+ return dateValue;
602
+ }
603
+ }
604
+ if (typeof value === 'number') {
605
+ return value;
606
+ }
607
+ return String(value);
608
+ }
609
+ /**
610
+ * Maps Vega-Lite interpolate to Fluent curve options
611
+ * Note: Only maps to curve types supported by LineChartLineOptions
612
+ */ function mapInterpolateToCurve(interpolate) {
613
+ if (!interpolate) {
614
+ return undefined;
615
+ }
616
+ switch(interpolate){
617
+ case 'linear':
618
+ case 'linear-closed':
619
+ return 'linear';
620
+ case 'step':
621
+ return 'step';
622
+ case 'step-before':
623
+ return 'stepBefore';
624
+ case 'step-after':
625
+ return 'stepAfter';
626
+ case 'natural':
627
+ return 'natural';
628
+ case 'monotone':
629
+ return 'linear';
630
+ // Note: basis, cardinal, catmull-rom are not supported by LineChartLineOptions
631
+ default:
632
+ return 'linear';
633
+ }
634
+ }
635
+ /**
636
+ * Extracts mark properties from VegaLiteMarkDef
637
+ */ function getMarkProperties(mark) {
638
+ if (typeof mark === 'string') {
639
+ return {};
640
+ }
641
+ return {
642
+ color: mark.color,
643
+ interpolate: mark.interpolate,
644
+ strokeWidth: mark.strokeWidth,
645
+ strokeDash: mark.strokeDash,
646
+ point: mark.point
647
+ };
648
+ }
649
+ /**
650
+ * Extracts annotations from Vega-Lite layers with text or rule marks
651
+ * Text marks become text annotations, rule marks become reference lines
652
+ */ function extractAnnotations(spec) {
653
+ const annotations = [];
654
+ if (!spec.layer || !Array.isArray(spec.layer)) {
655
+ return annotations;
656
+ }
657
+ spec.layer.forEach((layer, index)=>{
658
+ const mark = getMarkType(layer.mark);
659
+ const encoding = layer.encoding || {};
660
+ // Text marks become annotations
661
+ if (mark === 'text' && encoding.x && encoding.y) {
662
+ var _encoding_text, _encoding_text1, _encoding_text2;
663
+ const textValue = ((_encoding_text = encoding.text) === null || _encoding_text === void 0 ? void 0 : _encoding_text.datum) || ((_encoding_text1 = encoding.text) === null || _encoding_text1 === void 0 ? void 0 : _encoding_text1.value) || ((_encoding_text2 = encoding.text) === null || _encoding_text2 === void 0 ? void 0 : _encoding_text2.field) || '';
664
+ const xValue = encoding.x.datum || encoding.x.value || encoding.x.field;
665
+ const yValue = encoding.y.datum || encoding.y.value || encoding.y.field;
666
+ if (textValue && (xValue !== undefined || encoding.x.datum !== undefined) && (yValue !== undefined || encoding.y.datum !== undefined)) {
667
+ annotations.push({
668
+ id: `text-annotation-${index}`,
669
+ text: String(textValue),
670
+ coordinates: {
671
+ type: 'data',
672
+ x: encoding.x.datum || xValue || 0,
673
+ y: encoding.y.datum || yValue || 0
674
+ },
675
+ style: {
676
+ textColor: typeof layer.mark === 'object' ? layer.mark.color : undefined
677
+ }
678
+ });
679
+ }
680
+ }
681
+ // Rule marks can become reference lines (horizontal or vertical)
682
+ if (mark === 'rule') {
683
+ const markColor = typeof layer.mark === 'object' ? layer.mark.color : '#000';
684
+ const markStrokeWidth = typeof layer.mark === 'object' ? layer.mark.strokeWidth || 1 : 1;
685
+ const markStrokeDash = typeof layer.mark === 'object' ? layer.mark.strokeDash : undefined;
686
+ // Horizontal rule (y value constant)
687
+ if (encoding.y && (encoding.y.value !== undefined || encoding.y.datum !== undefined)) {
688
+ var _spec_layer, _companionText_encoding_text, _companionText_encoding, _companionText_encoding_text1, _companionText_encoding1;
689
+ var _encoding_y_value;
690
+ const yValue = (_encoding_y_value = encoding.y.value) !== null && _encoding_y_value !== void 0 ? _encoding_y_value : encoding.y.datum;
691
+ // Look for a companion text annotation at the same y-value
692
+ const companionText = (_spec_layer = spec.layer) === null || _spec_layer === void 0 ? void 0 : _spec_layer.find((l, i)=>{
693
+ var _l_encoding;
694
+ if (i === index) {
695
+ return false;
696
+ }
697
+ const m = getMarkType(l.mark);
698
+ var _l_encoding_y_datum;
699
+ return m === 'text' && ((_l_encoding = l.encoding) === null || _l_encoding === void 0 ? void 0 : _l_encoding.y) && ((_l_encoding_y_datum = l.encoding.y.datum) !== null && _l_encoding_y_datum !== void 0 ? _l_encoding_y_datum : l.encoding.y.value) === yValue;
700
+ });
701
+ const ruleText = companionText ? String(((_companionText_encoding = companionText.encoding) === null || _companionText_encoding === void 0 ? void 0 : (_companionText_encoding_text = _companionText_encoding.text) === null || _companionText_encoding_text === void 0 ? void 0 : _companionText_encoding_text.datum) || ((_companionText_encoding1 = companionText.encoding) === null || _companionText_encoding1 === void 0 ? void 0 : (_companionText_encoding_text1 = _companionText_encoding1.text) === null || _companionText_encoding_text1 === void 0 ? void 0 : _companionText_encoding_text1.value) || yValue) : String(yValue);
702
+ annotations.push({
703
+ id: `rule-h-${index}`,
704
+ text: ruleText,
705
+ coordinates: {
706
+ type: 'data',
707
+ x: 0,
708
+ y: yValue
709
+ },
710
+ style: {
711
+ textColor: markColor,
712
+ borderColor: markColor,
713
+ borderWidth: markStrokeWidth,
714
+ ...markStrokeDash && Array.isArray(markStrokeDash) ? {
715
+ borderRadius: 0
716
+ } // Indicate dashed style
717
+ : {}
718
+ }
719
+ });
720
+ } else if (encoding.x && (encoding.x.value !== undefined || encoding.x.datum !== undefined)) {
721
+ var _encoding_x_value;
722
+ const xValue = (_encoding_x_value = encoding.x.value) !== null && _encoding_x_value !== void 0 ? _encoding_x_value : encoding.x.datum;
723
+ annotations.push({
724
+ id: `rule-v-${index}`,
725
+ text: String(xValue),
726
+ coordinates: {
727
+ type: 'data',
728
+ x: xValue,
729
+ y: 0
730
+ },
731
+ style: {
732
+ textColor: markColor,
733
+ borderColor: markColor,
734
+ borderWidth: markStrokeWidth
735
+ }
736
+ });
737
+ }
738
+ }
739
+ });
740
+ return annotations;
741
+ }
742
+ /**
743
+ * Extracts color fill bars (background regions) from rect marks with x/x2 or y/y2 encodings
744
+ */ function extractColorFillBars(spec, colorMap, isDarkTheme) {
745
+ const colorFillBars = [];
746
+ if (!spec.layer || !Array.isArray(spec.layer)) {
747
+ return colorFillBars;
748
+ }
749
+ // Detect if x-axis is temporal by checking the primary data layer (non-rect layer)
750
+ const isXTemporal = spec.layer.some((layer)=>{
751
+ var _layer_encoding_x, _layer_encoding;
752
+ const layerMark = getMarkType(layer.mark);
753
+ // Skip rect layers, look at line/point/area layers
754
+ if (layerMark === 'rect') {
755
+ return false;
756
+ }
757
+ return ((_layer_encoding = layer.encoding) === null || _layer_encoding === void 0 ? void 0 : (_layer_encoding_x = _layer_encoding.x) === null || _layer_encoding_x === void 0 ? void 0 : _layer_encoding_x.type) === 'temporal';
758
+ });
759
+ spec.layer.forEach((layer, index)=>{
760
+ const mark = getMarkType(layer.mark);
761
+ const encoding = layer.encoding || {};
762
+ // Rect marks with x and x2 become color fill bars (vertical regions)
763
+ if (mark === 'rect' && encoding.x && encoding.x2) {
764
+ const legend = `region-${index}`;
765
+ const color = typeof layer.mark === 'object' && layer.mark.color ? layer.mark.color : (0, _VegaLiteColorAdapter.getVegaColorFromMap)(legend, colorMap, undefined, undefined, isDarkTheme);
766
+ // Extract start and end x values
767
+ const rawStartX = encoding.x.datum || encoding.x.value;
768
+ const rawEndX = encoding.x2.datum || encoding.x2.value;
769
+ if (rawStartX !== undefined && rawEndX !== undefined) {
770
+ // Convert to Date if x-axis is temporal and values are date-like strings
771
+ let startX = rawStartX;
772
+ let endX = rawEndX;
773
+ if (isXTemporal) {
774
+ const parsedStart = new Date(rawStartX);
775
+ const parsedEnd = new Date(rawEndX);
776
+ if (!isNaN(parsedStart.getTime())) {
777
+ startX = parsedStart;
778
+ }
779
+ if (!isNaN(parsedEnd.getTime())) {
780
+ endX = parsedEnd;
781
+ }
782
+ }
783
+ colorFillBars.push({
784
+ legend,
785
+ color,
786
+ data: [
787
+ {
788
+ startX,
789
+ endX
790
+ }
791
+ ],
792
+ applyPattern: false
793
+ });
794
+ }
795
+ }
796
+ });
797
+ return colorFillBars;
798
+ }
799
+ /**
800
+ * Extracts tick configuration from axis properties
801
+ */ function extractTickConfig(spec) {
802
+ var _encoding_x, _encoding_y;
803
+ const config = {};
804
+ const encoding = spec.encoding || {};
805
+ if ((_encoding_x = encoding.x) === null || _encoding_x === void 0 ? void 0 : _encoding_x.axis) {
806
+ if (encoding.x.axis.values) {
807
+ config.tickValues = encoding.x.axis.values;
808
+ }
809
+ if (encoding.x.axis.tickCount) {
810
+ config.xAxisTickCount = encoding.x.axis.tickCount;
811
+ }
812
+ }
813
+ if ((_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : _encoding_y.axis) {
814
+ if (encoding.y.axis.tickCount) {
815
+ config.yAxisTickCount = encoding.y.axis.tickCount;
816
+ }
817
+ }
818
+ return config;
819
+ }
820
+ /**
821
+ * Validates that data array is not empty and contains valid values for the specified field
822
+ * @param data - Array of data objects
823
+ * @param field - Field name to validate
824
+ * @param chartType - Chart type for error message context
825
+ * @throws Error if data is empty or field has no valid values
826
+ */ function validateDataArray(data, field, chartType) {
827
+ if (!data || data.length === 0) {
828
+ throw new Error(`VegaLiteSchemaAdapter: Empty data array for ${chartType}`);
829
+ }
830
+ const hasValidValues = data.some((row)=>row[field] !== undefined && row[field] !== null);
831
+ if (!hasValidValues) {
832
+ throw new Error(`VegaLiteSchemaAdapter: No valid values found for field '${field}' in ${chartType}`);
833
+ }
834
+ }
835
+ /**
836
+ * Validates that nested arrays are not present in the data field (unsupported)
837
+ * @param data - Array of data objects
838
+ * @param field - Field name to validate
839
+ * @throws Error if nested arrays are detected
840
+ */ function validateNoNestedArrays(data, field) {
841
+ const hasNestedArrays = data.some((row)=>Array.isArray(row[field]));
842
+ if (hasNestedArrays) {
843
+ throw new Error(`VegaLiteSchemaAdapter: Nested arrays not supported for field '${field}'. ` + `Use flat data structures only.`);
844
+ }
845
+ }
846
+ /**
847
+ * Validates data type compatibility with encoding type
848
+ * @param data - Array of data objects
849
+ * @param field - Field name to validate
850
+ * @param expectedType - Expected Vega-Lite type (quantitative, temporal, nominal, ordinal)
851
+ * @throws Error if data type doesn't match encoding type
852
+ */ /**
853
+ * Validates and potentially auto-corrects encoding types based on actual data
854
+ * Returns the corrected type if auto-correction was applied
855
+ *
856
+ * @param data - Array of data values
857
+ * @param field - Field name to validate
858
+ * @param expectedType - Expected Vega-Lite type from schema
859
+ * @param encoding - Encoding object to potentially modify
860
+ * @param channelName - Name of encoding channel (x, y, etc.) for auto-correction
861
+ * @returns Corrected type if auto-correction was applied, otherwise undefined
862
+ */ function validateEncodingType(data, field, expectedType, encoding, channelName) {
863
+ if (!expectedType || expectedType === 'nominal' || expectedType === 'ordinal' || expectedType === 'geojson') {
864
+ return; // Nominal, ordinal, and geojson accept any type
865
+ }
866
+ // Find first non-null value to check type
867
+ const sampleValue = data.map((row)=>row[field]).find((v)=>v !== null && v !== undefined);
868
+ if (!sampleValue) {
869
+ return; // No values to validate
870
+ }
871
+ if (expectedType === 'quantitative') {
872
+ if (typeof sampleValue !== 'number' && !isFinite(Number(sampleValue))) {
873
+ // Type mismatch: quantitative declared but data is not numeric
874
+ const actualType = typeof sampleValue;
875
+ if (actualType === 'string') {
876
+ // Auto-correct: treat as nominal for categorical string data
877
+ // This matches Plotly behavior - render as categorical chart
878
+ // Modify encoding to use nominal type
879
+ if (encoding && channelName && encoding[channelName]) {
880
+ encoding[channelName].type = 'nominal';
881
+ }
882
+ return 'nominal';
883
+ }
884
+ // For non-string types, still throw error (truly invalid)
885
+ throw new Error(`VegaLiteSchemaAdapter: Field '${field}' marked as quantitative but contains non-numeric values (${actualType}).`);
886
+ }
887
+ } else if (expectedType === 'temporal') {
888
+ const isValidDate = sampleValue instanceof Date || typeof sampleValue === 'string' && !isNaN(Date.parse(sampleValue));
889
+ if (!isValidDate) {
890
+ let suggestion = '';
891
+ if (typeof sampleValue === 'number') {
892
+ suggestion = ' The data contains numbers. Change the type to "quantitative" instead.';
893
+ } else if (typeof sampleValue === 'string') {
894
+ suggestion = ` The data contains strings that are not valid dates (e.g., "${sampleValue}"). Ensure dates are in ISO format (YYYY-MM-DD) or valid date strings.`;
895
+ }
896
+ throw new Error(`VegaLiteSchemaAdapter: Field '${field}' marked as temporal but contains invalid date values.${suggestion}`);
897
+ }
898
+ }
899
+ return undefined;
900
+ }
901
+ /**
902
+ * Validates X and Y encodings for charts requiring both axes
903
+ * Performs comprehensive validation including data array, nested arrays, and encoding types
904
+ * Can auto-correct type mismatches (e.g., quantitative with string data → nominal)
905
+ *
906
+ * @param data - Array of data objects
907
+ * @param xField - X field name
908
+ * @param yField - Y field name
909
+ * @param xType - Expected X encoding type
910
+ * @param yType - Expected Y encoding type
911
+ * @param chartType - Chart type for error message context
912
+ * @param encoding - Encoding object (optional, for auto-correction)
913
+ * @throws Error if validation fails
914
+ */ function validateXYEncodings(data, xField, yField, xType, yType, chartType, encoding) {
915
+ validateDataArray(data, xField, chartType);
916
+ validateDataArray(data, yField, chartType);
917
+ validateNoNestedArrays(data, xField);
918
+ validateNoNestedArrays(data, yField);
919
+ // Validate types with auto-correction support
920
+ validateEncodingType(data, xField, xType, encoding, 'x');
921
+ validateEncodingType(data, yField, yType, encoding, 'y');
922
+ }
923
+ /**
924
+ * Extracts Y-axis scale type from encoding
925
+ * Returns 'log' if logarithmic scale is specified, undefined otherwise
926
+ */ function extractYAxisType(encoding) {
927
+ var _encoding_y;
928
+ const yScale = encoding === null || encoding === void 0 ? void 0 : (_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : _encoding_y.scale;
929
+ return (yScale === null || yScale === void 0 ? void 0 : yScale.type) === 'log' ? 'log' : undefined;
930
+ }
931
+ /**
932
+ * Extracts y-axis min/max considering both scale.domain and scale.zero.
933
+ * When scale.zero is false and no explicit domain, yMinValue is computed from data.
934
+ */ function extractYMinMax(encoding, dataValues) {
935
+ var _encoding_y;
936
+ const yScale = encoding === null || encoding === void 0 ? void 0 : (_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : _encoding_y.scale;
937
+ const domain = yScale === null || yScale === void 0 ? void 0 : yScale.domain;
938
+ // Explicit domain takes priority
939
+ if (Array.isArray(domain)) {
940
+ return {
941
+ yMinValue: domain[0],
942
+ yMaxValue: domain[1]
943
+ };
944
+ }
945
+ // When zero is explicitly false, compute min from data so y-axis doesn't start at 0
946
+ if ((yScale === null || yScale === void 0 ? void 0 : yScale.zero) === false) {
947
+ var _encoding_y1;
948
+ const yField = encoding === null || encoding === void 0 ? void 0 : (_encoding_y1 = encoding.y) === null || _encoding_y1 === void 0 ? void 0 : _encoding_y1.field;
949
+ if (yField) {
950
+ const yValues = dataValues.map((row)=>Number(row[yField])).filter((v)=>!isNaN(v));
951
+ if (yValues.length > 0) {
952
+ var _d3Min;
953
+ const dataMin = (_d3Min = (0, _d3array.min)(yValues)) !== null && _d3Min !== void 0 ? _d3Min : 0;
954
+ var _d3Max;
955
+ const dataMax = (_d3Max = (0, _d3array.max)(yValues)) !== null && _d3Max !== void 0 ? _d3Max : 0;
956
+ const padding = (dataMax - dataMin) * 0.05;
957
+ return {
958
+ yMinValue: dataMin - padding
959
+ };
960
+ }
961
+ }
962
+ }
963
+ return {};
964
+ }
965
+ /**
966
+ * Creates a value formatter from a d3-format specifier string.
967
+ * Returns undefined if no format is specified or if the format is invalid.
968
+ */ function createValueFormatter(formatSpec) {
969
+ if (!formatSpec) {
970
+ return undefined;
971
+ }
972
+ try {
973
+ const formatter = (0, _d3format.format)(formatSpec);
974
+ return formatter;
975
+ } catch {
976
+ return undefined;
977
+ }
978
+ }
979
+ /**
980
+ * Converts Vega-Lite sort specification to Fluent Charts AxisCategoryOrder
981
+ * Supports: 'ascending', 'descending', null, array, or object with op/order
982
+ * @param sort - Vega-Lite sort specification
983
+ * @returns AxisCategoryOrder compatible value
984
+ */ function convertVegaSortToAxisCategoryOrder(sort) {
985
+ if (!sort) {
986
+ return undefined;
987
+ }
988
+ // Handle string sorts: 'ascending' | 'descending'
989
+ if (typeof sort === 'string') {
990
+ if (sort === 'ascending') {
991
+ return 'category ascending';
992
+ }
993
+ if (sort === 'descending') {
994
+ return 'category descending';
995
+ }
996
+ return undefined;
997
+ }
998
+ // Handle array sort (explicit ordering)
999
+ if (Array.isArray(sort)) {
1000
+ return sort;
1001
+ }
1002
+ // Handle object sort with op and order
1003
+ if (typeof sort === 'object' && sort.op && sort.order) {
1004
+ const op = sort.op === 'average' ? 'mean' : sort.op; // Map 'average' to 'mean'
1005
+ const order = sort.order === 'ascending' ? 'ascending' : 'descending';
1006
+ return `${op} ${order}`;
1007
+ }
1008
+ return undefined;
1009
+ }
1010
+ /**
1011
+ * Extracts axis category ordering from Vega-Lite encoding
1012
+ * Returns props for xAxisCategoryOrder and yAxisCategoryOrder
1013
+ */ function extractAxisCategoryOrderProps(encoding) {
1014
+ var _encoding_x, _encoding_y;
1015
+ const result = {};
1016
+ if (encoding === null || encoding === void 0 ? void 0 : (_encoding_x = encoding.x) === null || _encoding_x === void 0 ? void 0 : _encoding_x.sort) {
1017
+ const xOrder = convertVegaSortToAxisCategoryOrder(encoding.x.sort);
1018
+ if (xOrder) {
1019
+ result.xAxisCategoryOrder = xOrder;
1020
+ }
1021
+ }
1022
+ if (encoding === null || encoding === void 0 ? void 0 : (_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : _encoding_y.sort) {
1023
+ const yOrder = convertVegaSortToAxisCategoryOrder(encoding.y.sort);
1024
+ if (yOrder) {
1025
+ result.yAxisCategoryOrder = yOrder;
1026
+ }
1027
+ }
1028
+ return result;
1029
+ }
1030
+ /**
1031
+ * Initializes the transformation context by normalizing spec and extracting common data
1032
+ * This reduces boilerplate across all transformer functions
1033
+ *
1034
+ * @param spec - Vega-Lite specification
1035
+ * @returns Normalized context with unit specs, data values, encoding, and mark properties
1036
+ */ function initializeTransformContext(spec) {
1037
+ var _colorEnc_condition, _encoding_x, _encoding_x1;
1038
+ const unitSpecs = normalizeSpec(spec);
1039
+ if (unitSpecs.length === 0) {
1040
+ throw new Error('VegaLiteSchemaAdapter: No valid unit specs found in specification');
1041
+ }
1042
+ const primarySpec = unitSpecs[0];
1043
+ const rawDataValues = extractDataValues(primarySpec.data);
1044
+ // Apply any transforms from both top-level spec and primary unit spec
1045
+ let dataValues = applyTransforms(rawDataValues, spec.transform);
1046
+ dataValues = applyTransforms(dataValues, primarySpec.transform);
1047
+ const encoding = primarySpec.encoding || {};
1048
+ // Handle conditional color encoding — evaluate test expressions and materialize color values
1049
+ const colorEnc = encoding.color;
1050
+ if ((colorEnc === null || colorEnc === void 0 ? void 0 : colorEnc.condition) && typeof ((_colorEnc_condition = colorEnc.condition) === null || _colorEnc_condition === void 0 ? void 0 : _colorEnc_condition.test) === 'string') {
1051
+ const condition = colorEnc.condition;
1052
+ const elseValue = colorEnc.value || '#999';
1053
+ const colorField = '__conditional_color__';
1054
+ dataValues.forEach((row)=>{
1055
+ try {
1056
+ const testResult = (0, _VegaLiteExpressionEvaluator.safeEvaluateExpression)(condition.test, row);
1057
+ row[colorField] = testResult ? condition.value : elseValue;
1058
+ } catch {
1059
+ row[colorField] = elseValue;
1060
+ }
1061
+ });
1062
+ // Replace conditional color with a field-based color encoding using the materialized values
1063
+ encoding.color = {
1064
+ field: colorField,
1065
+ type: 'nominal',
1066
+ scale: {
1067
+ domain: [
1068
+ condition.value,
1069
+ elseValue
1070
+ ],
1071
+ range: [
1072
+ condition.value,
1073
+ elseValue
1074
+ ]
1075
+ },
1076
+ legend: null
1077
+ };
1078
+ }
1079
+ // Handle timeUnit on x/y encodings — aggregate data by time unit
1080
+ if (((_encoding_x = encoding.x) === null || _encoding_x === void 0 ? void 0 : _encoding_x.timeUnit) && ((_encoding_x1 = encoding.x) === null || _encoding_x1 === void 0 ? void 0 : _encoding_x1.field)) {
1081
+ var _encoding_y, _encoding_y1, _encoding_y2;
1082
+ const field = encoding.x.field;
1083
+ const unit = encoding.x.timeUnit;
1084
+ const yField = (_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : _encoding_y.field;
1085
+ const yAgg = ((_encoding_y1 = encoding.y) === null || _encoding_y1 === void 0 ? void 0 : _encoding_y1.aggregate) || (yField ? 'mean' : 'count');
1086
+ const groups = new Map();
1087
+ dataValues.forEach((row)=>{
1088
+ const dateVal = new Date(row[field]);
1089
+ if (isNaN(dateVal.getTime())) {
1090
+ return;
1091
+ }
1092
+ let key;
1093
+ switch(unit){
1094
+ case 'year':
1095
+ key = String(dateVal.getFullYear());
1096
+ break;
1097
+ case 'quarter':
1098
+ key = `Q${Math.floor(dateVal.getMonth() / 3) + 1}`;
1099
+ break;
1100
+ case 'month':
1101
+ key = dateVal.toLocaleString('en', {
1102
+ month: 'short'
1103
+ });
1104
+ break;
1105
+ case 'day':
1106
+ key = String(dateVal.getDate());
1107
+ break;
1108
+ case 'hours':
1109
+ key = String(dateVal.getHours());
1110
+ break;
1111
+ default:
1112
+ key = String(dateVal);
1113
+ }
1114
+ if (!groups.has(key)) {
1115
+ groups.set(key, []);
1116
+ }
1117
+ groups.get(key).push(row);
1118
+ });
1119
+ dataValues = Array.from(groups.entries()).map(([key, rows])=>{
1120
+ const result = {
1121
+ [field]: key
1122
+ };
1123
+ if (yField && yAgg !== 'count') {
1124
+ const vals = rows.map((r)=>Number(r[yField])).filter((v)=>!isNaN(v));
1125
+ var _d3Mean;
1126
+ result[yField] = yAgg === 'sum' ? (0, _d3array.sum)(vals) : (_d3Mean = (0, _d3array.mean)(vals)) !== null && _d3Mean !== void 0 ? _d3Mean : 0;
1127
+ } else {
1128
+ result[yField || '__count'] = rows.length;
1129
+ }
1130
+ return result;
1131
+ });
1132
+ // Switch x from temporal to ordinal since we've aggregated
1133
+ encoding.x.type = 'ordinal';
1134
+ delete encoding.x.timeUnit;
1135
+ if ((_encoding_y2 = encoding.y) === null || _encoding_y2 === void 0 ? void 0 : _encoding_y2.aggregate) {
1136
+ delete encoding.y.aggregate;
1137
+ }
1138
+ }
1139
+ const markProps = getMarkProperties(primarySpec.mark);
1140
+ return {
1141
+ unitSpecs,
1142
+ primarySpec,
1143
+ dataValues,
1144
+ encoding,
1145
+ markProps
1146
+ };
1147
+ }
1148
+ /**
1149
+ * Extracts common encoding fields and aggregates from Vega-Lite encoding
1150
+ *
1151
+ * @param encoding - Vega-Lite encoding specification
1152
+ * @returns Object containing extracted field names and aggregates
1153
+ */ function extractEncodingFields(encoding) {
1154
+ var _encoding_x, _encoding_y, _encoding_x2, _encoding_color, _encoding_size, _encoding_theta, _encoding_radius, _encoding_x1, _encoding_y1;
1155
+ return {
1156
+ xField: (_encoding_x = encoding.x) === null || _encoding_x === void 0 ? void 0 : _encoding_x.field,
1157
+ yField: (_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : _encoding_y.field,
1158
+ x2Field: (_encoding_x2 = encoding.x2) === null || _encoding_x2 === void 0 ? void 0 : _encoding_x2.field,
1159
+ colorField: (_encoding_color = encoding.color) === null || _encoding_color === void 0 ? void 0 : _encoding_color.field,
1160
+ sizeField: (_encoding_size = encoding.size) === null || _encoding_size === void 0 ? void 0 : _encoding_size.field,
1161
+ thetaField: (_encoding_theta = encoding.theta) === null || _encoding_theta === void 0 ? void 0 : _encoding_theta.field,
1162
+ radiusField: (_encoding_radius = encoding.radius) === null || _encoding_radius === void 0 ? void 0 : _encoding_radius.field,
1163
+ xAggregate: (_encoding_x1 = encoding.x) === null || _encoding_x1 === void 0 ? void 0 : _encoding_x1.aggregate,
1164
+ yAggregate: (_encoding_y1 = encoding.y) === null || _encoding_y1 === void 0 ? void 0 : _encoding_y1.aggregate
1165
+ };
1166
+ }
1167
+ /**
1168
+ * Computes aggregate values for bar charts
1169
+ * Supports count, sum, mean, min, max aggregations
1170
+ *
1171
+ * @param data - Array of data values
1172
+ * @param groupField - Field to group by (x-axis field)
1173
+ * @param valueField - Field to aggregate (y-axis field, optional for count)
1174
+ * @param aggregate - Aggregate function (count, sum, mean, min, max)
1175
+ * @returns Array of {category, value} objects
1176
+ */ function computeAggregateData(data, groupField, valueField, aggregate) {
1177
+ // Group data by category
1178
+ const groups = new Map();
1179
+ data.forEach((row)=>{
1180
+ const category = String(row[groupField]);
1181
+ if (aggregate === 'count') {
1182
+ // For count, just track the count
1183
+ if (!groups.has(category)) {
1184
+ groups.set(category, []);
1185
+ }
1186
+ groups.get(category).push(1);
1187
+ } else if (valueField && row[valueField] !== undefined && row[valueField] !== null) {
1188
+ // For other aggregates, collect values
1189
+ const value = Number(row[valueField]);
1190
+ if (!isNaN(value)) {
1191
+ if (!groups.has(category)) {
1192
+ groups.set(category, []);
1193
+ }
1194
+ groups.get(category).push(value);
1195
+ }
1196
+ }
1197
+ });
1198
+ // Compute aggregate for each group
1199
+ const result = [];
1200
+ groups.forEach((values, category)=>{
1201
+ let aggregatedValue;
1202
+ switch(aggregate){
1203
+ case 'count':
1204
+ aggregatedValue = values.length;
1205
+ break;
1206
+ case 'sum':
1207
+ aggregatedValue = values.reduce((a, b)=>a + b, 0);
1208
+ break;
1209
+ case 'mean':
1210
+ case 'average':
1211
+ aggregatedValue = values.reduce((a, b)=>a + b, 0) / values.length;
1212
+ break;
1213
+ case 'min':
1214
+ var _d3Min;
1215
+ aggregatedValue = (_d3Min = (0, _d3array.min)(values)) !== null && _d3Min !== void 0 ? _d3Min : 0;
1216
+ break;
1217
+ case 'max':
1218
+ var _d3Max;
1219
+ aggregatedValue = (_d3Max = (0, _d3array.max)(values)) !== null && _d3Max !== void 0 ? _d3Max : 0;
1220
+ break;
1221
+ default:
1222
+ aggregatedValue = values.length; // Default to count
1223
+ }
1224
+ result.push({
1225
+ category,
1226
+ value: aggregatedValue
1227
+ });
1228
+ });
1229
+ return result;
1230
+ }
1231
+ /**
1232
+ * Counts rows per x-category, optionally grouped by a secondary (color) field.
1233
+ * Returns a Map<xKey, Map<legend, count>>.
1234
+ */ function countByCategory(dataValues, xField, colorField, defaultLegend) {
1235
+ const countMap = new Map();
1236
+ dataValues.forEach((row)=>{
1237
+ const xValue = row[xField];
1238
+ if (xValue === undefined) {
1239
+ return;
1240
+ }
1241
+ const xKey = String(xValue);
1242
+ const legend = colorField && row[colorField] !== undefined ? String(row[colorField]) : defaultLegend;
1243
+ if (!countMap.has(xKey)) {
1244
+ countMap.set(xKey, new Map());
1245
+ }
1246
+ const legendMap = countMap.get(xKey);
1247
+ legendMap.set(legend, (legendMap.get(legend) || 0) + 1);
1248
+ });
1249
+ return countMap;
1250
+ }
1251
+ /**
1252
+ * Extracts color configuration from Vega-Lite encoding
1253
+ *
1254
+ * @param encoding - Vega-Lite encoding specification
1255
+ * @returns Color scheme and range configuration
1256
+ */ function extractColorConfig(encoding) {
1257
+ var _encoding_color_scale, _encoding_color, _encoding_color_scale1, _encoding_color1;
1258
+ return {
1259
+ colorScheme: (_encoding_color = encoding.color) === null || _encoding_color === void 0 ? void 0 : (_encoding_color_scale = _encoding_color.scale) === null || _encoding_color_scale === void 0 ? void 0 : _encoding_color_scale.scheme,
1260
+ colorRange: (_encoding_color1 = encoding.color) === null || _encoding_color1 === void 0 ? void 0 : (_encoding_color_scale1 = _encoding_color1.scale) === null || _encoding_color_scale1 === void 0 ? void 0 : _encoding_color_scale1.range
1261
+ };
1262
+ }
1263
+ /**
1264
+ * Groups data rows into series based on color encoding field
1265
+ * Returns a map of series name to data points and ordinal mapping for categorical x-axis
1266
+ */ function groupDataBySeries(dataValues, xField, yField, colorField, isXTemporal, isYTemporal, xType, sizeField, yType) {
1267
+ const seriesMap = new Map();
1268
+ if (!xField || !yField) {
1269
+ return {
1270
+ seriesMap
1271
+ };
1272
+ }
1273
+ // Check if x-axis is ordinal/nominal (categorical)
1274
+ const isXOrdinal = xType === 'ordinal' || xType === 'nominal';
1275
+ const ordinalMapping = isXOrdinal ? new Map() : undefined;
1276
+ const ordinalLabels = [];
1277
+ // Check if y-axis is ordinal/nominal (categorical)
1278
+ const isYOrdinal = yType === 'ordinal' || yType === 'nominal';
1279
+ const yOrdinalMapping = isYOrdinal ? new Map() : undefined;
1280
+ const yOrdinalLabels = [];
1281
+ dataValues.forEach((row)=>{
1282
+ const xValue = parseValue(row[xField], isXTemporal);
1283
+ const yValue = parseValue(row[yField], isYTemporal);
1284
+ // Skip invalid values using chart-utilities validation
1285
+ if ((0, _chartutilities.isInvalidValue)(xValue) || (0, _chartutilities.isInvalidValue)(yValue)) {
1286
+ return;
1287
+ }
1288
+ // Skip if x or y is empty string (from null/undefined) or y is not a valid number/string
1289
+ if (xValue === '' || yValue === '' || typeof yValue !== 'number' && typeof yValue !== 'string') {
1290
+ return;
1291
+ }
1292
+ const seriesName = colorField && row[colorField] !== undefined ? String(row[colorField]) : 'default';
1293
+ if (!seriesMap.has(seriesName)) {
1294
+ seriesMap.set(seriesName, []);
1295
+ }
1296
+ // Handle x-value based on type
1297
+ let numericX;
1298
+ if (isXOrdinal && typeof xValue === 'string') {
1299
+ // For ordinal data, map each unique string to a sequential index
1300
+ if (!ordinalMapping.has(xValue)) {
1301
+ ordinalMapping.set(xValue, ordinalMapping.size);
1302
+ ordinalLabels.push(xValue);
1303
+ }
1304
+ numericX = ordinalMapping.get(xValue);
1305
+ } else if (typeof xValue === 'string') {
1306
+ // For non-ordinal strings, try to parse as float (fallback to 0)
1307
+ const parsed = parseFloat(xValue);
1308
+ if (isNaN(parsed)) {
1309
+ return;
1310
+ }
1311
+ numericX = parsed;
1312
+ } else {
1313
+ numericX = xValue;
1314
+ }
1315
+ const markerSize = sizeField && row[sizeField] !== undefined ? Number(row[sizeField]) : undefined;
1316
+ // Handle y-value: numeric or ordinal mapping
1317
+ let numericY;
1318
+ if (isYOrdinal && typeof yValue === 'string') {
1319
+ if (!yOrdinalMapping.has(yValue)) {
1320
+ yOrdinalMapping.set(yValue, yOrdinalMapping.size);
1321
+ yOrdinalLabels.push(yValue);
1322
+ }
1323
+ numericY = yOrdinalMapping.get(yValue);
1324
+ } else {
1325
+ numericY = typeof yValue === 'number' ? yValue : 0;
1326
+ }
1327
+ seriesMap.get(seriesName).push({
1328
+ x: numericX,
1329
+ y: numericY,
1330
+ ...markerSize !== undefined && !isNaN(markerSize) && {
1331
+ markerSize
1332
+ }
1333
+ });
1334
+ });
1335
+ return {
1336
+ seriesMap,
1337
+ ordinalMapping,
1338
+ ordinalLabels: ordinalLabels.length > 0 ? ordinalLabels : undefined,
1339
+ yOrdinalLabels: yOrdinalLabels.length > 0 ? yOrdinalLabels : undefined
1340
+ };
1341
+ }
1342
+ /**
1343
+ * Finds the primary data layer from unit specs for line/area charts
1344
+ * Skips rect layers (used for color fill bars) and finds the actual line/point/area layer
1345
+ *
1346
+ * @param unitSpecs - Array of normalized unit specs
1347
+ * @returns The primary spec containing the actual chart data, or undefined if not found
1348
+ */ function findPrimaryLineSpec(unitSpecs) {
1349
+ // First, try to find a line, point, or area layer
1350
+ const lineSpec = unitSpecs.find((spec)=>{
1351
+ const markType = getMarkType(spec.mark);
1352
+ return markType === 'line' || markType === 'point' || markType === 'area';
1353
+ });
1354
+ if (lineSpec) {
1355
+ return lineSpec;
1356
+ }
1357
+ // If no line/point/area layer, find first layer with actual field encodings (not just datum)
1358
+ const dataSpec = unitSpecs.find((spec)=>{
1359
+ var _encoding_x, _encoding_y;
1360
+ const encoding = spec.encoding || {};
1361
+ return ((_encoding_x = encoding.x) === null || _encoding_x === void 0 ? void 0 : _encoding_x.field) || ((_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : _encoding_y.field);
1362
+ });
1363
+ return dataSpec || unitSpecs[0];
1364
+ }
1365
+ function autoCorrectEncodingTypes(spec) {
1366
+ var _encoding_x, _encoding_y;
1367
+ const unitSpec = spec.layer ? spec.layer[0] : spec;
1368
+ if (!unitSpec) {
1369
+ return;
1370
+ }
1371
+ const encoding = unitSpec.encoding;
1372
+ var _unitSpec_data;
1373
+ const data = extractDataValues((_unitSpec_data = unitSpec.data) !== null && _unitSpec_data !== void 0 ? _unitSpec_data : spec.data);
1374
+ if (!encoding || data.length === 0) {
1375
+ return;
1376
+ }
1377
+ // Check x encoding
1378
+ if ((_encoding_x = encoding.x) === null || _encoding_x === void 0 ? void 0 : _encoding_x.field) {
1379
+ const sample = data.map((row)=>row[encoding.x.field]).find((v)=>v !== null && v !== undefined);
1380
+ if (sample !== undefined) {
1381
+ if (encoding.x.type === 'quantitative') {
1382
+ if (typeof sample === 'string' && !isFinite(Number(sample))) {
1383
+ encoding.x.type = 'nominal';
1384
+ } else if (typeof sample === 'object') {
1385
+ encoding.x.type = 'nominal';
1386
+ }
1387
+ } else if (encoding.x.type === 'temporal') {
1388
+ const isValidDate = sample instanceof Date || typeof sample === 'string' && !isNaN(Date.parse(sample));
1389
+ if (!isValidDate) {
1390
+ encoding.x.type = typeof sample === 'number' ? 'quantitative' : 'nominal';
1391
+ }
1392
+ }
1393
+ }
1394
+ }
1395
+ // Check y encoding
1396
+ if ((_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : _encoding_y.field) {
1397
+ const sample = data.map((row)=>row[encoding.y.field]).find((v)=>v !== null && v !== undefined);
1398
+ if (sample !== undefined) {
1399
+ if (encoding.y.type === 'quantitative') {
1400
+ if (typeof sample === 'string' && !isFinite(Number(sample))) {
1401
+ encoding.y.type = 'nominal';
1402
+ } else if (typeof sample === 'object' && sample !== null && !Array.isArray(sample) && !(sample instanceof Date)) {
1403
+ // Try to extract a numeric value from the object
1404
+ const numericKeys = Object.keys(sample).filter((k)=>typeof sample[k] === 'number');
1405
+ if (numericKeys.length === 1) {
1406
+ // Object has exactly one numeric property - use it as the value
1407
+ const numericKey = numericKeys[0];
1408
+ const yField = encoding.y.field;
1409
+ data.forEach((row)=>{
1410
+ const obj = row[yField];
1411
+ if (typeof obj === 'object' && obj !== null) {
1412
+ row[yField] = obj[numericKey];
1413
+ }
1414
+ });
1415
+ // Keep type as quantitative since we extracted numeric values
1416
+ } else {
1417
+ encoding.y.type = 'nominal';
1418
+ }
1419
+ } else if (typeof sample === 'object') {
1420
+ encoding.y.type = 'nominal';
1421
+ }
1422
+ } else if (encoding.y.type === 'temporal') {
1423
+ const isValidDate = sample instanceof Date || typeof sample === 'string' && !isNaN(Date.parse(sample));
1424
+ if (!isValidDate) {
1425
+ encoding.y.type = typeof sample === 'number' ? 'quantitative' : 'nominal';
1426
+ }
1427
+ }
1428
+ }
1429
+ }
1430
+ }
1431
+ function getChartType(spec) {
1432
+ var _spec_layer_, _spec_layer_1, _encoding_color, _encoding_x, _encoding_y, _encoding_color1;
1433
+ // Auto-correct encoding types based on actual data BEFORE chart type detection
1434
+ autoCorrectEncodingTypes(spec);
1435
+ // Handle layered specs - check if it's a bar+line combo for stacked bar with lines
1436
+ if (spec.layer && spec.layer.length > 1) {
1437
+ const marks = spec.layer.map((layer)=>getMarkType(layer.mark));
1438
+ const hasBar = marks.includes('bar');
1439
+ const hasLine = marks.includes('line') || marks.includes('point');
1440
+ // Bar + line combo should use stacked bar chart (which supports line overlays)
1441
+ if (hasBar && hasLine) {
1442
+ var _barLayer_encoding_color, _barLayer_encoding;
1443
+ const barLayer = spec.layer.find((layer)=>getMarkType(layer.mark) === 'bar');
1444
+ if (barLayer === null || barLayer === void 0 ? void 0 : (_barLayer_encoding = barLayer.encoding) === null || _barLayer_encoding === void 0 ? void 0 : (_barLayer_encoding_color = _barLayer_encoding.color) === null || _barLayer_encoding_color === void 0 ? void 0 : _barLayer_encoding_color.field) {
1445
+ return {
1446
+ type: 'stacked-bar',
1447
+ mark: 'bar'
1448
+ };
1449
+ }
1450
+ return {
1451
+ type: 'stacked-bar',
1452
+ mark: 'bar'
1453
+ };
1454
+ }
1455
+ }
1456
+ // Handle layered specs - use first layer's mark for other cases
1457
+ const mark = spec.layer ? (_spec_layer_ = spec.layer[0]) === null || _spec_layer_ === void 0 ? void 0 : _spec_layer_.mark : spec.mark;
1458
+ const markType = getMarkType(mark);
1459
+ const encoding = spec.layer ? (_spec_layer_1 = spec.layer[0]) === null || _spec_layer_1 === void 0 ? void 0 : _spec_layer_1.encoding : spec.encoding;
1460
+ const hasColorEncoding = !!(encoding === null || encoding === void 0 ? void 0 : (_encoding_color = encoding.color) === null || _encoding_color === void 0 ? void 0 : _encoding_color.field);
1461
+ // Polar charts with arc marks: theta AND radius encodings
1462
+ if (markType === 'arc' && (encoding === null || encoding === void 0 ? void 0 : encoding.theta) && (encoding === null || encoding === void 0 ? void 0 : encoding.radius)) {
1463
+ return {
1464
+ type: 'polar',
1465
+ mark: markType
1466
+ };
1467
+ }
1468
+ // Arc marks for pie/donut charts (theta only, no radius)
1469
+ if (markType === 'arc' && (encoding === null || encoding === void 0 ? void 0 : encoding.theta)) {
1470
+ return {
1471
+ type: 'donut',
1472
+ mark: markType
1473
+ };
1474
+ }
1475
+ // Polar charts: non-arc marks with theta and radius encodings
1476
+ if ((encoding === null || encoding === void 0 ? void 0 : encoding.theta) && (encoding === null || encoding === void 0 ? void 0 : encoding.radius)) {
1477
+ return {
1478
+ type: 'polar',
1479
+ mark: markType
1480
+ };
1481
+ }
1482
+ // Rect marks for heatmaps (quantitative or nominal color)
1483
+ if (markType === 'rect' && (encoding === null || encoding === void 0 ? void 0 : (_encoding_x = encoding.x) === null || _encoding_x === void 0 ? void 0 : _encoding_x.field) && (encoding === null || encoding === void 0 ? void 0 : (_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : _encoding_y.field) && (encoding === null || encoding === void 0 ? void 0 : (_encoding_color1 = encoding.color) === null || _encoding_color1 === void 0 ? void 0 : _encoding_color1.field)) {
1484
+ return {
1485
+ type: 'heatmap',
1486
+ mark: markType
1487
+ };
1488
+ }
1489
+ // Bar charts
1490
+ if (markType === 'bar') {
1491
+ var _encoding_x1, _encoding_x2, _encoding_x3, _encoding_y1, _encoding_y2, _encoding_x4, _spec_data;
1492
+ if (encoding === null || encoding === void 0 ? void 0 : (_encoding_x1 = encoding.x) === null || _encoding_x1 === void 0 ? void 0 : _encoding_x1.bin) {
1493
+ return {
1494
+ type: 'histogram',
1495
+ mark: markType
1496
+ };
1497
+ }
1498
+ const isXNominal = (encoding === null || encoding === void 0 ? void 0 : (_encoding_x2 = encoding.x) === null || _encoding_x2 === void 0 ? void 0 : _encoding_x2.type) === 'nominal' || (encoding === null || encoding === void 0 ? void 0 : (_encoding_x3 = encoding.x) === null || _encoding_x3 === void 0 ? void 0 : _encoding_x3.type) === 'ordinal';
1499
+ const isYNominal = (encoding === null || encoding === void 0 ? void 0 : (_encoding_y1 = encoding.y) === null || _encoding_y1 === void 0 ? void 0 : _encoding_y1.type) === 'nominal' || (encoding === null || encoding === void 0 ? void 0 : (_encoding_y2 = encoding.y) === null || _encoding_y2 === void 0 ? void 0 : _encoding_y2.type) === 'ordinal';
1500
+ if (isYNominal && !isXNominal) {
1501
+ return {
1502
+ type: 'horizontal-bar',
1503
+ mark: markType
1504
+ };
1505
+ }
1506
+ if (hasColorEncoding) {
1507
+ const hasXOffset = !!(encoding === null || encoding === void 0 ? void 0 : encoding.xOffset);
1508
+ if (hasXOffset) {
1509
+ return {
1510
+ type: 'grouped-bar',
1511
+ mark: markType
1512
+ };
1513
+ }
1514
+ return {
1515
+ type: 'stacked-bar',
1516
+ mark: markType
1517
+ };
1518
+ }
1519
+ const xField = encoding === null || encoding === void 0 ? void 0 : (_encoding_x4 = encoding.x) === null || _encoding_x4 === void 0 ? void 0 : _encoding_x4.field;
1520
+ const dataValues = (_spec_data = spec.data) === null || _spec_data === void 0 ? void 0 : _spec_data.values;
1521
+ if (xField && Array.isArray(dataValues) && dataValues.length > 0) {
1522
+ const xValues = dataValues.map((row)=>row[xField]);
1523
+ const uniqueXValues = new Set(xValues);
1524
+ if (uniqueXValues.size < xValues.length) {
1525
+ return {
1526
+ type: 'stacked-bar',
1527
+ mark: markType
1528
+ };
1529
+ }
1530
+ }
1531
+ return {
1532
+ type: 'bar',
1533
+ mark: markType
1534
+ };
1535
+ }
1536
+ if (markType === 'area') {
1537
+ return {
1538
+ type: 'area',
1539
+ mark: markType
1540
+ };
1541
+ }
1542
+ if (markType === 'point' || markType === 'circle' || markType === 'square' || markType === 'tick') {
1543
+ return {
1544
+ type: 'scatter',
1545
+ mark: markType
1546
+ };
1547
+ }
1548
+ // Trail marks rendered as line charts (size encoding as markerSize)
1549
+ if (markType === 'trail') {
1550
+ return {
1551
+ type: 'line',
1552
+ mark: 'line'
1553
+ };
1554
+ }
1555
+ // Error bar/band marks rendered as line charts
1556
+ if (markType === 'errorbar' || markType === 'errorband') {
1557
+ return {
1558
+ type: 'line',
1559
+ mark: 'line'
1560
+ };
1561
+ }
1562
+ return {
1563
+ type: 'line',
1564
+ mark: markType || 'line'
1565
+ };
1566
+ }
1567
+ function transformVegaLiteToLineChartProps(spec, colorMap, isDarkTheme) {
1568
+ var _encoding_x, _encoding_y, _encoding_x1, _encoding_y1, _encoding_x2, _encoding_y2, _spec_title, _encoding_x_axis, _encoding_x3, _encoding_y_axis, _encoding_y3, _encoding_x_axis1, _encoding_x4, _encoding_y_axis1, _encoding_y4, _encoding_x_axis2, _encoding_x5, _encoding_y_axis2, _encoding_y5, _encoding_color_legend, _encoding_color;
1569
+ // Initialize transformation context, but find the primary line/point layer for layered specs
1570
+ const unitSpecs = normalizeSpec(spec);
1571
+ if (unitSpecs.length === 0) {
1572
+ throw new Error('VegaLiteSchemaAdapter: No valid unit specs found in specification');
1573
+ }
1574
+ // For layered specs, find the actual line/point layer (not rect layers for color fill bars)
1575
+ const primarySpec = findPrimaryLineSpec(unitSpecs);
1576
+ if (!primarySpec) {
1577
+ throw new Error('VegaLiteSchemaAdapter: No valid line/point layer found in specification');
1578
+ }
1579
+ // Check if there's a point layer in addition to line layer (for line+point combo charts)
1580
+ const hasPointLayer = unitSpecs.some((unitSpec)=>getMarkType(unitSpec.mark) === 'point');
1581
+ const hasLineLayer = unitSpecs.some((unitSpec)=>getMarkType(unitSpec.mark) === 'line');
1582
+ const shouldShowPoints = hasPointLayer && hasLineLayer;
1583
+ const rawDataValues = extractDataValues(primarySpec.data);
1584
+ // Apply any transforms (fold, etc.) from the spec
1585
+ const dataValues = applyTransforms(rawDataValues, spec.transform);
1586
+ const encoding = primarySpec.encoding || {};
1587
+ const markProps = getMarkProperties(primarySpec.mark);
1588
+ // Extract field names
1589
+ const { xField, yField, colorField } = extractEncodingFields(encoding);
1590
+ // Check for size encoding from any layer (e.g., point layer with size in line+point combo)
1591
+ let sizeField;
1592
+ if (unitSpecs.length > 1) {
1593
+ for (const unitSpec of unitSpecs){
1594
+ var _unitEncoding_size;
1595
+ const unitEncoding = unitSpec.encoding || {};
1596
+ if ((_unitEncoding_size = unitEncoding.size) === null || _unitEncoding_size === void 0 ? void 0 : _unitEncoding_size.field) {
1597
+ sizeField = unitEncoding.size.field;
1598
+ break;
1599
+ }
1600
+ }
1601
+ } else {
1602
+ var _encoding_size;
1603
+ sizeField = (_encoding_size = encoding.size) === null || _encoding_size === void 0 ? void 0 : _encoding_size.field;
1604
+ }
1605
+ // Validate data and encodings
1606
+ if (!xField || !yField) {
1607
+ throw new Error('VegaLiteSchemaAdapter: Line chart requires both x and y encodings with field names');
1608
+ }
1609
+ validateXYEncodings(dataValues, xField, yField, (_encoding_x = encoding.x) === null || _encoding_x === void 0 ? void 0 : _encoding_x.type, (_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : _encoding_y.type, 'LineChart', encoding);
1610
+ const isXTemporal = ((_encoding_x1 = encoding.x) === null || _encoding_x1 === void 0 ? void 0 : _encoding_x1.type) === 'temporal';
1611
+ const isYTemporal = ((_encoding_y1 = encoding.y) === null || _encoding_y1 === void 0 ? void 0 : _encoding_y1.type) === 'temporal';
1612
+ // Group data into series
1613
+ const { seriesMap, ordinalLabels, yOrdinalLabels } = groupDataBySeries(dataValues, xField, yField, colorField, isXTemporal, isYTemporal, (_encoding_x2 = encoding.x) === null || _encoding_x2 === void 0 ? void 0 : _encoding_x2.type, sizeField, (_encoding_y2 = encoding.y) === null || _encoding_y2 === void 0 ? void 0 : _encoding_y2.type);
1614
+ // Extract color configuration
1615
+ const { colorScheme, colorRange } = extractColorConfig(encoding);
1616
+ // Convert series map to LineChartPoints array
1617
+ const lineChartData = [];
1618
+ const colorIndex = new Map();
1619
+ let currentColorIndex = 0;
1620
+ seriesMap.forEach((dataPoints, seriesName)=>{
1621
+ if (!colorIndex.has(seriesName)) {
1622
+ colorIndex.set(seriesName, currentColorIndex++);
1623
+ }
1624
+ const color = resolveColor(seriesName, colorIndex.get(seriesName), undefined, markProps.color, colorMap, colorScheme, colorRange, isDarkTheme);
1625
+ const curveOption = mapInterpolateToCurve(markProps.interpolate);
1626
+ // Build line options with curve, strokeDash, and strokeWidth
1627
+ const lineOptions = {};
1628
+ if (curveOption) {
1629
+ lineOptions.curve = curveOption;
1630
+ }
1631
+ if (markProps.strokeDash) {
1632
+ lineOptions.strokeDasharray = markProps.strokeDash.join(' ');
1633
+ }
1634
+ if (markProps.strokeWidth) {
1635
+ lineOptions.strokeWidth = markProps.strokeWidth;
1636
+ }
1637
+ lineChartData.push({
1638
+ legend: seriesName,
1639
+ data: dataPoints,
1640
+ color,
1641
+ hideNonActiveDots: !shouldShowPoints,
1642
+ ...Object.keys(lineOptions).length > 0 && {
1643
+ lineOptions
1644
+ }
1645
+ });
1646
+ });
1647
+ // Extract chart title
1648
+ const chartTitle = typeof spec.title === 'string' ? spec.title : (_spec_title = spec.title) === null || _spec_title === void 0 ? void 0 : _spec_title.text;
1649
+ var _encoding_x_axis_title;
1650
+ // Extract axis titles and formats
1651
+ const xAxisTitle = (_encoding_x_axis_title = (_encoding_x3 = encoding.x) === null || _encoding_x3 === void 0 ? void 0 : (_encoding_x_axis = _encoding_x3.axis) === null || _encoding_x_axis === void 0 ? void 0 : _encoding_x_axis.title) !== null && _encoding_x_axis_title !== void 0 ? _encoding_x_axis_title : undefined;
1652
+ var _encoding_y_axis_title;
1653
+ const yAxisTitle = (_encoding_y_axis_title = (_encoding_y3 = encoding.y) === null || _encoding_y3 === void 0 ? void 0 : (_encoding_y_axis = _encoding_y3.axis) === null || _encoding_y_axis === void 0 ? void 0 : _encoding_y_axis.title) !== null && _encoding_y_axis_title !== void 0 ? _encoding_y_axis_title : undefined;
1654
+ const tickFormat = (_encoding_x4 = encoding.x) === null || _encoding_x4 === void 0 ? void 0 : (_encoding_x_axis1 = _encoding_x4.axis) === null || _encoding_x_axis1 === void 0 ? void 0 : _encoding_x_axis1.format;
1655
+ const yAxisTickFormat = (_encoding_y4 = encoding.y) === null || _encoding_y4 === void 0 ? void 0 : (_encoding_y_axis1 = _encoding_y4.axis) === null || _encoding_y_axis1 === void 0 ? void 0 : _encoding_y_axis1.format;
1656
+ // Extract tick values and counts
1657
+ // Use ordinalLabels for ordinal x-axis, otherwise use explicit values from spec
1658
+ const tickValues = ordinalLabels || ((_encoding_x5 = encoding.x) === null || _encoding_x5 === void 0 ? void 0 : (_encoding_x_axis2 = _encoding_x5.axis) === null || _encoding_x_axis2 === void 0 ? void 0 : _encoding_x_axis2.values);
1659
+ const yAxisTickCount = (_encoding_y5 = encoding.y) === null || _encoding_y5 === void 0 ? void 0 : (_encoding_y_axis2 = _encoding_y5.axis) === null || _encoding_y_axis2 === void 0 ? void 0 : _encoding_y_axis2.tickCount;
1660
+ // Extract domain/range for min/max values
1661
+ const { yMinValue, yMaxValue } = extractYMinMax(encoding, dataValues);
1662
+ // Extract annotations and color fill bars from layers
1663
+ const annotations = extractAnnotations(spec);
1664
+ const colorFillBars = extractColorFillBars(spec, colorMap, isDarkTheme);
1665
+ // Convert rule marks in layered specs to reference line series
1666
+ // Each horizontal rule becomes a 2-point line at constant y spanning the data x-range
1667
+ if (spec.layer && Array.isArray(spec.layer) && lineChartData.length > 0) {
1668
+ const allXValues = lineChartData.flatMap((series)=>series.data.map((p)=>p.x));
1669
+ const xMin = allXValues.length > 0 ? allXValues.reduce((a, b)=>a < b ? a : b) : 0;
1670
+ const xMax = allXValues.length > 0 ? allXValues.reduce((a, b)=>a > b ? a : b) : 0;
1671
+ spec.layer.forEach((layer, layerIndex)=>{
1672
+ var _ruleEncoding_y, _ruleEncoding_y1, _textLayer_encoding_text, _textLayer_encoding, _textLayer_encoding_text1, _textLayer_encoding1;
1673
+ const layerMark = getMarkType(layer.mark);
1674
+ if (layerMark !== 'rule') {
1675
+ return;
1676
+ }
1677
+ const ruleEncoding = layer.encoding || {};
1678
+ var _ruleEncoding_y_datum;
1679
+ const yDatum = (_ruleEncoding_y_datum = (_ruleEncoding_y = ruleEncoding.y) === null || _ruleEncoding_y === void 0 ? void 0 : _ruleEncoding_y.datum) !== null && _ruleEncoding_y_datum !== void 0 ? _ruleEncoding_y_datum : (_ruleEncoding_y1 = ruleEncoding.y) === null || _ruleEncoding_y1 === void 0 ? void 0 : _ruleEncoding_y1.value;
1680
+ if (yDatum === undefined) {
1681
+ return;
1682
+ }
1683
+ const ruleMarkProps = getMarkProperties(layer.mark);
1684
+ const ruleColor = ruleMarkProps.color || '#d62728';
1685
+ // Find companion text annotation for legend name
1686
+ const textLayer = spec.layer.find((l)=>{
1687
+ var _l_encoding;
1688
+ const m = getMarkType(l.mark);
1689
+ var _l_encoding_y_datum;
1690
+ return m === 'text' && ((_l_encoding = l.encoding) === null || _l_encoding === void 0 ? void 0 : _l_encoding.y) && ((_l_encoding_y_datum = l.encoding.y.datum) !== null && _l_encoding_y_datum !== void 0 ? _l_encoding_y_datum : l.encoding.y.value) === yDatum;
1691
+ });
1692
+ const ruleLegend = textLayer ? String(((_textLayer_encoding = textLayer.encoding) === null || _textLayer_encoding === void 0 ? void 0 : (_textLayer_encoding_text = _textLayer_encoding.text) === null || _textLayer_encoding_text === void 0 ? void 0 : _textLayer_encoding_text.datum) || ((_textLayer_encoding1 = textLayer.encoding) === null || _textLayer_encoding1 === void 0 ? void 0 : (_textLayer_encoding_text1 = _textLayer_encoding1.text) === null || _textLayer_encoding_text1 === void 0 ? void 0 : _textLayer_encoding_text1.value) || `y=${yDatum}`) : `y=${yDatum}`;
1693
+ const ruleLineOptions = {};
1694
+ if (ruleMarkProps.strokeDash) {
1695
+ ruleLineOptions.strokeDasharray = ruleMarkProps.strokeDash.join(' ');
1696
+ }
1697
+ if (ruleMarkProps.strokeWidth) {
1698
+ ruleLineOptions.strokeWidth = ruleMarkProps.strokeWidth;
1699
+ }
1700
+ lineChartData.push({
1701
+ legend: ruleLegend,
1702
+ data: [
1703
+ {
1704
+ x: xMin,
1705
+ y: yDatum
1706
+ },
1707
+ {
1708
+ x: xMax,
1709
+ y: yDatum
1710
+ }
1711
+ ],
1712
+ color: ruleColor,
1713
+ hideNonActiveDots: true,
1714
+ ...Object.keys(ruleLineOptions).length > 0 && {
1715
+ lineOptions: ruleLineOptions
1716
+ }
1717
+ });
1718
+ });
1719
+ }
1720
+ // Check for log scale on Y-axis
1721
+ const yAxisType = extractYAxisType(encoding);
1722
+ // Extract axis category ordering
1723
+ const categoryOrderProps = extractAxisCategoryOrderProps(encoding);
1724
+ // Build LineChartProps
1725
+ const chartProps = {
1726
+ lineChartData,
1727
+ ...chartTitle && {
1728
+ chartTitle
1729
+ }
1730
+ };
1731
+ var _encoding_color_legend_disable;
1732
+ return {
1733
+ data: chartProps,
1734
+ width: typeof spec.width === 'number' ? spec.width : undefined,
1735
+ height: typeof spec.height === 'number' ? spec.height : undefined,
1736
+ ...xAxisTitle && {
1737
+ chartTitle: xAxisTitle
1738
+ },
1739
+ ...yAxisTitle && {
1740
+ yAxisTitle
1741
+ },
1742
+ ...tickFormat && {
1743
+ tickFormat
1744
+ },
1745
+ ...yAxisTickFormat && {
1746
+ yAxisTickFormat
1747
+ },
1748
+ ...tickValues && {
1749
+ tickValues
1750
+ },
1751
+ ...yAxisTickCount && {
1752
+ yAxisTickCount
1753
+ },
1754
+ ...yMinValue !== undefined && {
1755
+ yMinValue
1756
+ },
1757
+ ...yMaxValue !== undefined && {
1758
+ yMaxValue
1759
+ },
1760
+ ...annotations.length > 0 && {
1761
+ annotations
1762
+ },
1763
+ ...colorFillBars.length > 0 && {
1764
+ colorFillBars
1765
+ },
1766
+ ...yAxisType && {
1767
+ yScaleType: yAxisType
1768
+ },
1769
+ // For nominal y-axis, provide tick values and labels
1770
+ ...yOrdinalLabels && yOrdinalLabels.length > 0 && {
1771
+ yAxisTickValues: Array.from({
1772
+ length: yOrdinalLabels.length
1773
+ }, (_, i)=>i),
1774
+ yAxisTickFormat: (val)=>{
1775
+ var _yOrdinalLabels_val;
1776
+ return (_yOrdinalLabels_val = yOrdinalLabels[val]) !== null && _yOrdinalLabels_val !== void 0 ? _yOrdinalLabels_val : String(val);
1777
+ },
1778
+ yMinValue: -0.5,
1779
+ yMaxValue: yOrdinalLabels.length - 0.5
1780
+ },
1781
+ ...categoryOrderProps,
1782
+ hideLegend: (_encoding_color_legend_disable = (_encoding_color = encoding.color) === null || _encoding_color === void 0 ? void 0 : (_encoding_color_legend = _encoding_color.legend) === null || _encoding_color_legend === void 0 ? void 0 : _encoding_color_legend.disable) !== null && _encoding_color_legend_disable !== void 0 ? _encoding_color_legend_disable : false
1783
+ };
1784
+ }
1785
+ function getVegaLiteLegendsProps(spec, colorMap, isDarkTheme) {
1786
+ var _encoding_color;
1787
+ const unitSpecs = normalizeSpec(spec);
1788
+ const legends = [];
1789
+ if (unitSpecs.length === 0) {
1790
+ return {
1791
+ legends,
1792
+ centerLegends: true,
1793
+ enabledWrapLines: true,
1794
+ canSelectMultipleLegends: true
1795
+ };
1796
+ }
1797
+ const primarySpec = unitSpecs[0];
1798
+ const dataValues = extractDataValues(primarySpec.data);
1799
+ const encoding = primarySpec.encoding || {};
1800
+ const colorField = (_encoding_color = encoding.color) === null || _encoding_color === void 0 ? void 0 : _encoding_color.field;
1801
+ if (!colorField) {
1802
+ return {
1803
+ legends,
1804
+ centerLegends: true,
1805
+ enabledWrapLines: true,
1806
+ canSelectMultipleLegends: true
1807
+ };
1808
+ }
1809
+ // Extract unique series names
1810
+ const seriesNames = new Set();
1811
+ dataValues.forEach((row)=>{
1812
+ if (row[colorField] !== undefined) {
1813
+ seriesNames.add(String(row[colorField]));
1814
+ }
1815
+ });
1816
+ // Generate legends
1817
+ seriesNames.forEach((seriesName)=>{
1818
+ const color = (0, _VegaLiteColorAdapter.getVegaColorFromMap)(seriesName, colorMap, undefined, undefined, isDarkTheme);
1819
+ legends.push({
1820
+ title: seriesName,
1821
+ color
1822
+ });
1823
+ });
1824
+ return {
1825
+ legends,
1826
+ centerLegends: true,
1827
+ enabledWrapLines: true,
1828
+ canSelectMultipleLegends: true
1829
+ };
1830
+ }
1831
+ function getVegaLiteTitles(spec) {
1832
+ var _spec_title, _encoding_x, _encoding_x_axis, _encoding_x1, _encoding_y, _encoding_y_axis, _encoding_y1;
1833
+ const unitSpecs = normalizeSpec(spec);
1834
+ if (unitSpecs.length === 0) {
1835
+ return {};
1836
+ }
1837
+ const primarySpec = unitSpecs[0];
1838
+ const encoding = primarySpec.encoding || {};
1839
+ // Extract chart title
1840
+ const chartTitle = typeof spec.title === 'string' ? spec.title : (_spec_title = spec.title) === null || _spec_title === void 0 ? void 0 : _spec_title.text;
1841
+ // Extract title styles if title is an object
1842
+ let titleStyles;
1843
+ if (typeof spec.title === 'object' && spec.title !== null) {
1844
+ const titleObj = spec.title;
1845
+ // Build titleFont object if any font properties are present
1846
+ const titleFont = {};
1847
+ if (titleObj.font) {
1848
+ titleFont.family = titleObj.font;
1849
+ }
1850
+ if (titleObj.fontSize) {
1851
+ titleFont.size = titleObj.fontSize;
1852
+ }
1853
+ if (titleObj.fontWeight) {
1854
+ // Convert string weights to numbers (Font interface expects number)
1855
+ const weight = titleObj.fontWeight;
1856
+ if (typeof weight === 'string') {
1857
+ const weightMap = {
1858
+ normal: 400,
1859
+ bold: 700,
1860
+ lighter: 300,
1861
+ bolder: 600
1862
+ };
1863
+ titleFont.weight = weightMap[weight.toLowerCase()] || 400;
1864
+ } else {
1865
+ titleFont.weight = weight;
1866
+ }
1867
+ }
1868
+ if (titleObj.color) {
1869
+ titleFont.color = titleObj.color;
1870
+ }
1871
+ // Map Vega-Lite anchor values to TitleStyles anchor values
1872
+ const anchorMap = {
1873
+ start: 'left',
1874
+ middle: 'center',
1875
+ end: 'right'
1876
+ };
1877
+ titleStyles = {
1878
+ ...Object.keys(titleFont).length > 0 ? {
1879
+ titleFont
1880
+ } : {},
1881
+ ...titleObj.anchor && anchorMap[titleObj.anchor] ? {
1882
+ titleXAnchor: anchorMap[titleObj.anchor]
1883
+ } : {},
1884
+ ...titleObj.offset !== undefined || titleObj.subtitlePadding !== undefined ? {
1885
+ titlePad: {
1886
+ t: titleObj.offset,
1887
+ b: titleObj.subtitlePadding
1888
+ }
1889
+ } : {}
1890
+ };
1891
+ // Only include titleStyles if it has properties
1892
+ if (Object.keys(titleStyles).length === 0) {
1893
+ titleStyles = undefined;
1894
+ }
1895
+ }
1896
+ var _encoding_x_title, _ref, _encoding_y_title, _ref1;
1897
+ return {
1898
+ chartTitle,
1899
+ xAxisTitle: (_ref = (_encoding_x_title = (_encoding_x = encoding.x) === null || _encoding_x === void 0 ? void 0 : _encoding_x.title) !== null && _encoding_x_title !== void 0 ? _encoding_x_title : (_encoding_x1 = encoding.x) === null || _encoding_x1 === void 0 ? void 0 : (_encoding_x_axis = _encoding_x1.axis) === null || _encoding_x_axis === void 0 ? void 0 : _encoding_x_axis.title) !== null && _ref !== void 0 ? _ref : undefined,
1900
+ yAxisTitle: (_ref1 = (_encoding_y_title = (_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : _encoding_y.title) !== null && _encoding_y_title !== void 0 ? _encoding_y_title : (_encoding_y1 = encoding.y) === null || _encoding_y1 === void 0 ? void 0 : (_encoding_y_axis = _encoding_y1.axis) === null || _encoding_y_axis === void 0 ? void 0 : _encoding_y_axis.title) !== null && _ref1 !== void 0 ? _ref1 : undefined,
1901
+ ...titleStyles ? {
1902
+ titleStyles
1903
+ } : {}
1904
+ };
1905
+ }
1906
+ function transformVegaLiteToVerticalBarChartProps(spec, colorMap, isDarkTheme) {
1907
+ var _encoding_color, _encoding_y_axis, _encoding_y, _barData_, _encoding_color_legend, _encoding_color1;
1908
+ // Initialize transformation context
1909
+ const { dataValues, encoding, markProps } = initializeTransformContext(spec);
1910
+ // Extract field names and aggregates
1911
+ const { xField, yField, colorField, yAggregate } = extractEncodingFields(encoding);
1912
+ // Check if this is an aggregate bar chart
1913
+ // Aggregate can be: count (no field needed) or sum/mean/etc (with field)
1914
+ const isAggregate = !!yAggregate;
1915
+ if (!xField && !isAggregate) {
1916
+ throw new Error('VegaLiteSchemaAdapter: x encoding is required for bar charts');
1917
+ }
1918
+ // For aggregate charts, compute aggregated data
1919
+ let aggregatedData;
1920
+ if (isAggregate && xField) {
1921
+ aggregatedData = computeAggregateData(dataValues, xField, yField, yAggregate);
1922
+ }
1923
+ // Validate data and encodings (skip for aggregate charts)
1924
+ if (!isAggregate && xField && yField) {
1925
+ var _encoding_x, _encoding_y1;
1926
+ validateXYEncodings(dataValues, xField, yField, (_encoding_x = encoding.x) === null || _encoding_x === void 0 ? void 0 : _encoding_x.type, (_encoding_y1 = encoding.y) === null || _encoding_y1 === void 0 ? void 0 : _encoding_y1.type, 'VerticalBarChart', encoding);
1927
+ }
1928
+ // Extract color configuration
1929
+ const { colorScheme, colorRange } = extractColorConfig(encoding);
1930
+ const colorValue = (_encoding_color = encoding.color) === null || _encoding_color === void 0 ? void 0 : _encoding_color.value;
1931
+ const barData = [];
1932
+ const colorIndex = new Map();
1933
+ let currentColorIndex = 0;
1934
+ // When there's no color field, all bars share a single legend
1935
+ const useSingleLegendForAggregate = !colorField;
1936
+ if (aggregatedData) {
1937
+ // Use aggregated data
1938
+ aggregatedData.forEach(({ category, value })=>{
1939
+ const legend = useSingleLegendForAggregate ? 'Bar' : String(category);
1940
+ if (!colorIndex.has(legend)) {
1941
+ colorIndex.set(legend, currentColorIndex++);
1942
+ }
1943
+ const color = resolveColor(legend, colorIndex.get(legend), colorValue, markProps.color, colorMap, colorScheme, colorRange, isDarkTheme);
1944
+ barData.push({
1945
+ x: category,
1946
+ y: value,
1947
+ legend,
1948
+ color
1949
+ });
1950
+ });
1951
+ } else if (xField && yField) {
1952
+ var _dataValues_find;
1953
+ // Check if y values are numeric; if not, fall back to count aggregation
1954
+ const firstYValue = (_dataValues_find = dataValues.find((r)=>r[yField] !== undefined)) === null || _dataValues_find === void 0 ? void 0 : _dataValues_find[yField];
1955
+ const yIsNumeric = typeof firstYValue === 'number';
1956
+ if (!yIsNumeric) {
1957
+ // y values are non-numeric: compute count per x category
1958
+ const counts = countByCategory(dataValues, xField, undefined, '');
1959
+ counts.forEach((legendMap, xKey)=>{
1960
+ // No color grouping - each xKey gets one bar; use xKey as legend
1961
+ const totalCount = Array.from(legendMap.values()).reduce((a, b)=>a + b, 0);
1962
+ const legend = xKey;
1963
+ if (!colorIndex.has(legend)) {
1964
+ colorIndex.set(legend, currentColorIndex++);
1965
+ }
1966
+ const color = resolveColor(legend, colorIndex.get(legend), colorValue, markProps.color, colorMap, colorScheme, colorRange, isDarkTheme);
1967
+ barData.push({
1968
+ x: xKey,
1969
+ y: totalCount,
1970
+ legend,
1971
+ color
1972
+ });
1973
+ });
1974
+ } else {
1975
+ var _encoding_y_axis1, _encoding_y2;
1976
+ // When there's no color field encoding, use a single legend name for all bars
1977
+ // This ensures: uniform bar color, single legend entry, no tooltip duplication
1978
+ const useSingleLegend = !colorField;
1979
+ // Create value formatter for bar data labels
1980
+ const yFormatter = createValueFormatter((_encoding_y2 = encoding.y) === null || _encoding_y2 === void 0 ? void 0 : (_encoding_y_axis1 = _encoding_y2.axis) === null || _encoding_y_axis1 === void 0 ? void 0 : _encoding_y_axis1.format);
1981
+ // Use raw data (normal numeric y values)
1982
+ dataValues.forEach((row)=>{
1983
+ const xValue = row[xField];
1984
+ const yValue = row[yField];
1985
+ // Use chart-utilities validation
1986
+ if ((0, _chartutilities.isInvalidValue)(xValue) || (0, _chartutilities.isInvalidValue)(yValue) || typeof yValue !== 'number') {
1987
+ return;
1988
+ }
1989
+ const legend = colorField && row[colorField] !== undefined ? String(row[colorField]) : useSingleLegend ? 'Bar' : String(xValue);
1990
+ if (!colorIndex.has(legend)) {
1991
+ colorIndex.set(legend, currentColorIndex++);
1992
+ }
1993
+ const color = resolveColor(legend, colorIndex.get(legend), colorValue, markProps.color, colorMap, colorScheme, colorRange, isDarkTheme);
1994
+ // For bar charts, x-axis values are treated as categories (even if numeric)
1995
+ // Convert to string to ensure consistent categorical positioning
1996
+ const xCategory = typeof xValue === 'number' ? String(xValue) : xValue;
1997
+ barData.push({
1998
+ x: xCategory,
1999
+ y: yValue,
2000
+ legend,
2001
+ color,
2002
+ ...yFormatter && {
2003
+ yAxisCalloutData: yFormatter(yValue),
2004
+ barLabel: yFormatter(yValue)
2005
+ }
2006
+ });
2007
+ });
2008
+ }
2009
+ }
2010
+ const titles = getVegaLiteTitles(spec);
2011
+ // Extract axis category ordering
2012
+ const categoryOrderProps = extractAxisCategoryOrderProps(encoding);
2013
+ // Extract tick configuration
2014
+ const tickConfig = extractTickConfig(spec);
2015
+ // Extract y-axis formatting and scale props
2016
+ const yAxisTickFormat = (_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : (_encoding_y_axis = _encoding_y.axis) === null || _encoding_y_axis === void 0 ? void 0 : _encoding_y_axis.format;
2017
+ const { yMinValue, yMaxValue } = extractYMinMax(encoding, dataValues);
2018
+ const yAxisType = extractYAxisType(encoding);
2019
+ // Compute truncation based on number of unique x-axis categories
2020
+ const uniqueXCount = new Set(barData.map((d)=>String(d.x))).size;
2021
+ const barTruncateChars = uniqueXCount > 20 ? 6 : uniqueXCount > 10 ? 10 : DEFAULT_TRUNCATE_CHARS;
2022
+ var _encoding_color_legend_disable;
2023
+ const result = {
2024
+ data: barData,
2025
+ chartTitle: titles.chartTitle,
2026
+ xAxisTitle: titles.xAxisTitle,
2027
+ yAxisTitle: titles.yAxisTitle,
2028
+ ...titles.titleStyles ? titles.titleStyles : {},
2029
+ roundCorners: true,
2030
+ wrapXAxisLables: typeof ((_barData_ = barData[0]) === null || _barData_ === void 0 ? void 0 : _barData_.x) === 'string',
2031
+ hideTickOverlap: true,
2032
+ noOfCharsToTruncate: barTruncateChars,
2033
+ xAxis: {
2034
+ tickLayout: 'auto'
2035
+ },
2036
+ ...yAxisTickFormat && {
2037
+ yAxisTickFormat
2038
+ },
2039
+ ...yMinValue !== undefined && {
2040
+ yMinValue
2041
+ },
2042
+ ...yMaxValue !== undefined && {
2043
+ yMaxValue
2044
+ },
2045
+ ...yAxisType && {
2046
+ yScaleType: yAxisType
2047
+ },
2048
+ ...categoryOrderProps,
2049
+ // Hide legend for single-series bar charts (no color encoding) to avoid showing "Bar" legend
2050
+ hideLegend: !colorField ? true : (_encoding_color_legend_disable = (_encoding_color1 = encoding.color) === null || _encoding_color1 === void 0 ? void 0 : (_encoding_color_legend = _encoding_color1.legend) === null || _encoding_color_legend === void 0 ? void 0 : _encoding_color_legend.disable) !== null && _encoding_color_legend_disable !== void 0 ? _encoding_color_legend_disable : false
2051
+ };
2052
+ if (tickConfig.tickValues) {
2053
+ result.tickValues = tickConfig.tickValues;
2054
+ }
2055
+ if (tickConfig.xAxisTickCount) {
2056
+ result.xAxisTickCount = tickConfig.xAxisTickCount;
2057
+ }
2058
+ return result;
2059
+ }
2060
+ function transformVegaLiteToVerticalStackedBarChartProps(spec, colorMap, isDarkTheme) {
2061
+ var _encoding_color, _encoding_y_axis, _encoding_y, _encoding_color_legend, _encoding_color1, _chartData_;
2062
+ // Initialize transformation context (skip warnings as we handle layered spec differently)
2063
+ const { unitSpecs } = initializeTransformContext(spec);
2064
+ // Separate bar, line, and rule specs from layered specifications
2065
+ const barSpecs = unitSpecs.filter((s)=>getMarkType(s.mark) === 'bar');
2066
+ const lineSpecs = unitSpecs.filter((s)=>{
2067
+ const mark = getMarkType(s.mark);
2068
+ return mark === 'line' || mark === 'point';
2069
+ });
2070
+ const ruleSpecs = unitSpecs.filter((s)=>getMarkType(s.mark) === 'rule');
2071
+ // Use bar specs if available, otherwise fall back to first unit spec
2072
+ const primarySpec = barSpecs.length > 0 ? barSpecs[0] : unitSpecs[0];
2073
+ const rawDataValues = extractDataValues(primarySpec.data);
2074
+ // Apply transforms from both top-level spec and primary spec
2075
+ let dataValues = applyTransforms(rawDataValues, spec.transform);
2076
+ dataValues = applyTransforms(dataValues, primarySpec.transform);
2077
+ const encoding = primarySpec.encoding || {};
2078
+ const markProps = getMarkProperties(primarySpec.mark);
2079
+ // Extract field names and aggregates
2080
+ const { xField, yField, colorField, yAggregate } = extractEncodingFields(encoding);
2081
+ const colorValue = (_encoding_color = encoding.color) === null || _encoding_color === void 0 ? void 0 : _encoding_color.value; // Static color value
2082
+ // Support aggregate encodings (e.g., count, sum)
2083
+ const isAggregate = !!yAggregate;
2084
+ if (!xField) {
2085
+ throw new Error('VegaLiteSchemaAdapter: x encoding is required for stacked bar charts');
2086
+ }
2087
+ // For aggregate charts, compute aggregated data
2088
+ let aggregatedData;
2089
+ if (isAggregate) {
2090
+ aggregatedData = computeAggregateData(dataValues, xField, yField, yAggregate);
2091
+ } else if (!yField) {
2092
+ throw new Error('VegaLiteSchemaAdapter: y encoding is required for stacked bar charts');
2093
+ }
2094
+ // Extract color configuration
2095
+ const { colorScheme, colorRange } = extractColorConfig(encoding);
2096
+ // Group data by x value, then by color (stack)
2097
+ const mapXToDataPoints = {};
2098
+ const colorIndex = new Map();
2099
+ let currentColorIndex = 0;
2100
+ if (aggregatedData) {
2101
+ // Use aggregated data
2102
+ aggregatedData.forEach(({ category, value })=>{
2103
+ const xKey = String(category);
2104
+ const legend = 'Bar';
2105
+ if (!mapXToDataPoints[xKey]) {
2106
+ mapXToDataPoints[xKey] = {
2107
+ xAxisPoint: category,
2108
+ chartData: [],
2109
+ lineData: []
2110
+ };
2111
+ }
2112
+ if (!colorIndex.has(legend)) {
2113
+ colorIndex.set(legend, currentColorIndex++);
2114
+ }
2115
+ const color = resolveColor(legend, colorIndex.get(legend), colorValue, markProps.color, colorMap, colorScheme, colorRange, isDarkTheme);
2116
+ mapXToDataPoints[xKey].chartData.push({
2117
+ legend,
2118
+ data: value,
2119
+ color
2120
+ });
2121
+ });
2122
+ } else {
2123
+ var _dataValues_find;
2124
+ // Check if y values are actually numeric; if not, fall back to count aggregation
2125
+ const firstYValue = (_dataValues_find = dataValues.find((r)=>r[yField] !== undefined)) === null || _dataValues_find === void 0 ? void 0 : _dataValues_find[yField];
2126
+ const yIsNumeric = typeof firstYValue === 'number';
2127
+ if (!yIsNumeric && yField) {
2128
+ // y values are non-numeric (e.g., strings after auto-correction from quantitative to nominal)
2129
+ // Fall back to count aggregation: count rows per x category and color
2130
+ const counts = countByCategory(dataValues, xField, colorField, 'Bar');
2131
+ counts.forEach((legendMap, xKey)=>{
2132
+ mapXToDataPoints[xKey] = {
2133
+ xAxisPoint: xKey,
2134
+ chartData: [],
2135
+ lineData: []
2136
+ };
2137
+ legendMap.forEach((count, legend)=>{
2138
+ if (!colorIndex.has(legend)) {
2139
+ colorIndex.set(legend, currentColorIndex++);
2140
+ }
2141
+ const color = resolveColor(legend, colorIndex.get(legend), colorValue, markProps.color, colorMap, colorScheme, colorRange, isDarkTheme);
2142
+ mapXToDataPoints[xKey].chartData.push({
2143
+ legend,
2144
+ data: count,
2145
+ color
2146
+ });
2147
+ });
2148
+ });
2149
+ } else {
2150
+ // Process bar data (normal numeric y values)
2151
+ dataValues.forEach((row)=>{
2152
+ var _encoding_y_axis, _encoding_y;
2153
+ const xValue = row[xField];
2154
+ const yValue = row[yField];
2155
+ const stackValue = colorField ? row[colorField] : 'Bar'; // Default legend if no color field
2156
+ if ((0, _chartutilities.isInvalidValue)(xValue) || (0, _chartutilities.isInvalidValue)(yValue) || typeof yValue !== 'number') {
2157
+ return;
2158
+ }
2159
+ const xKey = String(xValue);
2160
+ const legend = stackValue !== undefined ? String(stackValue) : 'Bar';
2161
+ if (!mapXToDataPoints[xKey]) {
2162
+ // For bar charts, x-axis values are treated as categories (even if numeric)
2163
+ const xCategory = typeof xValue === 'number' ? String(xValue) : xValue;
2164
+ mapXToDataPoints[xKey] = {
2165
+ xAxisPoint: xCategory,
2166
+ chartData: [],
2167
+ lineData: []
2168
+ };
2169
+ }
2170
+ if (!colorIndex.has(legend)) {
2171
+ colorIndex.set(legend, currentColorIndex++);
2172
+ }
2173
+ // Use static color if provided, otherwise use color scheme/scale
2174
+ const color = resolveColor(legend, colorIndex.get(legend), colorValue, markProps.color, colorMap, colorScheme, colorRange, isDarkTheme);
2175
+ const stackYFormatter = createValueFormatter((_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : (_encoding_y_axis = _encoding_y.axis) === null || _encoding_y_axis === void 0 ? void 0 : _encoding_y_axis.format);
2176
+ mapXToDataPoints[xKey].chartData.push({
2177
+ legend,
2178
+ data: yValue,
2179
+ color,
2180
+ ...stackYFormatter && {
2181
+ yAxisCalloutData: stackYFormatter(yValue),
2182
+ barLabel: stackYFormatter(yValue)
2183
+ }
2184
+ });
2185
+ });
2186
+ }
2187
+ } // end else (non-aggregate)
2188
+ // Process line data from additional layers (if any)
2189
+ lineSpecs.forEach((lineSpec, lineIndex)=>{
2190
+ var _lineEncoding_x, _lineEncoding_y, _lineEncoding_color;
2191
+ let lineDataValues = extractDataValues(lineSpec.data);
2192
+ // Apply transforms from both top-level spec and line spec
2193
+ lineDataValues = applyTransforms(lineDataValues, spec.transform);
2194
+ lineDataValues = applyTransforms(lineDataValues, lineSpec.transform);
2195
+ const lineEncoding = lineSpec.encoding || {};
2196
+ const lineMarkProps = getMarkProperties(lineSpec.mark);
2197
+ const lineXField = (_lineEncoding_x = lineEncoding.x) === null || _lineEncoding_x === void 0 ? void 0 : _lineEncoding_x.field;
2198
+ const lineYField = (_lineEncoding_y = lineEncoding.y) === null || _lineEncoding_y === void 0 ? void 0 : _lineEncoding_y.field;
2199
+ const lineColorField = (_lineEncoding_color = lineEncoding.color) === null || _lineEncoding_color === void 0 ? void 0 : _lineEncoding_color.field;
2200
+ if (!lineXField || !lineYField) {
2201
+ return; // Skip if required fields are missing
2202
+ }
2203
+ const lineLegendBase = lineColorField ? 'Line' : `Line ${lineIndex + 1}`;
2204
+ lineDataValues.forEach((row)=>{
2205
+ var _spec_resolve_scale, _spec_resolve;
2206
+ const xValue = row[lineXField];
2207
+ const yValue = row[lineYField];
2208
+ if ((0, _chartutilities.isInvalidValue)(xValue) || (0, _chartutilities.isInvalidValue)(yValue)) {
2209
+ return;
2210
+ }
2211
+ const xKey = String(xValue);
2212
+ const lineLegend = lineColorField && row[lineColorField] !== undefined ? String(row[lineColorField]) : lineLegendBase;
2213
+ // Ensure x-axis point exists
2214
+ if (!mapXToDataPoints[xKey]) {
2215
+ mapXToDataPoints[xKey] = {
2216
+ xAxisPoint: xValue,
2217
+ chartData: [],
2218
+ lineData: []
2219
+ };
2220
+ }
2221
+ // Determine line color
2222
+ if (!colorIndex.has(lineLegend)) {
2223
+ colorIndex.set(lineLegend, currentColorIndex++);
2224
+ }
2225
+ let lineColor;
2226
+ if (lineMarkProps.color) {
2227
+ lineColor = lineMarkProps.color;
2228
+ } else {
2229
+ // Use lineLegend for consistent color assignment
2230
+ lineColor = resolveColor(lineLegend, colorIndex.get(lineLegend), undefined, undefined, colorMap, undefined, undefined, isDarkTheme);
2231
+ }
2232
+ // Determine if this line should use secondary Y-axis
2233
+ // Check if spec has independent Y scales AND line uses different Y field than bars
2234
+ const hasIndependentYScales = ((_spec_resolve = spec.resolve) === null || _spec_resolve === void 0 ? void 0 : (_spec_resolve_scale = _spec_resolve.scale) === null || _spec_resolve_scale === void 0 ? void 0 : _spec_resolve_scale.y) === 'independent';
2235
+ const useSecondaryYScale = hasIndependentYScales && lineYField !== yField;
2236
+ const lineData = {
2237
+ y: yValue,
2238
+ color: lineColor,
2239
+ legend: lineLegend,
2240
+ legendShape: 'triangle',
2241
+ data: typeof yValue === 'number' ? yValue : undefined,
2242
+ useSecondaryYScale
2243
+ };
2244
+ // Add line options if available
2245
+ if (lineMarkProps.strokeWidth || lineMarkProps.strokeDash) {
2246
+ lineData.lineOptions = {
2247
+ ...lineMarkProps.strokeWidth && {
2248
+ strokeWidth: lineMarkProps.strokeWidth
2249
+ },
2250
+ ...lineMarkProps.strokeDash && {
2251
+ strokeDasharray: lineMarkProps.strokeDash.join(' ')
2252
+ }
2253
+ };
2254
+ }
2255
+ mapXToDataPoints[xKey].lineData.push(lineData);
2256
+ });
2257
+ });
2258
+ // Process rule specs as horizontal reference lines
2259
+ // Each rule with a constant y-value becomes a flat line across all x-axis points
2260
+ ruleSpecs.forEach((ruleSpec, ruleIndex)=>{
2261
+ var _ruleEncoding_y, _ruleEncoding_y1;
2262
+ const ruleEncoding = ruleSpec.encoding || {};
2263
+ const ruleMarkProps = getMarkProperties(ruleSpec.mark);
2264
+ var _ruleEncoding_y_datum;
2265
+ const yDatum = (_ruleEncoding_y_datum = (_ruleEncoding_y = ruleEncoding.y) === null || _ruleEncoding_y === void 0 ? void 0 : _ruleEncoding_y.datum) !== null && _ruleEncoding_y_datum !== void 0 ? _ruleEncoding_y_datum : (_ruleEncoding_y1 = ruleEncoding.y) === null || _ruleEncoding_y1 === void 0 ? void 0 : _ruleEncoding_y1.value;
2266
+ if (yDatum !== undefined) {
2267
+ var _textSpec_encoding_text, _textSpec_encoding, _textSpec_encoding_text1, _textSpec_encoding1;
2268
+ const ruleLegend = `Reference_${ruleIndex}`;
2269
+ const ruleColor = ruleMarkProps.color || '#d62728';
2270
+ if (!colorIndex.has(ruleLegend)) {
2271
+ colorIndex.set(ruleLegend, currentColorIndex++);
2272
+ }
2273
+ const lineOptions = {};
2274
+ if (ruleMarkProps.strokeDash) {
2275
+ lineOptions.strokeDasharray = ruleMarkProps.strokeDash.join(' ');
2276
+ }
2277
+ if (ruleMarkProps.strokeWidth) {
2278
+ lineOptions.strokeWidth = ruleMarkProps.strokeWidth;
2279
+ }
2280
+ // Look for companion text annotation at the same y-value
2281
+ const textSpec = unitSpecs.find((s, i)=>{
2282
+ var _s_encoding;
2283
+ var _s_encoding_y_datum;
2284
+ return getMarkType(s.mark) === 'text' && ((_s_encoding = s.encoding) === null || _s_encoding === void 0 ? void 0 : _s_encoding.y) && ((_s_encoding_y_datum = s.encoding.y.datum) !== null && _s_encoding_y_datum !== void 0 ? _s_encoding_y_datum : s.encoding.y.value) === yDatum;
2285
+ });
2286
+ const ruleText = textSpec ? String(((_textSpec_encoding = textSpec.encoding) === null || _textSpec_encoding === void 0 ? void 0 : (_textSpec_encoding_text = _textSpec_encoding.text) === null || _textSpec_encoding_text === void 0 ? void 0 : _textSpec_encoding_text.datum) || ((_textSpec_encoding1 = textSpec.encoding) === null || _textSpec_encoding1 === void 0 ? void 0 : (_textSpec_encoding_text1 = _textSpec_encoding1.text) === null || _textSpec_encoding_text1 === void 0 ? void 0 : _textSpec_encoding_text1.value) || yDatum) : String(yDatum);
2287
+ // Add the constant y-value line to every x-axis point
2288
+ Object.keys(mapXToDataPoints).forEach((xKey)=>{
2289
+ mapXToDataPoints[xKey].lineData.push({
2290
+ y: yDatum,
2291
+ legend: ruleText,
2292
+ color: ruleColor,
2293
+ ...Object.keys(lineOptions).length > 0 && {
2294
+ lineOptions
2295
+ },
2296
+ useSecondaryYScale: false
2297
+ });
2298
+ });
2299
+ }
2300
+ });
2301
+ const chartData = Object.values(mapXToDataPoints);
2302
+ const titles = getVegaLiteTitles(spec);
2303
+ // Check if we have secondary Y-axis data
2304
+ const hasSecondaryYAxis = chartData.some((point)=>{
2305
+ var _point_lineData;
2306
+ return (_point_lineData = point.lineData) === null || _point_lineData === void 0 ? void 0 : _point_lineData.some((line)=>line.useSecondaryYScale);
2307
+ });
2308
+ // Extract secondary Y-axis properties from line layers
2309
+ let secondaryYAxisProps = {};
2310
+ if (hasSecondaryYAxis && lineSpecs.length > 0) {
2311
+ var _lineEncoding_y;
2312
+ const lineSpec = lineSpecs[0];
2313
+ const lineEncoding = lineSpec.encoding || {};
2314
+ const lineYAxis = (_lineEncoding_y = lineEncoding.y) === null || _lineEncoding_y === void 0 ? void 0 : _lineEncoding_y.axis;
2315
+ if (lineYAxis === null || lineYAxis === void 0 ? void 0 : lineYAxis.title) {
2316
+ secondaryYAxisProps.secondaryYAxistitle = lineYAxis.title;
2317
+ }
2318
+ // Compute secondary Y scale domain from line data values
2319
+ const allLineYValues = [];
2320
+ chartData.forEach((point)=>{
2321
+ var _point_lineData;
2322
+ (_point_lineData = point.lineData) === null || _point_lineData === void 0 ? void 0 : _point_lineData.forEach((line)=>{
2323
+ if (line.useSecondaryYScale && typeof line.y === 'number') {
2324
+ allLineYValues.push(line.y);
2325
+ }
2326
+ });
2327
+ });
2328
+ if (allLineYValues.length > 0) {
2329
+ var _lineEncoding_y_scale, _lineEncoding_y1;
2330
+ // Use explicit domain from line encoding if available, otherwise compute from data
2331
+ const lineDomain = (_lineEncoding_y1 = lineEncoding.y) === null || _lineEncoding_y1 === void 0 ? void 0 : (_lineEncoding_y_scale = _lineEncoding_y1.scale) === null || _lineEncoding_y_scale === void 0 ? void 0 : _lineEncoding_y_scale.domain;
2332
+ var _d3Min;
2333
+ const secYMin = Array.isArray(lineDomain) ? lineDomain[0] : (_d3Min = (0, _d3array.min)(allLineYValues)) !== null && _d3Min !== void 0 ? _d3Min : 0;
2334
+ var _d3Max;
2335
+ const secYMax = Array.isArray(lineDomain) ? lineDomain[1] : (_d3Max = (0, _d3array.max)(allLineYValues)) !== null && _d3Max !== void 0 ? _d3Max : 0;
2336
+ secondaryYAxisProps.secondaryYScaleOptions = {
2337
+ yMinValue: secYMin,
2338
+ yMaxValue: secYMax
2339
+ };
2340
+ }
2341
+ }
2342
+ // Check for log scale on primary Y-axis
2343
+ const yAxisType = extractYAxisType(encoding);
2344
+ // Extract y-axis formatting and domain props
2345
+ const yAxisTickFormat = (_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : (_encoding_y_axis = _encoding_y.axis) === null || _encoding_y_axis === void 0 ? void 0 : _encoding_y_axis.format;
2346
+ const { yMinValue, yMaxValue } = extractYMinMax(encoding, dataValues);
2347
+ // Extract axis category ordering
2348
+ const categoryOrderProps = extractAxisCategoryOrderProps(encoding);
2349
+ var _spec_height, _encoding_color_legend_disable;
2350
+ return {
2351
+ data: chartData,
2352
+ chartTitle: titles.chartTitle,
2353
+ xAxisTitle: titles.xAxisTitle,
2354
+ yAxisTitle: titles.yAxisTitle,
2355
+ ...titles.titleStyles ? titles.titleStyles : {},
2356
+ width: spec.width,
2357
+ height: (_spec_height = spec.height) !== null && _spec_height !== void 0 ? _spec_height : DEFAULT_CHART_HEIGHT,
2358
+ hideLegend: (_encoding_color_legend_disable = (_encoding_color1 = encoding.color) === null || _encoding_color1 === void 0 ? void 0 : (_encoding_color_legend = _encoding_color1.legend) === null || _encoding_color_legend === void 0 ? void 0 : _encoding_color_legend.disable) !== null && _encoding_color_legend_disable !== void 0 ? _encoding_color_legend_disable : false,
2359
+ showYAxisLables: true,
2360
+ roundCorners: true,
2361
+ hideTickOverlap: true,
2362
+ barGapMax: 2,
2363
+ noOfCharsToTruncate: DEFAULT_TRUNCATE_CHARS,
2364
+ showYAxisLablesTooltip: true,
2365
+ wrapXAxisLables: typeof ((_chartData_ = chartData[0]) === null || _chartData_ === void 0 ? void 0 : _chartData_.xAxisPoint) === 'string',
2366
+ xAxis: {
2367
+ tickLayout: 'auto'
2368
+ },
2369
+ ...yAxisTickFormat && {
2370
+ yAxisTickFormat
2371
+ },
2372
+ ...yMinValue !== undefined && {
2373
+ yMinValue
2374
+ },
2375
+ ...yMaxValue !== undefined && {
2376
+ yMaxValue
2377
+ },
2378
+ ...yAxisType && {
2379
+ yScaleType: yAxisType
2380
+ },
2381
+ ...secondaryYAxisProps,
2382
+ ...categoryOrderProps
2383
+ };
2384
+ }
2385
+ function transformVegaLiteToGroupedVerticalBarChartProps(spec, colorMap, isDarkTheme) {
2386
+ var _encoding_y_axis, _encoding_y;
2387
+ // Initialize transformation context
2388
+ const { dataValues, encoding } = initializeTransformContext(spec);
2389
+ // Extract field names
2390
+ const { xField, yField, colorField } = extractEncodingFields(encoding);
2391
+ if (!xField || !yField || !colorField) {
2392
+ throw new Error('VegaLiteSchemaAdapter: x, y, and color encodings are required for grouped bar charts');
2393
+ }
2394
+ // Extract color configuration
2395
+ const { colorScheme, colorRange } = extractColorConfig(encoding);
2396
+ // Group data by x value (name), then by color (series)
2397
+ const groupedData = {};
2398
+ const colorIndex = new Map();
2399
+ let currentColorIndex = 0;
2400
+ dataValues.forEach((row)=>{
2401
+ const xValue = row[xField];
2402
+ const yValue = row[yField];
2403
+ const groupValue = row[colorField];
2404
+ if ((0, _chartutilities.isInvalidValue)(xValue) || (0, _chartutilities.isInvalidValue)(yValue) || typeof yValue !== 'number' || (0, _chartutilities.isInvalidValue)(groupValue)) {
2405
+ return;
2406
+ }
2407
+ const xKey = String(xValue);
2408
+ const legend = String(groupValue);
2409
+ if (!groupedData[xKey]) {
2410
+ groupedData[xKey] = {};
2411
+ }
2412
+ groupedData[xKey][legend] = yValue;
2413
+ if (!colorIndex.has(legend)) {
2414
+ colorIndex.set(legend, currentColorIndex++);
2415
+ }
2416
+ });
2417
+ // Convert to GroupedVerticalBarChartData format
2418
+ const chartData = Object.keys(groupedData).map((name)=>{
2419
+ const series = Object.keys(groupedData[name]).map((legend)=>({
2420
+ key: legend,
2421
+ data: groupedData[name][legend],
2422
+ legend,
2423
+ color: resolveColor(legend, colorIndex.get(legend), undefined, undefined, colorMap, colorScheme, colorRange, isDarkTheme)
2424
+ }));
2425
+ return {
2426
+ name,
2427
+ series
2428
+ };
2429
+ });
2430
+ const titles = getVegaLiteTitles(spec);
2431
+ // Extract y-axis formatting and scale props
2432
+ const yAxisTickFormat = (_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : (_encoding_y_axis = _encoding_y.axis) === null || _encoding_y_axis === void 0 ? void 0 : _encoding_y_axis.format;
2433
+ const { yMinValue, yMaxValue } = extractYMinMax(encoding, dataValues);
2434
+ const yAxisType = extractYAxisType(encoding);
2435
+ return {
2436
+ data: chartData,
2437
+ chartTitle: titles.chartTitle,
2438
+ xAxisTitle: titles.xAxisTitle,
2439
+ yAxisTitle: titles.yAxisTitle,
2440
+ ...titles.titleStyles ? titles.titleStyles : {},
2441
+ ...yAxisTickFormat && {
2442
+ yAxisTickFormat
2443
+ },
2444
+ ...yMinValue !== undefined && {
2445
+ yMinValue
2446
+ },
2447
+ ...yMaxValue !== undefined && {
2448
+ yMaxValue
2449
+ },
2450
+ ...yAxisType && {
2451
+ yScaleType: yAxisType
2452
+ }
2453
+ };
2454
+ }
2455
+ function transformVegaLiteToHorizontalBarChartProps(spec, colorMap, isDarkTheme) {
2456
+ var _encoding_color, _encoding_color_legend, _encoding_color1;
2457
+ // Initialize transformation context
2458
+ const { dataValues, encoding, markProps } = initializeTransformContext(spec);
2459
+ // Extract field names and aggregates
2460
+ const { xField, yField, colorField, xAggregate, x2Field } = extractEncodingFields(encoding);
2461
+ // Check if this is an aggregate bar chart
2462
+ // Aggregate can be: count (no field needed) or sum/mean/etc (with field)
2463
+ const isAggregate = !!xAggregate;
2464
+ if (!yField && !isAggregate) {
2465
+ throw new Error('VegaLiteSchemaAdapter: y encoding is required for horizontal bar charts');
2466
+ }
2467
+ // For aggregate charts, compute aggregated data
2468
+ let aggregatedData;
2469
+ if (isAggregate && yField) {
2470
+ aggregatedData = computeAggregateData(dataValues, yField, xField, xAggregate);
2471
+ }
2472
+ const colorValue = (_encoding_color = encoding.color) === null || _encoding_color === void 0 ? void 0 : _encoding_color.value;
2473
+ const barData = [];
2474
+ const colorIndex = new Map();
2475
+ let currentColorIndex = 0;
2476
+ if (aggregatedData) {
2477
+ // Use aggregated data
2478
+ aggregatedData.forEach(({ category, value })=>{
2479
+ const legend = String(category);
2480
+ if (!colorIndex.has(legend)) {
2481
+ colorIndex.set(legend, currentColorIndex++);
2482
+ }
2483
+ const color = resolveColor(legend, colorIndex.get(legend), colorValue, markProps.color, colorMap, undefined, undefined, isDarkTheme);
2484
+ barData.push({
2485
+ x: value,
2486
+ y: category,
2487
+ legend,
2488
+ color
2489
+ });
2490
+ });
2491
+ } else if (x2Field && xField && yField) {
2492
+ var _encoding_x;
2493
+ // Gantt chart: bar mark with x/x2 temporal range encoding
2494
+ const isXTemporal = ((_encoding_x = encoding.x) === null || _encoding_x === void 0 ? void 0 : _encoding_x.type) === 'temporal';
2495
+ dataValues.forEach((row)=>{
2496
+ const startVal = row[xField];
2497
+ const endVal = row[x2Field];
2498
+ const yValue = row[yField];
2499
+ if (startVal === undefined || endVal === undefined || yValue === undefined) {
2500
+ return;
2501
+ }
2502
+ let xNumeric;
2503
+ if (isXTemporal) {
2504
+ const startDate = new Date(startVal);
2505
+ const endDate = new Date(endVal);
2506
+ if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
2507
+ return;
2508
+ }
2509
+ xNumeric = Math.round((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));
2510
+ } else {
2511
+ xNumeric = Number(endVal) - Number(startVal);
2512
+ if (isNaN(xNumeric)) {
2513
+ return;
2514
+ }
2515
+ }
2516
+ const legend = colorField && row[colorField] !== undefined ? String(row[colorField]) : String(yValue);
2517
+ if (!colorIndex.has(legend)) {
2518
+ colorIndex.set(legend, currentColorIndex++);
2519
+ }
2520
+ const color = resolveColor(legend, colorIndex.get(legend), colorValue, markProps.color, colorMap, undefined, undefined, isDarkTheme);
2521
+ barData.push({
2522
+ x: xNumeric,
2523
+ y: yValue,
2524
+ legend,
2525
+ color
2526
+ });
2527
+ });
2528
+ } else if (xField && yField) {
2529
+ // Use raw data
2530
+ dataValues.forEach((row)=>{
2531
+ const xValue = row[xField];
2532
+ const yValue = row[yField];
2533
+ if ((0, _chartutilities.isInvalidValue)(xValue) || (0, _chartutilities.isInvalidValue)(yValue) || typeof xValue !== 'number') {
2534
+ return;
2535
+ }
2536
+ // When no color field, use single legend to avoid tooltip duplication with y-axis labels
2537
+ const legend = colorField && row[colorField] !== undefined ? String(row[colorField]) : !colorField ? 'Bar' : String(yValue);
2538
+ if (!colorIndex.has(legend)) {
2539
+ colorIndex.set(legend, currentColorIndex++);
2540
+ }
2541
+ const color = resolveColor(legend, colorIndex.get(legend), colorValue, markProps.color, colorMap, undefined, undefined, isDarkTheme);
2542
+ barData.push({
2543
+ x: xValue,
2544
+ y: yValue,
2545
+ legend,
2546
+ color
2547
+ });
2548
+ });
2549
+ }
2550
+ const titles = getVegaLiteTitles(spec);
2551
+ const annotations = extractAnnotations(spec);
2552
+ const tickConfig = extractTickConfig(spec);
2553
+ var _encoding_color_legend_disable;
2554
+ const result = {
2555
+ data: barData,
2556
+ chartTitle: titles.chartTitle,
2557
+ xAxisTitle: titles.xAxisTitle,
2558
+ yAxisTitle: titles.yAxisTitle,
2559
+ ...titles.titleStyles ? titles.titleStyles : {},
2560
+ // Hide legend for single-series horizontal bars (no color encoding)
2561
+ hideLegend: !colorField ? true : (_encoding_color_legend_disable = (_encoding_color1 = encoding.color) === null || _encoding_color1 === void 0 ? void 0 : (_encoding_color_legend = _encoding_color1.legend) === null || _encoding_color_legend === void 0 ? void 0 : _encoding_color_legend.disable) !== null && _encoding_color_legend_disable !== void 0 ? _encoding_color_legend_disable : false
2562
+ };
2563
+ if (annotations.length > 0) {
2564
+ result.annotations = annotations;
2565
+ }
2566
+ if (tickConfig.tickValues) {
2567
+ result.tickValues = tickConfig.tickValues;
2568
+ }
2569
+ if (tickConfig.xAxisTickCount) {
2570
+ result.xAxisTickCount = tickConfig.xAxisTickCount;
2571
+ }
2572
+ if (tickConfig.yAxisTickCount) {
2573
+ result.yAxisTickCount = tickConfig.yAxisTickCount;
2574
+ }
2575
+ return result;
2576
+ }
2577
+ function transformVegaLiteToAreaChartProps(spec, colorMap, isDarkTheme) {
2578
+ var _encoding_color, _encoding_y;
2579
+ // Area charts use the same structure as line charts in Fluent Charts
2580
+ // The only difference is the component renders with filled areas
2581
+ const lineChartProps = transformVegaLiteToLineChartProps(spec, colorMap, isDarkTheme);
2582
+ // Determine stacking mode based on Vega-Lite spec
2583
+ const unitSpecs = normalizeSpec(spec);
2584
+ // Use findPrimaryLineSpec to skip auxiliary layers (like rect for color fill bars)
2585
+ const primarySpec = findPrimaryLineSpec(unitSpecs);
2586
+ const encoding = (primarySpec === null || primarySpec === void 0 ? void 0 : primarySpec.encoding) || {};
2587
+ // Check if stacking is enabled
2588
+ // In Vega-Lite, area charts stack by default when color encoding is present
2589
+ // stack can be explicitly set to null to disable stacking
2590
+ const hasColorEncoding = !!((_encoding_color = encoding.color) === null || _encoding_color === void 0 ? void 0 : _encoding_color.field);
2591
+ const stackConfig = (_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : _encoding_y.stack;
2592
+ const isStacked = stackConfig !== null && (stackConfig === 'zero' || hasColorEncoding);
2593
+ // Set mode: 'tozeroy' for single series, 'tonexty' for stacked
2594
+ const mode = isStacked ? 'tonexty' : 'tozeroy';
2595
+ return {
2596
+ ...lineChartProps,
2597
+ mode
2598
+ };
2599
+ }
2600
+ function transformVegaLiteToScatterChartProps(spec, colorMap, isDarkTheme) {
2601
+ var _encoding_x, _encoding_y, _encoding_y1, _encoding_y2, _encoding_y_axis, _encoding_y3, _encoding_color_legend, _encoding_color;
2602
+ // Initialize transformation context
2603
+ const { dataValues, encoding, markProps } = initializeTransformContext(spec);
2604
+ // Extract field names
2605
+ const { xField, yField, colorField, sizeField } = extractEncodingFields(encoding);
2606
+ if (!xField || !yField) {
2607
+ throw new Error('VegaLiteSchemaAdapter: Both x and y encodings are required for scatter charts');
2608
+ }
2609
+ const isXTemporal = ((_encoding_x = encoding.x) === null || _encoding_x === void 0 ? void 0 : _encoding_x.type) === 'temporal';
2610
+ const isYTemporal = ((_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : _encoding_y.type) === 'temporal';
2611
+ // Check if y-values are strings (nominal/ordinal) and build ordinal mapping
2612
+ const yIsNominal = ((_encoding_y1 = encoding.y) === null || _encoding_y1 === void 0 ? void 0 : _encoding_y1.type) === 'nominal' || ((_encoding_y2 = encoding.y) === null || _encoding_y2 === void 0 ? void 0 : _encoding_y2.type) === 'ordinal';
2613
+ const yOrdinalMap = new Map();
2614
+ const yOrdinalLabels = [];
2615
+ if (yIsNominal) {
2616
+ // Collect unique y-values in order
2617
+ dataValues.forEach((row)=>{
2618
+ const yVal = row[yField];
2619
+ if (yVal !== undefined) {
2620
+ const key = String(yVal);
2621
+ if (!yOrdinalMap.has(key)) {
2622
+ yOrdinalMap.set(key, yOrdinalMap.size);
2623
+ yOrdinalLabels.push(key);
2624
+ }
2625
+ }
2626
+ });
2627
+ }
2628
+ // Group data by series (color encoding)
2629
+ const groupedData = {};
2630
+ dataValues.forEach((row)=>{
2631
+ const seriesName = colorField && row[colorField] !== undefined ? String(row[colorField]) : 'default';
2632
+ if (!groupedData[seriesName]) {
2633
+ groupedData[seriesName] = [];
2634
+ }
2635
+ groupedData[seriesName].push(row);
2636
+ });
2637
+ const seriesNames = Object.keys(groupedData);
2638
+ const colorIndex = new Map();
2639
+ let currentColorIndex = 0;
2640
+ const chartData = seriesNames.map((seriesName, index)=>{
2641
+ var _encoding_color_scale, _encoding_color;
2642
+ if (!colorIndex.has(seriesName)) {
2643
+ colorIndex.set(seriesName, currentColorIndex++);
2644
+ }
2645
+ const seriesData = groupedData[seriesName];
2646
+ const points = seriesData.map((row)=>{
2647
+ const xValue = parseValue(row[xField], isXTemporal);
2648
+ const yValue = parseValue(row[yField], isYTemporal);
2649
+ const markerSize = sizeField && row[sizeField] !== undefined ? Number(row[sizeField]) : undefined;
2650
+ // Map nominal y-values to numeric indices
2651
+ let numericY;
2652
+ if (yIsNominal && typeof yValue === 'string') {
2653
+ var _yOrdinalMap_get;
2654
+ numericY = (_yOrdinalMap_get = yOrdinalMap.get(yValue)) !== null && _yOrdinalMap_get !== void 0 ? _yOrdinalMap_get : 0;
2655
+ } else {
2656
+ numericY = typeof yValue === 'number' ? yValue : 0;
2657
+ }
2658
+ return {
2659
+ x: typeof xValue === 'number' || xValue instanceof Date ? xValue : String(xValue),
2660
+ y: numericY,
2661
+ ...markerSize !== undefined && {
2662
+ markerSize
2663
+ }
2664
+ };
2665
+ });
2666
+ // Get color for this series
2667
+ const colorValue = colorField && ((_encoding_color = encoding.color) === null || _encoding_color === void 0 ? void 0 : (_encoding_color_scale = _encoding_color.scale) === null || _encoding_color_scale === void 0 ? void 0 : _encoding_color_scale.range) && Array.isArray(encoding.color.scale.range) ? encoding.color.scale.range[index] : markProps.color;
2668
+ const color = typeof colorValue === 'string' ? colorValue : resolveColor(seriesName, colorIndex.get(seriesName), undefined, undefined, colorMap, undefined, undefined, isDarkTheme);
2669
+ return {
2670
+ legend: seriesName,
2671
+ data: points,
2672
+ color,
2673
+ legendShape: 'circle'
2674
+ };
2675
+ });
2676
+ const titles = getVegaLiteTitles(spec);
2677
+ const annotations = extractAnnotations(spec);
2678
+ const tickConfig = extractTickConfig(spec);
2679
+ // Check for log scale on Y-axis
2680
+ const yAxisType = extractYAxisType(encoding);
2681
+ // Extract y-axis formatting and domain props
2682
+ const yAxisTickFormat = (_encoding_y3 = encoding.y) === null || _encoding_y3 === void 0 ? void 0 : (_encoding_y_axis = _encoding_y3.axis) === null || _encoding_y_axis === void 0 ? void 0 : _encoding_y_axis.format;
2683
+ const { yMinValue, yMaxValue } = extractYMinMax(encoding, dataValues);
2684
+ // Extract axis category ordering
2685
+ const categoryOrderProps = extractAxisCategoryOrderProps(encoding);
2686
+ var _encoding_color_legend_disable;
2687
+ const result = {
2688
+ data: {
2689
+ chartTitle: titles.chartTitle,
2690
+ scatterChartData: chartData
2691
+ },
2692
+ xAxisTitle: titles.xAxisTitle,
2693
+ yAxisTitle: titles.yAxisTitle,
2694
+ ...titles.titleStyles ? titles.titleStyles : {},
2695
+ ...yAxisTickFormat && {
2696
+ yAxisTickFormat
2697
+ },
2698
+ ...yMinValue !== undefined && {
2699
+ yMinValue
2700
+ },
2701
+ ...yMaxValue !== undefined && {
2702
+ yMaxValue
2703
+ },
2704
+ ...yAxisType && {
2705
+ yScaleType: yAxisType
2706
+ },
2707
+ // For nominal y-axis, provide tick values and labels
2708
+ ...yIsNominal && yOrdinalLabels.length > 0 && {
2709
+ yAxisTickValues: Array.from({
2710
+ length: yOrdinalLabels.length
2711
+ }, (_, i)=>i),
2712
+ yAxisTickFormat: (val)=>{
2713
+ var _yOrdinalLabels_val;
2714
+ return (_yOrdinalLabels_val = yOrdinalLabels[val]) !== null && _yOrdinalLabels_val !== void 0 ? _yOrdinalLabels_val : String(val);
2715
+ },
2716
+ yMinValue: -0.5,
2717
+ yMaxValue: yOrdinalLabels.length - 0.5
2718
+ },
2719
+ ...categoryOrderProps,
2720
+ hideLegend: (_encoding_color_legend_disable = (_encoding_color = encoding.color) === null || _encoding_color === void 0 ? void 0 : (_encoding_color_legend = _encoding_color.legend) === null || _encoding_color_legend === void 0 ? void 0 : _encoding_color_legend.disable) !== null && _encoding_color_legend_disable !== void 0 ? _encoding_color_legend_disable : false
2721
+ };
2722
+ if (annotations.length > 0) {
2723
+ result.annotations = annotations;
2724
+ }
2725
+ if (tickConfig.tickValues) {
2726
+ result.tickValues = tickConfig.tickValues;
2727
+ }
2728
+ if (tickConfig.xAxisTickCount) {
2729
+ result.xAxisTickCount = tickConfig.xAxisTickCount;
2730
+ }
2731
+ if (tickConfig.yAxisTickCount) {
2732
+ result.yAxisTickCount = tickConfig.yAxisTickCount;
2733
+ }
2734
+ return result;
2735
+ }
2736
+ function transformVegaLiteToDonutChartProps(spec, colorMap, isDarkTheme) {
2737
+ // Initialize transformation context
2738
+ const { dataValues, encoding, primarySpec } = initializeTransformContext(spec);
2739
+ // Extract field names
2740
+ const { thetaField, colorField } = extractEncodingFields(encoding);
2741
+ if (!thetaField) {
2742
+ throw new Error('VegaLiteSchemaAdapter: Theta encoding is required for donut charts');
2743
+ }
2744
+ // Extract color configuration
2745
+ const { colorScheme, colorRange } = extractColorConfig(encoding);
2746
+ // Extract innerRadius from mark properties if available
2747
+ const mark = primarySpec.mark;
2748
+ const innerRadius = typeof mark === 'object' && (mark === null || mark === void 0 ? void 0 : mark.innerRadius) !== undefined ? mark.innerRadius : 0;
2749
+ const chartData = [];
2750
+ const colorIndex = new Map();
2751
+ let currentColorIndex = 0;
2752
+ dataValues.forEach((row)=>{
2753
+ const value = row[thetaField];
2754
+ const legend = colorField && row[colorField] !== undefined ? String(row[colorField]) : String(value);
2755
+ if (value === undefined || typeof value !== 'number') {
2756
+ return;
2757
+ }
2758
+ if (!colorIndex.has(legend)) {
2759
+ colorIndex.set(legend, currentColorIndex++);
2760
+ }
2761
+ chartData.push({
2762
+ legend,
2763
+ data: value,
2764
+ color: resolveColor(legend, colorIndex.get(legend), undefined, undefined, colorMap, colorScheme, colorRange, isDarkTheme)
2765
+ });
2766
+ });
2767
+ const titles = getVegaLiteTitles(spec);
2768
+ return {
2769
+ data: {
2770
+ chartTitle: titles.chartTitle,
2771
+ chartData
2772
+ },
2773
+ innerRadius,
2774
+ width: typeof spec.width === 'number' ? spec.width : undefined,
2775
+ height: typeof spec.height === 'number' ? spec.height : undefined,
2776
+ ...titles.titleStyles ? titles.titleStyles : {}
2777
+ };
2778
+ }
2779
+ function transformVegaLiteToHeatMapChartProps(spec, colorMap, isDarkTheme) {
2780
+ var _encoding_color, _encoding_color1, _encoding_x, _encoding_x1, _encoding_color_scale, _encoding_color2, _encoding_color_scale1, _encoding_color3;
2781
+ // Initialize transformation context
2782
+ const { dataValues, encoding } = initializeTransformContext(spec);
2783
+ // Extract field names
2784
+ const { xField, yField, colorField } = extractEncodingFields(encoding);
2785
+ if (!xField || !yField || !colorField) {
2786
+ throw new Error('VegaLiteSchemaAdapter: x, y, and color encodings are required for heatmap charts');
2787
+ }
2788
+ const heatmapDataPoints = [];
2789
+ let minValue = Number.POSITIVE_INFINITY;
2790
+ let maxValue = Number.NEGATIVE_INFINITY;
2791
+ // Check if color values are nominal (strings) rather than quantitative (numbers)
2792
+ const isNominalColor = ((_encoding_color = encoding.color) === null || _encoding_color === void 0 ? void 0 : _encoding_color.type) === 'nominal' || ((_encoding_color1 = encoding.color) === null || _encoding_color1 === void 0 ? void 0 : _encoding_color1.type) === 'ordinal' || dataValues.some((row)=>row[colorField] !== undefined && typeof row[colorField] !== 'number');
2793
+ const nominalColorMap = new Map();
2794
+ dataValues.forEach((row)=>{
2795
+ const xValue = row[xField];
2796
+ const yValue = row[yField];
2797
+ const colorValue = row[colorField];
2798
+ if ((0, _chartutilities.isInvalidValue)(xValue) || (0, _chartutilities.isInvalidValue)(yValue) || (0, _chartutilities.isInvalidValue)(colorValue)) {
2799
+ return;
2800
+ }
2801
+ let value;
2802
+ if (isNominalColor) {
2803
+ // Map nominal color values to sequential numeric indices
2804
+ const key = String(colorValue);
2805
+ if (!nominalColorMap.has(key)) {
2806
+ nominalColorMap.set(key, nominalColorMap.size);
2807
+ }
2808
+ value = nominalColorMap.get(key);
2809
+ } else {
2810
+ value = typeof colorValue === 'number' ? colorValue : 0;
2811
+ }
2812
+ minValue = Math.min(minValue, value);
2813
+ maxValue = Math.max(maxValue, value);
2814
+ heatmapDataPoints.push({
2815
+ x: xValue,
2816
+ y: yValue,
2817
+ value,
2818
+ rectText: isNominalColor ? String(colorValue) : value
2819
+ });
2820
+ });
2821
+ // Validate that we have complete grid data
2822
+ if (heatmapDataPoints.length === 0) {
2823
+ throw new Error('VegaLiteSchemaAdapter: Heatmap requires data points with x, y, and color values');
2824
+ }
2825
+ // Extract unique x and y values and create complete grid
2826
+ const uniqueXValues = new Set(heatmapDataPoints.map((p)=>String(p.x)));
2827
+ const uniqueYValues = new Set(heatmapDataPoints.map((p)=>String(p.y)));
2828
+ // Build a map of existing data points for quick lookup
2829
+ const dataPointMap = new Map();
2830
+ const rectTextMap = new Map();
2831
+ heatmapDataPoints.forEach((point)=>{
2832
+ const key = `${String(point.x)}|${String(point.y)}`;
2833
+ dataPointMap.set(key, point.value);
2834
+ var _point_rectText;
2835
+ rectTextMap.set(key, (_point_rectText = point.rectText) !== null && _point_rectText !== void 0 ? _point_rectText : point.value);
2836
+ });
2837
+ // Generate complete grid - fill missing cells with 0
2838
+ const completeGridDataPoints = [];
2839
+ let xValuesArray = Array.from(uniqueXValues);
2840
+ const yValuesArray = Array.from(uniqueYValues);
2841
+ // Sort x-values chronologically if they appear to be dates
2842
+ const isXTemporal = ((_encoding_x = encoding.x) === null || _encoding_x === void 0 ? void 0 : _encoding_x.type) === 'temporal' || ((_encoding_x1 = encoding.x) === null || _encoding_x1 === void 0 ? void 0 : _encoding_x1.type) === 'ordinal';
2843
+ if (isXTemporal) {
2844
+ const firstX = xValuesArray[0];
2845
+ const parsedDate = new Date(firstX);
2846
+ if (!isNaN(parsedDate.getTime())) {
2847
+ // Values are parseable as dates — sort chronologically
2848
+ xValuesArray = xValuesArray.sort((a, b)=>new Date(a).getTime() - new Date(b).getTime());
2849
+ }
2850
+ }
2851
+ yValuesArray.forEach((yVal)=>{
2852
+ xValuesArray.forEach((xVal)=>{
2853
+ const key = `${xVal}|${yVal}`;
2854
+ var _dataPointMap_get;
2855
+ const value = (_dataPointMap_get = dataPointMap.get(key)) !== null && _dataPointMap_get !== void 0 ? _dataPointMap_get : 0; // Use 0 for missing cells
2856
+ // Update min/max to include filled values
2857
+ if (value !== 0 || dataPointMap.has(key)) {
2858
+ minValue = Math.min(minValue, value);
2859
+ maxValue = Math.max(maxValue, value);
2860
+ }
2861
+ var _rectTextMap_get;
2862
+ completeGridDataPoints.push({
2863
+ x: xVal,
2864
+ y: yVal,
2865
+ value,
2866
+ rectText: (_rectTextMap_get = rectTextMap.get(key)) !== null && _rectTextMap_get !== void 0 ? _rectTextMap_get : value
2867
+ });
2868
+ });
2869
+ });
2870
+ const heatmapData = {
2871
+ legend: '',
2872
+ data: completeGridDataPoints,
2873
+ value: 0
2874
+ };
2875
+ const titles = getVegaLiteTitles(spec);
2876
+ // Create color scale domain and range
2877
+ let domainValues = [];
2878
+ let rangeValues = [];
2879
+ // Check for named color scheme or custom range from encoding
2880
+ const colorScheme = (_encoding_color2 = encoding.color) === null || _encoding_color2 === void 0 ? void 0 : (_encoding_color_scale = _encoding_color2.scale) === null || _encoding_color_scale === void 0 ? void 0 : _encoding_color_scale.scheme;
2881
+ const customRange = (_encoding_color3 = encoding.color) === null || _encoding_color3 === void 0 ? void 0 : (_encoding_color_scale1 = _encoding_color3.scale) === null || _encoding_color_scale1 === void 0 ? void 0 : _encoding_color_scale1.range;
2882
+ if (isNominalColor && nominalColorMap.size > 0) {
2883
+ // For nominal colors, use categorical color scale
2884
+ const numCategories = nominalColorMap.size;
2885
+ domainValues = Array.from({
2886
+ length: numCategories
2887
+ }, (_, i)=>i);
2888
+ if (customRange && customRange.length >= numCategories) {
2889
+ rangeValues = customRange.slice(0, numCategories);
2890
+ } else {
2891
+ // Use distinct categorical colors for each category
2892
+ for(let i = 0; i < numCategories; i++){
2893
+ rangeValues.push((0, _VegaLiteColorAdapter.getVegaColor)(i, colorScheme, customRange, isDarkTheme !== null && isDarkTheme !== void 0 ? isDarkTheme : false));
2894
+ }
2895
+ }
2896
+ } else {
2897
+ // Quantitative color scale
2898
+ const steps = 5;
2899
+ for(let i = 0; i < steps; i++){
2900
+ const t = i / (steps - 1);
2901
+ domainValues.push(minValue + (maxValue - minValue) * t);
2902
+ }
2903
+ if (customRange && customRange.length > 0) {
2904
+ rangeValues = customRange.length >= steps ? customRange.slice(0, steps) : customRange;
2905
+ } else if (colorScheme) {
2906
+ const schemeColors = (0, _VegaLiteColorAdapter.getSequentialSchemeColors)(colorScheme, steps);
2907
+ if (schemeColors) {
2908
+ var _encoding_color4, _encoding_color_scale2, _encoding_color5;
2909
+ const isReversed = ((_encoding_color4 = encoding.color) === null || _encoding_color4 === void 0 ? void 0 : _encoding_color4.sort) === 'descending' || ((_encoding_color5 = encoding.color) === null || _encoding_color5 === void 0 ? void 0 : (_encoding_color_scale2 = _encoding_color5.scale) === null || _encoding_color_scale2 === void 0 ? void 0 : _encoding_color_scale2.reverse) === true;
2910
+ rangeValues = isReversed ? schemeColors.reverse() : schemeColors;
2911
+ }
2912
+ }
2913
+ // Fall back to default blue-to-red gradient if no scheme matched
2914
+ if (rangeValues.length === 0) {
2915
+ for(let i = 0; i < steps; i++){
2916
+ const t = i / (steps - 1);
2917
+ if (isDarkTheme) {
2918
+ const r = Math.round(0 + 255 * t);
2919
+ const g = Math.round(100 + (165 - 100) * t);
2920
+ const b = Math.round(255 - 255 * t);
2921
+ rangeValues.push(`rgb(${r}, ${g}, ${b})`);
2922
+ } else {
2923
+ const r = Math.round(0 + 255 * t);
2924
+ const g = Math.round(150 - 150 * t);
2925
+ const b = Math.round(255 - 255 * t);
2926
+ rangeValues.push(`rgb(${r}, ${g}, ${b})`);
2927
+ }
2928
+ }
2929
+ }
2930
+ }
2931
+ var _spec_height;
2932
+ return {
2933
+ chartTitle: titles.chartTitle,
2934
+ data: [
2935
+ heatmapData
2936
+ ],
2937
+ domainValuesForColorScale: domainValues,
2938
+ rangeValuesForColorScale: rangeValues,
2939
+ xAxisTitle: titles.xAxisTitle,
2940
+ yAxisTitle: titles.yAxisTitle,
2941
+ ...titles.titleStyles ? titles.titleStyles : {},
2942
+ width: spec.width,
2943
+ height: (_spec_height = spec.height) !== null && _spec_height !== void 0 ? _spec_height : DEFAULT_CHART_HEIGHT,
2944
+ hideLegend: true,
2945
+ showYAxisLables: true,
2946
+ sortOrder: 'none',
2947
+ hideTickOverlap: true,
2948
+ noOfCharsToTruncate: xValuesArray.length > 20 ? 6 : xValuesArray.length > 10 ? 10 : DEFAULT_TRUNCATE_CHARS,
2949
+ showYAxisLablesTooltip: true,
2950
+ wrapXAxisLables: true
2951
+ };
2952
+ }
2953
+ /**
2954
+ * Helper function to get bin center for display
2955
+ */ function getBinCenter(bin) {
2956
+ return (bin.x0 + bin.x1) / 2;
2957
+ }
2958
+ /**
2959
+ * Helper function to calculate histogram aggregation function
2960
+ *
2961
+ * @param aggregate - Aggregation type (count, sum, mean, min, max)
2962
+ * @param bin - Binned data values
2963
+ * @returns Aggregated value
2964
+ */ function calculateHistogramAggregate(aggregate, bin) {
2965
+ switch(aggregate){
2966
+ case 'sum':
2967
+ return (0, _d3array.sum)(bin);
2968
+ case 'mean':
2969
+ case 'average':
2970
+ var _d3Mean;
2971
+ return bin.length === 0 ? 0 : (_d3Mean = (0, _d3array.mean)(bin)) !== null && _d3Mean !== void 0 ? _d3Mean : 0;
2972
+ case 'min':
2973
+ var _d3Min;
2974
+ return (_d3Min = (0, _d3array.min)(bin)) !== null && _d3Min !== void 0 ? _d3Min : 0;
2975
+ case 'max':
2976
+ var _d3Max;
2977
+ return (_d3Max = (0, _d3array.max)(bin)) !== null && _d3Max !== void 0 ? _d3Max : 0;
2978
+ case 'count':
2979
+ default:
2980
+ return bin.length;
2981
+ }
2982
+ }
2983
+ function transformVegaLiteToHistogramProps(spec, colorMap, isDarkTheme) {
2984
+ var _encoding_y, _encoding_x, _encoding_color, _dataValues_, _encoding_y1, _encoding_y_axis, _encoding_y2;
2985
+ // Initialize transformation context
2986
+ const { dataValues, encoding } = initializeTransformContext(spec);
2987
+ // Extract field names
2988
+ const { xField } = extractEncodingFields(encoding);
2989
+ const yAggregate = ((_encoding_y = encoding.y) === null || _encoding_y === void 0 ? void 0 : _encoding_y.aggregate) || 'count';
2990
+ const binConfig = (_encoding_x = encoding.x) === null || _encoding_x === void 0 ? void 0 : _encoding_x.bin;
2991
+ if (!xField || !binConfig) {
2992
+ throw new Error('VegaLiteSchemaAdapter: Histogram requires x encoding with bin property');
2993
+ }
2994
+ // Validate data
2995
+ validateDataArray(dataValues, xField, 'Histogram');
2996
+ validateNoNestedArrays(dataValues, xField);
2997
+ // Extract numeric values from the field
2998
+ const allValues = dataValues.map((row)=>row[xField]).filter((val)=>!(0, _chartutilities.isInvalidValue)(val));
2999
+ const values = allValues.filter((val)=>typeof val === 'number');
3000
+ if (values.length === 0) {
3001
+ // Provide helpful error message based on actual data type
3002
+ const sampleValue = allValues[0];
3003
+ const actualType = typeof sampleValue;
3004
+ let suggestion = '';
3005
+ if (actualType === 'string') {
3006
+ // Check if strings contain numbers
3007
+ const hasEmbeddedNumbers = allValues.some((val)=>typeof val === 'string' && /\d/.test(val));
3008
+ if (hasEmbeddedNumbers) {
3009
+ suggestion = ' The data contains strings with embedded numbers (e.g., "40 salads"). ' + 'Consider extracting the numeric values first, or change the encoding type to "nominal" or "ordinal" for a categorical bar chart.';
3010
+ } else {
3011
+ suggestion = ` The data contains categorical strings (e.g., "${sampleValue}"). ` + 'Change the x encoding type to "nominal" or "ordinal" for a categorical bar chart, ' + 'or remove bin: true to create a simple bar chart.';
3012
+ }
3013
+ } else if (actualType === 'undefined') {
3014
+ suggestion = ' The field may not exist in the data.';
3015
+ }
3016
+ throw new Error(`VegaLiteSchemaAdapter: No numeric values found for histogram binning on field "${xField}". ` + `Found ${actualType} values instead.${suggestion}`);
3017
+ }
3018
+ // Create bins using d3
3019
+ const [minVal, maxVal] = (0, _d3array.extent)(values);
3020
+ const binGenerator = (0, _d3array.bin)().domain([
3021
+ minVal,
3022
+ maxVal
3023
+ ]);
3024
+ // Apply bin configuration
3025
+ if (typeof binConfig === 'object') {
3026
+ if (binConfig.maxbins) {
3027
+ binGenerator.thresholds(binConfig.maxbins);
3028
+ }
3029
+ if (binConfig.extent) {
3030
+ binGenerator.domain(binConfig.extent);
3031
+ }
3032
+ }
3033
+ const bins = binGenerator(values);
3034
+ // Calculate histogram data points
3035
+ const legend = ((_encoding_color = encoding.color) === null || _encoding_color === void 0 ? void 0 : _encoding_color.field) ? String((_dataValues_ = dataValues[0]) === null || _dataValues_ === void 0 ? void 0 : _dataValues_[encoding.color.field]) : 'Frequency';
3036
+ const color = resolveColor(legend, 0, undefined, undefined, colorMap, undefined, undefined, isDarkTheme);
3037
+ const yField = (_encoding_y1 = encoding.y) === null || _encoding_y1 === void 0 ? void 0 : _encoding_y1.field;
3038
+ const histogramData = bins.map((bin)=>{
3039
+ const x = getBinCenter(bin);
3040
+ let y;
3041
+ if (yAggregate !== 'count' && yField) {
3042
+ // For non-count aggregates, collect y-field values for rows whose x-value falls in this bin
3043
+ const yValues = dataValues.filter((row)=>{
3044
+ const xVal = Number(row[xField]);
3045
+ return !isNaN(xVal) && xVal >= bin.x0 && xVal < bin.x1;
3046
+ }).map((row)=>Number(row[yField])).filter((v)=>!isNaN(v));
3047
+ // Include the last bin's upper bound (x1 is inclusive for the last bin)
3048
+ if (bin === bins[bins.length - 1]) {
3049
+ const extraRows = dataValues.filter((row)=>Number(row[xField]) === bin.x1).map((row)=>Number(row[yField])).filter((v)=>!isNaN(v));
3050
+ yValues.push(...extraRows);
3051
+ }
3052
+ y = calculateHistogramAggregate(yAggregate, yValues);
3053
+ } else {
3054
+ y = calculateHistogramAggregate(yAggregate, bin);
3055
+ }
3056
+ const xAxisCalloutData = `[${bin.x0} - ${bin.x1})`;
3057
+ return {
3058
+ x,
3059
+ y,
3060
+ legend,
3061
+ color,
3062
+ xAxisCalloutData
3063
+ };
3064
+ });
3065
+ const titles = getVegaLiteTitles(spec);
3066
+ const annotations = extractAnnotations(spec);
3067
+ const yAxisTickFormat = (_encoding_y2 = encoding.y) === null || _encoding_y2 === void 0 ? void 0 : (_encoding_y_axis = _encoding_y2.axis) === null || _encoding_y_axis === void 0 ? void 0 : _encoding_y_axis.format;
3068
+ return {
3069
+ data: histogramData,
3070
+ chartTitle: titles.chartTitle,
3071
+ xAxisTitle: titles.xAxisTitle || xField,
3072
+ yAxisTitle: titles.yAxisTitle || yAggregate,
3073
+ ...titles.titleStyles ? titles.titleStyles : {},
3074
+ roundCorners: true,
3075
+ hideTickOverlap: true,
3076
+ maxBarWidth: DEFAULT_MAX_BAR_WIDTH,
3077
+ ...annotations.length > 0 && {
3078
+ annotations
3079
+ },
3080
+ ...yAxisTickFormat && {
3081
+ yAxisTickFormat
3082
+ },
3083
+ mode: 'histogram'
3084
+ };
3085
+ }
3086
+ function transformVegaLiteToPolarChartProps(spec, colorMap, isDarkTheme) {
3087
+ var _encoding_theta, _encoding_color_legend, _encoding_color;
3088
+ // Initialize transformation context
3089
+ const { dataValues, encoding, markProps, primarySpec } = initializeTransformContext(spec);
3090
+ // Extract field names
3091
+ const { thetaField, radiusField, colorField } = extractEncodingFields(encoding);
3092
+ // Validate polar encodings
3093
+ if (!thetaField || !radiusField) {
3094
+ throw new Error('VegaLiteSchemaAdapter: Both theta and radius encodings are required for polar charts');
3095
+ }
3096
+ validateDataArray(dataValues, thetaField, 'PolarChart');
3097
+ validateDataArray(dataValues, radiusField, 'PolarChart');
3098
+ // Determine mark type for polar chart series type
3099
+ const mark = primarySpec.mark;
3100
+ const markType = typeof mark === 'string' ? mark : mark === null || mark === void 0 ? void 0 : mark.type;
3101
+ // Arc marks with theta+radius should be treated as area polar (radial/rose charts)
3102
+ const isAreaMark = markType === 'area' || markType === 'arc';
3103
+ const isLineMark = markType === 'line';
3104
+ // Extract color configuration
3105
+ const { colorScheme, colorRange } = extractColorConfig(encoding);
3106
+ // Group data by series (color field)
3107
+ const seriesMap = new Map();
3108
+ const colorIndex = new Map();
3109
+ let currentColorIndex = 0;
3110
+ dataValues.forEach((row)=>{
3111
+ const thetaValue = row[thetaField];
3112
+ const radiusValue = row[radiusField];
3113
+ // Skip invalid values
3114
+ if ((0, _chartutilities.isInvalidValue)(thetaValue) || (0, _chartutilities.isInvalidValue)(radiusValue)) {
3115
+ return;
3116
+ }
3117
+ const seriesName = colorField && row[colorField] !== undefined ? String(row[colorField]) : 'default';
3118
+ if (!colorIndex.has(seriesName)) {
3119
+ colorIndex.set(seriesName, currentColorIndex++);
3120
+ }
3121
+ if (!seriesMap.has(seriesName)) {
3122
+ seriesMap.set(seriesName, []);
3123
+ }
3124
+ // Convert theta value - handle different types
3125
+ let theta;
3126
+ if (typeof thetaValue === 'number') {
3127
+ // Numeric theta - assume degrees
3128
+ theta = thetaValue;
3129
+ } else {
3130
+ // Categorical theta
3131
+ theta = String(thetaValue);
3132
+ }
3133
+ // Convert radius value
3134
+ const r = typeof radiusValue === 'number' ? radiusValue : Number(radiusValue);
3135
+ seriesMap.get(seriesName).push({
3136
+ theta,
3137
+ r
3138
+ });
3139
+ });
3140
+ // Convert series map to polar chart data array
3141
+ const polarData = [];
3142
+ seriesMap.forEach((dataPoints, seriesName)=>{
3143
+ const color = resolveColor(seriesName, colorIndex.get(seriesName), undefined, markProps.color, colorMap, colorScheme, colorRange, isDarkTheme);
3144
+ const curveOption = mapInterpolateToCurve(markProps.interpolate);
3145
+ // Build line options with curve, strokeDash, and strokeWidth
3146
+ const lineOptions = {};
3147
+ if (curveOption) {
3148
+ lineOptions.curve = curveOption;
3149
+ }
3150
+ if (markProps.strokeDash) {
3151
+ lineOptions.strokeDasharray = markProps.strokeDash.join(' ');
3152
+ }
3153
+ if (markProps.strokeWidth) {
3154
+ lineOptions.strokeWidth = markProps.strokeWidth;
3155
+ }
3156
+ if (isAreaMark) {
3157
+ const series = {
3158
+ type: 'areapolar',
3159
+ legend: seriesName,
3160
+ color,
3161
+ data: dataPoints,
3162
+ ...Object.keys(lineOptions).length > 0 && {
3163
+ lineOptions
3164
+ }
3165
+ };
3166
+ polarData.push(series);
3167
+ } else if (isLineMark) {
3168
+ const series = {
3169
+ type: 'linepolar',
3170
+ legend: seriesName,
3171
+ color,
3172
+ data: dataPoints,
3173
+ ...Object.keys(lineOptions).length > 0 && {
3174
+ lineOptions
3175
+ }
3176
+ };
3177
+ polarData.push(series);
3178
+ } else {
3179
+ // Default to scatter polar for point marks
3180
+ const series = {
3181
+ type: 'scatterpolar',
3182
+ legend: seriesName,
3183
+ color,
3184
+ data: dataPoints
3185
+ };
3186
+ polarData.push(series);
3187
+ }
3188
+ });
3189
+ // Extract chart titles
3190
+ const titles = getVegaLiteTitles(spec);
3191
+ // Build axis props from encoding
3192
+ const radialAxis = {};
3193
+ const angularAxis = {};
3194
+ // Determine angular axis category order if theta is categorical
3195
+ const thetaType = (_encoding_theta = encoding.theta) === null || _encoding_theta === void 0 ? void 0 : _encoding_theta.type;
3196
+ if (thetaType === 'nominal' || thetaType === 'ordinal') {
3197
+ // Get unique theta values in order for category order
3198
+ const thetaValues = Array.from(new Set(dataValues.map((row)=>String(row[thetaField]))));
3199
+ angularAxis.categoryOrder = thetaValues;
3200
+ }
3201
+ var _encoding_color_legend_disable;
3202
+ return {
3203
+ data: polarData,
3204
+ ...titles.chartTitle && {
3205
+ chartTitle: titles.chartTitle
3206
+ },
3207
+ ...titles.titleStyles ? titles.titleStyles : {},
3208
+ width: typeof spec.width === 'number' ? spec.width : undefined,
3209
+ height: typeof spec.height === 'number' ? spec.height : 400,
3210
+ hideLegend: (_encoding_color_legend_disable = (_encoding_color = encoding.color) === null || _encoding_color === void 0 ? void 0 : (_encoding_color_legend = _encoding_color.legend) === null || _encoding_color_legend === void 0 ? void 0 : _encoding_color_legend.disable) !== null && _encoding_color_legend_disable !== void 0 ? _encoding_color_legend_disable : false,
3211
+ radialAxis,
3212
+ angularAxis
3213
+ };
3214
+ }