@classic-homes/charts-react 0.1.38 → 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.
package/dist/index.cjs CHANGED
@@ -36,6 +36,7 @@ __export(index_exports, {
36
36
  ChartContainer: () => ChartContainer,
37
37
  ChartEmpty: () => ChartEmpty,
38
38
  ChartError: () => ChartError,
39
+ ChartLegend: () => ChartLegend,
39
40
  ChartSkeleton: () => ChartSkeleton,
40
41
  DonutChart: () => DonutChart,
41
42
  FunnelChart: () => FunnelChart,
@@ -50,6 +51,7 @@ __export(index_exports, {
50
51
  cn: () => cn,
51
52
  registerClassicThemes: () => registerClassicThemes,
52
53
  useChart: () => useChart,
54
+ useChartLegend: () => useChartLegend,
53
55
  useChartResize: () => useChartResize,
54
56
  useChartTheme: () => useChartTheme,
55
57
  useReducedMotion: () => useReducedMotion
@@ -211,6 +213,8 @@ var ChartContainer = React4.forwardRef(
211
213
  onRetry,
212
214
  className,
213
215
  wrapperClassName,
216
+ legend,
217
+ legendPosition = "bottom",
214
218
  children,
215
219
  ...props
216
220
  }, ref) => {
@@ -233,7 +237,7 @@ var ChartContainer = React4.forwardRef(
233
237
  if (empty) {
234
238
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChartEmpty, { message: emptyMessage, height, className: wrapperClassName });
235
239
  }
236
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
240
+ const chart = /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
237
241
  "div",
238
242
  {
239
243
  ref,
@@ -241,7 +245,7 @@ var ChartContainer = React4.forwardRef(
241
245
  "aria-label": title,
242
246
  "aria-describedby": description ? descriptionId : void 0,
243
247
  "data-testid": "chart-container",
244
- className: cn("rounded-lg border border-border bg-card", wrapperClassName),
248
+ className: legend ? "min-h-0 min-w-0 flex-1" : cn("rounded-lg border border-border bg-card", wrapperClassName),
245
249
  ...props,
246
250
  children: [
247
251
  description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { id: descriptionId, className: "sr-only", children: description }),
@@ -249,17 +253,88 @@ var ChartContainer = React4.forwardRef(
249
253
  ]
250
254
  }
251
255
  );
256
+ if (!legend) return chart;
257
+ const side = legendPosition === "left" || legendPosition === "right";
258
+ const leading = legendPosition === "top" || legendPosition === "left";
259
+ const legendNode = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
260
+ "div",
261
+ {
262
+ "data-testid": "chart-legend",
263
+ className: cn("flex shrink-0 p-2", side ? "items-center" : "justify-center"),
264
+ children: legend
265
+ }
266
+ );
267
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
268
+ "div",
269
+ {
270
+ className: cn(
271
+ "flex rounded-lg border border-border bg-card",
272
+ side ? "flex-row" : "flex-col",
273
+ wrapperClassName
274
+ ),
275
+ children: [
276
+ leading && legendNode,
277
+ chart,
278
+ !leading && legendNode
279
+ ]
280
+ }
281
+ );
252
282
  }
253
283
  );
254
284
  ChartContainer.displayName = "ChartContainer";
255
285
 
256
- // src/components/core/LineChart.tsx
286
+ // src/components/base/ChartLegend.tsx
257
287
  var React5 = __toESM(require("react"), 1);
288
+ var import_jsx_runtime5 = require("react/jsx-runtime");
289
+ var ChartLegend = React5.forwardRef(
290
+ ({ items, toggle, position = "bottom", chartTitle, orientation, className }, ref) => {
291
+ const vertical = (orientation ?? (position === "left" || position === "right" ? "vertical" : "horizontal")) === "vertical";
292
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
293
+ "ul",
294
+ {
295
+ ref,
296
+ "aria-label": chartTitle ? `${chartTitle} legend` : "Chart legend",
297
+ className: cn(
298
+ "flex gap-1 text-sm",
299
+ vertical ? "flex-col" : "flex-wrap justify-center",
300
+ className
301
+ ),
302
+ children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
303
+ "button",
304
+ {
305
+ type: "button",
306
+ "aria-pressed": item.visible,
307
+ onClick: () => toggle(item.name),
308
+ className: cn(
309
+ "inline-flex items-center gap-2 rounded-md px-2 py-1 text-left hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
310
+ item.visible ? "text-foreground" : "text-muted-foreground line-through"
311
+ ),
312
+ children: [
313
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
314
+ "span",
315
+ {
316
+ "aria-hidden": "true",
317
+ className: "h-3 w-3 shrink-0 rounded-sm",
318
+ style: { backgroundColor: item.color, opacity: item.visible ? 1 : 0.35 }
319
+ }
320
+ ),
321
+ item.name
322
+ ]
323
+ }
324
+ ) }, item.name))
325
+ }
326
+ );
327
+ }
328
+ );
329
+ ChartLegend.displayName = "ChartLegend";
330
+
331
+ // src/components/core/LineChart.tsx
332
+ var React6 = __toESM(require("react"), 1);
258
333
  var echarts3 = __toESM(require("echarts/core"), 1);
259
334
  var import_charts = require("echarts/charts");
260
335
  var import_components = require("echarts/components");
261
336
  var import_renderers = require("echarts/renderers");
262
- var import_charts_core3 = require("@classic-homes/charts-core");
337
+ var import_charts_core4 = require("@classic-homes/charts-core");
263
338
 
264
339
  // src/hooks/useChart.ts
265
340
  var import_react4 = require("react");
@@ -415,6 +490,10 @@ function useChart({
415
490
  };
416
491
  }
417
492
 
493
+ // src/hooks/useChartLegend.ts
494
+ var import_react6 = require("react");
495
+ var import_charts_core3 = require("@classic-homes/charts-core");
496
+
418
497
  // src/hooks/useLatestRef.ts
419
498
  var import_react5 = require("react");
420
499
  function useLatestRef(value) {
@@ -423,8 +502,62 @@ function useLatestRef(value) {
423
502
  return ref;
424
503
  }
425
504
 
505
+ // src/hooks/useChartLegend.ts
506
+ var LEGEND_EVENTS = [
507
+ "legendselectchanged",
508
+ "legendselected",
509
+ "legendunselected",
510
+ "legendselectall",
511
+ "legendinverseselect"
512
+ ];
513
+ function useChartLegend({
514
+ names,
515
+ colors,
516
+ palette,
517
+ config,
518
+ title,
519
+ onLegendChange
520
+ }) {
521
+ const chartRef = (0, import_react6.useRef)(null);
522
+ const [visible, setVisible] = (0, import_react6.useState)({});
523
+ const latest = useLatestRef({ onLegendChange });
524
+ const attach = (0, import_react6.useCallback)(
525
+ (chart) => {
526
+ chartRef.current = chart;
527
+ setVisible({});
528
+ const handler = (params) => {
529
+ const selected = { ...params.selected };
530
+ setVisible(selected);
531
+ latest.current.onLegendChange?.(selected);
532
+ };
533
+ for (const event of LEGEND_EVENTS) chart.on(event, handler);
534
+ },
535
+ [latest]
536
+ );
537
+ const dispatch = (action) => {
538
+ const chart = chartRef.current;
539
+ if (chart && !chart.isDisposed()) chart.dispatchAction(action);
540
+ };
541
+ return {
542
+ attach,
543
+ renderProps: {
544
+ items: (0, import_charts_core3.getLegendItems)(names, palette, colors, visible),
545
+ position: config?.position ?? "bottom",
546
+ chartTitle: title,
547
+ toggle: (name) => dispatch({ type: "legendToggleSelect", name }),
548
+ setVisible: (name, show) => dispatch({ type: show ? "legendSelect" : "legendUnSelect", name }),
549
+ showOnly: (name) => {
550
+ for (const other of names) {
551
+ dispatch({ type: other === name ? "legendSelect" : "legendUnSelect", name: other });
552
+ }
553
+ },
554
+ showAll: () => dispatch({ type: "legendAllSelect" })
555
+ }
556
+ };
557
+ }
558
+
426
559
  // src/components/core/LineChart.tsx
427
- var import_jsx_runtime5 = require("react/jsx-runtime");
560
+ var import_jsx_runtime6 = require("react/jsx-runtime");
428
561
  echarts3.use([
429
562
  import_charts.LineChart,
430
563
  import_components.GridComponent,
@@ -433,7 +566,7 @@ echarts3.use([
433
566
  import_components.TitleComponent,
434
567
  import_renderers.CanvasRenderer
435
568
  ]);
436
- var LineChart = React5.forwardRef(
569
+ var LineChart = React6.forwardRef(
437
570
  ({
438
571
  title,
439
572
  description,
@@ -451,6 +584,16 @@ var LineChart = React5.forwardRef(
451
584
  areaFilled = false,
452
585
  showDataPoints = false,
453
586
  stacked = false,
587
+ showLabels = false,
588
+ labelFormatter,
589
+ xAxisConfig,
590
+ yAxisConfig,
591
+ tooltipFormatter,
592
+ tooltipValueFormatter,
593
+ legend: legendConfig,
594
+ renderLegend,
595
+ onLegendChange,
596
+ option: optionOverride,
454
597
  onClick,
455
598
  onHover,
456
599
  class: className,
@@ -458,58 +601,93 @@ var LineChart = React5.forwardRef(
458
601
  }, ref) => {
459
602
  const isEmpty = !data?.categories?.length || !data?.series?.length || data.series.every((s) => !s.data?.length);
460
603
  const currentTheme = useChartTheme(theme);
461
- const chartColors = (0, import_charts_core3.getChartColors)(currentTheme);
462
- const option = React5.useMemo(() => {
604
+ const chartColors = (0, import_charts_core4.getChartColors)(currentTheme);
605
+ const legend = useChartLegend({
606
+ names: data?.series?.map((s) => s.name) ?? [],
607
+ colors: data?.series?.map((s) => s.color) ?? [],
608
+ palette: chartColors.series,
609
+ config: legendConfig,
610
+ title,
611
+ onLegendChange
612
+ });
613
+ const customLegend = renderLegend !== void 0;
614
+ const option = React6.useMemo(() => {
463
615
  if (isEmpty) return {};
464
- return {
465
- grid: {
466
- left: "3%",
467
- right: "4%",
468
- bottom: "3%",
469
- containLabel: true
470
- },
471
- tooltip: showTooltip ? {
472
- trigger: "axis",
473
- axisPointer: {
474
- type: "cross",
475
- label: {
476
- backgroundColor: chartColors.axisPointerLabel
616
+ const getLabel = (0, import_charts_core4.seriesLabelLookup)(data.series);
617
+ const showSymbol = showDataPoints || showLabels || (0, import_charts_core4.hasPointLabels)(data.series);
618
+ return (0, import_charts_core4.mergeChartOption)(
619
+ {
620
+ grid: {
621
+ left: "3%",
622
+ right: "4%",
623
+ bottom: "3%",
624
+ containLabel: true,
625
+ ...(0, import_charts_core4.gridInset)(legendConfig, showLegend && !customLegend, xAxisConfig, yAxisConfig)
626
+ },
627
+ tooltip: (0, import_charts_core4.buildTooltip)(
628
+ {
629
+ trigger: "axis",
630
+ axisPointer: {
631
+ type: "cross",
632
+ label: {
633
+ backgroundColor: chartColors.axisPointerLabel
634
+ }
635
+ }
636
+ },
637
+ {
638
+ show: showTooltip,
639
+ formatter: tooltipFormatter,
640
+ valueFormatter: tooltipValueFormatter,
641
+ getLabel
477
642
  }
478
- }
479
- } : void 0,
480
- legend: showLegend ? {
481
- data: data.series.map((s) => s.name),
482
- bottom: 0
483
- } : void 0,
484
- xAxis: {
485
- type: "category",
486
- boundaryGap: false,
487
- data: data.categories,
488
- splitLine: {
489
- show: showGrid
490
- }
491
- },
492
- yAxis: {
493
- type: "value",
494
- splitLine: {
495
- show: showGrid
496
- }
643
+ ),
644
+ legend: (0, import_charts_core4.buildLegend)(
645
+ data.series.map((s) => s.name),
646
+ legendConfig,
647
+ { show: showLegend, custom: customLegend }
648
+ ),
649
+ xAxis: (0, import_charts_core4.buildAxis)(
650
+ {
651
+ type: "category",
652
+ boundaryGap: false,
653
+ data: data.categories,
654
+ splitLine: {
655
+ show: showGrid
656
+ }
657
+ },
658
+ xAxisConfig
659
+ ),
660
+ yAxis: (0, import_charts_core4.buildAxis)(
661
+ {
662
+ type: "value",
663
+ splitLine: {
664
+ show: showGrid
665
+ }
666
+ },
667
+ yAxisConfig
668
+ ),
669
+ series: data.series.map((series) => ({
670
+ name: series.name,
671
+ type: "line",
672
+ data: (0, import_charts_core4.withPointLabels)(series.data, series.labels, labelFormatter, { position: "top" }),
673
+ smooth,
674
+ showSymbol,
675
+ symbolSize: showDataPoints ? 6 : 0,
676
+ stack: stacked ? "Total" : void 0,
677
+ areaStyle: areaFilled ? { opacity: 0.3 } : void 0,
678
+ itemStyle: series.color ? { color: series.color } : void 0,
679
+ label: (0, import_charts_core4.buildDataLabel)({ show: true, position: "top" }, void 0, {
680
+ show: showLabels,
681
+ formatter: labelFormatter,
682
+ getLabel
683
+ }),
684
+ emphasis: {
685
+ focus: "series"
686
+ }
687
+ }))
497
688
  },
498
- series: data.series.map((series) => ({
499
- name: series.name,
500
- type: "line",
501
- data: series.data,
502
- smooth,
503
- showSymbol: showDataPoints,
504
- symbolSize: showDataPoints ? 6 : 0,
505
- stack: stacked ? "Total" : void 0,
506
- areaStyle: areaFilled ? { opacity: 0.3 } : void 0,
507
- itemStyle: series.color ? { color: series.color } : void 0,
508
- emphasis: {
509
- focus: "series"
510
- }
511
- }))
512
- };
689
+ optionOverride
690
+ );
513
691
  }, [
514
692
  data,
515
693
  isEmpty,
@@ -520,14 +698,24 @@ var LineChart = React5.forwardRef(
520
698
  areaFilled,
521
699
  showDataPoints,
522
700
  stacked,
523
- chartColors
701
+ chartColors,
702
+ showLabels,
703
+ labelFormatter,
704
+ xAxisConfig,
705
+ yAxisConfig,
706
+ tooltipFormatter,
707
+ tooltipValueFormatter,
708
+ legendConfig,
709
+ customLegend,
710
+ optionOverride
524
711
  ]);
525
712
  const latest = useLatestRef({ onClick, onHover });
526
713
  const { containerRef } = useChart({
527
714
  option,
528
- theme: currentTheme === import_charts_core3.THEME_DARK ? "dark" : "light",
715
+ theme: currentTheme === import_charts_core4.THEME_DARK ? "dark" : "light",
529
716
  animation,
530
717
  onReady: (chart) => {
718
+ legend.attach(chart);
531
719
  chart.on("click", (params) => {
532
720
  const { onClick: onClick2 } = latest.current;
533
721
  if (!onClick2) return;
@@ -558,8 +746,8 @@ var LineChart = React5.forwardRef(
558
746
  });
559
747
  }
560
748
  });
561
- const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core3.generateLineChartDescription)(title, data) : void 0);
562
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
749
+ const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core4.generateLineChartDescription)(title, data) : void 0);
750
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
563
751
  ChartContainer,
564
752
  {
565
753
  ref,
@@ -571,8 +759,10 @@ var LineChart = React5.forwardRef(
571
759
  empty: isEmpty,
572
760
  emptyMessage,
573
761
  className: cn(className),
762
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
763
+ legendPosition: legendConfig?.position,
574
764
  ...props,
575
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: containerRef, className: "h-full w-full" })
765
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: containerRef, className: "h-full w-full" })
576
766
  }
577
767
  );
578
768
  }
@@ -580,13 +770,13 @@ var LineChart = React5.forwardRef(
580
770
  LineChart.displayName = "LineChart";
581
771
 
582
772
  // src/components/core/BarChart.tsx
583
- var React6 = __toESM(require("react"), 1);
773
+ var React7 = __toESM(require("react"), 1);
584
774
  var echarts4 = __toESM(require("echarts/core"), 1);
585
775
  var import_charts2 = require("echarts/charts");
586
776
  var import_components2 = require("echarts/components");
587
777
  var import_renderers2 = require("echarts/renderers");
588
- var import_charts_core4 = require("@classic-homes/charts-core");
589
- var import_jsx_runtime6 = require("react/jsx-runtime");
778
+ var import_charts_core5 = require("@classic-homes/charts-core");
779
+ var import_jsx_runtime7 = require("react/jsx-runtime");
590
780
  echarts4.use([
591
781
  import_charts2.BarChart,
592
782
  import_components2.GridComponent,
@@ -595,7 +785,7 @@ echarts4.use([
595
785
  import_components2.TitleComponent,
596
786
  import_renderers2.CanvasRenderer
597
787
  ]);
598
- var BarChart = React6.forwardRef(
788
+ var BarChart = React7.forwardRef(
599
789
  ({
600
790
  title,
601
791
  description,
@@ -612,13 +802,32 @@ var BarChart = React6.forwardRef(
612
802
  orientation = "vertical",
613
803
  stacked = false,
614
804
  showValues = false,
805
+ labelFormatter,
806
+ xAxisConfig,
807
+ yAxisConfig,
808
+ tooltipFormatter,
809
+ tooltipValueFormatter,
810
+ legend: legendConfig,
811
+ renderLegend,
812
+ onLegendChange,
813
+ option: optionOverride,
615
814
  onClick,
616
815
  class: className,
617
816
  ...props
618
817
  }, ref) => {
619
818
  const isEmpty = !data?.categories?.length || !data?.series?.length || data.series.every((s) => !s.data?.length);
620
819
  const isHorizontal = orientation === "horizontal";
621
- const option = React6.useMemo(() => {
820
+ const currentTheme = useChartTheme(theme);
821
+ const legend = useChartLegend({
822
+ names: data?.series?.map((s) => s.name) ?? [],
823
+ colors: data?.series?.map((s) => s.color) ?? [],
824
+ palette: (0, import_charts_core5.getChartColors)(currentTheme).series,
825
+ config: legendConfig,
826
+ title,
827
+ onLegendChange
828
+ });
829
+ const customLegend = renderLegend !== void 0;
830
+ const option = React7.useMemo(() => {
622
831
  if (isEmpty) return {};
623
832
  const categoryAxis = {
624
833
  type: "category",
@@ -633,47 +842,85 @@ var BarChart = React6.forwardRef(
633
842
  show: showGrid
634
843
  }
635
844
  };
636
- return {
637
- grid: {
638
- left: "3%",
639
- right: "4%",
640
- bottom: "3%",
641
- containLabel: true
845
+ const getLabel = (0, import_charts_core5.seriesLabelLookup)(data.series);
846
+ return (0, import_charts_core5.mergeChartOption)(
847
+ {
848
+ grid: {
849
+ left: "3%",
850
+ right: "4%",
851
+ bottom: "3%",
852
+ containLabel: true,
853
+ ...(0, import_charts_core5.gridInset)(legendConfig, showLegend && !customLegend, xAxisConfig, yAxisConfig)
854
+ },
855
+ tooltip: (0, import_charts_core5.buildTooltip)(
856
+ {
857
+ trigger: "axis",
858
+ axisPointer: {
859
+ type: "shadow"
860
+ }
861
+ },
862
+ {
863
+ show: showTooltip,
864
+ formatter: tooltipFormatter,
865
+ valueFormatter: tooltipValueFormatter,
866
+ getLabel
867
+ }
868
+ ),
869
+ legend: (0, import_charts_core5.buildLegend)(
870
+ data.series.map((s) => s.name),
871
+ legendConfig,
872
+ { show: showLegend, custom: customLegend }
873
+ ),
874
+ xAxis: (0, import_charts_core5.buildAxis)(isHorizontal ? valueAxis : categoryAxis, xAxisConfig),
875
+ yAxis: (0, import_charts_core5.buildAxis)(isHorizontal ? categoryAxis : valueAxis, yAxisConfig),
876
+ series: data.series.map((series) => ({
877
+ name: series.name,
878
+ type: "bar",
879
+ data: (0, import_charts_core5.withPointLabels)(series.data, series.labels, labelFormatter, {
880
+ position: isHorizontal ? "right" : "top"
881
+ }),
882
+ stack: stacked ? "Total" : void 0,
883
+ itemStyle: series.color ? { color: series.color } : void 0,
884
+ label: (0, import_charts_core5.buildDataLabel)(
885
+ {
886
+ show: true,
887
+ position: isHorizontal ? "right" : "top"
888
+ },
889
+ void 0,
890
+ { show: showValues, formatter: labelFormatter, getLabel }
891
+ ),
892
+ emphasis: {
893
+ focus: "series"
894
+ }
895
+ }))
642
896
  },
643
- tooltip: showTooltip ? {
644
- trigger: "axis",
645
- axisPointer: {
646
- type: "shadow"
647
- }
648
- } : void 0,
649
- legend: showLegend ? {
650
- data: data.series.map((s) => s.name),
651
- bottom: 0
652
- } : void 0,
653
- xAxis: isHorizontal ? valueAxis : categoryAxis,
654
- yAxis: isHorizontal ? categoryAxis : valueAxis,
655
- series: data.series.map((series) => ({
656
- name: series.name,
657
- type: "bar",
658
- data: series.data,
659
- stack: stacked ? "Total" : void 0,
660
- itemStyle: series.color ? { color: series.color } : void 0,
661
- label: showValues ? {
662
- show: true,
663
- position: isHorizontal ? "right" : "top"
664
- } : void 0,
665
- emphasis: {
666
- focus: "series"
667
- }
668
- }))
669
- };
670
- }, [data, isEmpty, showTooltip, showLegend, showGrid, isHorizontal, stacked, showValues]);
897
+ optionOverride
898
+ );
899
+ }, [
900
+ data,
901
+ isEmpty,
902
+ showTooltip,
903
+ showLegend,
904
+ showGrid,
905
+ isHorizontal,
906
+ stacked,
907
+ showValues,
908
+ labelFormatter,
909
+ xAxisConfig,
910
+ yAxisConfig,
911
+ tooltipFormatter,
912
+ tooltipValueFormatter,
913
+ legendConfig,
914
+ customLegend,
915
+ optionOverride
916
+ ]);
671
917
  const latest = useLatestRef({ onClick });
672
918
  const { containerRef } = useChart({
673
919
  option,
674
920
  theme,
675
921
  animation,
676
922
  onReady: (chart) => {
923
+ legend.attach(chart);
677
924
  chart.on("click", (params) => {
678
925
  const { onClick: onClick2 } = latest.current;
679
926
  if (!onClick2) return;
@@ -690,8 +937,8 @@ var BarChart = React6.forwardRef(
690
937
  });
691
938
  }
692
939
  });
693
- const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core4.generateBarChartDescription)(title, data) : void 0);
694
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
940
+ const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core5.generateBarChartDescription)(title, data) : void 0);
941
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
695
942
  ChartContainer,
696
943
  {
697
944
  ref,
@@ -703,8 +950,10 @@ var BarChart = React6.forwardRef(
703
950
  empty: isEmpty,
704
951
  emptyMessage,
705
952
  className: cn(className),
953
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
954
+ legendPosition: legendConfig?.position,
706
955
  ...props,
707
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: containerRef, className: "h-full w-full" })
956
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: containerRef, className: "h-full w-full" })
708
957
  }
709
958
  );
710
959
  }
@@ -712,15 +961,15 @@ var BarChart = React6.forwardRef(
712
961
  BarChart.displayName = "BarChart";
713
962
 
714
963
  // src/components/core/PieChart.tsx
715
- var React7 = __toESM(require("react"), 1);
964
+ var React8 = __toESM(require("react"), 1);
716
965
  var echarts5 = __toESM(require("echarts/core"), 1);
717
966
  var import_charts3 = require("echarts/charts");
718
967
  var import_components3 = require("echarts/components");
719
968
  var import_renderers3 = require("echarts/renderers");
720
- var import_charts_core5 = require("@classic-homes/charts-core");
721
- var import_jsx_runtime7 = require("react/jsx-runtime");
969
+ var import_charts_core6 = require("@classic-homes/charts-core");
970
+ var import_jsx_runtime8 = require("react/jsx-runtime");
722
971
  echarts5.use([import_charts3.PieChart, import_components3.TooltipComponent, import_components3.LegendComponent, import_components3.TitleComponent, import_renderers3.CanvasRenderer]);
723
- var PieChart = React7.forwardRef(
972
+ var PieChart = React8.forwardRef(
724
973
  ({
725
974
  title,
726
975
  description,
@@ -736,58 +985,107 @@ var PieChart = React7.forwardRef(
736
985
  showLabels = true,
737
986
  labelPosition = "outside",
738
987
  innerRadius = 0,
988
+ labelFormatter,
989
+ tooltipFormatter,
990
+ tooltipValueFormatter,
991
+ legend: legendConfig,
992
+ renderLegend,
993
+ onLegendChange,
994
+ option: optionOverride,
739
995
  onClick,
740
996
  class: className,
741
997
  ...props
742
998
  }, ref) => {
743
999
  const isEmpty = !data?.length;
744
- const option = React7.useMemo(() => {
1000
+ const currentTheme = useChartTheme(theme);
1001
+ const legend = useChartLegend({
1002
+ names: data?.map((d) => d.name) ?? [],
1003
+ colors: data?.map((d) => d.color) ?? [],
1004
+ palette: (0, import_charts_core6.getChartColors)(currentTheme).series,
1005
+ config: legendConfig,
1006
+ title,
1007
+ onLegendChange
1008
+ });
1009
+ const customLegend = renderLegend !== void 0;
1010
+ const option = React8.useMemo(() => {
745
1011
  if (isEmpty) return {};
746
- return {
747
- tooltip: showTooltip ? {
748
- trigger: "item",
749
- formatter: "{a} <br/>{b}: {c} ({d}%)"
750
- } : void 0,
751
- legend: showLegend ? {
752
- orient: "horizontal",
753
- bottom: 0,
754
- data: data.map((d) => d.name)
755
- } : void 0,
756
- series: [
757
- {
758
- name: title,
759
- type: "pie",
760
- radius: innerRadius ? [innerRadius, "70%"] : "70%",
761
- center: ["50%", "45%"],
762
- data: data.map((item) => ({
763
- name: item.name,
764
- value: item.value,
765
- itemStyle: item.color ? { color: item.color } : void 0
766
- })),
767
- label: showLabels ? {
768
- show: true,
769
- position: labelPosition,
770
- formatter: labelPosition === "inside" ? "{d}%" : "{b}: {d}%"
771
- } : {
772
- show: false
773
- },
774
- emphasis: {
775
- itemStyle: {
776
- shadowBlur: 10,
777
- shadowOffsetX: 0,
778
- shadowColor: "rgba(0, 0, 0, 0.5)"
1012
+ const valueFormatter = tooltipFormatter === void 0 ? tooltipValueFormatter : void 0;
1013
+ const tooltipBase = {
1014
+ trigger: "item",
1015
+ formatter: valueFormatter ? (params) => {
1016
+ const p = params;
1017
+ return `${(0, import_charts_core6.escapeHtml)(p.seriesName)} <br/>${(0, import_charts_core6.escapeHtml)(p.name)}: ${(0, import_charts_core6.escapeHtml)((0, import_charts_core6.formatTooltipValue)(p.value, valueFormatter))} (${p.percent}%)`;
1018
+ } : "{a} <br/>{b}: {c} ({d}%)"
1019
+ };
1020
+ return (0, import_charts_core6.mergeChartOption)(
1021
+ {
1022
+ tooltip: (0, import_charts_core6.buildTooltip)(tooltipBase, {
1023
+ show: showTooltip,
1024
+ formatter: tooltipFormatter,
1025
+ valueFormatter: tooltipValueFormatter,
1026
+ getLabel: (0, import_charts_core6.itemLabelLookup)(data)
1027
+ }),
1028
+ legend: (0, import_charts_core6.buildLegend)(
1029
+ data.map((d) => d.name),
1030
+ legendConfig,
1031
+ { show: showLegend, custom: customLegend, defaults: { orient: "horizontal" } }
1032
+ ),
1033
+ series: [
1034
+ {
1035
+ name: title,
1036
+ type: "pie",
1037
+ radius: innerRadius ? [innerRadius, "70%"] : "70%",
1038
+ center: (0, import_charts_core6.legendCenter)(legendConfig, showLegend && !customLegend, ["50%", "45%"]),
1039
+ data: data.map((item) => ({
1040
+ name: item.name,
1041
+ value: item.value,
1042
+ itemStyle: item.color ? { color: item.color } : void 0,
1043
+ label: (0, import_charts_core6.pointLabel)(item.label, labelFormatter, { position: labelPosition })
1044
+ })),
1045
+ label: (0, import_charts_core6.buildDataLabel)(
1046
+ {
1047
+ show: true,
1048
+ position: labelPosition,
1049
+ formatter: labelPosition === "inside" ? "{d}%" : "{b}: {d}%"
1050
+ },
1051
+ { show: false },
1052
+ { show: showLabels, formatter: labelFormatter, getLabel: (0, import_charts_core6.itemLabelLookup)(data) }
1053
+ ),
1054
+ emphasis: {
1055
+ itemStyle: {
1056
+ shadowBlur: 10,
1057
+ shadowOffsetX: 0,
1058
+ shadowColor: "rgba(0, 0, 0, 0.5)"
1059
+ }
779
1060
  }
780
1061
  }
781
- }
782
- ]
783
- };
784
- }, [data, isEmpty, showTooltip, showLegend, showLabels, labelPosition, innerRadius, title]);
1062
+ ]
1063
+ },
1064
+ optionOverride
1065
+ );
1066
+ }, [
1067
+ data,
1068
+ isEmpty,
1069
+ showTooltip,
1070
+ showLegend,
1071
+ showLabels,
1072
+ labelPosition,
1073
+ innerRadius,
1074
+ title,
1075
+ labelFormatter,
1076
+ tooltipFormatter,
1077
+ tooltipValueFormatter,
1078
+ legendConfig,
1079
+ customLegend,
1080
+ optionOverride
1081
+ ]);
785
1082
  const latest = useLatestRef({ onClick });
786
1083
  const { containerRef } = useChart({
787
1084
  option,
788
1085
  theme,
789
1086
  animation,
790
1087
  onReady: (chart) => {
1088
+ legend.attach(chart);
791
1089
  chart.on("click", (params) => {
792
1090
  const { onClick: onClick2 } = latest.current;
793
1091
  if (!onClick2) return;
@@ -802,8 +1100,8 @@ var PieChart = React7.forwardRef(
802
1100
  });
803
1101
  }
804
1102
  });
805
- const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core5.generatePieChartDescription)(title, data, false) : void 0);
806
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1103
+ const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core6.generatePieChartDescription)(title, data, false) : void 0);
1104
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
807
1105
  ChartContainer,
808
1106
  {
809
1107
  ref,
@@ -815,8 +1113,10 @@ var PieChart = React7.forwardRef(
815
1113
  empty: isEmpty,
816
1114
  emptyMessage,
817
1115
  className: cn(className),
1116
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
1117
+ legendPosition: legendConfig?.position,
818
1118
  ...props,
819
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1119
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { ref: containerRef, className: "h-full w-full" })
820
1120
  }
821
1121
  );
822
1122
  }
@@ -824,13 +1124,13 @@ var PieChart = React7.forwardRef(
824
1124
  PieChart.displayName = "PieChart";
825
1125
 
826
1126
  // src/components/core/AreaChart.tsx
827
- var React8 = __toESM(require("react"), 1);
1127
+ var React9 = __toESM(require("react"), 1);
828
1128
  var echarts6 = __toESM(require("echarts/core"), 1);
829
1129
  var import_charts4 = require("echarts/charts");
830
1130
  var import_components4 = require("echarts/components");
831
1131
  var import_renderers4 = require("echarts/renderers");
832
- var import_charts_core6 = require("@classic-homes/charts-core");
833
- var import_jsx_runtime8 = require("react/jsx-runtime");
1132
+ var import_charts_core7 = require("@classic-homes/charts-core");
1133
+ var import_jsx_runtime9 = require("react/jsx-runtime");
834
1134
  echarts6.use([
835
1135
  import_charts4.LineChart,
836
1136
  import_components4.GridComponent,
@@ -839,7 +1139,7 @@ echarts6.use([
839
1139
  import_components4.TitleComponent,
840
1140
  import_renderers4.CanvasRenderer
841
1141
  ]);
842
- var AreaChart = React8.forwardRef(
1142
+ var AreaChart = React9.forwardRef(
843
1143
  ({
844
1144
  title,
845
1145
  description,
@@ -856,76 +1156,143 @@ var AreaChart = React8.forwardRef(
856
1156
  smooth = false,
857
1157
  stacked = false,
858
1158
  gradient = true,
1159
+ showLabels = false,
1160
+ labelFormatter,
1161
+ xAxisConfig,
1162
+ yAxisConfig,
1163
+ tooltipFormatter,
1164
+ tooltipValueFormatter,
1165
+ legend: legendConfig,
1166
+ renderLegend,
1167
+ onLegendChange,
1168
+ option: optionOverride,
859
1169
  onClick,
860
1170
  class: className,
861
1171
  ...props
862
1172
  }, ref) => {
863
1173
  const isEmpty = !data?.categories?.length || !data?.series?.length || data.series.every((s) => !s.data?.length);
864
1174
  const currentTheme = useChartTheme(theme);
865
- const chartColors = (0, import_charts_core6.getChartColors)(currentTheme);
866
- const option = React8.useMemo(() => {
1175
+ const chartColors = (0, import_charts_core7.getChartColors)(currentTheme);
1176
+ const legend = useChartLegend({
1177
+ names: data?.series?.map((s) => s.name) ?? [],
1178
+ colors: data?.series?.map((s) => s.color) ?? [],
1179
+ palette: chartColors.series,
1180
+ config: legendConfig,
1181
+ title,
1182
+ onLegendChange
1183
+ });
1184
+ const customLegend = renderLegend !== void 0;
1185
+ const option = React9.useMemo(() => {
867
1186
  if (isEmpty) return {};
868
- return {
869
- grid: {
870
- left: "3%",
871
- right: "4%",
872
- bottom: "3%",
873
- containLabel: true
874
- },
875
- tooltip: showTooltip ? {
876
- trigger: "axis",
877
- axisPointer: {
878
- type: "cross",
879
- label: {
880
- backgroundColor: chartColors.axisPointerLabel
1187
+ const getLabel = (0, import_charts_core7.seriesLabelLookup)(data.series);
1188
+ const showSymbol = showLabels || (0, import_charts_core7.hasPointLabels)(data.series);
1189
+ return (0, import_charts_core7.mergeChartOption)(
1190
+ {
1191
+ grid: {
1192
+ left: "3%",
1193
+ right: "4%",
1194
+ bottom: "3%",
1195
+ containLabel: true,
1196
+ ...(0, import_charts_core7.gridInset)(legendConfig, showLegend && !customLegend, xAxisConfig, yAxisConfig)
1197
+ },
1198
+ tooltip: (0, import_charts_core7.buildTooltip)(
1199
+ {
1200
+ trigger: "axis",
1201
+ axisPointer: {
1202
+ type: "cross",
1203
+ label: {
1204
+ backgroundColor: chartColors.axisPointerLabel
1205
+ }
1206
+ }
1207
+ },
1208
+ {
1209
+ show: showTooltip,
1210
+ formatter: tooltipFormatter,
1211
+ valueFormatter: tooltipValueFormatter,
1212
+ getLabel
881
1213
  }
882
- }
883
- } : void 0,
884
- legend: showLegend ? {
885
- data: data.series.map((s) => s.name),
886
- bottom: 0
887
- } : void 0,
888
- xAxis: {
889
- type: "category",
890
- boundaryGap: false,
891
- data: data.categories
892
- },
893
- yAxis: {
894
- type: "value",
895
- splitLine: {
896
- show: showGrid
897
- }
898
- },
899
- series: data.series.map((series, index) => {
900
- const base = series.color || chartColors.series[index % chartColors.series.length];
901
- return {
902
- name: series.name,
903
- type: "line",
904
- data: series.data,
905
- smooth,
906
- stack: stacked ? "Total" : void 0,
907
- areaStyle: gradient ? {
908
- opacity: 0.8,
909
- color: new echarts6.graphic.LinearGradient(0, 0, 0, 1, [
910
- { offset: 0, color: (0, import_charts_core6.colorWithAlpha)(base, 0.8) },
911
- { offset: 1, color: (0, import_charts_core6.colorWithAlpha)(base, 0.1) }
912
- ])
913
- } : { opacity: 0.5 },
914
- itemStyle: series.color ? { color: series.color } : void 0,
915
- emphasis: {
916
- focus: "series"
1214
+ ),
1215
+ legend: (0, import_charts_core7.buildLegend)(
1216
+ data.series.map((s) => s.name),
1217
+ legendConfig,
1218
+ { show: showLegend, custom: customLegend }
1219
+ ),
1220
+ xAxis: (0, import_charts_core7.buildAxis)(
1221
+ {
1222
+ type: "category",
1223
+ boundaryGap: false,
1224
+ data: data.categories
917
1225
  },
918
- showSymbol: false
919
- };
920
- })
921
- };
922
- }, [data, isEmpty, showTooltip, showLegend, showGrid, smooth, stacked, gradient, chartColors]);
1226
+ xAxisConfig
1227
+ ),
1228
+ yAxis: (0, import_charts_core7.buildAxis)(
1229
+ {
1230
+ type: "value",
1231
+ splitLine: {
1232
+ show: showGrid
1233
+ }
1234
+ },
1235
+ yAxisConfig
1236
+ ),
1237
+ series: data.series.map((series, index) => {
1238
+ const base = series.color || chartColors.series[index % chartColors.series.length];
1239
+ return {
1240
+ name: series.name,
1241
+ type: "line",
1242
+ data: (0, import_charts_core7.withPointLabels)(series.data, series.labels, labelFormatter, {
1243
+ position: "top"
1244
+ }),
1245
+ smooth,
1246
+ stack: stacked ? "Total" : void 0,
1247
+ areaStyle: gradient ? {
1248
+ opacity: 0.8,
1249
+ color: new echarts6.graphic.LinearGradient(0, 0, 0, 1, [
1250
+ { offset: 0, color: (0, import_charts_core7.colorWithAlpha)(base, 0.8) },
1251
+ { offset: 1, color: (0, import_charts_core7.colorWithAlpha)(base, 0.1) }
1252
+ ])
1253
+ } : { opacity: 0.5 },
1254
+ itemStyle: series.color ? { color: series.color } : void 0,
1255
+ label: (0, import_charts_core7.buildDataLabel)({ show: true, position: "top" }, void 0, {
1256
+ show: showLabels,
1257
+ formatter: labelFormatter,
1258
+ getLabel
1259
+ }),
1260
+ emphasis: {
1261
+ focus: "series"
1262
+ },
1263
+ showSymbol
1264
+ };
1265
+ })
1266
+ },
1267
+ optionOverride
1268
+ );
1269
+ }, [
1270
+ data,
1271
+ isEmpty,
1272
+ showTooltip,
1273
+ showLegend,
1274
+ showGrid,
1275
+ smooth,
1276
+ stacked,
1277
+ gradient,
1278
+ chartColors,
1279
+ showLabels,
1280
+ labelFormatter,
1281
+ xAxisConfig,
1282
+ yAxisConfig,
1283
+ tooltipFormatter,
1284
+ tooltipValueFormatter,
1285
+ legendConfig,
1286
+ customLegend,
1287
+ optionOverride
1288
+ ]);
923
1289
  const latest = useLatestRef({ onClick });
924
1290
  const { containerRef } = useChart({
925
1291
  option,
926
- theme: currentTheme === import_charts_core6.THEME_DARK ? "dark" : "light",
1292
+ theme: currentTheme === import_charts_core7.THEME_DARK ? "dark" : "light",
927
1293
  animation,
928
1294
  onReady: (chart) => {
1295
+ legend.attach(chart);
929
1296
  chart.on("click", (params) => {
930
1297
  const { onClick: onClick2 } = latest.current;
931
1298
  if (!onClick2) return;
@@ -943,7 +1310,7 @@ var AreaChart = React8.forwardRef(
943
1310
  }
944
1311
  });
945
1312
  const accessibilityDescription = description || (!isEmpty ? `${title}. Area chart with ${data.categories.length} data points across ${data.series.length} series.` : void 0);
946
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1313
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
947
1314
  ChartContainer,
948
1315
  {
949
1316
  ref,
@@ -955,8 +1322,10 @@ var AreaChart = React8.forwardRef(
955
1322
  empty: isEmpty,
956
1323
  emptyMessage,
957
1324
  className: cn(className),
1325
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
1326
+ legendPosition: legendConfig?.position,
958
1327
  ...props,
959
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1328
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { ref: containerRef, className: "h-full w-full" })
960
1329
  }
961
1330
  );
962
1331
  }
@@ -964,13 +1333,13 @@ var AreaChart = React8.forwardRef(
964
1333
  AreaChart.displayName = "AreaChart";
965
1334
 
966
1335
  // src/components/core/ScatterChart.tsx
967
- var React9 = __toESM(require("react"), 1);
1336
+ var React10 = __toESM(require("react"), 1);
968
1337
  var echarts7 = __toESM(require("echarts/core"), 1);
969
1338
  var import_charts5 = require("echarts/charts");
970
1339
  var import_components5 = require("echarts/components");
971
1340
  var import_renderers5 = require("echarts/renderers");
972
- var import_charts_core7 = require("@classic-homes/charts-core");
973
- var import_jsx_runtime9 = require("react/jsx-runtime");
1341
+ var import_charts_core8 = require("@classic-homes/charts-core");
1342
+ var import_jsx_runtime10 = require("react/jsx-runtime");
974
1343
  echarts7.use([
975
1344
  import_charts5.ScatterChart,
976
1345
  // Trend lines are line series
@@ -981,7 +1350,7 @@ echarts7.use([
981
1350
  import_components5.TitleComponent,
982
1351
  import_renderers5.CanvasRenderer
983
1352
  ]);
984
- var ScatterChart = React9.forwardRef(
1353
+ var ScatterChart = React10.forwardRef(
985
1354
  ({
986
1355
  title,
987
1356
  description,
@@ -997,19 +1366,45 @@ var ScatterChart = React9.forwardRef(
997
1366
  showGrid = true,
998
1367
  showTrendLine = false,
999
1368
  symbolSize = 10,
1369
+ showLabels = false,
1370
+ labelFormatter,
1371
+ xAxisConfig,
1372
+ yAxisConfig,
1373
+ tooltipFormatter,
1374
+ tooltipValueFormatter,
1375
+ legend: legendConfig,
1376
+ renderLegend,
1377
+ onLegendChange,
1378
+ option: optionOverride,
1000
1379
  onClick,
1001
1380
  class: className,
1002
1381
  ...props
1003
1382
  }, ref) => {
1004
1383
  const isEmpty = !data?.series?.length || data.series.every((s) => !s.data?.length);
1005
- const option = React9.useMemo(() => {
1384
+ const currentTheme = useChartTheme(theme);
1385
+ const legend = useChartLegend({
1386
+ names: data?.series?.map((s) => s.name) ?? [],
1387
+ colors: data?.series?.map((s) => s.color) ?? [],
1388
+ palette: (0, import_charts_core8.getChartColors)(currentTheme).series,
1389
+ config: legendConfig,
1390
+ title,
1391
+ onLegendChange
1392
+ });
1393
+ const customLegend = renderLegend !== void 0;
1394
+ const option = React10.useMemo(() => {
1006
1395
  if (isEmpty) return {};
1396
+ const getLabel = (0, import_charts_core8.seriesLabelLookup)(data.series);
1007
1397
  const series = data.series.map((s) => ({
1008
1398
  name: s.name,
1009
1399
  type: "scatter",
1010
- data: s.data,
1400
+ data: (0, import_charts_core8.withPointLabels)(s.data, s.labels, labelFormatter, { position: "top" }),
1011
1401
  symbolSize: typeof symbolSize === "function" ? symbolSize : symbolSize,
1012
1402
  itemStyle: s.color ? { color: s.color } : void 0,
1403
+ label: (0, import_charts_core8.buildDataLabel)({ show: true, position: "top" }, void 0, {
1404
+ show: showLabels,
1405
+ formatter: labelFormatter,
1406
+ getLabel
1407
+ }),
1013
1408
  emphasis: {
1014
1409
  focus: "series",
1015
1410
  itemStyle: {
@@ -1057,46 +1452,85 @@ var ScatterChart = React9.forwardRef(
1057
1452
  });
1058
1453
  });
1059
1454
  }
1060
- return {
1061
- grid: {
1062
- left: "3%",
1063
- right: "4%",
1064
- bottom: "3%",
1065
- containLabel: true
1066
- },
1067
- tooltip: showTooltip ? {
1068
- trigger: "item",
1069
- formatter: (params) => {
1070
- const p = params;
1071
- if (!p.value) return "";
1072
- return `${(0, import_charts_core7.escapeHtml)(p.seriesName)}<br/>X: ${(0, import_charts_core7.escapeHtml)(p.value[0])}<br/>Y: ${(0, import_charts_core7.escapeHtml)(p.value[1])}`;
1073
- }
1074
- } : void 0,
1075
- legend: showLegend ? {
1076
- data: data.series.map((s) => s.name),
1077
- bottom: 0
1078
- } : void 0,
1079
- xAxis: {
1080
- type: "value",
1081
- splitLine: {
1082
- show: showGrid
1083
- }
1084
- },
1085
- yAxis: {
1086
- type: "value",
1087
- splitLine: {
1088
- show: showGrid
1089
- }
1455
+ return (0, import_charts_core8.mergeChartOption)(
1456
+ {
1457
+ grid: {
1458
+ left: "3%",
1459
+ right: "4%",
1460
+ bottom: "3%",
1461
+ containLabel: true,
1462
+ ...(0, import_charts_core8.gridInset)(legendConfig, showLegend && !customLegend, xAxisConfig, yAxisConfig)
1463
+ },
1464
+ tooltip: (0, import_charts_core8.buildTooltip)(
1465
+ {
1466
+ trigger: "item",
1467
+ formatter: (params) => {
1468
+ const p = params;
1469
+ if (!p.value) return "";
1470
+ const x = (0, import_charts_core8.formatTooltipValue)(p.value[0], tooltipValueFormatter);
1471
+ const y = (0, import_charts_core8.formatTooltipValue)(p.value[1], tooltipValueFormatter);
1472
+ return `${(0, import_charts_core8.escapeHtml)(p.seriesName)}<br/>X: ${(0, import_charts_core8.escapeHtml)(x)}<br/>Y: ${(0, import_charts_core8.escapeHtml)(y)}`;
1473
+ }
1474
+ },
1475
+ {
1476
+ show: showTooltip,
1477
+ formatter: tooltipFormatter,
1478
+ valueFormatter: tooltipValueFormatter,
1479
+ getLabel
1480
+ }
1481
+ ),
1482
+ legend: (0, import_charts_core8.buildLegend)(
1483
+ data.series.map((s) => s.name),
1484
+ legendConfig,
1485
+ { show: showLegend, custom: customLegend }
1486
+ ),
1487
+ xAxis: (0, import_charts_core8.buildAxis)(
1488
+ {
1489
+ type: "value",
1490
+ splitLine: {
1491
+ show: showGrid
1492
+ }
1493
+ },
1494
+ xAxisConfig
1495
+ ),
1496
+ yAxis: (0, import_charts_core8.buildAxis)(
1497
+ {
1498
+ type: "value",
1499
+ splitLine: {
1500
+ show: showGrid
1501
+ }
1502
+ },
1503
+ yAxisConfig
1504
+ ),
1505
+ series
1090
1506
  },
1091
- series
1092
- };
1093
- }, [data, isEmpty, showTooltip, showLegend, showGrid, showTrendLine, symbolSize]);
1507
+ optionOverride
1508
+ );
1509
+ }, [
1510
+ data,
1511
+ isEmpty,
1512
+ showTooltip,
1513
+ showLegend,
1514
+ showGrid,
1515
+ showTrendLine,
1516
+ symbolSize,
1517
+ showLabels,
1518
+ labelFormatter,
1519
+ xAxisConfig,
1520
+ yAxisConfig,
1521
+ tooltipFormatter,
1522
+ tooltipValueFormatter,
1523
+ legendConfig,
1524
+ customLegend,
1525
+ optionOverride
1526
+ ]);
1094
1527
  const latest = useLatestRef({ onClick });
1095
1528
  const { containerRef } = useChart({
1096
1529
  option,
1097
1530
  theme,
1098
1531
  animation,
1099
1532
  onReady: (chart) => {
1533
+ legend.attach(chart);
1100
1534
  chart.on("click", (params) => {
1101
1535
  const { onClick: onClick2 } = latest.current;
1102
1536
  if (!onClick2) return;
@@ -1113,8 +1547,8 @@ var ScatterChart = React9.forwardRef(
1113
1547
  });
1114
1548
  }
1115
1549
  });
1116
- const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core7.generateScatterChartDescription)(title, data) : void 0);
1117
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1550
+ const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core8.generateScatterChartDescription)(title, data) : void 0);
1551
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1118
1552
  ChartContainer,
1119
1553
  {
1120
1554
  ref,
@@ -1126,8 +1560,10 @@ var ScatterChart = React9.forwardRef(
1126
1560
  empty: isEmpty,
1127
1561
  emptyMessage,
1128
1562
  className: cn(className),
1563
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
1564
+ legendPosition: legendConfig?.position,
1129
1565
  ...props,
1130
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1566
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1131
1567
  }
1132
1568
  );
1133
1569
  }
@@ -1135,15 +1571,15 @@ var ScatterChart = React9.forwardRef(
1135
1571
  ScatterChart.displayName = "ScatterChart";
1136
1572
 
1137
1573
  // src/components/core/DonutChart.tsx
1138
- var React10 = __toESM(require("react"), 1);
1574
+ var React11 = __toESM(require("react"), 1);
1139
1575
  var echarts8 = __toESM(require("echarts/core"), 1);
1140
1576
  var import_charts6 = require("echarts/charts");
1141
1577
  var import_components6 = require("echarts/components");
1142
1578
  var import_renderers6 = require("echarts/renderers");
1143
- var import_charts_core8 = require("@classic-homes/charts-core");
1144
- var import_jsx_runtime10 = require("react/jsx-runtime");
1579
+ var import_charts_core9 = require("@classic-homes/charts-core");
1580
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1145
1581
  echarts8.use([import_charts6.PieChart, import_components6.TooltipComponent, import_components6.LegendComponent, import_components6.TitleComponent, import_renderers6.CanvasRenderer]);
1146
- var DonutChart = React10.forwardRef(
1582
+ var DonutChart = React11.forwardRef(
1147
1583
  ({
1148
1584
  title,
1149
1585
  description,
@@ -1161,95 +1597,134 @@ var DonutChart = React10.forwardRef(
1161
1597
  innerRadius = "50%",
1162
1598
  centerLabel,
1163
1599
  centerValue,
1600
+ labelFormatter,
1601
+ tooltipFormatter,
1602
+ tooltipValueFormatter,
1603
+ legend: legendConfig,
1604
+ renderLegend,
1605
+ onLegendChange,
1606
+ option: optionOverride,
1164
1607
  onClick,
1165
1608
  class: className,
1166
1609
  ...props
1167
1610
  }, ref) => {
1168
1611
  const isEmpty = !data?.length;
1169
1612
  const currentTheme = useChartTheme(theme);
1170
- const chartColors = (0, import_charts_core8.getChartColors)(currentTheme);
1171
- const option = React10.useMemo(() => {
1613
+ const chartColors = (0, import_charts_core9.getChartColors)(currentTheme);
1614
+ const legend = useChartLegend({
1615
+ names: data?.map((d) => d.name) ?? [],
1616
+ colors: data?.map((d) => d.color) ?? [],
1617
+ palette: chartColors.series,
1618
+ config: legendConfig,
1619
+ title,
1620
+ onLegendChange
1621
+ });
1622
+ const customLegend = renderLegend !== void 0;
1623
+ const option = React11.useMemo(() => {
1172
1624
  if (isEmpty) return {};
1625
+ const pieCenter = (0, import_charts_core9.legendCenter)(legendConfig, showLegend && !customLegend, ["50%", "45%"]);
1173
1626
  const graphicElements = [];
1174
1627
  if (centerLabel || centerValue) {
1175
- graphicElements.push({
1176
- type: "group",
1177
- left: "center",
1178
- top: "40%",
1179
- children: [
1180
- ...centerValue !== void 0 ? [
1181
- {
1182
- type: "text",
1183
- style: {
1184
- text: String(centerValue),
1185
- fontSize: 28,
1186
- fontWeight: "bold",
1187
- fill: chartColors.foreground,
1188
- textAlign: "center"
1189
- },
1190
- top: 0,
1191
- left: "center"
1192
- }
1193
- ] : [],
1194
- ...centerLabel ? [
1195
- {
1196
- type: "text",
1197
- style: {
1198
- text: centerLabel,
1199
- fontSize: 14,
1200
- fill: chartColors.mutedForeground,
1201
- textAlign: "center"
1202
- },
1203
- top: centerValue !== void 0 ? 35 : 0,
1204
- left: "center"
1205
- }
1206
- ] : []
1207
- ]
1208
- });
1209
- }
1210
- return {
1211
- tooltip: showTooltip ? {
1212
- trigger: "item",
1213
- formatter: "{a} <br/>{b}: {c} ({d}%)"
1214
- } : void 0,
1215
- legend: showLegend ? {
1216
- orient: "horizontal",
1217
- bottom: 0,
1218
- data: data.map((d) => d.name)
1219
- } : void 0,
1220
- graphic: graphicElements.length > 0 ? graphicElements : void 0,
1221
- series: [
1222
- {
1223
- name: title,
1224
- type: "pie",
1225
- radius: [innerRadius, "70%"],
1226
- center: ["50%", "45%"],
1227
- avoidLabelOverlap: true,
1228
- data: data.map((item) => ({
1229
- name: item.name,
1230
- value: item.value,
1231
- itemStyle: item.color ? { color: item.color } : void 0
1232
- })),
1233
- label: showLabels ? {
1234
- show: true,
1235
- position: labelPosition,
1236
- formatter: labelPosition === "inside" ? "{d}%" : "{b}: {d}%"
1237
- } : {
1238
- show: false
1239
- },
1240
- labelLine: {
1241
- show: showLabels && labelPosition === "outside"
1628
+ graphicElements.push(
1629
+ (0, import_charts_core9.anchorCenterGraphic)(
1630
+ {
1631
+ type: "group",
1632
+ left: "center",
1633
+ top: "40%",
1634
+ children: [
1635
+ ...centerValue !== void 0 ? [
1636
+ {
1637
+ type: "text",
1638
+ style: {
1639
+ text: String(centerValue),
1640
+ fontSize: 28,
1641
+ fontWeight: "bold",
1642
+ fill: chartColors.foreground,
1643
+ textAlign: "center"
1644
+ },
1645
+ top: 0,
1646
+ left: "center"
1647
+ }
1648
+ ] : [],
1649
+ ...centerLabel ? [
1650
+ {
1651
+ type: "text",
1652
+ style: {
1653
+ text: centerLabel,
1654
+ fontSize: 14,
1655
+ fill: chartColors.mutedForeground,
1656
+ textAlign: "center"
1657
+ },
1658
+ top: centerValue !== void 0 ? 35 : 0,
1659
+ left: "center"
1660
+ }
1661
+ ] : []
1662
+ ]
1242
1663
  },
1243
- emphasis: {
1244
- itemStyle: {
1245
- shadowBlur: 10,
1246
- shadowOffsetX: 0,
1247
- shadowColor: "rgba(0, 0, 0, 0.5)"
1664
+ pieCenter,
1665
+ ["50%", "45%"]
1666
+ )
1667
+ );
1668
+ }
1669
+ const valueFormatter = tooltipFormatter === void 0 ? tooltipValueFormatter : void 0;
1670
+ const tooltipBase = {
1671
+ trigger: "item",
1672
+ formatter: valueFormatter ? (params) => {
1673
+ const p = params;
1674
+ return `${(0, import_charts_core9.escapeHtml)(p.seriesName)} <br/>${(0, import_charts_core9.escapeHtml)(p.name)}: ${(0, import_charts_core9.escapeHtml)((0, import_charts_core9.formatTooltipValue)(p.value, valueFormatter))} (${p.percent}%)`;
1675
+ } : "{a} <br/>{b}: {c} ({d}%)"
1676
+ };
1677
+ return (0, import_charts_core9.mergeChartOption)(
1678
+ {
1679
+ tooltip: (0, import_charts_core9.buildTooltip)(tooltipBase, {
1680
+ show: showTooltip,
1681
+ formatter: tooltipFormatter,
1682
+ valueFormatter: tooltipValueFormatter,
1683
+ getLabel: (0, import_charts_core9.itemLabelLookup)(data)
1684
+ }),
1685
+ legend: (0, import_charts_core9.buildLegend)(
1686
+ data.map((d) => d.name),
1687
+ legendConfig,
1688
+ { show: showLegend, custom: customLegend, defaults: { orient: "horizontal" } }
1689
+ ),
1690
+ graphic: graphicElements.length > 0 ? graphicElements : void 0,
1691
+ series: [
1692
+ {
1693
+ name: title,
1694
+ type: "pie",
1695
+ radius: [innerRadius, "70%"],
1696
+ center: pieCenter,
1697
+ avoidLabelOverlap: true,
1698
+ data: data.map((item) => ({
1699
+ name: item.name,
1700
+ value: item.value,
1701
+ itemStyle: item.color ? { color: item.color } : void 0,
1702
+ label: (0, import_charts_core9.pointLabel)(item.label, labelFormatter, { position: labelPosition })
1703
+ })),
1704
+ label: (0, import_charts_core9.buildDataLabel)(
1705
+ {
1706
+ show: true,
1707
+ position: labelPosition,
1708
+ formatter: labelPosition === "inside" ? "{d}%" : "{b}: {d}%"
1709
+ },
1710
+ { show: false },
1711
+ { show: showLabels, formatter: labelFormatter, getLabel: (0, import_charts_core9.itemLabelLookup)(data) }
1712
+ ),
1713
+ labelLine: {
1714
+ show: showLabels && labelPosition === "outside"
1715
+ },
1716
+ emphasis: {
1717
+ itemStyle: {
1718
+ shadowBlur: 10,
1719
+ shadowOffsetX: 0,
1720
+ shadowColor: "rgba(0, 0, 0, 0.5)"
1721
+ }
1248
1722
  }
1249
1723
  }
1250
- }
1251
- ]
1252
- };
1724
+ ]
1725
+ },
1726
+ optionOverride
1727
+ );
1253
1728
  }, [
1254
1729
  data,
1255
1730
  isEmpty,
@@ -1261,14 +1736,21 @@ var DonutChart = React10.forwardRef(
1261
1736
  centerLabel,
1262
1737
  centerValue,
1263
1738
  title,
1264
- chartColors
1739
+ chartColors,
1740
+ labelFormatter,
1741
+ tooltipFormatter,
1742
+ tooltipValueFormatter,
1743
+ legendConfig,
1744
+ customLegend,
1745
+ optionOverride
1265
1746
  ]);
1266
1747
  const latest = useLatestRef({ onClick });
1267
1748
  const { containerRef } = useChart({
1268
1749
  option,
1269
- theme: currentTheme === import_charts_core8.THEME_DARK ? "dark" : "light",
1750
+ theme: currentTheme === import_charts_core9.THEME_DARK ? "dark" : "light",
1270
1751
  animation,
1271
1752
  onReady: (chart) => {
1753
+ legend.attach(chart);
1272
1754
  chart.on("click", (params) => {
1273
1755
  const { onClick: onClick2 } = latest.current;
1274
1756
  if (!onClick2) return;
@@ -1283,8 +1765,8 @@ var DonutChart = React10.forwardRef(
1283
1765
  });
1284
1766
  }
1285
1767
  });
1286
- const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core8.generatePieChartDescription)(title, data, true) : void 0);
1287
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1768
+ const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core9.generatePieChartDescription)(title, data, true) : void 0);
1769
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1288
1770
  ChartContainer,
1289
1771
  {
1290
1772
  ref,
@@ -1296,8 +1778,10 @@ var DonutChart = React10.forwardRef(
1296
1778
  empty: isEmpty,
1297
1779
  emptyMessage,
1298
1780
  className: cn(className),
1781
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
1782
+ legendPosition: legendConfig?.position,
1299
1783
  ...props,
1300
- children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1784
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1301
1785
  }
1302
1786
  );
1303
1787
  }
@@ -1305,12 +1789,13 @@ var DonutChart = React10.forwardRef(
1305
1789
  DonutChart.displayName = "DonutChart";
1306
1790
 
1307
1791
  // src/components/extended/RadarChart.tsx
1308
- var React11 = __toESM(require("react"), 1);
1792
+ var React12 = __toESM(require("react"), 1);
1309
1793
  var echarts9 = __toESM(require("echarts/core"), 1);
1310
1794
  var import_charts7 = require("echarts/charts");
1311
1795
  var import_components7 = require("echarts/components");
1312
1796
  var import_renderers7 = require("echarts/renderers");
1313
- var import_jsx_runtime11 = require("react/jsx-runtime");
1797
+ var import_charts_core10 = require("@classic-homes/charts-core");
1798
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1314
1799
  echarts9.use([
1315
1800
  import_charts7.RadarChart,
1316
1801
  import_components7.TooltipComponent,
@@ -1319,7 +1804,7 @@ echarts9.use([
1319
1804
  import_components7.RadarComponent,
1320
1805
  import_renderers7.CanvasRenderer
1321
1806
  ]);
1322
- var RadarChart = React11.forwardRef(
1807
+ var RadarChart = React12.forwardRef(
1323
1808
  ({
1324
1809
  title,
1325
1810
  description,
@@ -1334,50 +1819,101 @@ var RadarChart = React11.forwardRef(
1334
1819
  showLegend = true,
1335
1820
  showTooltip = true,
1336
1821
  shape = "polygon",
1822
+ showLabels = false,
1823
+ labelFormatter,
1824
+ tooltipFormatter,
1825
+ tooltipValueFormatter,
1826
+ legend: legendConfig,
1827
+ renderLegend,
1828
+ onLegendChange,
1829
+ option: optionOverride,
1337
1830
  class: className,
1338
1831
  ...props
1339
1832
  }, ref) => {
1340
1833
  const isEmpty = !data?.series?.length || !indicators?.length || data.series.every((s) => !s.data?.length);
1341
- const option = React11.useMemo(() => {
1834
+ const currentTheme = useChartTheme(theme);
1835
+ const legend = useChartLegend({
1836
+ names: data?.series?.map((s) => s.name) ?? [],
1837
+ colors: data?.series?.map((s) => s.color) ?? [],
1838
+ palette: (0, import_charts_core10.getChartColors)(currentTheme).series,
1839
+ config: legendConfig,
1840
+ title,
1841
+ onLegendChange
1842
+ });
1843
+ const customLegend = renderLegend !== void 0;
1844
+ const option = React12.useMemo(() => {
1342
1845
  if (isEmpty) return {};
1343
- return {
1344
- tooltip: showTooltip ? {
1345
- trigger: "item"
1346
- } : void 0,
1347
- legend: showLegend ? {
1348
- data: data.series.map((s) => s.name),
1349
- bottom: 0
1350
- } : void 0,
1351
- radar: {
1352
- indicator: indicators.map((ind) => ({
1353
- name: ind.name,
1354
- max: ind.max,
1355
- min: ind.min
1356
- })),
1357
- shape
1846
+ return (0, import_charts_core10.mergeChartOption)(
1847
+ {
1848
+ tooltip: (0, import_charts_core10.buildTooltip)(
1849
+ {
1850
+ trigger: "item"
1851
+ },
1852
+ {
1853
+ show: showTooltip,
1854
+ formatter: tooltipFormatter,
1855
+ valueFormatter: tooltipValueFormatter
1856
+ }
1857
+ ),
1858
+ legend: (0, import_charts_core10.buildLegend)(
1859
+ data.series.map((s) => s.name),
1860
+ legendConfig,
1861
+ { show: showLegend, custom: customLegend }
1862
+ ),
1863
+ radar: {
1864
+ indicator: indicators.map((ind) => ({
1865
+ name: ind.name,
1866
+ max: ind.max,
1867
+ min: ind.min
1868
+ })),
1869
+ shape,
1870
+ center: (0, import_charts_core10.legendCenter)(legendConfig, showLegend && !customLegend, ["50%", "50%"])
1871
+ },
1872
+ series: [
1873
+ {
1874
+ type: "radar",
1875
+ label: (0, import_charts_core10.buildDataLabel)({ show: true }, void 0, {
1876
+ show: showLabels,
1877
+ formatter: labelFormatter
1878
+ }),
1879
+ data: data.series.map((series) => ({
1880
+ name: series.name,
1881
+ value: series.data,
1882
+ itemStyle: series.color ? { color: series.color } : void 0,
1883
+ areaStyle: {
1884
+ opacity: 0.2
1885
+ }
1886
+ }))
1887
+ }
1888
+ ]
1358
1889
  },
1359
- series: [
1360
- {
1361
- type: "radar",
1362
- data: data.series.map((series) => ({
1363
- name: series.name,
1364
- value: series.data,
1365
- itemStyle: series.color ? { color: series.color } : void 0,
1366
- areaStyle: {
1367
- opacity: 0.2
1368
- }
1369
- }))
1370
- }
1371
- ]
1372
- };
1373
- }, [data, indicators, isEmpty, showTooltip, showLegend, shape]);
1890
+ optionOverride
1891
+ );
1892
+ }, [
1893
+ data,
1894
+ indicators,
1895
+ isEmpty,
1896
+ showTooltip,
1897
+ showLegend,
1898
+ shape,
1899
+ showLabels,
1900
+ labelFormatter,
1901
+ tooltipFormatter,
1902
+ tooltipValueFormatter,
1903
+ legendConfig,
1904
+ customLegend,
1905
+ optionOverride
1906
+ ]);
1374
1907
  const { containerRef } = useChart({
1375
1908
  option,
1376
1909
  theme,
1377
- animation
1910
+ animation,
1911
+ onReady: (chart) => {
1912
+ legend.attach(chart);
1913
+ }
1378
1914
  });
1379
1915
  const accessibilityDescription = description || (!isEmpty ? `${title}. Radar chart with ${indicators.length} dimensions comparing ${data.series.length} series.` : void 0);
1380
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1916
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1381
1917
  ChartContainer,
1382
1918
  {
1383
1919
  ref,
@@ -1389,8 +1925,10 @@ var RadarChart = React11.forwardRef(
1389
1925
  empty: isEmpty,
1390
1926
  emptyMessage,
1391
1927
  className: cn(className),
1928
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
1929
+ legendPosition: legendConfig?.position,
1392
1930
  ...props,
1393
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1931
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1394
1932
  }
1395
1933
  );
1396
1934
  }
@@ -1398,12 +1936,13 @@ var RadarChart = React11.forwardRef(
1398
1936
  RadarChart.displayName = "RadarChart";
1399
1937
 
1400
1938
  // src/components/extended/FunnelChart.tsx
1401
- var React12 = __toESM(require("react"), 1);
1939
+ var React13 = __toESM(require("react"), 1);
1402
1940
  var echarts10 = __toESM(require("echarts/core"), 1);
1403
1941
  var import_charts8 = require("echarts/charts");
1404
1942
  var import_components8 = require("echarts/components");
1405
1943
  var import_renderers8 = require("echarts/renderers");
1406
- var import_jsx_runtime12 = require("react/jsx-runtime");
1944
+ var import_charts_core11 = require("@classic-homes/charts-core");
1945
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1407
1946
  echarts10.use([
1408
1947
  import_charts8.FunnelChart,
1409
1948
  import_components8.TooltipComponent,
@@ -1411,7 +1950,7 @@ echarts10.use([
1411
1950
  import_components8.TitleComponent,
1412
1951
  import_renderers8.CanvasRenderer
1413
1952
  ]);
1414
- var FunnelChart = React12.forwardRef(
1953
+ var FunnelChart = React13.forwardRef(
1415
1954
  ({
1416
1955
  title,
1417
1956
  description,
@@ -1427,68 +1966,119 @@ var FunnelChart = React12.forwardRef(
1427
1966
  sort = "descending",
1428
1967
  orientation = "vertical",
1429
1968
  showLabels = true,
1969
+ labelFormatter,
1970
+ tooltipFormatter,
1971
+ tooltipValueFormatter,
1972
+ legend: legendConfig,
1973
+ renderLegend,
1974
+ onLegendChange,
1975
+ option: optionOverride,
1430
1976
  onClick,
1431
1977
  class: className,
1432
1978
  ...props
1433
1979
  }, ref) => {
1434
1980
  const isEmpty = !data?.length;
1435
- const option = React12.useMemo(() => {
1981
+ const currentTheme = useChartTheme(theme);
1982
+ const legend = useChartLegend({
1983
+ names: data?.map((d) => d.name) ?? [],
1984
+ colors: data?.map((d) => d.color) ?? [],
1985
+ palette: (0, import_charts_core11.getChartColors)(currentTheme).series,
1986
+ config: legendConfig,
1987
+ title,
1988
+ onLegendChange
1989
+ });
1990
+ const customLegend = renderLegend !== void 0;
1991
+ const option = React13.useMemo(() => {
1436
1992
  if (isEmpty) return {};
1437
- return {
1438
- tooltip: showTooltip ? {
1439
- trigger: "item",
1440
- formatter: "{a} <br/>{b}: {c}"
1441
- } : void 0,
1442
- legend: showLegend ? {
1443
- data: data.map((d) => d.name),
1444
- bottom: 0
1445
- } : void 0,
1446
- series: [
1447
- {
1448
- name: title,
1449
- type: "funnel",
1450
- left: "10%",
1451
- top: 60,
1452
- bottom: 60,
1453
- width: "80%",
1454
- min: 0,
1455
- // reduce, not Math.max(...): spreading large arrays overflows the stack
1456
- max: data.reduce((m, d) => d.value > m ? d.value : m, -Infinity),
1457
- minSize: "0%",
1458
- maxSize: "100%",
1459
- sort,
1460
- orient: orientation,
1461
- gap: 2,
1462
- label: showLabels ? {
1463
- show: true,
1464
- position: "inside",
1465
- formatter: "{b}: {c}"
1466
- } : {
1467
- show: false
1468
- },
1469
- labelLine: {
1470
- show: false
1471
- },
1472
- emphasis: {
1473
- label: {
1474
- fontSize: 16
1475
- }
1476
- },
1477
- data: data.map((item) => ({
1478
- name: item.name,
1479
- value: item.value,
1480
- itemStyle: item.color ? { color: item.color } : void 0
1481
- }))
1482
- }
1483
- ]
1993
+ const getLabel = (0, import_charts_core11.itemLabelLookup)(data);
1994
+ const valueFormatter = tooltipFormatter === void 0 ? tooltipValueFormatter : void 0;
1995
+ const tooltipBase = {
1996
+ trigger: "item",
1997
+ formatter: valueFormatter ? (params) => {
1998
+ const p = params;
1999
+ return `${(0, import_charts_core11.escapeHtml)(p.seriesName)} <br/>${(0, import_charts_core11.escapeHtml)(p.name)}: ${(0, import_charts_core11.escapeHtml)((0, import_charts_core11.formatTooltipValue)(p.value, valueFormatter))}`;
2000
+ } : "{a} <br/>{b}: {c}"
1484
2001
  };
1485
- }, [data, isEmpty, showTooltip, showLegend, sort, orientation, showLabels, title]);
2002
+ return (0, import_charts_core11.mergeChartOption)(
2003
+ {
2004
+ tooltip: (0, import_charts_core11.buildTooltip)(tooltipBase, {
2005
+ show: showTooltip,
2006
+ formatter: tooltipFormatter,
2007
+ valueFormatter: tooltipValueFormatter,
2008
+ getLabel
2009
+ }),
2010
+ legend: (0, import_charts_core11.buildLegend)(
2011
+ data.map((d) => d.name),
2012
+ legendConfig,
2013
+ { show: showLegend, custom: customLegend }
2014
+ ),
2015
+ series: [
2016
+ {
2017
+ name: title,
2018
+ type: "funnel",
2019
+ left: "10%",
2020
+ top: 60,
2021
+ bottom: 60,
2022
+ width: "80%",
2023
+ min: 0,
2024
+ // reduce, not Math.max(...): spreading large arrays overflows the stack
2025
+ max: data.reduce((m, d) => d.value > m ? d.value : m, -Infinity),
2026
+ minSize: "0%",
2027
+ maxSize: "100%",
2028
+ sort,
2029
+ orient: orientation,
2030
+ gap: 2,
2031
+ label: (0, import_charts_core11.buildDataLabel)(
2032
+ {
2033
+ show: true,
2034
+ position: "inside",
2035
+ formatter: "{b}: {c}"
2036
+ },
2037
+ { show: false },
2038
+ { show: showLabels, formatter: labelFormatter, getLabel }
2039
+ ),
2040
+ labelLine: {
2041
+ show: false
2042
+ },
2043
+ emphasis: {
2044
+ label: {
2045
+ fontSize: 16
2046
+ }
2047
+ },
2048
+ data: data.map((item) => ({
2049
+ name: item.name,
2050
+ value: item.value,
2051
+ itemStyle: item.color ? { color: item.color } : void 0,
2052
+ label: (0, import_charts_core11.pointLabel)(item.label, labelFormatter, { position: "inside" })
2053
+ }))
2054
+ }
2055
+ ]
2056
+ },
2057
+ optionOverride
2058
+ );
2059
+ }, [
2060
+ data,
2061
+ isEmpty,
2062
+ showTooltip,
2063
+ showLegend,
2064
+ sort,
2065
+ orientation,
2066
+ showLabels,
2067
+ title,
2068
+ labelFormatter,
2069
+ tooltipFormatter,
2070
+ tooltipValueFormatter,
2071
+ legendConfig,
2072
+ customLegend,
2073
+ optionOverride
2074
+ ]);
1486
2075
  const latest = useLatestRef({ onClick });
1487
2076
  const { containerRef } = useChart({
1488
2077
  option,
1489
2078
  theme,
1490
2079
  animation,
1491
2080
  onReady: (chart) => {
2081
+ legend.attach(chart);
1492
2082
  chart.on("click", (params) => {
1493
2083
  const { onClick: onClick2 } = latest.current;
1494
2084
  if (!onClick2) return;
@@ -1503,7 +2093,7 @@ var FunnelChart = React12.forwardRef(
1503
2093
  }
1504
2094
  });
1505
2095
  const accessibilityDescription = description || (!isEmpty ? `${title}. Funnel chart with ${data.length} stages.` : void 0);
1506
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2096
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1507
2097
  ChartContainer,
1508
2098
  {
1509
2099
  ref,
@@ -1515,8 +2105,10 @@ var FunnelChart = React12.forwardRef(
1515
2105
  empty: isEmpty,
1516
2106
  emptyMessage,
1517
2107
  className: cn(className),
2108
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
2109
+ legendPosition: legendConfig?.position,
1518
2110
  ...props,
1519
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { ref: containerRef, className: "h-full w-full" })
2111
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1520
2112
  }
1521
2113
  );
1522
2114
  }
@@ -1524,15 +2116,15 @@ var FunnelChart = React12.forwardRef(
1524
2116
  FunnelChart.displayName = "FunnelChart";
1525
2117
 
1526
2118
  // src/components/extended/GaugeChart.tsx
1527
- var React13 = __toESM(require("react"), 1);
2119
+ var React14 = __toESM(require("react"), 1);
1528
2120
  var echarts11 = __toESM(require("echarts/core"), 1);
1529
2121
  var import_charts9 = require("echarts/charts");
1530
2122
  var import_components9 = require("echarts/components");
1531
2123
  var import_renderers9 = require("echarts/renderers");
1532
- var import_charts_core9 = require("@classic-homes/charts-core");
1533
- var import_jsx_runtime13 = require("react/jsx-runtime");
1534
- echarts11.use([import_charts9.GaugeChart, import_components9.TitleComponent, import_renderers9.CanvasRenderer]);
1535
- var GaugeChart = React13.forwardRef(
2124
+ var import_charts_core12 = require("@classic-homes/charts-core");
2125
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2126
+ echarts11.use([import_charts9.GaugeChart, import_components9.TitleComponent, import_components9.TooltipComponent, import_renderers9.CanvasRenderer]);
2127
+ var GaugeChart = React14.forwardRef(
1536
2128
  ({
1537
2129
  title,
1538
2130
  description,
@@ -1548,84 +2140,100 @@ var GaugeChart = React13.forwardRef(
1548
2140
  splitNumber = 10,
1549
2141
  showProgress = true,
1550
2142
  formatValue,
2143
+ showTooltip = true,
2144
+ tooltipFormatter,
2145
+ tooltipValueFormatter,
2146
+ option: optionOverride,
1551
2147
  class: className,
1552
2148
  ...props
1553
2149
  }, ref) => {
1554
2150
  const currentTheme = useChartTheme(theme);
1555
- const chartColors = (0, import_charts_core9.getChartColors)(currentTheme);
2151
+ const chartColors = (0, import_charts_core12.getChartColors)(currentTheme);
1556
2152
  const reducedMotion = useReducedMotion();
1557
- const option = React13.useMemo(() => {
1558
- return {
1559
- series: [
1560
- {
1561
- type: "gauge",
1562
- center: ["50%", "60%"],
1563
- radius: "80%",
1564
- min,
1565
- max,
1566
- splitNumber,
1567
- progress: showProgress ? {
1568
- show: true,
1569
- width: 18
1570
- } : void 0,
1571
- axisLine: {
1572
- lineStyle: {
2153
+ const option = React14.useMemo(() => {
2154
+ return (0, import_charts_core12.mergeChartOption)(
2155
+ {
2156
+ // No tooltip by default; one appears once its content is customised
2157
+ tooltip: tooltipFormatter || tooltipValueFormatter ? (0, import_charts_core12.buildTooltip)(
2158
+ { trigger: "item" },
2159
+ {
2160
+ show: showTooltip,
2161
+ formatter: tooltipFormatter,
2162
+ valueFormatter: tooltipValueFormatter
2163
+ }
2164
+ ) : void 0,
2165
+ series: [
2166
+ {
2167
+ type: "gauge",
2168
+ center: ["50%", "60%"],
2169
+ radius: "80%",
2170
+ min,
2171
+ max,
2172
+ splitNumber,
2173
+ progress: showProgress ? {
2174
+ show: true,
1573
2175
  width: 18
1574
- }
1575
- },
1576
- axisTick: {
1577
- distance: -30,
1578
- splitNumber: 5,
1579
- lineStyle: {
1580
- width: 2,
1581
- color: chartColors.mutedForeground
1582
- }
1583
- },
1584
- splitLine: {
1585
- distance: -32,
1586
- length: 10,
1587
- lineStyle: {
1588
- width: 3,
1589
- color: chartColors.mutedForeground
1590
- }
1591
- },
1592
- axisLabel: {
1593
- distance: -20,
1594
- color: chartColors.mutedForeground,
1595
- fontSize: 12
1596
- },
1597
- anchor: {
1598
- show: true,
1599
- size: 20,
1600
- itemStyle: {
1601
- borderWidth: 2
1602
- }
1603
- },
1604
- pointer: {
1605
- width: 6,
1606
- length: "60%"
1607
- },
1608
- title: {
1609
- show: true,
1610
- offsetCenter: [0, "70%"],
1611
- fontSize: 14
1612
- },
1613
- detail: {
1614
- valueAnimation: animation && !reducedMotion,
1615
- fontSize: 28,
1616
- fontWeight: "bold",
1617
- offsetCenter: [0, "40%"],
1618
- formatter: formatValue || "{value}"
1619
- },
1620
- data: [
1621
- {
1622
- value,
1623
- name: title
1624
- }
1625
- ]
1626
- }
1627
- ]
1628
- };
2176
+ } : void 0,
2177
+ axisLine: {
2178
+ lineStyle: {
2179
+ width: 18
2180
+ }
2181
+ },
2182
+ axisTick: {
2183
+ distance: -30,
2184
+ splitNumber: 5,
2185
+ lineStyle: {
2186
+ width: 2,
2187
+ color: chartColors.mutedForeground
2188
+ }
2189
+ },
2190
+ splitLine: {
2191
+ distance: -32,
2192
+ length: 10,
2193
+ lineStyle: {
2194
+ width: 3,
2195
+ color: chartColors.mutedForeground
2196
+ }
2197
+ },
2198
+ axisLabel: {
2199
+ distance: -20,
2200
+ color: chartColors.mutedForeground,
2201
+ fontSize: 12
2202
+ },
2203
+ anchor: {
2204
+ show: true,
2205
+ size: 20,
2206
+ itemStyle: {
2207
+ borderWidth: 2
2208
+ }
2209
+ },
2210
+ pointer: {
2211
+ width: 6,
2212
+ length: "60%"
2213
+ },
2214
+ title: {
2215
+ show: true,
2216
+ offsetCenter: [0, "70%"],
2217
+ fontSize: 14
2218
+ },
2219
+ detail: {
2220
+ valueAnimation: animation && !reducedMotion,
2221
+ fontSize: 28,
2222
+ fontWeight: "bold",
2223
+ offsetCenter: [0, "40%"],
2224
+ formatter: formatValue || "{value}"
2225
+ },
2226
+ data: [
2227
+ {
2228
+ value,
2229
+ name: title
2230
+ }
2231
+ ]
2232
+ }
2233
+ ]
2234
+ },
2235
+ optionOverride
2236
+ );
1629
2237
  }, [
1630
2238
  value,
1631
2239
  min,
@@ -1636,15 +2244,19 @@ var GaugeChart = React13.forwardRef(
1636
2244
  title,
1637
2245
  animation,
1638
2246
  reducedMotion,
1639
- chartColors
2247
+ chartColors,
2248
+ showTooltip,
2249
+ tooltipFormatter,
2250
+ tooltipValueFormatter,
2251
+ optionOverride
1640
2252
  ]);
1641
2253
  const { containerRef } = useChart({
1642
2254
  option,
1643
- theme: currentTheme === import_charts_core9.THEME_DARK ? "dark" : "light",
2255
+ theme: currentTheme === import_charts_core12.THEME_DARK ? "dark" : "light",
1644
2256
  animation
1645
2257
  });
1646
2258
  const accessibilityDescription = description || `${title}. Gauge showing value ${value} on scale from ${min} to ${max}.`;
1647
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2259
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1648
2260
  ChartContainer,
1649
2261
  {
1650
2262
  ref,
@@ -1656,7 +2268,7 @@ var GaugeChart = React13.forwardRef(
1656
2268
  emptyMessage,
1657
2269
  className: cn(className),
1658
2270
  ...props,
1659
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { ref: containerRef, className: "h-full w-full" })
2271
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1660
2272
  }
1661
2273
  );
1662
2274
  }
@@ -1664,13 +2276,13 @@ var GaugeChart = React13.forwardRef(
1664
2276
  GaugeChart.displayName = "GaugeChart";
1665
2277
 
1666
2278
  // src/components/extended/HeatmapChart.tsx
1667
- var React14 = __toESM(require("react"), 1);
2279
+ var React15 = __toESM(require("react"), 1);
1668
2280
  var echarts12 = __toESM(require("echarts/core"), 1);
1669
2281
  var import_charts10 = require("echarts/charts");
1670
2282
  var import_components10 = require("echarts/components");
1671
2283
  var import_renderers10 = require("echarts/renderers");
1672
- var import_charts_core10 = require("@classic-homes/charts-core");
1673
- var import_jsx_runtime14 = require("react/jsx-runtime");
2284
+ var import_charts_core13 = require("@classic-homes/charts-core");
2285
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1674
2286
  echarts12.use([
1675
2287
  import_charts10.HeatmapChart,
1676
2288
  import_components10.GridComponent,
@@ -1679,7 +2291,7 @@ echarts12.use([
1679
2291
  import_components10.VisualMapComponent,
1680
2292
  import_renderers10.CanvasRenderer
1681
2293
  ]);
1682
- var HeatmapChart = React14.forwardRef(
2294
+ var HeatmapChart = React15.forwardRef(
1683
2295
  ({
1684
2296
  title,
1685
2297
  description,
@@ -1694,6 +2306,12 @@ var HeatmapChart = React14.forwardRef(
1694
2306
  animation = true,
1695
2307
  showTooltip = true,
1696
2308
  showValues = false,
2309
+ labelFormatter,
2310
+ xAxisConfig,
2311
+ yAxisConfig,
2312
+ tooltipFormatter,
2313
+ tooltipValueFormatter,
2314
+ option: optionOverride,
1697
2315
  onClick,
1698
2316
  class: className,
1699
2317
  ...props
@@ -1707,64 +2325,97 @@ var HeatmapChart = React14.forwardRef(
1707
2325
  }
1708
2326
  const minValue = data?.min ?? dataMin;
1709
2327
  const maxValue = data?.max ?? dataMax;
1710
- const option = React14.useMemo(() => {
2328
+ const option = React15.useMemo(() => {
1711
2329
  if (isEmpty) return {};
1712
- return {
1713
- tooltip: showTooltip ? {
1714
- position: "top",
1715
- formatter: (params) => {
1716
- const p = params;
1717
- if (!p.value) return "";
1718
- const [xIdx, yIdx, value] = p.value;
1719
- return `${(0, import_charts_core10.escapeHtml)(xAxis[xIdx])} / ${(0, import_charts_core10.escapeHtml)(yAxis[yIdx])}: ${(0, import_charts_core10.escapeHtml)(value)}`;
1720
- }
1721
- } : void 0,
1722
- grid: {
1723
- left: "3%",
1724
- right: "10%",
1725
- bottom: "3%",
1726
- containLabel: true
1727
- },
1728
- xAxis: {
1729
- type: "category",
1730
- data: xAxis,
1731
- splitArea: {
1732
- show: true
1733
- }
1734
- },
1735
- yAxis: {
1736
- type: "category",
1737
- data: yAxis,
1738
- splitArea: {
1739
- show: true
1740
- }
1741
- },
1742
- visualMap: {
1743
- min: minValue,
1744
- max: maxValue,
1745
- calculable: true,
1746
- orient: "vertical",
1747
- right: "0%",
1748
- top: "center"
1749
- },
1750
- series: [
1751
- {
1752
- name: title,
1753
- type: "heatmap",
1754
- data: data.data,
1755
- label: showValues ? {
1756
- show: true
1757
- } : void 0,
1758
- emphasis: {
1759
- itemStyle: {
1760
- shadowBlur: 10,
1761
- shadowColor: "rgba(0, 0, 0, 0.5)"
2330
+ return (0, import_charts_core13.mergeChartOption)(
2331
+ {
2332
+ tooltip: (0, import_charts_core13.buildTooltip)(
2333
+ {
2334
+ position: "top",
2335
+ formatter: (params) => {
2336
+ const p = params;
2337
+ if (!p.value) return "";
2338
+ const [xIdx, yIdx, value] = p.value;
2339
+ return `${(0, import_charts_core13.escapeHtml)(xAxis[xIdx])} / ${(0, import_charts_core13.escapeHtml)(yAxis[yIdx])}: ${(0, import_charts_core13.escapeHtml)((0, import_charts_core13.formatTooltipValue)(value, tooltipValueFormatter))}`;
1762
2340
  }
2341
+ },
2342
+ {
2343
+ show: showTooltip,
2344
+ formatter: tooltipFormatter,
2345
+ valueFormatter: tooltipValueFormatter
1763
2346
  }
1764
- }
1765
- ]
1766
- };
1767
- }, [data, xAxis, yAxis, isEmpty, showTooltip, showValues, minValue, maxValue, title]);
2347
+ ),
2348
+ grid: {
2349
+ left: "3%",
2350
+ right: "10%",
2351
+ bottom: "3%",
2352
+ containLabel: true
2353
+ },
2354
+ xAxis: (0, import_charts_core13.buildAxis)(
2355
+ {
2356
+ type: "category",
2357
+ data: xAxis,
2358
+ splitArea: {
2359
+ show: true
2360
+ }
2361
+ },
2362
+ xAxisConfig
2363
+ ),
2364
+ yAxis: (0, import_charts_core13.buildAxis)(
2365
+ {
2366
+ type: "category",
2367
+ data: yAxis,
2368
+ splitArea: {
2369
+ show: true
2370
+ }
2371
+ },
2372
+ yAxisConfig
2373
+ ),
2374
+ visualMap: {
2375
+ min: minValue,
2376
+ max: maxValue,
2377
+ calculable: true,
2378
+ orient: "vertical",
2379
+ right: "0%",
2380
+ top: "center"
2381
+ },
2382
+ series: [
2383
+ {
2384
+ name: title,
2385
+ type: "heatmap",
2386
+ data: data.data,
2387
+ label: (0, import_charts_core13.buildDataLabel)({ show: true }, void 0, {
2388
+ show: showValues,
2389
+ formatter: labelFormatter
2390
+ }),
2391
+ emphasis: {
2392
+ itemStyle: {
2393
+ shadowBlur: 10,
2394
+ shadowColor: "rgba(0, 0, 0, 0.5)"
2395
+ }
2396
+ }
2397
+ }
2398
+ ]
2399
+ },
2400
+ optionOverride
2401
+ );
2402
+ }, [
2403
+ data,
2404
+ xAxis,
2405
+ yAxis,
2406
+ isEmpty,
2407
+ showTooltip,
2408
+ showValues,
2409
+ minValue,
2410
+ maxValue,
2411
+ title,
2412
+ labelFormatter,
2413
+ xAxisConfig,
2414
+ yAxisConfig,
2415
+ tooltipFormatter,
2416
+ tooltipValueFormatter,
2417
+ optionOverride
2418
+ ]);
1768
2419
  const latest = useLatestRef({ onClick, xAxis, yAxis });
1769
2420
  const { containerRef } = useChart({
1770
2421
  option,
@@ -1788,7 +2439,7 @@ var HeatmapChart = React14.forwardRef(
1788
2439
  }
1789
2440
  });
1790
2441
  const accessibilityDescription = description || (!isEmpty ? `${title}. Heatmap with ${xAxis.length} columns and ${yAxis.length} rows.` : void 0);
1791
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2442
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1792
2443
  ChartContainer,
1793
2444
  {
1794
2445
  ref,
@@ -1801,7 +2452,7 @@ var HeatmapChart = React14.forwardRef(
1801
2452
  emptyMessage,
1802
2453
  className: cn(className),
1803
2454
  ...props,
1804
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { ref: containerRef, className: "h-full w-full" })
2455
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1805
2456
  }
1806
2457
  );
1807
2458
  }
@@ -1809,15 +2460,15 @@ var HeatmapChart = React14.forwardRef(
1809
2460
  HeatmapChart.displayName = "HeatmapChart";
1810
2461
 
1811
2462
  // src/components/extended/TreemapChart.tsx
1812
- var React15 = __toESM(require("react"), 1);
2463
+ var React16 = __toESM(require("react"), 1);
1813
2464
  var echarts13 = __toESM(require("echarts/core"), 1);
1814
2465
  var import_charts11 = require("echarts/charts");
1815
2466
  var import_components11 = require("echarts/components");
1816
2467
  var import_renderers11 = require("echarts/renderers");
1817
- var import_charts_core11 = require("@classic-homes/charts-core");
1818
- var import_jsx_runtime15 = require("react/jsx-runtime");
2468
+ var import_charts_core14 = require("@classic-homes/charts-core");
2469
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1819
2470
  echarts13.use([import_charts11.TreemapChart, import_components11.TooltipComponent, import_components11.TitleComponent, import_renderers11.CanvasRenderer]);
1820
- var TreemapChart = React15.forwardRef(
2471
+ var TreemapChart = React16.forwardRef(
1821
2472
  ({
1822
2473
  title,
1823
2474
  description,
@@ -1830,73 +2481,103 @@ var TreemapChart = React15.forwardRef(
1830
2481
  theme = "auto",
1831
2482
  animation = true,
1832
2483
  showTooltip = true,
2484
+ labelFormatter,
2485
+ tooltipFormatter,
2486
+ tooltipValueFormatter,
2487
+ option: optionOverride,
1833
2488
  onClick,
1834
2489
  class: className,
1835
2490
  ...props
1836
2491
  }, ref) => {
1837
2492
  const isEmpty = !data?.length;
1838
2493
  const currentTheme = useChartTheme(theme);
1839
- const chartColors = (0, import_charts_core11.getChartColors)(currentTheme);
1840
- const option = React15.useMemo(() => {
2494
+ const chartColors = (0, import_charts_core14.getChartColors)(currentTheme);
2495
+ const option = React16.useMemo(() => {
1841
2496
  if (isEmpty) return {};
1842
- return {
1843
- tooltip: showTooltip ? {
1844
- formatter: (params) => {
1845
- const p = params;
1846
- return `${(0, import_charts_core11.escapeHtml)(p.name)}: ${(0, import_charts_core11.escapeHtml)(p.value)}`;
1847
- }
1848
- } : void 0,
1849
- series: [
1850
- {
1851
- type: "treemap",
1852
- data,
1853
- roam: false,
1854
- levels: levels || [
1855
- {
1856
- itemStyle: {
1857
- borderColor: chartColors.divider,
1858
- borderWidth: 4,
1859
- gapWidth: 4
1860
- }
1861
- },
1862
- {
1863
- colorSaturation: [0.3, 0.6],
1864
- itemStyle: {
1865
- borderColorSaturation: 0.7,
1866
- gapWidth: 2,
1867
- borderWidth: 2
1868
- }
1869
- },
1870
- {
1871
- colorSaturation: [0.3, 0.5],
1872
- itemStyle: {
1873
- borderColorSaturation: 0.6,
1874
- gapWidth: 1
2497
+ const toNode = (node) => ({
2498
+ ...node,
2499
+ children: node.children?.map(toNode),
2500
+ label: (0, import_charts_core14.pointLabel)(node.label, labelFormatter)
2501
+ });
2502
+ return (0, import_charts_core14.mergeChartOption)(
2503
+ {
2504
+ tooltip: (0, import_charts_core14.buildTooltip)(
2505
+ {
2506
+ formatter: (params) => {
2507
+ const p = params;
2508
+ return `${(0, import_charts_core14.escapeHtml)(p.name)}: ${(0, import_charts_core14.escapeHtml)((0, import_charts_core14.formatTooltipValue)(p.value, tooltipValueFormatter))}`;
2509
+ }
2510
+ },
2511
+ {
2512
+ show: showTooltip,
2513
+ formatter: tooltipFormatter,
2514
+ valueFormatter: tooltipValueFormatter
2515
+ }
2516
+ ),
2517
+ series: [
2518
+ {
2519
+ type: "treemap",
2520
+ data: data.map(toNode),
2521
+ roam: false,
2522
+ levels: levels || [
2523
+ {
2524
+ itemStyle: {
2525
+ borderColor: chartColors.divider,
2526
+ borderWidth: 4,
2527
+ gapWidth: 4
2528
+ }
2529
+ },
2530
+ {
2531
+ colorSaturation: [0.3, 0.6],
2532
+ itemStyle: {
2533
+ borderColorSaturation: 0.7,
2534
+ gapWidth: 2,
2535
+ borderWidth: 2
2536
+ }
2537
+ },
2538
+ {
2539
+ colorSaturation: [0.3, 0.5],
2540
+ itemStyle: {
2541
+ borderColorSaturation: 0.6,
2542
+ gapWidth: 1
2543
+ }
2544
+ },
2545
+ {
2546
+ colorSaturation: [0.3, 0.5]
1875
2547
  }
2548
+ ],
2549
+ label: (0, import_charts_core14.buildDataLabel)({ show: true, formatter: "{b}" }, void 0, {
2550
+ show: true,
2551
+ formatter: labelFormatter,
2552
+ getLabel: (0, import_charts_core14.nameLabelLookup)(data)
2553
+ }),
2554
+ upperLabel: {
2555
+ show: true,
2556
+ height: 30
1876
2557
  },
1877
- {
1878
- colorSaturation: [0.3, 0.5]
2558
+ breadcrumb: {
2559
+ show: true
1879
2560
  }
1880
- ],
1881
- label: {
1882
- show: true,
1883
- formatter: "{b}"
1884
- },
1885
- upperLabel: {
1886
- show: true,
1887
- height: 30
1888
- },
1889
- breadcrumb: {
1890
- show: true
1891
2561
  }
1892
- }
1893
- ]
1894
- };
1895
- }, [data, levels, isEmpty, showTooltip, chartColors]);
2562
+ ]
2563
+ },
2564
+ optionOverride
2565
+ );
2566
+ }, [
2567
+ data,
2568
+ levels,
2569
+ isEmpty,
2570
+ showTooltip,
2571
+ chartColors,
2572
+ labelFormatter,
2573
+ tooltipFormatter,
2574
+ tooltipValueFormatter,
2575
+ optionOverride
2576
+ ]);
1896
2577
  const latest = useLatestRef({ onClick });
1897
2578
  const { containerRef } = useChart({
1898
2579
  option,
1899
- theme: currentTheme === import_charts_core11.THEME_DARK ? "dark" : "light",
2580
+ theme: currentTheme === import_charts_core14.THEME_DARK ? "dark" : "light",
1900
2581
  animation,
1901
2582
  onReady: (chart) => {
1902
2583
  chart.on("click", (params) => {
@@ -1915,7 +2596,7 @@ var TreemapChart = React15.forwardRef(
1915
2596
  }
1916
2597
  });
1917
2598
  const accessibilityDescription = description || (!isEmpty ? `${title}. Treemap chart showing hierarchical data.` : void 0);
1918
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2599
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1919
2600
  ChartContainer,
1920
2601
  {
1921
2602
  ref,
@@ -1928,7 +2609,7 @@ var TreemapChart = React15.forwardRef(
1928
2609
  emptyMessage,
1929
2610
  className: cn(className),
1930
2611
  ...props,
1931
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { ref: containerRef, className: "h-full w-full" })
2612
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1932
2613
  }
1933
2614
  );
1934
2615
  }
@@ -1936,13 +2617,13 @@ var TreemapChart = React15.forwardRef(
1936
2617
  TreemapChart.displayName = "TreemapChart";
1937
2618
 
1938
2619
  // src/components/extended/CandlestickChart.tsx
1939
- var React16 = __toESM(require("react"), 1);
2620
+ var React17 = __toESM(require("react"), 1);
1940
2621
  var echarts14 = __toESM(require("echarts/core"), 1);
1941
2622
  var import_charts12 = require("echarts/charts");
1942
2623
  var import_components12 = require("echarts/components");
1943
2624
  var import_renderers12 = require("echarts/renderers");
1944
- var import_charts_core12 = require("@classic-homes/charts-core");
1945
- var import_jsx_runtime16 = require("react/jsx-runtime");
2625
+ var import_charts_core15 = require("@classic-homes/charts-core");
2626
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1946
2627
  echarts14.use([
1947
2628
  import_charts12.CandlestickChart,
1948
2629
  import_charts12.BarChart,
@@ -1952,7 +2633,7 @@ echarts14.use([
1952
2633
  import_components12.DataZoomComponent,
1953
2634
  import_renderers12.CanvasRenderer
1954
2635
  ]);
1955
- var CandlestickChart = React16.forwardRef(
2636
+ var CandlestickChart = React17.forwardRef(
1956
2637
  ({
1957
2638
  title,
1958
2639
  description,
@@ -1965,14 +2646,19 @@ var CandlestickChart = React16.forwardRef(
1965
2646
  animation = true,
1966
2647
  showTooltip = true,
1967
2648
  showVolume = false,
2649
+ xAxisConfig,
2650
+ yAxisConfig,
2651
+ tooltipFormatter,
2652
+ tooltipValueFormatter,
2653
+ option: optionOverride,
1968
2654
  onClick,
1969
2655
  class: className,
1970
2656
  ...props
1971
2657
  }, ref) => {
1972
2658
  const isEmpty = !data?.dates?.length || !data?.data?.length;
1973
2659
  const currentTheme = useChartTheme(theme);
1974
- const chartColors = (0, import_charts_core12.getChartColors)(currentTheme);
1975
- const option = React16.useMemo(() => {
2660
+ const chartColors = (0, import_charts_core15.getChartColors)(currentTheme);
2661
+ const option = React17.useMemo(() => {
1976
2662
  if (isEmpty) return {};
1977
2663
  const candlestickData = data.data.map((item) => [item.open, item.close, item.low, item.high]);
1978
2664
  const seriesArr = [
@@ -1999,23 +2685,29 @@ var CandlestickChart = React16.forwardRef(
1999
2685
  }
2000
2686
  ];
2001
2687
  const xAxisArr = [
2002
- {
2003
- type: "category",
2004
- data: data.dates,
2005
- boundaryGap: false,
2006
- axisLine: { onZero: false },
2007
- splitLine: { show: false },
2008
- min: "dataMin",
2009
- max: "dataMax"
2010
- }
2688
+ (0, import_charts_core15.buildAxis)(
2689
+ {
2690
+ type: "category",
2691
+ data: data.dates,
2692
+ boundaryGap: false,
2693
+ axisLine: { onZero: false },
2694
+ splitLine: { show: false },
2695
+ min: "dataMin",
2696
+ max: "dataMax"
2697
+ },
2698
+ xAxisConfig
2699
+ )
2011
2700
  ];
2012
2701
  const yAxisArr = [
2013
- {
2014
- scale: true,
2015
- splitArea: {
2016
- show: true
2017
- }
2018
- }
2702
+ (0, import_charts_core15.buildAxis)(
2703
+ {
2704
+ scale: true,
2705
+ splitArea: {
2706
+ show: true
2707
+ }
2708
+ },
2709
+ yAxisConfig
2710
+ )
2019
2711
  ];
2020
2712
  if (showVolume && data.volume) {
2021
2713
  gridArr.push({
@@ -2056,37 +2748,59 @@ var CandlestickChart = React16.forwardRef(
2056
2748
  }
2057
2749
  });
2058
2750
  }
2059
- return {
2060
- tooltip: showTooltip ? {
2061
- trigger: "axis",
2062
- axisPointer: {
2063
- type: "cross"
2064
- }
2065
- } : void 0,
2066
- grid: gridArr,
2067
- xAxis: xAxisArr,
2068
- yAxis: yAxisArr,
2069
- dataZoom: [
2070
- {
2071
- type: "inside",
2072
- start: 80,
2073
- end: 100
2074
- },
2075
- {
2076
- show: true,
2077
- type: "slider",
2078
- top: "90%",
2079
- start: 80,
2080
- end: 100
2081
- }
2082
- ],
2083
- series: seriesArr
2084
- };
2085
- }, [data, isEmpty, showTooltip, showVolume, title, chartColors]);
2751
+ return (0, import_charts_core15.mergeChartOption)(
2752
+ {
2753
+ tooltip: (0, import_charts_core15.buildTooltip)(
2754
+ {
2755
+ trigger: "axis",
2756
+ axisPointer: {
2757
+ type: "cross"
2758
+ }
2759
+ },
2760
+ {
2761
+ show: showTooltip,
2762
+ formatter: tooltipFormatter,
2763
+ valueFormatter: tooltipValueFormatter
2764
+ }
2765
+ ),
2766
+ grid: gridArr,
2767
+ xAxis: xAxisArr,
2768
+ yAxis: yAxisArr,
2769
+ dataZoom: [
2770
+ {
2771
+ type: "inside",
2772
+ start: 80,
2773
+ end: 100
2774
+ },
2775
+ {
2776
+ show: true,
2777
+ type: "slider",
2778
+ top: "90%",
2779
+ start: 80,
2780
+ end: 100
2781
+ }
2782
+ ],
2783
+ series: seriesArr
2784
+ },
2785
+ optionOverride
2786
+ );
2787
+ }, [
2788
+ data,
2789
+ isEmpty,
2790
+ showTooltip,
2791
+ showVolume,
2792
+ title,
2793
+ chartColors,
2794
+ xAxisConfig,
2795
+ yAxisConfig,
2796
+ tooltipFormatter,
2797
+ tooltipValueFormatter,
2798
+ optionOverride
2799
+ ]);
2086
2800
  const latest = useLatestRef({ onClick, data });
2087
2801
  const { containerRef } = useChart({
2088
2802
  option,
2089
- theme: currentTheme === import_charts_core12.THEME_DARK ? "dark" : "light",
2803
+ theme: currentTheme === import_charts_core15.THEME_DARK ? "dark" : "light",
2090
2804
  animation,
2091
2805
  onReady: (chart) => {
2092
2806
  chart.on("click", (params) => {
@@ -2109,7 +2823,7 @@ var CandlestickChart = React16.forwardRef(
2109
2823
  }
2110
2824
  });
2111
2825
  const accessibilityDescription = description || (!isEmpty ? `${title}. Candlestick chart with ${data.dates.length} data points.` : void 0);
2112
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2826
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2113
2827
  ChartContainer,
2114
2828
  {
2115
2829
  ref,
@@ -2122,7 +2836,7 @@ var CandlestickChart = React16.forwardRef(
2122
2836
  emptyMessage,
2123
2837
  className: cn(className),
2124
2838
  ...props,
2125
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { ref: containerRef, className: "h-full w-full" })
2839
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { ref: containerRef, className: "h-full w-full" })
2126
2840
  }
2127
2841
  );
2128
2842
  }
@@ -2130,14 +2844,15 @@ var CandlestickChart = React16.forwardRef(
2130
2844
  CandlestickChart.displayName = "CandlestickChart";
2131
2845
 
2132
2846
  // src/components/extended/SankeyChart.tsx
2133
- var React17 = __toESM(require("react"), 1);
2847
+ var React18 = __toESM(require("react"), 1);
2134
2848
  var echarts15 = __toESM(require("echarts/core"), 1);
2135
2849
  var import_charts13 = require("echarts/charts");
2136
2850
  var import_components13 = require("echarts/components");
2137
2851
  var import_renderers13 = require("echarts/renderers");
2138
- var import_jsx_runtime17 = require("react/jsx-runtime");
2852
+ var import_charts_core16 = require("@classic-homes/charts-core");
2853
+ var import_jsx_runtime18 = require("react/jsx-runtime");
2139
2854
  echarts15.use([import_charts13.SankeyChart, import_components13.TooltipComponent, import_components13.TitleComponent, import_renderers13.CanvasRenderer]);
2140
- var SankeyChart = React17.forwardRef(
2855
+ var SankeyChart = React18.forwardRef(
2141
2856
  ({
2142
2857
  title,
2143
2858
  description,
@@ -2150,45 +2865,73 @@ var SankeyChart = React17.forwardRef(
2150
2865
  animation = true,
2151
2866
  showTooltip = true,
2152
2867
  orient = "horizontal",
2868
+ labelFormatter,
2869
+ tooltipFormatter,
2870
+ tooltipValueFormatter,
2871
+ option: optionOverride,
2153
2872
  onClick,
2154
2873
  class: className,
2155
2874
  ...props
2156
2875
  }, ref) => {
2157
2876
  const isEmpty = !data?.nodes?.length || !data?.links?.length;
2158
- const option = React17.useMemo(() => {
2877
+ const option = React18.useMemo(() => {
2159
2878
  if (isEmpty) return {};
2160
- return {
2161
- tooltip: showTooltip ? {
2162
- trigger: "item",
2163
- triggerOn: "mousemove"
2164
- } : void 0,
2165
- series: [
2166
- {
2167
- type: "sankey",
2168
- orient,
2169
- emphasis: {
2170
- focus: "adjacency"
2171
- },
2172
- data: data.nodes.map((node) => ({
2173
- name: node.name,
2174
- itemStyle: node.color ? { color: node.color } : void 0
2175
- })),
2176
- links: data.links.map((link) => ({
2177
- source: link.source,
2178
- target: link.target,
2179
- value: link.value
2180
- })),
2181
- lineStyle: {
2182
- color: "gradient",
2183
- curveness: 0.5
2879
+ return (0, import_charts_core16.mergeChartOption)(
2880
+ {
2881
+ tooltip: (0, import_charts_core16.buildTooltip)(
2882
+ {
2883
+ trigger: "item",
2884
+ triggerOn: "mousemove"
2184
2885
  },
2185
- label: {
2186
- position: orient === "horizontal" ? "right" : "bottom"
2886
+ {
2887
+ show: showTooltip,
2888
+ formatter: tooltipFormatter,
2889
+ valueFormatter: tooltipValueFormatter
2187
2890
  }
2188
- }
2189
- ]
2190
- };
2191
- }, [data, isEmpty, showTooltip, orient]);
2891
+ ),
2892
+ series: [
2893
+ {
2894
+ type: "sankey",
2895
+ orient,
2896
+ emphasis: {
2897
+ focus: "adjacency"
2898
+ },
2899
+ data: data.nodes.map((node) => ({
2900
+ name: node.name,
2901
+ itemStyle: node.color ? { color: node.color } : void 0,
2902
+ label: (0, import_charts_core16.pointLabel)(node.label, labelFormatter)
2903
+ })),
2904
+ links: data.links.map((link) => ({
2905
+ source: link.source,
2906
+ target: link.target,
2907
+ value: link.value
2908
+ })),
2909
+ lineStyle: {
2910
+ color: "gradient",
2911
+ curveness: 0.5
2912
+ },
2913
+ label: (0, import_charts_core16.buildDataLabel)(
2914
+ {
2915
+ position: orient === "horizontal" ? "right" : "bottom"
2916
+ },
2917
+ void 0,
2918
+ { show: true, formatter: labelFormatter, getLabel: (0, import_charts_core16.nameLabelLookup)(data.nodes) }
2919
+ )
2920
+ }
2921
+ ]
2922
+ },
2923
+ optionOverride
2924
+ );
2925
+ }, [
2926
+ data,
2927
+ isEmpty,
2928
+ showTooltip,
2929
+ orient,
2930
+ labelFormatter,
2931
+ tooltipFormatter,
2932
+ tooltipValueFormatter,
2933
+ optionOverride
2934
+ ]);
2192
2935
  const latest = useLatestRef({ onClick });
2193
2936
  const { containerRef } = useChart({
2194
2937
  option,
@@ -2212,7 +2955,7 @@ var SankeyChart = React17.forwardRef(
2212
2955
  }
2213
2956
  });
2214
2957
  const accessibilityDescription = description || (!isEmpty ? `${title}. Sankey diagram with ${data.nodes.length} nodes and ${data.links.length} links.` : void 0);
2215
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2958
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2216
2959
  ChartContainer,
2217
2960
  {
2218
2961
  ref,
@@ -2225,7 +2968,7 @@ var SankeyChart = React17.forwardRef(
2225
2968
  emptyMessage,
2226
2969
  className: cn(className),
2227
2970
  ...props,
2228
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { ref: containerRef, className: "h-full w-full" })
2971
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref: containerRef, className: "h-full w-full" })
2229
2972
  }
2230
2973
  );
2231
2974
  }
@@ -2239,6 +2982,7 @@ SankeyChart.displayName = "SankeyChart";
2239
2982
  ChartContainer,
2240
2983
  ChartEmpty,
2241
2984
  ChartError,
2985
+ ChartLegend,
2242
2986
  ChartSkeleton,
2243
2987
  DonutChart,
2244
2988
  FunnelChart,
@@ -2253,6 +2997,7 @@ SankeyChart.displayName = "SankeyChart";
2253
2997
  cn,
2254
2998
  registerClassicThemes,
2255
2999
  useChart,
3000
+ useChartLegend,
2256
3001
  useChartResize,
2257
3002
  useChartTheme,
2258
3003
  useReducedMotion