@classic-homes/charts-core 0.1.36 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { colors, semanticColors, borderRadius, fontFamily } from '@classic-homes/theme-tokens';
1
+ import { semanticColors, colors, borderRadius, fontFamily } from '@classic-homes/theme-tokens';
2
2
 
3
3
  // src/theme/generator.ts
4
4
  function generateLightTheme() {
@@ -270,8 +270,43 @@ function generateDarkTheme() {
270
270
  var lightTheme = generateLightTheme();
271
271
  var darkTheme = generateDarkTheme();
272
272
 
273
- // src/theme/index.ts
273
+ // src/theme/constants.ts
274
274
  var THEME_LIGHT = "classic-light";
275
275
  var THEME_DARK = "classic-dark";
276
276
 
277
- export { THEME_DARK, THEME_LIGHT, darkTheme, generateDarkTheme, generateLightTheme, lightTheme };
277
+ // src/theme/semantic.ts
278
+ var lightChartColors = {
279
+ series: lightTheme.color,
280
+ foreground: semanticColors.light.foreground,
281
+ mutedForeground: semanticColors.light.mutedForeground,
282
+ axisPointerLabel: semanticColors.light.mutedForeground,
283
+ divider: colors.gray[600],
284
+ success: semanticColors.light.success,
285
+ destructive: semanticColors.light.destructive
286
+ };
287
+ var darkChartColors = {
288
+ series: darkTheme.color,
289
+ foreground: semanticColors.dark.foreground,
290
+ mutedForeground: semanticColors.dark.mutedForeground,
291
+ // mutedForeground (gray[400]) is too light under the white label text
292
+ axisPointerLabel: colors.gray[600],
293
+ divider: colors.gray[700],
294
+ success: semanticColors.dark.success,
295
+ destructive: semanticColors.dark.destructive
296
+ };
297
+ function getChartColors(themeName) {
298
+ return themeName === THEME_DARK ? darkChartColors : lightChartColors;
299
+ }
300
+ function colorWithAlpha(color, alpha) {
301
+ let hex = /^#([\da-f]{3}|[\da-f]{6})$/i.exec(color)?.[1];
302
+ if (!hex) return color;
303
+ if (hex.length === 3) {
304
+ hex = hex.split("").map((c) => c + c).join("");
305
+ }
306
+ const r = parseInt(hex.slice(0, 2), 16);
307
+ const g = parseInt(hex.slice(2, 4), 16);
308
+ const b = parseInt(hex.slice(4, 6), 16);
309
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
310
+ }
311
+
312
+ export { THEME_DARK, THEME_LIGHT, colorWithAlpha, darkChartColors, darkTheme, generateDarkTheme, generateLightTheme, getChartColors, lightChartColors, lightTheme };
package/dist/index.cjs CHANGED
@@ -272,10 +272,45 @@ function generateDarkTheme() {
272
272
  var lightTheme = generateLightTheme();
273
273
  var darkTheme = generateDarkTheme();
274
274
 
275
- // src/theme/index.ts
275
+ // src/theme/constants.ts
276
276
  var THEME_LIGHT = "classic-light";
277
277
  var THEME_DARK = "classic-dark";
278
278
 
279
+ // src/theme/semantic.ts
280
+ var lightChartColors = {
281
+ series: lightTheme.color,
282
+ foreground: themeTokens.semanticColors.light.foreground,
283
+ mutedForeground: themeTokens.semanticColors.light.mutedForeground,
284
+ axisPointerLabel: themeTokens.semanticColors.light.mutedForeground,
285
+ divider: themeTokens.colors.gray[600],
286
+ success: themeTokens.semanticColors.light.success,
287
+ destructive: themeTokens.semanticColors.light.destructive
288
+ };
289
+ var darkChartColors = {
290
+ series: darkTheme.color,
291
+ foreground: themeTokens.semanticColors.dark.foreground,
292
+ mutedForeground: themeTokens.semanticColors.dark.mutedForeground,
293
+ // mutedForeground (gray[400]) is too light under the white label text
294
+ axisPointerLabel: themeTokens.colors.gray[600],
295
+ divider: themeTokens.colors.gray[700],
296
+ success: themeTokens.semanticColors.dark.success,
297
+ destructive: themeTokens.semanticColors.dark.destructive
298
+ };
299
+ function getChartColors(themeName) {
300
+ return themeName === THEME_DARK ? darkChartColors : lightChartColors;
301
+ }
302
+ function colorWithAlpha(color, alpha) {
303
+ let hex = /^#([\da-f]{3}|[\da-f]{6})$/i.exec(color)?.[1];
304
+ if (!hex) return color;
305
+ if (hex.length === 3) {
306
+ hex = hex.split("").map((c) => c + c).join("");
307
+ }
308
+ const r = parseInt(hex.slice(0, 2), 16);
309
+ const g = parseInt(hex.slice(2, 4), 16);
310
+ const b = parseInt(hex.slice(4, 6), 16);
311
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
312
+ }
313
+
279
314
  // src/utils/format.ts
280
315
  function formatNumber(value, options) {
281
316
  return new Intl.NumberFormat("en-US", options).format(value);
@@ -305,7 +340,18 @@ function abbreviateNumber(value, decimals = 1) {
305
340
  abbreviated /= 1e3;
306
341
  suffixIndex++;
307
342
  }
308
- return abbreviated.toFixed(decimals).replace(/\.0+$/, "") + suffixes[suffixIndex];
343
+ if (Math.abs(Number(abbreviated.toFixed(decimals))) >= 1e3 && suffixIndex < suffixes.length - 1) {
344
+ abbreviated /= 1e3;
345
+ suffixIndex++;
346
+ }
347
+ let fixed = abbreviated.toFixed(decimals);
348
+ if (fixed.includes(".")) {
349
+ fixed = fixed.replace(/\.?0+$/, "");
350
+ }
351
+ return fixed + suffixes[suffixIndex];
352
+ }
353
+ function escapeHtml(value) {
354
+ return String(value).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
309
355
  }
310
356
  function createTooltipFormatter(options = {}) {
311
357
  const { showSeriesName = true, valueFormatter = formatNumber } = options;
@@ -320,9 +366,9 @@ function createTooltipFormatter(options = {}) {
320
366
  const formatted = valueFormatter(value);
321
367
  const percent = param.percent ? ` (${param.percent.toFixed(1)}%)` : "";
322
368
  if (showSeriesName && param.seriesName) {
323
- return `${param.seriesName}: ${formatted}${percent}`;
369
+ return `${escapeHtml(param.seriesName)}: ${escapeHtml(formatted)}${percent}`;
324
370
  }
325
- return `${param.name}: ${formatted}${percent}`;
371
+ return `${escapeHtml(param.name)}: ${escapeHtml(formatted)}${percent}`;
326
372
  }).join("<br/>");
327
373
  };
328
374
  }
@@ -376,6 +422,9 @@ function generatePieChartDescription(title, data, isDonut = false) {
376
422
  const total = data.reduce((sum, slice) => sum + slice.value, 0);
377
423
  const chartType = isDonut ? "Donut" : "Pie";
378
424
  let description = `${title}. ${chartType} chart with ${data.length} segments`;
425
+ if (total === 0) {
426
+ return `${description}. All segments have a value of 0`;
427
+ }
379
428
  const sorted = [...data].sort((a, b) => b.value - a.value);
380
429
  const topSegments = sorted.slice(0, 3);
381
430
  const percentages = topSegments.map((segment) => {
@@ -396,8 +445,8 @@ function generateScatterChartDescription(title, data) {
396
445
  return description;
397
446
  }
398
447
  function generateDataTable(headers, rows) {
399
- const headerRow = headers.map((h) => `<th scope="col">${h}</th>`).join("");
400
- const bodyRows = rows.map((row) => `<tr>${row.map((cell) => `<td>${cell}</td>`).join("")}</tr>`).join("");
448
+ const headerRow = headers.map((h) => `<th scope="col">${escapeHtml(h)}</th>`).join("");
449
+ const bodyRows = rows.map((row) => `<tr>${row.map((cell) => `<td>${escapeHtml(cell)}</td>`).join("")}</tr>`).join("");
401
450
  return `
402
451
  <table class="sr-only">
403
452
  <thead><tr>${headerRow}</tr></thead>
@@ -406,15 +455,246 @@ function generateDataTable(headers, rows) {
406
455
  `;
407
456
  }
408
457
 
458
+ // src/utils/merge.ts
459
+ var ALWAYS_REPLACE = ["series", "graphic"];
460
+ function countComponents(value) {
461
+ if (value == null || value === false) return 0;
462
+ if (Array.isArray(value)) return value.filter((item) => item != null).length;
463
+ return 1;
464
+ }
465
+ function getReplaceMerge(prev, next, isComponentType) {
466
+ const types = new Set(ALWAYS_REPLACE);
467
+ if (prev) {
468
+ const before = prev;
469
+ const after = next;
470
+ for (const key of Object.keys(before)) {
471
+ if (countComponents(before[key]) > countComponents(after[key])) {
472
+ types.add(key);
473
+ }
474
+ }
475
+ }
476
+ return [...types].filter((type) => isComponentType(type));
477
+ }
478
+ function isPlainObject(value) {
479
+ if (value === null || typeof value !== "object") return false;
480
+ const proto = Object.getPrototypeOf(value);
481
+ return proto === Object.prototype || proto === null;
482
+ }
483
+ function mergeValue(base, override, topLevel = false) {
484
+ if (override === void 0) return base;
485
+ if (topLevel && (Array.isArray(base) || Array.isArray(override))) {
486
+ const from = Array.isArray(base) ? base : base === void 0 ? [] : [base];
487
+ const to = Array.isArray(override) ? override : [override];
488
+ const length = Math.max(from.length, to.length);
489
+ return Array.from({ length }, (_, index) => mergeValue(from[index], to[index]));
490
+ }
491
+ if (isPlainObject(base) && isPlainObject(override)) {
492
+ const merged = { ...base };
493
+ for (const key of Object.keys(override)) {
494
+ merged[key] = mergeValue(base[key], override[key]);
495
+ }
496
+ return merged;
497
+ }
498
+ return override;
499
+ }
500
+ function mergeChartOption(generated, override) {
501
+ if (!override) return generated;
502
+ const base = generated;
503
+ const merged = { ...base };
504
+ for (const [key, value] of Object.entries(override)) {
505
+ merged[key] = mergeValue(base[key], value, true);
506
+ }
507
+ return merged;
508
+ }
509
+
510
+ // src/utils/options.ts
511
+ function toParams(raw, getLabel) {
512
+ const seriesIndex = raw.seriesIndex ?? 0;
513
+ const dataIndex = raw.dataIndex ?? 0;
514
+ const name = raw.name ?? "";
515
+ const value = raw.value;
516
+ return {
517
+ name,
518
+ value: Array.isArray(value) ? value : value,
519
+ seriesName: raw.seriesName ?? "",
520
+ seriesIndex,
521
+ dataIndex,
522
+ percent: raw.percent,
523
+ color: typeof raw.color === "string" ? raw.color : void 0,
524
+ label: getLabel?.(seriesIndex, dataIndex, name)
525
+ };
526
+ }
527
+ function toTooltipParams(raw, getLabel) {
528
+ return {
529
+ ...toParams(raw, getLabel),
530
+ marker: typeof raw.marker === "string" ? raw.marker : "",
531
+ axisValue: raw.axisValue
532
+ };
533
+ }
534
+ var EDGE_ALIGN = { start: "left", center: "center", end: "right" };
535
+ var SIDE_ALIGN = { start: "top", center: "middle", end: "bottom" };
536
+ function buildLegend(names, config, { show, custom = false, defaults = {} }) {
537
+ if (custom) return { show: false, data: names };
538
+ if (!show) return void 0;
539
+ const { position = "bottom", align = "center", orient, icon, formatter, scroll } = config ?? {};
540
+ const legend = { ...defaults, data: names };
541
+ if (position === "top" || position === "bottom") {
542
+ legend[position] = 0;
543
+ if (align !== "center") legend.left = EDGE_ALIGN[align];
544
+ } else {
545
+ legend[position] = 0;
546
+ legend.top = SIDE_ALIGN[align];
547
+ legend.orient = "vertical";
548
+ }
549
+ if (orient) legend.orient = orient;
550
+ if (icon) legend.icon = icon;
551
+ if (formatter) legend.formatter = formatter;
552
+ if (scroll) legend.type = "scroll";
553
+ return legend;
554
+ }
555
+ function gridInset(legend, showLegend, xAxis, yAxis) {
556
+ const inset = {};
557
+ if (showLegend) {
558
+ const position = legend?.position ?? "bottom";
559
+ inset[position] = position === "left" || position === "right" ? 140 : 36;
560
+ }
561
+ if (xAxis?.name !== void 0 && (xAxis.nameLocation ?? "middle") === "middle") {
562
+ inset.bottom = (inset.bottom ?? 12) + 24;
563
+ }
564
+ if (yAxis?.name !== void 0 && (yAxis.nameLocation ?? "middle") === "middle") {
565
+ inset.left = (inset.left ?? 12) + 24;
566
+ }
567
+ return Object.keys(inset).length ? inset : void 0;
568
+ }
569
+ function legendCenter(config, show, fallback) {
570
+ if (!show) return fallback;
571
+ switch (config?.position) {
572
+ case "left":
573
+ return ["60%", "50%"];
574
+ case "right":
575
+ return ["40%", "50%"];
576
+ case "top":
577
+ return ["50%", "55%"];
578
+ default:
579
+ return fallback;
580
+ }
581
+ }
582
+ function anchorCenterGraphic(group, center, defaultCenter) {
583
+ if (center[0] === defaultCenter[0] && center[1] === defaultCenter[1]) return group;
584
+ const shiftY = parseFloat(center[1]) - parseFloat(defaultCenter[1]);
585
+ const top = typeof group.top === "string" ? `${parseFloat(group.top) + shiftY}%` : group.top;
586
+ return {
587
+ ...group,
588
+ left: center[0],
589
+ top,
590
+ bounding: "raw",
591
+ children: (group.children ?? []).map(
592
+ ({ left: _left, top: childTop, ...child }) => ({ ...child, x: 0, y: childTop ?? 0 })
593
+ )
594
+ };
595
+ }
596
+ function getLegendItems(names, palette, overrides, visible) {
597
+ return names.map((name, index) => ({
598
+ name,
599
+ color: overrides[index] || palette[index % palette.length] || "",
600
+ visible: visible[name] !== false
601
+ }));
602
+ }
603
+ function buildAxis(base, config) {
604
+ if (!config) return base;
605
+ const axis = { ...base };
606
+ const { show, name, nameLocation, nameGap, formatter, rotate, min, max, interval } = config;
607
+ if (show !== void 0) axis.show = show;
608
+ if (name !== void 0) {
609
+ axis.name = name;
610
+ axis.nameLocation = nameLocation ?? "middle";
611
+ axis.nameGap = nameGap ?? (axis.nameLocation === "middle" ? 32 : 15);
612
+ }
613
+ if (formatter !== void 0 || rotate !== void 0) {
614
+ const axisLabel = { ...base.axisLabel };
615
+ if (formatter !== void 0) axisLabel.formatter = formatter;
616
+ if (rotate !== void 0) axisLabel.rotate = rotate;
617
+ axis.axisLabel = axisLabel;
618
+ }
619
+ if (min !== void 0) axis.min = min;
620
+ if (max !== void 0) axis.max = max;
621
+ if (interval !== void 0) axis.interval = interval;
622
+ return axis;
623
+ }
624
+ function buildDataLabel(base, hidden, { show, formatter, getLabel }) {
625
+ if (formatter === void 0) return show ? base : hidden;
626
+ const wrapped = typeof formatter === "string" ? formatter : (raw) => formatter(toParams(raw, getLabel));
627
+ if (!show) return { ...hidden, show: false, formatter: wrapped };
628
+ return { ...base, show: true, formatter: wrapped };
629
+ }
630
+ function pointLabel(text, formatter, base = {}) {
631
+ if (text == null || text === "") return void 0;
632
+ return typeof formatter === "function" ? { ...base, show: true } : { ...base, show: true, formatter: text };
633
+ }
634
+ function withPointLabels(values, labels, formatter, base) {
635
+ if (!labels?.some((text) => text != null && text !== "")) return values;
636
+ return values.map((value, index) => {
637
+ const label = pointLabel(labels[index], formatter, base);
638
+ return label ? { value, label } : value;
639
+ });
640
+ }
641
+ function seriesLabelLookup(series) {
642
+ return (seriesIndex, dataIndex) => series[seriesIndex]?.labels?.[dataIndex] ?? void 0;
643
+ }
644
+ function itemLabelLookup(items) {
645
+ return (_seriesIndex, dataIndex) => items[dataIndex]?.label;
646
+ }
647
+ function nameLabelLookup(nodes) {
648
+ const labels = /* @__PURE__ */ new Map();
649
+ const visit = (list) => {
650
+ for (const node of list) {
651
+ if (node.label != null && !labels.has(node.name)) labels.set(node.name, node.label);
652
+ if (node.children) visit(node.children);
653
+ }
654
+ };
655
+ visit(nodes);
656
+ return (_seriesIndex, _dataIndex, name) => labels.get(name);
657
+ }
658
+ function hasPointLabels(series) {
659
+ return series.some((s) => s.labels?.some((text) => text != null && text !== ""));
660
+ }
661
+ function buildTooltip(base, { show, formatter, valueFormatter, getLabel }) {
662
+ if (!show) return void 0;
663
+ if (formatter === void 0 && valueFormatter === void 0) return base;
664
+ const tooltip = { ...base };
665
+ if (valueFormatter) {
666
+ tooltip.valueFormatter = (value) => typeof value === "number" ? valueFormatter(value) : String(value ?? "");
667
+ }
668
+ if (typeof formatter === "string") {
669
+ tooltip.formatter = formatter;
670
+ } else if (formatter) {
671
+ tooltip.formatter = (raw) => formatter((Array.isArray(raw) ? raw : [raw]).map((p) => toTooltipParams(p, getLabel)));
672
+ }
673
+ return tooltip;
674
+ }
675
+ function formatTooltipValue(value, valueFormatter) {
676
+ if (valueFormatter && typeof value === "number") return valueFormatter(value);
677
+ return String(value ?? "");
678
+ }
679
+
409
680
  exports.THEME_DARK = THEME_DARK;
410
681
  exports.THEME_LIGHT = THEME_LIGHT;
411
682
  exports.abbreviateNumber = abbreviateNumber;
683
+ exports.anchorCenterGraphic = anchorCenterGraphic;
684
+ exports.buildAxis = buildAxis;
685
+ exports.buildDataLabel = buildDataLabel;
686
+ exports.buildLegend = buildLegend;
687
+ exports.buildTooltip = buildTooltip;
688
+ exports.colorWithAlpha = colorWithAlpha;
412
689
  exports.createTooltipFormatter = createTooltipFormatter;
690
+ exports.darkChartColors = darkChartColors;
413
691
  exports.darkTheme = darkTheme;
692
+ exports.escapeHtml = escapeHtml;
414
693
  exports.formatCurrency = formatCurrency;
415
694
  exports.formatDate = formatDate;
416
695
  exports.formatNumber = formatNumber;
417
696
  exports.formatPercent = formatPercent;
697
+ exports.formatTooltipValue = formatTooltipValue;
418
698
  exports.generateBarChartDescription = generateBarChartDescription;
419
699
  exports.generateDarkTheme = generateDarkTheme;
420
700
  exports.generateDataTable = generateDataTable;
@@ -422,4 +702,17 @@ exports.generateLightTheme = generateLightTheme;
422
702
  exports.generateLineChartDescription = generateLineChartDescription;
423
703
  exports.generatePieChartDescription = generatePieChartDescription;
424
704
  exports.generateScatterChartDescription = generateScatterChartDescription;
705
+ exports.getChartColors = getChartColors;
706
+ exports.getLegendItems = getLegendItems;
707
+ exports.getReplaceMerge = getReplaceMerge;
708
+ exports.gridInset = gridInset;
709
+ exports.hasPointLabels = hasPointLabels;
710
+ exports.itemLabelLookup = itemLabelLookup;
711
+ exports.legendCenter = legendCenter;
712
+ exports.lightChartColors = lightChartColors;
425
713
  exports.lightTheme = lightTheme;
714
+ exports.mergeChartOption = mergeChartOption;
715
+ exports.nameLabelLookup = nameLabelLookup;
716
+ exports.pointLabel = pointLabel;
717
+ exports.seriesLabelLookup = seriesLabelLookup;
718
+ exports.withPointLabels = withPointLabels;
package/dist/index.d.cts CHANGED
@@ -1,6 +1,7 @@
1
- export { EChartsTheme, THEME_DARK, THEME_LIGHT, darkTheme, generateDarkTheme, generateLightTheme, lightTheme } from './theme/index.cjs';
2
- import { BarChartData, LineChartData, PieSliceData, ScatterChartData } from './types/index.cjs';
3
- export { AreaChartData, AreaChartProps, AreaSeriesData, BarChartProps, BarSeriesData, BaseChartProps, BaseEventParams, CandlestickChartProps, CandlestickData, CandlestickEventParams, CandlestickItem, DataPointEventParams, DonutChartProps, FunnelChartProps, FunnelSliceData, GaugeChartProps, HeatmapChartProps, HeatmapData, HeatmapEventParams, LineChartProps, LineSeriesData, PieChartProps, RadarChartData, RadarChartProps, RadarIndicator, RadarSeriesData, SankeyChartProps, SankeyData, SankeyEventParams, SankeyLink, SankeyNode, ScatterChartProps, ScatterSeriesData, SliceEventParams, TreemapChartProps, TreemapEventParams, TreemapLevel, TreemapNode } from './types/index.cjs';
1
+ export { ChartSemanticColors, EChartsTheme, THEME_DARK, THEME_LIGHT, colorWithAlpha, darkChartColors, darkTheme, generateDarkTheme, generateLightTheme, getChartColors, lightChartColors, lightTheme } from './theme/index.cjs';
2
+ import { BarChartData, LineChartData, PieSliceData, ScatterChartData, ChartFormatter, LabelFormatterParams, TooltipFormatterParams, AxisConfig, ChartLegendConfig, LegendItem } from './types/index.cjs';
3
+ export { AreaChartData, AreaChartProps, AreaSeriesData, AxisChartProps, BarChartProps, BarSeriesData, BaseChartProps, BaseEventParams, CandlestickChartProps, CandlestickData, CandlestickEventParams, CandlestickItem, ChartLegendPosition, DataPointEventParams, DonutChartProps, FunnelChartProps, FunnelSliceData, GaugeChartProps, HeatmapChartProps, HeatmapData, HeatmapEventParams, LabelChartProps, LegendChartProps, LegendRenderProps, LineChartProps, LineSeriesData, PieChartProps, RadarChartData, RadarChartProps, RadarIndicator, RadarSeriesData, SankeyChartProps, SankeyData, SankeyEventParams, SankeyLink, SankeyNode, ScatterChartProps, ScatterSeriesData, SliceEventParams, TreemapChartProps, TreemapEventParams, TreemapLevel, TreemapNode } from './types/index.cjs';
4
+ import 'echarts';
4
5
 
5
6
  /**
6
7
  * Formatting utilities for chart values
@@ -25,6 +26,11 @@ declare function formatDate(date: Date | string, options?: Intl.DateTimeFormatOp
25
26
  * Abbreviate large numbers (e.g., 1000 -> 1K, 1000000 -> 1M)
26
27
  */
27
28
  declare function abbreviateNumber(value: number, decimals?: number): string;
29
+ /**
30
+ * Escape a value for interpolation into HTML. ECharts renders the string a
31
+ * function tooltip formatter returns as innerHTML without encoding it.
32
+ */
33
+ declare function escapeHtml(value: unknown): string;
28
34
  /**
29
35
  * Create a tooltip formatter for ECharts
30
36
  */
@@ -64,4 +70,165 @@ declare function generateScatterChartDescription(title: string, data: ScatterCha
64
70
  */
65
71
  declare function generateDataTable(headers: string[], rows: (string | number)[][]): string;
66
72
 
67
- export { BarChartData, LineChartData, PieSliceData, ScatterChartData, abbreviateNumber, createTooltipFormatter, formatCurrency, formatDate, formatNumber, formatPercent, generateBarChartDescription, generateDataTable, generateLineChartDescription, generatePieChartDescription, generateScatterChartDescription };
73
+ /**
74
+ * Helpers for applying a complete ECharts option on top of a previous one.
75
+ */
76
+ /**
77
+ * Work out the `replaceMerge` list for `chart.setOption(next, { replaceMerge })`
78
+ * when `next` is a complete option that supersedes `prev`.
79
+ *
80
+ * `notMerge: true` would also drop removed series and components, but it
81
+ * rebuilds every component, so legend selection, dataZoom ranges and other
82
+ * interaction state reset on each data update. Instead this replaces only:
83
+ *
84
+ * - `series` and `graphic`, so removed series, mark lines and centre labels
85
+ * disappear;
86
+ * - any component type that `next` has fewer of than `prev` (a hidden legend
87
+ * or tooltip, a dropped volume grid and its axes).
88
+ *
89
+ * Everything else is merged, so its state survives. ECharts rejects types it
90
+ * has no component class for, so only types for which `isComponentType`
91
+ * returns true are listed; pass `echarts.ComponentModel.hasClass`.
92
+ */
93
+ declare function getReplaceMerge(prev: object | null | undefined, next: object, isComponentType: (mainType: string) => boolean): string[];
94
+ /**
95
+ * Deep-merge a user-supplied ECharts option over a chart's generated option.
96
+ *
97
+ * Plain objects merge key by key. Top-level component arrays (`series`,
98
+ * `xAxis`, `grid`, ...) merge index by index, matching how ECharts merges
99
+ * options, so `{ series: [{ label: { show: true } }] }` patches only the
100
+ * first series. Nested arrays (`data`, `center`, ...) and any other value
101
+ * (functions, class instances such as gradients, primitives) replace the
102
+ * generated one; `undefined` leaves it untouched.
103
+ */
104
+ declare function mergeChartOption<T extends object>(generated: T, override: object | undefined): T;
105
+
106
+ /**
107
+ * Builders for the label, axis, tooltip and legend parts of a chart option.
108
+ *
109
+ * Chart components pass the block they generate by default; with no
110
+ * customisation each builder returns that block unchanged.
111
+ */
112
+
113
+ type AnyObject = Record<string, unknown>;
114
+ /** Looks up the per-point label text for a data point */
115
+ type PointLabelLookup = (seriesIndex: number, dataIndex: number, name: string) => string | undefined;
116
+ interface BuildLegendOptions {
117
+ /** Whether the built-in legend is shown (the `showLegend` prop) */
118
+ show: boolean;
119
+ /**
120
+ * A custom legend replaces the built-in one: it is kept with `show: false`
121
+ * so legend selection actions keep working.
122
+ */
123
+ custom?: boolean;
124
+ /** Extra keys for the default bottom legend, e.g. `{ orient: 'horizontal' }` */
125
+ defaults?: AnyObject;
126
+ }
127
+ /**
128
+ * Build the ECharts `legend` component for a list of item names
129
+ */
130
+ declare function buildLegend(names: string[], config: ChartLegendConfig | undefined, { show, custom, defaults }: BuildLegendOptions): AnyObject | undefined;
131
+ /**
132
+ * Grid edges to move so a built-in legend and centred axis titles do not
133
+ * overlap the plot or its tick labels (`containLabel` only reserves room for
134
+ * tick labels). Returns a partial `grid` to spread over the default one, or
135
+ * nothing when no room is needed.
136
+ */
137
+ declare function gridInset(legend: ChartLegendConfig | undefined, showLegend: boolean, xAxis?: AxisConfig, yAxis?: AxisConfig): AnyObject | undefined;
138
+ /**
139
+ * Centre for a pie, donut or radar that leaves room for a built-in legend
140
+ */
141
+ declare function legendCenter(config: ChartLegendConfig | undefined, show: boolean, fallback: [string, string]): [string, string];
142
+ /**
143
+ * Re-anchor a centre graphic group (a donut's centre label) on a pie centre
144
+ * moved by `legendCenter`. `defaultCenter` is where the group's `left: 'center'`
145
+ * / `top` placement assumes the pie sits; the group is returned unchanged
146
+ * when the centre has not moved. A raw bounding positions the group by its
147
+ * origin, so children centred on x = 0 sit on the new centre.
148
+ */
149
+ declare function anchorCenterGraphic(group: AnyObject, center: [string, string], defaultCenter: [string, string]): AnyObject;
150
+ /**
151
+ * Legend entries with the colour the chart draws each in: an explicit
152
+ * override, else the theme palette by index
153
+ */
154
+ declare function getLegendItems(names: string[], palette: string[], overrides: (string | undefined)[], visible: Record<string, boolean>): LegendItem[];
155
+ /**
156
+ * Apply an axis customisation to a generated axis
157
+ */
158
+ declare function buildAxis<T extends AnyObject>(base: T, config: AxisConfig | undefined): T;
159
+ interface BuildDataLabelOptions {
160
+ /** Whether data labels are shown */
161
+ show: boolean;
162
+ /** The `labelFormatter` prop */
163
+ formatter?: ChartFormatter<LabelFormatterParams>;
164
+ /** Per-point label lookup, exposed to formatter functions as `label` */
165
+ getLabel?: PointLabelLookup;
166
+ }
167
+ /**
168
+ * Build a series `label` block. `base` is the block the chart shows by
169
+ * default; `hidden` is what it uses when labels are off.
170
+ */
171
+ declare function buildDataLabel(base: AnyObject, hidden: AnyObject | undefined, { show, formatter, getLabel }: BuildDataLabelOptions): AnyObject | undefined;
172
+ /**
173
+ * A per-data-item label override, for points with their own label text.
174
+ * When a formatter function is set it receives the text instead, so the
175
+ * item only switches its label on. `base` carries placement (e.g.
176
+ * `{ position: 'top' }`) for when the series' own labels are off.
177
+ */
178
+ declare function pointLabel(text: string | null | undefined, formatter: ChartFormatter<LabelFormatterParams> | undefined, base?: AnyObject): AnyObject | undefined;
179
+ /**
180
+ * Turn a plain value array into data items, attaching a label override to
181
+ * each point that has label text. Returns the values unchanged when none do.
182
+ */
183
+ declare function withPointLabels<V>(values: V[], labels: (string | null | undefined)[] | undefined, formatter: ChartFormatter<LabelFormatterParams> | undefined, base?: AnyObject): (V | {
184
+ value: V;
185
+ label: AnyObject;
186
+ })[];
187
+ /**
188
+ * Lookup for series-based per-point labels
189
+ */
190
+ declare function seriesLabelLookup(series: {
191
+ labels?: (string | null | undefined)[];
192
+ }[]): PointLabelLookup;
193
+ /**
194
+ * Lookup for item-based per-point labels (pie slices, funnel stages)
195
+ */
196
+ declare function itemLabelLookup(items: {
197
+ label?: string;
198
+ }[]): PointLabelLookup;
199
+ /**
200
+ * Lookup for per-point labels keyed by name, for nodes whose data index does
201
+ * not follow the input order (treemap, sankey). Walks `children` recursively.
202
+ */
203
+ declare function nameLabelLookup(nodes: {
204
+ name: string;
205
+ label?: string;
206
+ children?: unknown[];
207
+ }[]): PointLabelLookup;
208
+ /**
209
+ * Whether any series has per-point label text
210
+ */
211
+ declare function hasPointLabels(series: {
212
+ labels?: (string | null | undefined)[];
213
+ }[]): boolean;
214
+ interface BuildTooltipOptions {
215
+ /** Whether the tooltip is shown */
216
+ show: boolean;
217
+ /** The `tooltipFormatter` prop */
218
+ formatter?: ChartFormatter<TooltipFormatterParams[]>;
219
+ /** The `tooltipValueFormatter` prop */
220
+ valueFormatter?: (value: number) => string;
221
+ /** Per-point label lookup, exposed to formatter functions as `label` */
222
+ getLabel?: PointLabelLookup;
223
+ }
224
+ /**
225
+ * Build the `tooltip` component. `base` is the chart's default tooltip.
226
+ */
227
+ declare function buildTooltip(base: AnyObject, { show, formatter, valueFormatter, getLabel }: BuildTooltipOptions): AnyObject | undefined;
228
+ /**
229
+ * Format a value with `tooltipValueFormatter`, for charts whose default
230
+ * tooltip is custom HTML (where ECharts' `valueFormatter` does not apply)
231
+ */
232
+ declare function formatTooltipValue(value: unknown, valueFormatter: ((value: number) => string) | undefined): string;
233
+
234
+ export { AxisConfig, BarChartData, type BuildDataLabelOptions, type BuildLegendOptions, type BuildTooltipOptions, ChartFormatter, ChartLegendConfig, LabelFormatterParams, LegendItem, LineChartData, PieSliceData, type PointLabelLookup, ScatterChartData, TooltipFormatterParams, abbreviateNumber, anchorCenterGraphic, buildAxis, buildDataLabel, buildLegend, buildTooltip, createTooltipFormatter, escapeHtml, formatCurrency, formatDate, formatNumber, formatPercent, formatTooltipValue, generateBarChartDescription, generateDataTable, generateLineChartDescription, generatePieChartDescription, generateScatterChartDescription, getLegendItems, getReplaceMerge, gridInset, hasPointLabels, itemLabelLookup, legendCenter, mergeChartOption, nameLabelLookup, pointLabel, seriesLabelLookup, withPointLabels };