@classic-homes/charts-react 0.1.38 → 0.2.1

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
@@ -199,6 +201,30 @@ ChartEmpty.displayName = "ChartEmpty";
199
201
 
200
202
  // src/components/base/ChartContainer.tsx
201
203
  var import_jsx_runtime4 = require("react/jsx-runtime");
204
+ var CHART_ONLY_PROPS = [
205
+ "minHeight",
206
+ "maxHeight",
207
+ "rowHeight",
208
+ "showLegend",
209
+ "showGrid",
210
+ "valueFormat",
211
+ "locale",
212
+ "onReady",
213
+ "events",
214
+ "showDataTable",
215
+ "exportable",
216
+ "patterns",
217
+ "referenceLines",
218
+ "referenceBands",
219
+ "dataZoom",
220
+ "connectNulls",
221
+ "zoomStart"
222
+ ];
223
+ function domProps(props) {
224
+ const rest = { ...props };
225
+ for (const key of CHART_ONLY_PROPS) delete rest[key];
226
+ return rest;
227
+ }
202
228
  var ChartContainer = React4.forwardRef(
203
229
  ({
204
230
  title,
@@ -211,6 +237,8 @@ var ChartContainer = React4.forwardRef(
211
237
  onRetry,
212
238
  className,
213
239
  wrapperClassName,
240
+ legend,
241
+ legendPosition = "bottom",
214
242
  children,
215
243
  ...props
216
244
  }, ref) => {
@@ -233,7 +261,7 @@ var ChartContainer = React4.forwardRef(
233
261
  if (empty) {
234
262
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChartEmpty, { message: emptyMessage, height, className: wrapperClassName });
235
263
  }
236
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
264
+ const chart = /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
237
265
  "div",
238
266
  {
239
267
  ref,
@@ -241,25 +269,96 @@ var ChartContainer = React4.forwardRef(
241
269
  "aria-label": title,
242
270
  "aria-describedby": description ? descriptionId : void 0,
243
271
  "data-testid": "chart-container",
244
- className: cn("rounded-lg border border-border bg-card", wrapperClassName),
245
- ...props,
272
+ className: legend ? "min-h-0 min-w-0 flex-1" : cn("rounded-lg border border-border bg-card", wrapperClassName),
273
+ ...domProps(props),
246
274
  children: [
247
275
  description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { id: descriptionId, className: "sr-only", children: description }),
248
276
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: cn("h-full w-full", className), style: { height: heightStyle }, children })
249
277
  ]
250
278
  }
251
279
  );
280
+ if (!legend) return chart;
281
+ const side = legendPosition === "left" || legendPosition === "right";
282
+ const leading = legendPosition === "top" || legendPosition === "left";
283
+ const legendNode = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
284
+ "div",
285
+ {
286
+ "data-testid": "chart-legend",
287
+ className: cn("flex shrink-0 p-2", side ? "items-center" : "justify-center"),
288
+ children: legend
289
+ }
290
+ );
291
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
292
+ "div",
293
+ {
294
+ className: cn(
295
+ "flex rounded-lg border border-border bg-card",
296
+ side ? "flex-row" : "flex-col",
297
+ wrapperClassName
298
+ ),
299
+ children: [
300
+ leading && legendNode,
301
+ chart,
302
+ !leading && legendNode
303
+ ]
304
+ }
305
+ );
252
306
  }
253
307
  );
254
308
  ChartContainer.displayName = "ChartContainer";
255
309
 
256
- // src/components/core/LineChart.tsx
310
+ // src/components/base/ChartLegend.tsx
257
311
  var React5 = __toESM(require("react"), 1);
312
+ var import_jsx_runtime5 = require("react/jsx-runtime");
313
+ var ChartLegend = React5.forwardRef(
314
+ ({ items, toggle, position = "bottom", chartTitle, orientation, className }, ref) => {
315
+ const vertical = (orientation ?? (position === "left" || position === "right" ? "vertical" : "horizontal")) === "vertical";
316
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
317
+ "ul",
318
+ {
319
+ ref,
320
+ "aria-label": chartTitle ? `${chartTitle} legend` : "Chart legend",
321
+ className: cn(
322
+ "flex gap-1 text-sm",
323
+ vertical ? "flex-col" : "flex-wrap justify-center",
324
+ className
325
+ ),
326
+ children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
327
+ "button",
328
+ {
329
+ type: "button",
330
+ "aria-pressed": item.visible,
331
+ onClick: () => toggle(item.name),
332
+ className: cn(
333
+ "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",
334
+ item.visible ? "text-foreground" : "text-muted-foreground line-through"
335
+ ),
336
+ children: [
337
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
338
+ "span",
339
+ {
340
+ "aria-hidden": "true",
341
+ className: "h-3 w-3 shrink-0 rounded-sm",
342
+ style: { backgroundColor: item.color, opacity: item.visible ? 1 : 0.35 }
343
+ }
344
+ ),
345
+ item.name
346
+ ]
347
+ }
348
+ ) }, item.name))
349
+ }
350
+ );
351
+ }
352
+ );
353
+ ChartLegend.displayName = "ChartLegend";
354
+
355
+ // src/components/core/LineChart.tsx
356
+ var React6 = __toESM(require("react"), 1);
258
357
  var echarts3 = __toESM(require("echarts/core"), 1);
259
358
  var import_charts = require("echarts/charts");
260
359
  var import_components = require("echarts/components");
261
360
  var import_renderers = require("echarts/renderers");
262
- var import_charts_core3 = require("@classic-homes/charts-core");
361
+ var import_charts_core4 = require("@classic-homes/charts-core");
263
362
 
264
363
  // src/hooks/useChart.ts
265
364
  var import_react4 = require("react");
@@ -415,6 +514,10 @@ function useChart({
415
514
  };
416
515
  }
417
516
 
517
+ // src/hooks/useChartLegend.ts
518
+ var import_react6 = require("react");
519
+ var import_charts_core3 = require("@classic-homes/charts-core");
520
+
418
521
  // src/hooks/useLatestRef.ts
419
522
  var import_react5 = require("react");
420
523
  function useLatestRef(value) {
@@ -423,8 +526,62 @@ function useLatestRef(value) {
423
526
  return ref;
424
527
  }
425
528
 
529
+ // src/hooks/useChartLegend.ts
530
+ var LEGEND_EVENTS = [
531
+ "legendselectchanged",
532
+ "legendselected",
533
+ "legendunselected",
534
+ "legendselectall",
535
+ "legendinverseselect"
536
+ ];
537
+ function useChartLegend({
538
+ names,
539
+ colors,
540
+ palette,
541
+ config,
542
+ title,
543
+ onLegendChange
544
+ }) {
545
+ const chartRef = (0, import_react6.useRef)(null);
546
+ const [visible, setVisible] = (0, import_react6.useState)({});
547
+ const latest = useLatestRef({ onLegendChange });
548
+ const attach = (0, import_react6.useCallback)(
549
+ (chart) => {
550
+ chartRef.current = chart;
551
+ setVisible({});
552
+ const handler = (params) => {
553
+ const selected = { ...params.selected };
554
+ setVisible(selected);
555
+ latest.current.onLegendChange?.(selected);
556
+ };
557
+ for (const event of LEGEND_EVENTS) chart.on(event, handler);
558
+ },
559
+ [latest]
560
+ );
561
+ const dispatch = (action) => {
562
+ const chart = chartRef.current;
563
+ if (chart && !chart.isDisposed()) chart.dispatchAction(action);
564
+ };
565
+ return {
566
+ attach,
567
+ renderProps: {
568
+ items: (0, import_charts_core3.getLegendItems)(names, palette, colors, visible),
569
+ position: config?.position ?? "bottom",
570
+ chartTitle: title,
571
+ toggle: (name) => dispatch({ type: "legendToggleSelect", name }),
572
+ setVisible: (name, show) => dispatch({ type: show ? "legendSelect" : "legendUnSelect", name }),
573
+ showOnly: (name) => {
574
+ for (const other of names) {
575
+ dispatch({ type: other === name ? "legendSelect" : "legendUnSelect", name: other });
576
+ }
577
+ },
578
+ showAll: () => dispatch({ type: "legendAllSelect" })
579
+ }
580
+ };
581
+ }
582
+
426
583
  // src/components/core/LineChart.tsx
427
- var import_jsx_runtime5 = require("react/jsx-runtime");
584
+ var import_jsx_runtime6 = require("react/jsx-runtime");
428
585
  echarts3.use([
429
586
  import_charts.LineChart,
430
587
  import_components.GridComponent,
@@ -433,7 +590,7 @@ echarts3.use([
433
590
  import_components.TitleComponent,
434
591
  import_renderers.CanvasRenderer
435
592
  ]);
436
- var LineChart = React5.forwardRef(
593
+ var LineChart = React6.forwardRef(
437
594
  ({
438
595
  title,
439
596
  description,
@@ -451,6 +608,16 @@ var LineChart = React5.forwardRef(
451
608
  areaFilled = false,
452
609
  showDataPoints = false,
453
610
  stacked = false,
611
+ showLabels = false,
612
+ labelFormatter,
613
+ xAxisConfig,
614
+ yAxisConfig,
615
+ tooltipFormatter,
616
+ tooltipValueFormatter,
617
+ legend: legendConfig,
618
+ renderLegend,
619
+ onLegendChange,
620
+ option: optionOverride,
454
621
  onClick,
455
622
  onHover,
456
623
  class: className,
@@ -458,58 +625,93 @@ var LineChart = React5.forwardRef(
458
625
  }, ref) => {
459
626
  const isEmpty = !data?.categories?.length || !data?.series?.length || data.series.every((s) => !s.data?.length);
460
627
  const currentTheme = useChartTheme(theme);
461
- const chartColors = (0, import_charts_core3.getChartColors)(currentTheme);
462
- const option = React5.useMemo(() => {
628
+ const chartColors = (0, import_charts_core4.getChartColors)(currentTheme);
629
+ const legend = useChartLegend({
630
+ names: data?.series?.map((s) => s.name) ?? [],
631
+ colors: data?.series?.map((s) => s.color) ?? [],
632
+ palette: chartColors.series,
633
+ config: legendConfig,
634
+ title,
635
+ onLegendChange
636
+ });
637
+ const customLegend = renderLegend !== void 0;
638
+ const option = React6.useMemo(() => {
463
639
  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
640
+ const getLabel = (0, import_charts_core4.seriesLabelLookup)(data.series);
641
+ const showSymbol = showDataPoints || showLabels || (0, import_charts_core4.hasPointLabels)(data.series);
642
+ return (0, import_charts_core4.mergeChartOption)(
643
+ {
644
+ grid: {
645
+ left: "3%",
646
+ right: "4%",
647
+ bottom: "3%",
648
+ containLabel: true,
649
+ ...(0, import_charts_core4.gridInset)(legendConfig, showLegend && !customLegend, xAxisConfig, yAxisConfig)
650
+ },
651
+ tooltip: (0, import_charts_core4.buildTooltip)(
652
+ {
653
+ trigger: "axis",
654
+ axisPointer: {
655
+ type: "cross",
656
+ label: {
657
+ backgroundColor: chartColors.axisPointerLabel
658
+ }
659
+ }
660
+ },
661
+ {
662
+ show: showTooltip,
663
+ formatter: tooltipFormatter,
664
+ valueFormatter: tooltipValueFormatter,
665
+ getLabel
477
666
  }
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
- }
667
+ ),
668
+ legend: (0, import_charts_core4.buildLegend)(
669
+ data.series.map((s) => s.name),
670
+ legendConfig,
671
+ { show: showLegend, custom: customLegend }
672
+ ),
673
+ xAxis: (0, import_charts_core4.buildAxis)(
674
+ {
675
+ type: "category",
676
+ boundaryGap: false,
677
+ data: data.categories,
678
+ splitLine: {
679
+ show: showGrid
680
+ }
681
+ },
682
+ xAxisConfig
683
+ ),
684
+ yAxis: (0, import_charts_core4.buildAxis)(
685
+ {
686
+ type: "value",
687
+ splitLine: {
688
+ show: showGrid
689
+ }
690
+ },
691
+ yAxisConfig
692
+ ),
693
+ series: data.series.map((series) => ({
694
+ name: series.name,
695
+ type: "line",
696
+ data: (0, import_charts_core4.withPointLabels)(series.data, series.labels, labelFormatter, { position: "top" }),
697
+ smooth,
698
+ showSymbol,
699
+ symbolSize: showDataPoints ? 6 : 0,
700
+ stack: stacked ? "Total" : void 0,
701
+ areaStyle: areaFilled ? { opacity: 0.3 } : void 0,
702
+ itemStyle: series.color ? { color: series.color } : void 0,
703
+ label: (0, import_charts_core4.buildDataLabel)({ show: true, position: "top" }, void 0, {
704
+ show: showLabels,
705
+ formatter: labelFormatter,
706
+ getLabel
707
+ }),
708
+ emphasis: {
709
+ focus: "series"
710
+ }
711
+ }))
497
712
  },
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
- };
713
+ optionOverride
714
+ );
513
715
  }, [
514
716
  data,
515
717
  isEmpty,
@@ -520,14 +722,24 @@ var LineChart = React5.forwardRef(
520
722
  areaFilled,
521
723
  showDataPoints,
522
724
  stacked,
523
- chartColors
725
+ chartColors,
726
+ showLabels,
727
+ labelFormatter,
728
+ xAxisConfig,
729
+ yAxisConfig,
730
+ tooltipFormatter,
731
+ tooltipValueFormatter,
732
+ legendConfig,
733
+ customLegend,
734
+ optionOverride
524
735
  ]);
525
736
  const latest = useLatestRef({ onClick, onHover });
526
737
  const { containerRef } = useChart({
527
738
  option,
528
- theme: currentTheme === import_charts_core3.THEME_DARK ? "dark" : "light",
739
+ theme: currentTheme === import_charts_core4.THEME_DARK ? "dark" : "light",
529
740
  animation,
530
741
  onReady: (chart) => {
742
+ legend.attach(chart);
531
743
  chart.on("click", (params) => {
532
744
  const { onClick: onClick2 } = latest.current;
533
745
  if (!onClick2) return;
@@ -558,8 +770,8 @@ var LineChart = React5.forwardRef(
558
770
  });
559
771
  }
560
772
  });
561
- const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core3.generateLineChartDescription)(title, data) : void 0);
562
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
773
+ const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core4.generateLineChartDescription)(title, data) : void 0);
774
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
563
775
  ChartContainer,
564
776
  {
565
777
  ref,
@@ -571,8 +783,10 @@ var LineChart = React5.forwardRef(
571
783
  empty: isEmpty,
572
784
  emptyMessage,
573
785
  className: cn(className),
786
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
787
+ legendPosition: legendConfig?.position,
574
788
  ...props,
575
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: containerRef, className: "h-full w-full" })
789
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: containerRef, className: "h-full w-full" })
576
790
  }
577
791
  );
578
792
  }
@@ -580,13 +794,13 @@ var LineChart = React5.forwardRef(
580
794
  LineChart.displayName = "LineChart";
581
795
 
582
796
  // src/components/core/BarChart.tsx
583
- var React6 = __toESM(require("react"), 1);
797
+ var React7 = __toESM(require("react"), 1);
584
798
  var echarts4 = __toESM(require("echarts/core"), 1);
585
799
  var import_charts2 = require("echarts/charts");
586
800
  var import_components2 = require("echarts/components");
587
801
  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");
802
+ var import_charts_core5 = require("@classic-homes/charts-core");
803
+ var import_jsx_runtime7 = require("react/jsx-runtime");
590
804
  echarts4.use([
591
805
  import_charts2.BarChart,
592
806
  import_components2.GridComponent,
@@ -595,7 +809,7 @@ echarts4.use([
595
809
  import_components2.TitleComponent,
596
810
  import_renderers2.CanvasRenderer
597
811
  ]);
598
- var BarChart = React6.forwardRef(
812
+ var BarChart = React7.forwardRef(
599
813
  ({
600
814
  title,
601
815
  description,
@@ -612,17 +826,38 @@ var BarChart = React6.forwardRef(
612
826
  orientation = "vertical",
613
827
  stacked = false,
614
828
  showValues = false,
829
+ labelFormatter,
830
+ xAxisConfig,
831
+ yAxisConfig,
832
+ tooltipFormatter,
833
+ tooltipValueFormatter,
834
+ legend: legendConfig,
835
+ renderLegend,
836
+ onLegendChange,
837
+ option: optionOverride,
615
838
  onClick,
616
839
  class: className,
617
840
  ...props
618
841
  }, ref) => {
619
842
  const isEmpty = !data?.categories?.length || !data?.series?.length || data.series.every((s) => !s.data?.length);
620
843
  const isHorizontal = orientation === "horizontal";
621
- const option = React6.useMemo(() => {
844
+ const currentTheme = useChartTheme(theme);
845
+ const legend = useChartLegend({
846
+ names: data?.series?.map((s) => s.name) ?? [],
847
+ colors: data?.series?.map((s) => s.color) ?? [],
848
+ palette: (0, import_charts_core5.getChartColors)(currentTheme).series,
849
+ config: legendConfig,
850
+ title,
851
+ onLegendChange
852
+ });
853
+ const customLegend = renderLegend !== void 0;
854
+ const option = React7.useMemo(() => {
622
855
  if (isEmpty) return {};
623
856
  const categoryAxis = {
624
857
  type: "category",
625
858
  data: data.categories,
859
+ // Horizontal bars list the first category at the top
860
+ inverse: isHorizontal,
626
861
  axisTick: {
627
862
  alignWithLabel: true
628
863
  }
@@ -633,47 +868,85 @@ var BarChart = React6.forwardRef(
633
868
  show: showGrid
634
869
  }
635
870
  };
636
- return {
637
- grid: {
638
- left: "3%",
639
- right: "4%",
640
- bottom: "3%",
641
- containLabel: true
871
+ const getLabel = (0, import_charts_core5.seriesLabelLookup)(data.series);
872
+ return (0, import_charts_core5.mergeChartOption)(
873
+ {
874
+ grid: {
875
+ left: "3%",
876
+ right: "4%",
877
+ bottom: "3%",
878
+ containLabel: true,
879
+ ...(0, import_charts_core5.gridInset)(legendConfig, showLegend && !customLegend, xAxisConfig, yAxisConfig)
880
+ },
881
+ tooltip: (0, import_charts_core5.buildTooltip)(
882
+ {
883
+ trigger: "axis",
884
+ axisPointer: {
885
+ type: "shadow"
886
+ }
887
+ },
888
+ {
889
+ show: showTooltip,
890
+ formatter: tooltipFormatter,
891
+ valueFormatter: tooltipValueFormatter,
892
+ getLabel
893
+ }
894
+ ),
895
+ legend: (0, import_charts_core5.buildLegend)(
896
+ data.series.map((s) => s.name),
897
+ legendConfig,
898
+ { show: showLegend, custom: customLegend }
899
+ ),
900
+ xAxis: (0, import_charts_core5.buildAxis)(isHorizontal ? valueAxis : categoryAxis, xAxisConfig),
901
+ yAxis: (0, import_charts_core5.buildAxis)(isHorizontal ? categoryAxis : valueAxis, yAxisConfig),
902
+ series: data.series.map((series) => ({
903
+ name: series.name,
904
+ type: "bar",
905
+ data: (0, import_charts_core5.withPointLabels)(series.data, series.labels, labelFormatter, {
906
+ position: isHorizontal ? "right" : "top"
907
+ }),
908
+ stack: stacked ? "Total" : void 0,
909
+ itemStyle: series.color ? { color: series.color } : void 0,
910
+ label: (0, import_charts_core5.buildDataLabel)(
911
+ {
912
+ show: true,
913
+ position: isHorizontal ? "right" : "top"
914
+ },
915
+ void 0,
916
+ { show: showValues, formatter: labelFormatter, getLabel }
917
+ ),
918
+ emphasis: {
919
+ focus: "series"
920
+ }
921
+ }))
642
922
  },
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]);
923
+ optionOverride
924
+ );
925
+ }, [
926
+ data,
927
+ isEmpty,
928
+ showTooltip,
929
+ showLegend,
930
+ showGrid,
931
+ isHorizontal,
932
+ stacked,
933
+ showValues,
934
+ labelFormatter,
935
+ xAxisConfig,
936
+ yAxisConfig,
937
+ tooltipFormatter,
938
+ tooltipValueFormatter,
939
+ legendConfig,
940
+ customLegend,
941
+ optionOverride
942
+ ]);
671
943
  const latest = useLatestRef({ onClick });
672
944
  const { containerRef } = useChart({
673
945
  option,
674
946
  theme,
675
947
  animation,
676
948
  onReady: (chart) => {
949
+ legend.attach(chart);
677
950
  chart.on("click", (params) => {
678
951
  const { onClick: onClick2 } = latest.current;
679
952
  if (!onClick2) return;
@@ -690,8 +963,8 @@ var BarChart = React6.forwardRef(
690
963
  });
691
964
  }
692
965
  });
693
- const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core4.generateBarChartDescription)(title, data) : void 0);
694
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
966
+ const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core5.generateBarChartDescription)(title, data) : void 0);
967
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
695
968
  ChartContainer,
696
969
  {
697
970
  ref,
@@ -703,8 +976,10 @@ var BarChart = React6.forwardRef(
703
976
  empty: isEmpty,
704
977
  emptyMessage,
705
978
  className: cn(className),
979
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
980
+ legendPosition: legendConfig?.position,
706
981
  ...props,
707
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: containerRef, className: "h-full w-full" })
982
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: containerRef, className: "h-full w-full" })
708
983
  }
709
984
  );
710
985
  }
@@ -712,15 +987,15 @@ var BarChart = React6.forwardRef(
712
987
  BarChart.displayName = "BarChart";
713
988
 
714
989
  // src/components/core/PieChart.tsx
715
- var React7 = __toESM(require("react"), 1);
990
+ var React8 = __toESM(require("react"), 1);
716
991
  var echarts5 = __toESM(require("echarts/core"), 1);
717
992
  var import_charts3 = require("echarts/charts");
718
993
  var import_components3 = require("echarts/components");
719
994
  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");
995
+ var import_charts_core6 = require("@classic-homes/charts-core");
996
+ var import_jsx_runtime8 = require("react/jsx-runtime");
722
997
  echarts5.use([import_charts3.PieChart, import_components3.TooltipComponent, import_components3.LegendComponent, import_components3.TitleComponent, import_renderers3.CanvasRenderer]);
723
- var PieChart = React7.forwardRef(
998
+ var PieChart = React8.forwardRef(
724
999
  ({
725
1000
  title,
726
1001
  description,
@@ -736,58 +1011,107 @@ var PieChart = React7.forwardRef(
736
1011
  showLabels = true,
737
1012
  labelPosition = "outside",
738
1013
  innerRadius = 0,
1014
+ labelFormatter,
1015
+ tooltipFormatter,
1016
+ tooltipValueFormatter,
1017
+ legend: legendConfig,
1018
+ renderLegend,
1019
+ onLegendChange,
1020
+ option: optionOverride,
739
1021
  onClick,
740
1022
  class: className,
741
1023
  ...props
742
1024
  }, ref) => {
743
1025
  const isEmpty = !data?.length;
744
- const option = React7.useMemo(() => {
1026
+ const currentTheme = useChartTheme(theme);
1027
+ const legend = useChartLegend({
1028
+ names: data?.map((d) => d.name) ?? [],
1029
+ colors: data?.map((d) => d.color) ?? [],
1030
+ palette: (0, import_charts_core6.getChartColors)(currentTheme).series,
1031
+ config: legendConfig,
1032
+ title,
1033
+ onLegendChange
1034
+ });
1035
+ const customLegend = renderLegend !== void 0;
1036
+ const option = React8.useMemo(() => {
745
1037
  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)"
1038
+ const valueFormatter = tooltipFormatter === void 0 ? tooltipValueFormatter : void 0;
1039
+ const tooltipBase = {
1040
+ trigger: "item",
1041
+ formatter: valueFormatter ? (params) => {
1042
+ const p = params;
1043
+ 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}%)`;
1044
+ } : "{a} <br/>{b}: {c} ({d}%)"
1045
+ };
1046
+ return (0, import_charts_core6.mergeChartOption)(
1047
+ {
1048
+ tooltip: (0, import_charts_core6.buildTooltip)(tooltipBase, {
1049
+ show: showTooltip,
1050
+ formatter: tooltipFormatter,
1051
+ valueFormatter: tooltipValueFormatter,
1052
+ getLabel: (0, import_charts_core6.itemLabelLookup)(data)
1053
+ }),
1054
+ legend: (0, import_charts_core6.buildLegend)(
1055
+ data.map((d) => d.name),
1056
+ legendConfig,
1057
+ { show: showLegend, custom: customLegend, defaults: { orient: "horizontal" } }
1058
+ ),
1059
+ series: [
1060
+ {
1061
+ name: title,
1062
+ type: "pie",
1063
+ radius: innerRadius ? [innerRadius, "70%"] : "70%",
1064
+ center: (0, import_charts_core6.legendCenter)(legendConfig, showLegend && !customLegend, ["50%", "45%"]),
1065
+ data: data.map((item) => ({
1066
+ name: item.name,
1067
+ value: item.value,
1068
+ itemStyle: item.color ? { color: item.color } : void 0,
1069
+ label: (0, import_charts_core6.pointLabel)(item.label, labelFormatter, { position: labelPosition })
1070
+ })),
1071
+ label: (0, import_charts_core6.buildDataLabel)(
1072
+ {
1073
+ show: true,
1074
+ position: labelPosition,
1075
+ formatter: labelPosition === "inside" ? "{d}%" : "{b}: {d}%"
1076
+ },
1077
+ { show: false },
1078
+ { show: showLabels, formatter: labelFormatter, getLabel: (0, import_charts_core6.itemLabelLookup)(data) }
1079
+ ),
1080
+ emphasis: {
1081
+ itemStyle: {
1082
+ shadowBlur: 10,
1083
+ shadowOffsetX: 0,
1084
+ shadowColor: "rgba(0, 0, 0, 0.5)"
1085
+ }
779
1086
  }
780
1087
  }
781
- }
782
- ]
783
- };
784
- }, [data, isEmpty, showTooltip, showLegend, showLabels, labelPosition, innerRadius, title]);
1088
+ ]
1089
+ },
1090
+ optionOverride
1091
+ );
1092
+ }, [
1093
+ data,
1094
+ isEmpty,
1095
+ showTooltip,
1096
+ showLegend,
1097
+ showLabels,
1098
+ labelPosition,
1099
+ innerRadius,
1100
+ title,
1101
+ labelFormatter,
1102
+ tooltipFormatter,
1103
+ tooltipValueFormatter,
1104
+ legendConfig,
1105
+ customLegend,
1106
+ optionOverride
1107
+ ]);
785
1108
  const latest = useLatestRef({ onClick });
786
1109
  const { containerRef } = useChart({
787
1110
  option,
788
1111
  theme,
789
1112
  animation,
790
1113
  onReady: (chart) => {
1114
+ legend.attach(chart);
791
1115
  chart.on("click", (params) => {
792
1116
  const { onClick: onClick2 } = latest.current;
793
1117
  if (!onClick2) return;
@@ -802,8 +1126,8 @@ var PieChart = React7.forwardRef(
802
1126
  });
803
1127
  }
804
1128
  });
805
- const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core5.generatePieChartDescription)(title, data, false) : void 0);
806
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1129
+ const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core6.generatePieChartDescription)(title, data, false) : void 0);
1130
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
807
1131
  ChartContainer,
808
1132
  {
809
1133
  ref,
@@ -815,8 +1139,10 @@ var PieChart = React7.forwardRef(
815
1139
  empty: isEmpty,
816
1140
  emptyMessage,
817
1141
  className: cn(className),
1142
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
1143
+ legendPosition: legendConfig?.position,
818
1144
  ...props,
819
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1145
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { ref: containerRef, className: "h-full w-full" })
820
1146
  }
821
1147
  );
822
1148
  }
@@ -824,13 +1150,13 @@ var PieChart = React7.forwardRef(
824
1150
  PieChart.displayName = "PieChart";
825
1151
 
826
1152
  // src/components/core/AreaChart.tsx
827
- var React8 = __toESM(require("react"), 1);
1153
+ var React9 = __toESM(require("react"), 1);
828
1154
  var echarts6 = __toESM(require("echarts/core"), 1);
829
1155
  var import_charts4 = require("echarts/charts");
830
1156
  var import_components4 = require("echarts/components");
831
1157
  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");
1158
+ var import_charts_core7 = require("@classic-homes/charts-core");
1159
+ var import_jsx_runtime9 = require("react/jsx-runtime");
834
1160
  echarts6.use([
835
1161
  import_charts4.LineChart,
836
1162
  import_components4.GridComponent,
@@ -839,7 +1165,7 @@ echarts6.use([
839
1165
  import_components4.TitleComponent,
840
1166
  import_renderers4.CanvasRenderer
841
1167
  ]);
842
- var AreaChart = React8.forwardRef(
1168
+ var AreaChart = React9.forwardRef(
843
1169
  ({
844
1170
  title,
845
1171
  description,
@@ -856,76 +1182,143 @@ var AreaChart = React8.forwardRef(
856
1182
  smooth = false,
857
1183
  stacked = false,
858
1184
  gradient = true,
1185
+ showLabels = false,
1186
+ labelFormatter,
1187
+ xAxisConfig,
1188
+ yAxisConfig,
1189
+ tooltipFormatter,
1190
+ tooltipValueFormatter,
1191
+ legend: legendConfig,
1192
+ renderLegend,
1193
+ onLegendChange,
1194
+ option: optionOverride,
859
1195
  onClick,
860
1196
  class: className,
861
1197
  ...props
862
1198
  }, ref) => {
863
1199
  const isEmpty = !data?.categories?.length || !data?.series?.length || data.series.every((s) => !s.data?.length);
864
1200
  const currentTheme = useChartTheme(theme);
865
- const chartColors = (0, import_charts_core6.getChartColors)(currentTheme);
866
- const option = React8.useMemo(() => {
1201
+ const chartColors = (0, import_charts_core7.getChartColors)(currentTheme);
1202
+ const legend = useChartLegend({
1203
+ names: data?.series?.map((s) => s.name) ?? [],
1204
+ colors: data?.series?.map((s) => s.color) ?? [],
1205
+ palette: chartColors.series,
1206
+ config: legendConfig,
1207
+ title,
1208
+ onLegendChange
1209
+ });
1210
+ const customLegend = renderLegend !== void 0;
1211
+ const option = React9.useMemo(() => {
867
1212
  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
1213
+ const getLabel = (0, import_charts_core7.seriesLabelLookup)(data.series);
1214
+ const showSymbol = showLabels || (0, import_charts_core7.hasPointLabels)(data.series);
1215
+ return (0, import_charts_core7.mergeChartOption)(
1216
+ {
1217
+ grid: {
1218
+ left: "3%",
1219
+ right: "4%",
1220
+ bottom: "3%",
1221
+ containLabel: true,
1222
+ ...(0, import_charts_core7.gridInset)(legendConfig, showLegend && !customLegend, xAxisConfig, yAxisConfig)
1223
+ },
1224
+ tooltip: (0, import_charts_core7.buildTooltip)(
1225
+ {
1226
+ trigger: "axis",
1227
+ axisPointer: {
1228
+ type: "cross",
1229
+ label: {
1230
+ backgroundColor: chartColors.axisPointerLabel
1231
+ }
1232
+ }
1233
+ },
1234
+ {
1235
+ show: showTooltip,
1236
+ formatter: tooltipFormatter,
1237
+ valueFormatter: tooltipValueFormatter,
1238
+ getLabel
881
1239
  }
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"
1240
+ ),
1241
+ legend: (0, import_charts_core7.buildLegend)(
1242
+ data.series.map((s) => s.name),
1243
+ legendConfig,
1244
+ { show: showLegend, custom: customLegend }
1245
+ ),
1246
+ xAxis: (0, import_charts_core7.buildAxis)(
1247
+ {
1248
+ type: "category",
1249
+ boundaryGap: false,
1250
+ data: data.categories
917
1251
  },
918
- showSymbol: false
919
- };
920
- })
921
- };
922
- }, [data, isEmpty, showTooltip, showLegend, showGrid, smooth, stacked, gradient, chartColors]);
1252
+ xAxisConfig
1253
+ ),
1254
+ yAxis: (0, import_charts_core7.buildAxis)(
1255
+ {
1256
+ type: "value",
1257
+ splitLine: {
1258
+ show: showGrid
1259
+ }
1260
+ },
1261
+ yAxisConfig
1262
+ ),
1263
+ series: data.series.map((series, index) => {
1264
+ const base = series.color || chartColors.series[index % chartColors.series.length];
1265
+ return {
1266
+ name: series.name,
1267
+ type: "line",
1268
+ data: (0, import_charts_core7.withPointLabels)(series.data, series.labels, labelFormatter, {
1269
+ position: "top"
1270
+ }),
1271
+ smooth,
1272
+ stack: stacked ? "Total" : void 0,
1273
+ areaStyle: gradient ? {
1274
+ opacity: 0.8,
1275
+ color: new echarts6.graphic.LinearGradient(0, 0, 0, 1, [
1276
+ { offset: 0, color: (0, import_charts_core7.colorWithAlpha)(base, 0.8) },
1277
+ { offset: 1, color: (0, import_charts_core7.colorWithAlpha)(base, 0.1) }
1278
+ ])
1279
+ } : { opacity: 0.5 },
1280
+ itemStyle: series.color ? { color: series.color } : void 0,
1281
+ label: (0, import_charts_core7.buildDataLabel)({ show: true, position: "top" }, void 0, {
1282
+ show: showLabels,
1283
+ formatter: labelFormatter,
1284
+ getLabel
1285
+ }),
1286
+ emphasis: {
1287
+ focus: "series"
1288
+ },
1289
+ showSymbol
1290
+ };
1291
+ })
1292
+ },
1293
+ optionOverride
1294
+ );
1295
+ }, [
1296
+ data,
1297
+ isEmpty,
1298
+ showTooltip,
1299
+ showLegend,
1300
+ showGrid,
1301
+ smooth,
1302
+ stacked,
1303
+ gradient,
1304
+ chartColors,
1305
+ showLabels,
1306
+ labelFormatter,
1307
+ xAxisConfig,
1308
+ yAxisConfig,
1309
+ tooltipFormatter,
1310
+ tooltipValueFormatter,
1311
+ legendConfig,
1312
+ customLegend,
1313
+ optionOverride
1314
+ ]);
923
1315
  const latest = useLatestRef({ onClick });
924
1316
  const { containerRef } = useChart({
925
1317
  option,
926
- theme: currentTheme === import_charts_core6.THEME_DARK ? "dark" : "light",
1318
+ theme: currentTheme === import_charts_core7.THEME_DARK ? "dark" : "light",
927
1319
  animation,
928
1320
  onReady: (chart) => {
1321
+ legend.attach(chart);
929
1322
  chart.on("click", (params) => {
930
1323
  const { onClick: onClick2 } = latest.current;
931
1324
  if (!onClick2) return;
@@ -943,7 +1336,7 @@ var AreaChart = React8.forwardRef(
943
1336
  }
944
1337
  });
945
1338
  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)(
1339
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
947
1340
  ChartContainer,
948
1341
  {
949
1342
  ref,
@@ -955,8 +1348,10 @@ var AreaChart = React8.forwardRef(
955
1348
  empty: isEmpty,
956
1349
  emptyMessage,
957
1350
  className: cn(className),
1351
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
1352
+ legendPosition: legendConfig?.position,
958
1353
  ...props,
959
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1354
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { ref: containerRef, className: "h-full w-full" })
960
1355
  }
961
1356
  );
962
1357
  }
@@ -964,13 +1359,13 @@ var AreaChart = React8.forwardRef(
964
1359
  AreaChart.displayName = "AreaChart";
965
1360
 
966
1361
  // src/components/core/ScatterChart.tsx
967
- var React9 = __toESM(require("react"), 1);
1362
+ var React10 = __toESM(require("react"), 1);
968
1363
  var echarts7 = __toESM(require("echarts/core"), 1);
969
1364
  var import_charts5 = require("echarts/charts");
970
1365
  var import_components5 = require("echarts/components");
971
1366
  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");
1367
+ var import_charts_core8 = require("@classic-homes/charts-core");
1368
+ var import_jsx_runtime10 = require("react/jsx-runtime");
974
1369
  echarts7.use([
975
1370
  import_charts5.ScatterChart,
976
1371
  // Trend lines are line series
@@ -981,7 +1376,7 @@ echarts7.use([
981
1376
  import_components5.TitleComponent,
982
1377
  import_renderers5.CanvasRenderer
983
1378
  ]);
984
- var ScatterChart = React9.forwardRef(
1379
+ var ScatterChart = React10.forwardRef(
985
1380
  ({
986
1381
  title,
987
1382
  description,
@@ -997,19 +1392,45 @@ var ScatterChart = React9.forwardRef(
997
1392
  showGrid = true,
998
1393
  showTrendLine = false,
999
1394
  symbolSize = 10,
1395
+ showLabels = false,
1396
+ labelFormatter,
1397
+ xAxisConfig,
1398
+ yAxisConfig,
1399
+ tooltipFormatter,
1400
+ tooltipValueFormatter,
1401
+ legend: legendConfig,
1402
+ renderLegend,
1403
+ onLegendChange,
1404
+ option: optionOverride,
1000
1405
  onClick,
1001
1406
  class: className,
1002
1407
  ...props
1003
1408
  }, ref) => {
1004
1409
  const isEmpty = !data?.series?.length || data.series.every((s) => !s.data?.length);
1005
- const option = React9.useMemo(() => {
1410
+ const currentTheme = useChartTheme(theme);
1411
+ const legend = useChartLegend({
1412
+ names: data?.series?.map((s) => s.name) ?? [],
1413
+ colors: data?.series?.map((s) => s.color) ?? [],
1414
+ palette: (0, import_charts_core8.getChartColors)(currentTheme).series,
1415
+ config: legendConfig,
1416
+ title,
1417
+ onLegendChange
1418
+ });
1419
+ const customLegend = renderLegend !== void 0;
1420
+ const option = React10.useMemo(() => {
1006
1421
  if (isEmpty) return {};
1422
+ const getLabel = (0, import_charts_core8.seriesLabelLookup)(data.series);
1007
1423
  const series = data.series.map((s) => ({
1008
1424
  name: s.name,
1009
1425
  type: "scatter",
1010
- data: s.data,
1426
+ data: (0, import_charts_core8.withPointLabels)(s.data, s.labels, labelFormatter, { position: "top" }),
1011
1427
  symbolSize: typeof symbolSize === "function" ? symbolSize : symbolSize,
1012
1428
  itemStyle: s.color ? { color: s.color } : void 0,
1429
+ label: (0, import_charts_core8.buildDataLabel)({ show: true, position: "top" }, void 0, {
1430
+ show: showLabels,
1431
+ formatter: labelFormatter,
1432
+ getLabel
1433
+ }),
1013
1434
  emphasis: {
1014
1435
  focus: "series",
1015
1436
  itemStyle: {
@@ -1057,46 +1478,85 @@ var ScatterChart = React9.forwardRef(
1057
1478
  });
1058
1479
  });
1059
1480
  }
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
- }
1481
+ return (0, import_charts_core8.mergeChartOption)(
1482
+ {
1483
+ grid: {
1484
+ left: "3%",
1485
+ right: "4%",
1486
+ bottom: "3%",
1487
+ containLabel: true,
1488
+ ...(0, import_charts_core8.gridInset)(legendConfig, showLegend && !customLegend, xAxisConfig, yAxisConfig)
1489
+ },
1490
+ tooltip: (0, import_charts_core8.buildTooltip)(
1491
+ {
1492
+ trigger: "item",
1493
+ formatter: (params) => {
1494
+ const p = params;
1495
+ if (!p.value) return "";
1496
+ const x = (0, import_charts_core8.formatTooltipValue)(p.value[0], tooltipValueFormatter);
1497
+ const y = (0, import_charts_core8.formatTooltipValue)(p.value[1], tooltipValueFormatter);
1498
+ return `${(0, import_charts_core8.escapeHtml)(p.seriesName)}<br/>X: ${(0, import_charts_core8.escapeHtml)(x)}<br/>Y: ${(0, import_charts_core8.escapeHtml)(y)}`;
1499
+ }
1500
+ },
1501
+ {
1502
+ show: showTooltip,
1503
+ formatter: tooltipFormatter,
1504
+ valueFormatter: tooltipValueFormatter,
1505
+ getLabel
1506
+ }
1507
+ ),
1508
+ legend: (0, import_charts_core8.buildLegend)(
1509
+ data.series.map((s) => s.name),
1510
+ legendConfig,
1511
+ { show: showLegend, custom: customLegend }
1512
+ ),
1513
+ xAxis: (0, import_charts_core8.buildAxis)(
1514
+ {
1515
+ type: "value",
1516
+ splitLine: {
1517
+ show: showGrid
1518
+ }
1519
+ },
1520
+ xAxisConfig
1521
+ ),
1522
+ yAxis: (0, import_charts_core8.buildAxis)(
1523
+ {
1524
+ type: "value",
1525
+ splitLine: {
1526
+ show: showGrid
1527
+ }
1528
+ },
1529
+ yAxisConfig
1530
+ ),
1531
+ series
1090
1532
  },
1091
- series
1092
- };
1093
- }, [data, isEmpty, showTooltip, showLegend, showGrid, showTrendLine, symbolSize]);
1533
+ optionOverride
1534
+ );
1535
+ }, [
1536
+ data,
1537
+ isEmpty,
1538
+ showTooltip,
1539
+ showLegend,
1540
+ showGrid,
1541
+ showTrendLine,
1542
+ symbolSize,
1543
+ showLabels,
1544
+ labelFormatter,
1545
+ xAxisConfig,
1546
+ yAxisConfig,
1547
+ tooltipFormatter,
1548
+ tooltipValueFormatter,
1549
+ legendConfig,
1550
+ customLegend,
1551
+ optionOverride
1552
+ ]);
1094
1553
  const latest = useLatestRef({ onClick });
1095
1554
  const { containerRef } = useChart({
1096
1555
  option,
1097
1556
  theme,
1098
1557
  animation,
1099
1558
  onReady: (chart) => {
1559
+ legend.attach(chart);
1100
1560
  chart.on("click", (params) => {
1101
1561
  const { onClick: onClick2 } = latest.current;
1102
1562
  if (!onClick2) return;
@@ -1113,8 +1573,8 @@ var ScatterChart = React9.forwardRef(
1113
1573
  });
1114
1574
  }
1115
1575
  });
1116
- const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core7.generateScatterChartDescription)(title, data) : void 0);
1117
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1576
+ const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core8.generateScatterChartDescription)(title, data) : void 0);
1577
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1118
1578
  ChartContainer,
1119
1579
  {
1120
1580
  ref,
@@ -1126,8 +1586,10 @@ var ScatterChart = React9.forwardRef(
1126
1586
  empty: isEmpty,
1127
1587
  emptyMessage,
1128
1588
  className: cn(className),
1589
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
1590
+ legendPosition: legendConfig?.position,
1129
1591
  ...props,
1130
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1592
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1131
1593
  }
1132
1594
  );
1133
1595
  }
@@ -1135,15 +1597,15 @@ var ScatterChart = React9.forwardRef(
1135
1597
  ScatterChart.displayName = "ScatterChart";
1136
1598
 
1137
1599
  // src/components/core/DonutChart.tsx
1138
- var React10 = __toESM(require("react"), 1);
1600
+ var React11 = __toESM(require("react"), 1);
1139
1601
  var echarts8 = __toESM(require("echarts/core"), 1);
1140
1602
  var import_charts6 = require("echarts/charts");
1141
1603
  var import_components6 = require("echarts/components");
1142
1604
  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");
1605
+ var import_charts_core9 = require("@classic-homes/charts-core");
1606
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1145
1607
  echarts8.use([import_charts6.PieChart, import_components6.TooltipComponent, import_components6.LegendComponent, import_components6.TitleComponent, import_renderers6.CanvasRenderer]);
1146
- var DonutChart = React10.forwardRef(
1608
+ var DonutChart = React11.forwardRef(
1147
1609
  ({
1148
1610
  title,
1149
1611
  description,
@@ -1161,95 +1623,134 @@ var DonutChart = React10.forwardRef(
1161
1623
  innerRadius = "50%",
1162
1624
  centerLabel,
1163
1625
  centerValue,
1626
+ labelFormatter,
1627
+ tooltipFormatter,
1628
+ tooltipValueFormatter,
1629
+ legend: legendConfig,
1630
+ renderLegend,
1631
+ onLegendChange,
1632
+ option: optionOverride,
1164
1633
  onClick,
1165
1634
  class: className,
1166
1635
  ...props
1167
1636
  }, ref) => {
1168
1637
  const isEmpty = !data?.length;
1169
1638
  const currentTheme = useChartTheme(theme);
1170
- const chartColors = (0, import_charts_core8.getChartColors)(currentTheme);
1171
- const option = React10.useMemo(() => {
1639
+ const chartColors = (0, import_charts_core9.getChartColors)(currentTheme);
1640
+ const legend = useChartLegend({
1641
+ names: data?.map((d) => d.name) ?? [],
1642
+ colors: data?.map((d) => d.color) ?? [],
1643
+ palette: chartColors.series,
1644
+ config: legendConfig,
1645
+ title,
1646
+ onLegendChange
1647
+ });
1648
+ const customLegend = renderLegend !== void 0;
1649
+ const option = React11.useMemo(() => {
1172
1650
  if (isEmpty) return {};
1651
+ const pieCenter = (0, import_charts_core9.legendCenter)(legendConfig, showLegend && !customLegend, ["50%", "45%"]);
1173
1652
  const graphicElements = [];
1174
1653
  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"
1654
+ graphicElements.push(
1655
+ (0, import_charts_core9.anchorCenterGraphic)(
1656
+ {
1657
+ type: "group",
1658
+ left: "center",
1659
+ top: "40%",
1660
+ children: [
1661
+ ...centerValue !== void 0 ? [
1662
+ {
1663
+ type: "text",
1664
+ style: {
1665
+ text: String(centerValue),
1666
+ fontSize: 28,
1667
+ fontWeight: "bold",
1668
+ fill: chartColors.foreground,
1669
+ textAlign: "center"
1670
+ },
1671
+ top: 0,
1672
+ left: "center"
1673
+ }
1674
+ ] : [],
1675
+ ...centerLabel ? [
1676
+ {
1677
+ type: "text",
1678
+ style: {
1679
+ text: centerLabel,
1680
+ fontSize: 14,
1681
+ fill: chartColors.mutedForeground,
1682
+ textAlign: "center"
1683
+ },
1684
+ top: centerValue !== void 0 ? 35 : 0,
1685
+ left: "center"
1686
+ }
1687
+ ] : []
1688
+ ]
1242
1689
  },
1243
- emphasis: {
1244
- itemStyle: {
1245
- shadowBlur: 10,
1246
- shadowOffsetX: 0,
1247
- shadowColor: "rgba(0, 0, 0, 0.5)"
1690
+ pieCenter,
1691
+ ["50%", "45%"]
1692
+ )
1693
+ );
1694
+ }
1695
+ const valueFormatter = tooltipFormatter === void 0 ? tooltipValueFormatter : void 0;
1696
+ const tooltipBase = {
1697
+ trigger: "item",
1698
+ formatter: valueFormatter ? (params) => {
1699
+ const p = params;
1700
+ 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}%)`;
1701
+ } : "{a} <br/>{b}: {c} ({d}%)"
1702
+ };
1703
+ return (0, import_charts_core9.mergeChartOption)(
1704
+ {
1705
+ tooltip: (0, import_charts_core9.buildTooltip)(tooltipBase, {
1706
+ show: showTooltip,
1707
+ formatter: tooltipFormatter,
1708
+ valueFormatter: tooltipValueFormatter,
1709
+ getLabel: (0, import_charts_core9.itemLabelLookup)(data)
1710
+ }),
1711
+ legend: (0, import_charts_core9.buildLegend)(
1712
+ data.map((d) => d.name),
1713
+ legendConfig,
1714
+ { show: showLegend, custom: customLegend, defaults: { orient: "horizontal" } }
1715
+ ),
1716
+ graphic: graphicElements.length > 0 ? graphicElements : void 0,
1717
+ series: [
1718
+ {
1719
+ name: title,
1720
+ type: "pie",
1721
+ radius: [innerRadius, "70%"],
1722
+ center: pieCenter,
1723
+ avoidLabelOverlap: true,
1724
+ data: data.map((item) => ({
1725
+ name: item.name,
1726
+ value: item.value,
1727
+ itemStyle: item.color ? { color: item.color } : void 0,
1728
+ label: (0, import_charts_core9.pointLabel)(item.label, labelFormatter, { position: labelPosition })
1729
+ })),
1730
+ label: (0, import_charts_core9.buildDataLabel)(
1731
+ {
1732
+ show: true,
1733
+ position: labelPosition,
1734
+ formatter: labelPosition === "inside" ? "{d}%" : "{b}: {d}%"
1735
+ },
1736
+ { show: false },
1737
+ { show: showLabels, formatter: labelFormatter, getLabel: (0, import_charts_core9.itemLabelLookup)(data) }
1738
+ ),
1739
+ labelLine: {
1740
+ show: showLabels && labelPosition === "outside"
1741
+ },
1742
+ emphasis: {
1743
+ itemStyle: {
1744
+ shadowBlur: 10,
1745
+ shadowOffsetX: 0,
1746
+ shadowColor: "rgba(0, 0, 0, 0.5)"
1747
+ }
1248
1748
  }
1249
1749
  }
1250
- }
1251
- ]
1252
- };
1750
+ ]
1751
+ },
1752
+ optionOverride
1753
+ );
1253
1754
  }, [
1254
1755
  data,
1255
1756
  isEmpty,
@@ -1261,14 +1762,21 @@ var DonutChart = React10.forwardRef(
1261
1762
  centerLabel,
1262
1763
  centerValue,
1263
1764
  title,
1264
- chartColors
1765
+ chartColors,
1766
+ labelFormatter,
1767
+ tooltipFormatter,
1768
+ tooltipValueFormatter,
1769
+ legendConfig,
1770
+ customLegend,
1771
+ optionOverride
1265
1772
  ]);
1266
1773
  const latest = useLatestRef({ onClick });
1267
1774
  const { containerRef } = useChart({
1268
1775
  option,
1269
- theme: currentTheme === import_charts_core8.THEME_DARK ? "dark" : "light",
1776
+ theme: currentTheme === import_charts_core9.THEME_DARK ? "dark" : "light",
1270
1777
  animation,
1271
1778
  onReady: (chart) => {
1779
+ legend.attach(chart);
1272
1780
  chart.on("click", (params) => {
1273
1781
  const { onClick: onClick2 } = latest.current;
1274
1782
  if (!onClick2) return;
@@ -1283,8 +1791,8 @@ var DonutChart = React10.forwardRef(
1283
1791
  });
1284
1792
  }
1285
1793
  });
1286
- const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core8.generatePieChartDescription)(title, data, true) : void 0);
1287
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1794
+ const accessibilityDescription = description || (!isEmpty ? (0, import_charts_core9.generatePieChartDescription)(title, data, true) : void 0);
1795
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1288
1796
  ChartContainer,
1289
1797
  {
1290
1798
  ref,
@@ -1296,8 +1804,10 @@ var DonutChart = React10.forwardRef(
1296
1804
  empty: isEmpty,
1297
1805
  emptyMessage,
1298
1806
  className: cn(className),
1807
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
1808
+ legendPosition: legendConfig?.position,
1299
1809
  ...props,
1300
- children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1810
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1301
1811
  }
1302
1812
  );
1303
1813
  }
@@ -1305,12 +1815,13 @@ var DonutChart = React10.forwardRef(
1305
1815
  DonutChart.displayName = "DonutChart";
1306
1816
 
1307
1817
  // src/components/extended/RadarChart.tsx
1308
- var React11 = __toESM(require("react"), 1);
1818
+ var React12 = __toESM(require("react"), 1);
1309
1819
  var echarts9 = __toESM(require("echarts/core"), 1);
1310
1820
  var import_charts7 = require("echarts/charts");
1311
1821
  var import_components7 = require("echarts/components");
1312
1822
  var import_renderers7 = require("echarts/renderers");
1313
- var import_jsx_runtime11 = require("react/jsx-runtime");
1823
+ var import_charts_core10 = require("@classic-homes/charts-core");
1824
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1314
1825
  echarts9.use([
1315
1826
  import_charts7.RadarChart,
1316
1827
  import_components7.TooltipComponent,
@@ -1319,7 +1830,7 @@ echarts9.use([
1319
1830
  import_components7.RadarComponent,
1320
1831
  import_renderers7.CanvasRenderer
1321
1832
  ]);
1322
- var RadarChart = React11.forwardRef(
1833
+ var RadarChart = React12.forwardRef(
1323
1834
  ({
1324
1835
  title,
1325
1836
  description,
@@ -1334,50 +1845,117 @@ var RadarChart = React11.forwardRef(
1334
1845
  showLegend = true,
1335
1846
  showTooltip = true,
1336
1847
  shape = "polygon",
1848
+ showLabels = false,
1849
+ labelFormatter,
1850
+ tooltipFormatter,
1851
+ tooltipValueFormatter,
1852
+ legend: legendConfig,
1853
+ renderLegend,
1854
+ onLegendChange,
1855
+ option: optionOverride,
1856
+ onClick,
1337
1857
  class: className,
1338
1858
  ...props
1339
1859
  }, ref) => {
1340
1860
  const isEmpty = !data?.series?.length || !indicators?.length || data.series.every((s) => !s.data?.length);
1341
- const option = React11.useMemo(() => {
1861
+ const currentTheme = useChartTheme(theme);
1862
+ const legend = useChartLegend({
1863
+ names: data?.series?.map((s) => s.name) ?? [],
1864
+ colors: data?.series?.map((s) => s.color) ?? [],
1865
+ palette: (0, import_charts_core10.getChartColors)(currentTheme).series,
1866
+ config: legendConfig,
1867
+ title,
1868
+ onLegendChange
1869
+ });
1870
+ const customLegend = renderLegend !== void 0;
1871
+ const option = React12.useMemo(() => {
1342
1872
  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
1873
+ return (0, import_charts_core10.mergeChartOption)(
1874
+ {
1875
+ tooltip: (0, import_charts_core10.buildTooltip)(
1876
+ {
1877
+ trigger: "item"
1878
+ },
1879
+ {
1880
+ show: showTooltip,
1881
+ formatter: tooltipFormatter,
1882
+ valueFormatter: tooltipValueFormatter
1883
+ }
1884
+ ),
1885
+ legend: (0, import_charts_core10.buildLegend)(
1886
+ data.series.map((s) => s.name),
1887
+ legendConfig,
1888
+ { show: showLegend, custom: customLegend }
1889
+ ),
1890
+ radar: {
1891
+ indicator: indicators.map((ind) => ({
1892
+ name: ind.name,
1893
+ max: ind.max,
1894
+ min: ind.min
1895
+ })),
1896
+ shape,
1897
+ center: (0, import_charts_core10.legendCenter)(legendConfig, showLegend && !customLegend, ["50%", "50%"])
1898
+ },
1899
+ series: [
1900
+ {
1901
+ type: "radar",
1902
+ label: (0, import_charts_core10.buildDataLabel)({ show: true }, void 0, {
1903
+ show: showLabels,
1904
+ formatter: labelFormatter
1905
+ }),
1906
+ data: data.series.map((series) => ({
1907
+ name: series.name,
1908
+ value: series.data,
1909
+ itemStyle: series.color ? { color: series.color } : void 0,
1910
+ areaStyle: {
1911
+ opacity: 0.2
1912
+ }
1913
+ }))
1914
+ }
1915
+ ]
1358
1916
  },
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]);
1917
+ optionOverride
1918
+ );
1919
+ }, [
1920
+ data,
1921
+ indicators,
1922
+ isEmpty,
1923
+ showTooltip,
1924
+ showLegend,
1925
+ shape,
1926
+ showLabels,
1927
+ labelFormatter,
1928
+ tooltipFormatter,
1929
+ tooltipValueFormatter,
1930
+ legendConfig,
1931
+ customLegend,
1932
+ optionOverride
1933
+ ]);
1934
+ const latest = useLatestRef({ onClick });
1374
1935
  const { containerRef } = useChart({
1375
1936
  option,
1376
1937
  theme,
1377
- animation
1938
+ animation,
1939
+ onReady: (chart) => {
1940
+ legend.attach(chart);
1941
+ chart.on("click", (params) => {
1942
+ const { onClick: onClick2 } = latest.current;
1943
+ if (!onClick2) return;
1944
+ onClick2({
1945
+ type: params.type || "click",
1946
+ componentType: params.componentType || "series",
1947
+ seriesIndex: params.seriesIndex,
1948
+ seriesName: params.seriesName || "",
1949
+ dataIndex: params.dataIndex ?? 0,
1950
+ name: params.name || "",
1951
+ value: params.value,
1952
+ color: params.color
1953
+ });
1954
+ });
1955
+ }
1378
1956
  });
1379
1957
  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)(
1958
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1381
1959
  ChartContainer,
1382
1960
  {
1383
1961
  ref,
@@ -1389,8 +1967,10 @@ var RadarChart = React11.forwardRef(
1389
1967
  empty: isEmpty,
1390
1968
  emptyMessage,
1391
1969
  className: cn(className),
1970
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
1971
+ legendPosition: legendConfig?.position,
1392
1972
  ...props,
1393
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1973
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1394
1974
  }
1395
1975
  );
1396
1976
  }
@@ -1398,12 +1978,13 @@ var RadarChart = React11.forwardRef(
1398
1978
  RadarChart.displayName = "RadarChart";
1399
1979
 
1400
1980
  // src/components/extended/FunnelChart.tsx
1401
- var React12 = __toESM(require("react"), 1);
1981
+ var React13 = __toESM(require("react"), 1);
1402
1982
  var echarts10 = __toESM(require("echarts/core"), 1);
1403
1983
  var import_charts8 = require("echarts/charts");
1404
1984
  var import_components8 = require("echarts/components");
1405
1985
  var import_renderers8 = require("echarts/renderers");
1406
- var import_jsx_runtime12 = require("react/jsx-runtime");
1986
+ var import_charts_core11 = require("@classic-homes/charts-core");
1987
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1407
1988
  echarts10.use([
1408
1989
  import_charts8.FunnelChart,
1409
1990
  import_components8.TooltipComponent,
@@ -1411,7 +1992,7 @@ echarts10.use([
1411
1992
  import_components8.TitleComponent,
1412
1993
  import_renderers8.CanvasRenderer
1413
1994
  ]);
1414
- var FunnelChart = React12.forwardRef(
1995
+ var FunnelChart = React13.forwardRef(
1415
1996
  ({
1416
1997
  title,
1417
1998
  description,
@@ -1427,68 +2008,119 @@ var FunnelChart = React12.forwardRef(
1427
2008
  sort = "descending",
1428
2009
  orientation = "vertical",
1429
2010
  showLabels = true,
2011
+ labelFormatter,
2012
+ tooltipFormatter,
2013
+ tooltipValueFormatter,
2014
+ legend: legendConfig,
2015
+ renderLegend,
2016
+ onLegendChange,
2017
+ option: optionOverride,
1430
2018
  onClick,
1431
2019
  class: className,
1432
2020
  ...props
1433
2021
  }, ref) => {
1434
2022
  const isEmpty = !data?.length;
1435
- const option = React12.useMemo(() => {
2023
+ const currentTheme = useChartTheme(theme);
2024
+ const legend = useChartLegend({
2025
+ names: data?.map((d) => d.name) ?? [],
2026
+ colors: data?.map((d) => d.color) ?? [],
2027
+ palette: (0, import_charts_core11.getChartColors)(currentTheme).series,
2028
+ config: legendConfig,
2029
+ title,
2030
+ onLegendChange
2031
+ });
2032
+ const customLegend = renderLegend !== void 0;
2033
+ const option = React13.useMemo(() => {
1436
2034
  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
- ]
2035
+ const getLabel = (0, import_charts_core11.itemLabelLookup)(data);
2036
+ const valueFormatter = tooltipFormatter === void 0 ? tooltipValueFormatter : void 0;
2037
+ const tooltipBase = {
2038
+ trigger: "item",
2039
+ formatter: valueFormatter ? (params) => {
2040
+ const p = params;
2041
+ 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))}`;
2042
+ } : "{a} <br/>{b}: {c}"
1484
2043
  };
1485
- }, [data, isEmpty, showTooltip, showLegend, sort, orientation, showLabels, title]);
2044
+ return (0, import_charts_core11.mergeChartOption)(
2045
+ {
2046
+ tooltip: (0, import_charts_core11.buildTooltip)(tooltipBase, {
2047
+ show: showTooltip,
2048
+ formatter: tooltipFormatter,
2049
+ valueFormatter: tooltipValueFormatter,
2050
+ getLabel
2051
+ }),
2052
+ legend: (0, import_charts_core11.buildLegend)(
2053
+ data.map((d) => d.name),
2054
+ legendConfig,
2055
+ { show: showLegend, custom: customLegend }
2056
+ ),
2057
+ series: [
2058
+ {
2059
+ name: title,
2060
+ type: "funnel",
2061
+ left: "10%",
2062
+ top: 60,
2063
+ bottom: 60,
2064
+ width: "80%",
2065
+ min: 0,
2066
+ // reduce, not Math.max(...): spreading large arrays overflows the stack
2067
+ max: data.reduce((m, d) => d.value > m ? d.value : m, -Infinity),
2068
+ minSize: "0%",
2069
+ maxSize: "100%",
2070
+ sort,
2071
+ orient: orientation,
2072
+ gap: 2,
2073
+ label: (0, import_charts_core11.buildDataLabel)(
2074
+ {
2075
+ show: true,
2076
+ position: "inside",
2077
+ formatter: "{b}: {c}"
2078
+ },
2079
+ { show: false },
2080
+ { show: showLabels, formatter: labelFormatter, getLabel }
2081
+ ),
2082
+ labelLine: {
2083
+ show: false
2084
+ },
2085
+ emphasis: {
2086
+ label: {
2087
+ fontSize: 16
2088
+ }
2089
+ },
2090
+ data: data.map((item) => ({
2091
+ name: item.name,
2092
+ value: item.value,
2093
+ itemStyle: item.color ? { color: item.color } : void 0,
2094
+ label: (0, import_charts_core11.pointLabel)(item.label, labelFormatter, { position: "inside" })
2095
+ }))
2096
+ }
2097
+ ]
2098
+ },
2099
+ optionOverride
2100
+ );
2101
+ }, [
2102
+ data,
2103
+ isEmpty,
2104
+ showTooltip,
2105
+ showLegend,
2106
+ sort,
2107
+ orientation,
2108
+ showLabels,
2109
+ title,
2110
+ labelFormatter,
2111
+ tooltipFormatter,
2112
+ tooltipValueFormatter,
2113
+ legendConfig,
2114
+ customLegend,
2115
+ optionOverride
2116
+ ]);
1486
2117
  const latest = useLatestRef({ onClick });
1487
2118
  const { containerRef } = useChart({
1488
2119
  option,
1489
2120
  theme,
1490
2121
  animation,
1491
2122
  onReady: (chart) => {
2123
+ legend.attach(chart);
1492
2124
  chart.on("click", (params) => {
1493
2125
  const { onClick: onClick2 } = latest.current;
1494
2126
  if (!onClick2) return;
@@ -1503,7 +2135,7 @@ var FunnelChart = React12.forwardRef(
1503
2135
  }
1504
2136
  });
1505
2137
  const accessibilityDescription = description || (!isEmpty ? `${title}. Funnel chart with ${data.length} stages.` : void 0);
1506
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2138
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1507
2139
  ChartContainer,
1508
2140
  {
1509
2141
  ref,
@@ -1515,8 +2147,10 @@ var FunnelChart = React12.forwardRef(
1515
2147
  empty: isEmpty,
1516
2148
  emptyMessage,
1517
2149
  className: cn(className),
2150
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
2151
+ legendPosition: legendConfig?.position,
1518
2152
  ...props,
1519
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { ref: containerRef, className: "h-full w-full" })
2153
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1520
2154
  }
1521
2155
  );
1522
2156
  }
@@ -1524,15 +2158,15 @@ var FunnelChart = React12.forwardRef(
1524
2158
  FunnelChart.displayName = "FunnelChart";
1525
2159
 
1526
2160
  // src/components/extended/GaugeChart.tsx
1527
- var React13 = __toESM(require("react"), 1);
2161
+ var React14 = __toESM(require("react"), 1);
1528
2162
  var echarts11 = __toESM(require("echarts/core"), 1);
1529
2163
  var import_charts9 = require("echarts/charts");
1530
2164
  var import_components9 = require("echarts/components");
1531
2165
  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(
2166
+ var import_charts_core12 = require("@classic-homes/charts-core");
2167
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2168
+ echarts11.use([import_charts9.GaugeChart, import_components9.TitleComponent, import_components9.TooltipComponent, import_renderers9.CanvasRenderer]);
2169
+ var GaugeChart = React14.forwardRef(
1536
2170
  ({
1537
2171
  title,
1538
2172
  description,
@@ -1548,84 +2182,101 @@ var GaugeChart = React13.forwardRef(
1548
2182
  splitNumber = 10,
1549
2183
  showProgress = true,
1550
2184
  formatValue,
2185
+ showTooltip = true,
2186
+ tooltipFormatter,
2187
+ tooltipValueFormatter,
2188
+ option: optionOverride,
2189
+ onClick,
1551
2190
  class: className,
1552
2191
  ...props
1553
2192
  }, ref) => {
1554
2193
  const currentTheme = useChartTheme(theme);
1555
- const chartColors = (0, import_charts_core9.getChartColors)(currentTheme);
2194
+ const chartColors = (0, import_charts_core12.getChartColors)(currentTheme);
1556
2195
  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: {
2196
+ const option = React14.useMemo(() => {
2197
+ return (0, import_charts_core12.mergeChartOption)(
2198
+ {
2199
+ // No tooltip by default; one appears once its content is customised
2200
+ tooltip: tooltipFormatter || tooltipValueFormatter ? (0, import_charts_core12.buildTooltip)(
2201
+ { trigger: "item" },
2202
+ {
2203
+ show: showTooltip,
2204
+ formatter: tooltipFormatter,
2205
+ valueFormatter: tooltipValueFormatter
2206
+ }
2207
+ ) : void 0,
2208
+ series: [
2209
+ {
2210
+ type: "gauge",
2211
+ center: ["50%", "60%"],
2212
+ radius: "80%",
2213
+ min,
2214
+ max,
2215
+ splitNumber,
2216
+ progress: showProgress ? {
2217
+ show: true,
1573
2218
  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
- };
2219
+ } : void 0,
2220
+ axisLine: {
2221
+ lineStyle: {
2222
+ width: 18
2223
+ }
2224
+ },
2225
+ axisTick: {
2226
+ distance: -30,
2227
+ splitNumber: 5,
2228
+ lineStyle: {
2229
+ width: 2,
2230
+ color: chartColors.mutedForeground
2231
+ }
2232
+ },
2233
+ splitLine: {
2234
+ distance: -32,
2235
+ length: 10,
2236
+ lineStyle: {
2237
+ width: 3,
2238
+ color: chartColors.mutedForeground
2239
+ }
2240
+ },
2241
+ axisLabel: {
2242
+ distance: -20,
2243
+ color: chartColors.mutedForeground,
2244
+ fontSize: 12
2245
+ },
2246
+ anchor: {
2247
+ show: true,
2248
+ size: 20,
2249
+ itemStyle: {
2250
+ borderWidth: 2
2251
+ }
2252
+ },
2253
+ pointer: {
2254
+ width: 6,
2255
+ length: "60%"
2256
+ },
2257
+ title: {
2258
+ show: true,
2259
+ offsetCenter: [0, "70%"],
2260
+ fontSize: 14
2261
+ },
2262
+ detail: {
2263
+ valueAnimation: animation && !reducedMotion,
2264
+ fontSize: 28,
2265
+ fontWeight: "bold",
2266
+ offsetCenter: [0, "40%"],
2267
+ formatter: formatValue || "{value}"
2268
+ },
2269
+ data: [
2270
+ {
2271
+ value,
2272
+ name: title
2273
+ }
2274
+ ]
2275
+ }
2276
+ ]
2277
+ },
2278
+ optionOverride
2279
+ );
1629
2280
  }, [
1630
2281
  value,
1631
2282
  min,
@@ -1636,15 +2287,36 @@ var GaugeChart = React13.forwardRef(
1636
2287
  title,
1637
2288
  animation,
1638
2289
  reducedMotion,
1639
- chartColors
2290
+ chartColors,
2291
+ showTooltip,
2292
+ tooltipFormatter,
2293
+ tooltipValueFormatter,
2294
+ optionOverride
1640
2295
  ]);
2296
+ const latest = useLatestRef({ onClick });
1641
2297
  const { containerRef } = useChart({
1642
2298
  option,
1643
- theme: currentTheme === import_charts_core9.THEME_DARK ? "dark" : "light",
1644
- animation
2299
+ theme: currentTheme === import_charts_core12.THEME_DARK ? "dark" : "light",
2300
+ animation,
2301
+ onReady: (chart) => {
2302
+ chart.on("click", (params) => {
2303
+ const { onClick: onClick2 } = latest.current;
2304
+ if (!onClick2) return;
2305
+ onClick2({
2306
+ type: params.type || "click",
2307
+ componentType: params.componentType || "series",
2308
+ seriesIndex: params.seriesIndex,
2309
+ seriesName: params.seriesName || "",
2310
+ dataIndex: params.dataIndex ?? 0,
2311
+ name: params.name || "",
2312
+ value: params.value,
2313
+ color: params.color
2314
+ });
2315
+ });
2316
+ }
1645
2317
  });
1646
2318
  const accessibilityDescription = description || `${title}. Gauge showing value ${value} on scale from ${min} to ${max}.`;
1647
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2319
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1648
2320
  ChartContainer,
1649
2321
  {
1650
2322
  ref,
@@ -1656,7 +2328,7 @@ var GaugeChart = React13.forwardRef(
1656
2328
  emptyMessage,
1657
2329
  className: cn(className),
1658
2330
  ...props,
1659
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { ref: containerRef, className: "h-full w-full" })
2331
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1660
2332
  }
1661
2333
  );
1662
2334
  }
@@ -1664,13 +2336,13 @@ var GaugeChart = React13.forwardRef(
1664
2336
  GaugeChart.displayName = "GaugeChart";
1665
2337
 
1666
2338
  // src/components/extended/HeatmapChart.tsx
1667
- var React14 = __toESM(require("react"), 1);
2339
+ var React15 = __toESM(require("react"), 1);
1668
2340
  var echarts12 = __toESM(require("echarts/core"), 1);
1669
2341
  var import_charts10 = require("echarts/charts");
1670
2342
  var import_components10 = require("echarts/components");
1671
2343
  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");
2344
+ var import_charts_core13 = require("@classic-homes/charts-core");
2345
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1674
2346
  echarts12.use([
1675
2347
  import_charts10.HeatmapChart,
1676
2348
  import_components10.GridComponent,
@@ -1679,7 +2351,7 @@ echarts12.use([
1679
2351
  import_components10.VisualMapComponent,
1680
2352
  import_renderers10.CanvasRenderer
1681
2353
  ]);
1682
- var HeatmapChart = React14.forwardRef(
2354
+ var HeatmapChart = React15.forwardRef(
1683
2355
  ({
1684
2356
  title,
1685
2357
  description,
@@ -1694,6 +2366,12 @@ var HeatmapChart = React14.forwardRef(
1694
2366
  animation = true,
1695
2367
  showTooltip = true,
1696
2368
  showValues = false,
2369
+ labelFormatter,
2370
+ xAxisConfig,
2371
+ yAxisConfig,
2372
+ tooltipFormatter,
2373
+ tooltipValueFormatter,
2374
+ option: optionOverride,
1697
2375
  onClick,
1698
2376
  class: className,
1699
2377
  ...props
@@ -1707,64 +2385,97 @@ var HeatmapChart = React14.forwardRef(
1707
2385
  }
1708
2386
  const minValue = data?.min ?? dataMin;
1709
2387
  const maxValue = data?.max ?? dataMax;
1710
- const option = React14.useMemo(() => {
2388
+ const option = React15.useMemo(() => {
1711
2389
  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)"
2390
+ return (0, import_charts_core13.mergeChartOption)(
2391
+ {
2392
+ tooltip: (0, import_charts_core13.buildTooltip)(
2393
+ {
2394
+ position: "top",
2395
+ formatter: (params) => {
2396
+ const p = params;
2397
+ if (!p.value) return "";
2398
+ const [xIdx, yIdx, value] = p.value;
2399
+ 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
2400
  }
2401
+ },
2402
+ {
2403
+ show: showTooltip,
2404
+ formatter: tooltipFormatter,
2405
+ valueFormatter: tooltipValueFormatter
1763
2406
  }
1764
- }
1765
- ]
1766
- };
1767
- }, [data, xAxis, yAxis, isEmpty, showTooltip, showValues, minValue, maxValue, title]);
2407
+ ),
2408
+ grid: {
2409
+ left: "3%",
2410
+ right: "10%",
2411
+ bottom: "3%",
2412
+ containLabel: true
2413
+ },
2414
+ xAxis: (0, import_charts_core13.buildAxis)(
2415
+ {
2416
+ type: "category",
2417
+ data: xAxis,
2418
+ splitArea: {
2419
+ show: true
2420
+ }
2421
+ },
2422
+ xAxisConfig
2423
+ ),
2424
+ yAxis: (0, import_charts_core13.buildAxis)(
2425
+ {
2426
+ type: "category",
2427
+ data: yAxis,
2428
+ splitArea: {
2429
+ show: true
2430
+ }
2431
+ },
2432
+ yAxisConfig
2433
+ ),
2434
+ visualMap: {
2435
+ min: minValue,
2436
+ max: maxValue,
2437
+ calculable: true,
2438
+ orient: "vertical",
2439
+ right: "0%",
2440
+ top: "center"
2441
+ },
2442
+ series: [
2443
+ {
2444
+ name: title,
2445
+ type: "heatmap",
2446
+ data: data.data,
2447
+ label: (0, import_charts_core13.buildDataLabel)({ show: true }, void 0, {
2448
+ show: showValues,
2449
+ formatter: labelFormatter
2450
+ }),
2451
+ emphasis: {
2452
+ itemStyle: {
2453
+ shadowBlur: 10,
2454
+ shadowColor: "rgba(0, 0, 0, 0.5)"
2455
+ }
2456
+ }
2457
+ }
2458
+ ]
2459
+ },
2460
+ optionOverride
2461
+ );
2462
+ }, [
2463
+ data,
2464
+ xAxis,
2465
+ yAxis,
2466
+ isEmpty,
2467
+ showTooltip,
2468
+ showValues,
2469
+ minValue,
2470
+ maxValue,
2471
+ title,
2472
+ labelFormatter,
2473
+ xAxisConfig,
2474
+ yAxisConfig,
2475
+ tooltipFormatter,
2476
+ tooltipValueFormatter,
2477
+ optionOverride
2478
+ ]);
1768
2479
  const latest = useLatestRef({ onClick, xAxis, yAxis });
1769
2480
  const { containerRef } = useChart({
1770
2481
  option,
@@ -1788,7 +2499,7 @@ var HeatmapChart = React14.forwardRef(
1788
2499
  }
1789
2500
  });
1790
2501
  const accessibilityDescription = description || (!isEmpty ? `${title}. Heatmap with ${xAxis.length} columns and ${yAxis.length} rows.` : void 0);
1791
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2502
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1792
2503
  ChartContainer,
1793
2504
  {
1794
2505
  ref,
@@ -1801,7 +2512,7 @@ var HeatmapChart = React14.forwardRef(
1801
2512
  emptyMessage,
1802
2513
  className: cn(className),
1803
2514
  ...props,
1804
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { ref: containerRef, className: "h-full w-full" })
2515
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1805
2516
  }
1806
2517
  );
1807
2518
  }
@@ -1809,15 +2520,15 @@ var HeatmapChart = React14.forwardRef(
1809
2520
  HeatmapChart.displayName = "HeatmapChart";
1810
2521
 
1811
2522
  // src/components/extended/TreemapChart.tsx
1812
- var React15 = __toESM(require("react"), 1);
2523
+ var React16 = __toESM(require("react"), 1);
1813
2524
  var echarts13 = __toESM(require("echarts/core"), 1);
1814
2525
  var import_charts11 = require("echarts/charts");
1815
2526
  var import_components11 = require("echarts/components");
1816
2527
  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");
2528
+ var import_charts_core14 = require("@classic-homes/charts-core");
2529
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1819
2530
  echarts13.use([import_charts11.TreemapChart, import_components11.TooltipComponent, import_components11.TitleComponent, import_renderers11.CanvasRenderer]);
1820
- var TreemapChart = React15.forwardRef(
2531
+ var TreemapChart = React16.forwardRef(
1821
2532
  ({
1822
2533
  title,
1823
2534
  description,
@@ -1830,73 +2541,103 @@ var TreemapChart = React15.forwardRef(
1830
2541
  theme = "auto",
1831
2542
  animation = true,
1832
2543
  showTooltip = true,
2544
+ labelFormatter,
2545
+ tooltipFormatter,
2546
+ tooltipValueFormatter,
2547
+ option: optionOverride,
1833
2548
  onClick,
1834
2549
  class: className,
1835
2550
  ...props
1836
2551
  }, ref) => {
1837
2552
  const isEmpty = !data?.length;
1838
2553
  const currentTheme = useChartTheme(theme);
1839
- const chartColors = (0, import_charts_core11.getChartColors)(currentTheme);
1840
- const option = React15.useMemo(() => {
2554
+ const chartColors = (0, import_charts_core14.getChartColors)(currentTheme);
2555
+ const option = React16.useMemo(() => {
1841
2556
  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
2557
+ const toNode = (node) => ({
2558
+ ...node,
2559
+ children: node.children?.map(toNode),
2560
+ label: (0, import_charts_core14.pointLabel)(node.label, labelFormatter)
2561
+ });
2562
+ return (0, import_charts_core14.mergeChartOption)(
2563
+ {
2564
+ tooltip: (0, import_charts_core14.buildTooltip)(
2565
+ {
2566
+ formatter: (params) => {
2567
+ const p = params;
2568
+ return `${(0, import_charts_core14.escapeHtml)(p.name)}: ${(0, import_charts_core14.escapeHtml)((0, import_charts_core14.formatTooltipValue)(p.value, tooltipValueFormatter))}`;
2569
+ }
2570
+ },
2571
+ {
2572
+ show: showTooltip,
2573
+ formatter: tooltipFormatter,
2574
+ valueFormatter: tooltipValueFormatter
2575
+ }
2576
+ ),
2577
+ series: [
2578
+ {
2579
+ type: "treemap",
2580
+ data: data.map(toNode),
2581
+ roam: false,
2582
+ levels: levels || [
2583
+ {
2584
+ itemStyle: {
2585
+ borderColor: chartColors.divider,
2586
+ borderWidth: 4,
2587
+ gapWidth: 4
2588
+ }
2589
+ },
2590
+ {
2591
+ colorSaturation: [0.3, 0.6],
2592
+ itemStyle: {
2593
+ borderColorSaturation: 0.7,
2594
+ gapWidth: 2,
2595
+ borderWidth: 2
2596
+ }
2597
+ },
2598
+ {
2599
+ colorSaturation: [0.3, 0.5],
2600
+ itemStyle: {
2601
+ borderColorSaturation: 0.6,
2602
+ gapWidth: 1
2603
+ }
2604
+ },
2605
+ {
2606
+ colorSaturation: [0.3, 0.5]
1875
2607
  }
2608
+ ],
2609
+ label: (0, import_charts_core14.buildDataLabel)({ show: true, formatter: "{b}" }, void 0, {
2610
+ show: true,
2611
+ formatter: labelFormatter,
2612
+ getLabel: (0, import_charts_core14.nameLabelLookup)(data)
2613
+ }),
2614
+ upperLabel: {
2615
+ show: true,
2616
+ height: 30
1876
2617
  },
1877
- {
1878
- colorSaturation: [0.3, 0.5]
2618
+ breadcrumb: {
2619
+ show: true
1879
2620
  }
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
2621
  }
1892
- }
1893
- ]
1894
- };
1895
- }, [data, levels, isEmpty, showTooltip, chartColors]);
2622
+ ]
2623
+ },
2624
+ optionOverride
2625
+ );
2626
+ }, [
2627
+ data,
2628
+ levels,
2629
+ isEmpty,
2630
+ showTooltip,
2631
+ chartColors,
2632
+ labelFormatter,
2633
+ tooltipFormatter,
2634
+ tooltipValueFormatter,
2635
+ optionOverride
2636
+ ]);
1896
2637
  const latest = useLatestRef({ onClick });
1897
2638
  const { containerRef } = useChart({
1898
2639
  option,
1899
- theme: currentTheme === import_charts_core11.THEME_DARK ? "dark" : "light",
2640
+ theme: currentTheme === import_charts_core14.THEME_DARK ? "dark" : "light",
1900
2641
  animation,
1901
2642
  onReady: (chart) => {
1902
2643
  chart.on("click", (params) => {
@@ -1915,7 +2656,7 @@ var TreemapChart = React15.forwardRef(
1915
2656
  }
1916
2657
  });
1917
2658
  const accessibilityDescription = description || (!isEmpty ? `${title}. Treemap chart showing hierarchical data.` : void 0);
1918
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2659
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1919
2660
  ChartContainer,
1920
2661
  {
1921
2662
  ref,
@@ -1928,7 +2669,7 @@ var TreemapChart = React15.forwardRef(
1928
2669
  emptyMessage,
1929
2670
  className: cn(className),
1930
2671
  ...props,
1931
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { ref: containerRef, className: "h-full w-full" })
2672
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { ref: containerRef, className: "h-full w-full" })
1932
2673
  }
1933
2674
  );
1934
2675
  }
@@ -1936,13 +2677,13 @@ var TreemapChart = React15.forwardRef(
1936
2677
  TreemapChart.displayName = "TreemapChart";
1937
2678
 
1938
2679
  // src/components/extended/CandlestickChart.tsx
1939
- var React16 = __toESM(require("react"), 1);
2680
+ var React17 = __toESM(require("react"), 1);
1940
2681
  var echarts14 = __toESM(require("echarts/core"), 1);
1941
2682
  var import_charts12 = require("echarts/charts");
1942
2683
  var import_components12 = require("echarts/components");
1943
2684
  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");
2685
+ var import_charts_core15 = require("@classic-homes/charts-core");
2686
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1946
2687
  echarts14.use([
1947
2688
  import_charts12.CandlestickChart,
1948
2689
  import_charts12.BarChart,
@@ -1952,7 +2693,7 @@ echarts14.use([
1952
2693
  import_components12.DataZoomComponent,
1953
2694
  import_renderers12.CanvasRenderer
1954
2695
  ]);
1955
- var CandlestickChart = React16.forwardRef(
2696
+ var CandlestickChart = React17.forwardRef(
1956
2697
  ({
1957
2698
  title,
1958
2699
  description,
@@ -1965,14 +2706,19 @@ var CandlestickChart = React16.forwardRef(
1965
2706
  animation = true,
1966
2707
  showTooltip = true,
1967
2708
  showVolume = false,
2709
+ xAxisConfig,
2710
+ yAxisConfig,
2711
+ tooltipFormatter,
2712
+ tooltipValueFormatter,
2713
+ option: optionOverride,
1968
2714
  onClick,
1969
2715
  class: className,
1970
2716
  ...props
1971
2717
  }, ref) => {
1972
2718
  const isEmpty = !data?.dates?.length || !data?.data?.length;
1973
2719
  const currentTheme = useChartTheme(theme);
1974
- const chartColors = (0, import_charts_core12.getChartColors)(currentTheme);
1975
- const option = React16.useMemo(() => {
2720
+ const chartColors = (0, import_charts_core15.getChartColors)(currentTheme);
2721
+ const option = React17.useMemo(() => {
1976
2722
  if (isEmpty) return {};
1977
2723
  const candlestickData = data.data.map((item) => [item.open, item.close, item.low, item.high]);
1978
2724
  const seriesArr = [
@@ -1999,23 +2745,29 @@ var CandlestickChart = React16.forwardRef(
1999
2745
  }
2000
2746
  ];
2001
2747
  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
- }
2748
+ (0, import_charts_core15.buildAxis)(
2749
+ {
2750
+ type: "category",
2751
+ data: data.dates,
2752
+ boundaryGap: false,
2753
+ axisLine: { onZero: false },
2754
+ splitLine: { show: false },
2755
+ min: "dataMin",
2756
+ max: "dataMax"
2757
+ },
2758
+ xAxisConfig
2759
+ )
2011
2760
  ];
2012
2761
  const yAxisArr = [
2013
- {
2014
- scale: true,
2015
- splitArea: {
2016
- show: true
2017
- }
2018
- }
2762
+ (0, import_charts_core15.buildAxis)(
2763
+ {
2764
+ scale: true,
2765
+ splitArea: {
2766
+ show: true
2767
+ }
2768
+ },
2769
+ yAxisConfig
2770
+ )
2019
2771
  ];
2020
2772
  if (showVolume && data.volume) {
2021
2773
  gridArr.push({
@@ -2056,37 +2808,59 @@ var CandlestickChart = React16.forwardRef(
2056
2808
  }
2057
2809
  });
2058
2810
  }
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]);
2811
+ return (0, import_charts_core15.mergeChartOption)(
2812
+ {
2813
+ tooltip: (0, import_charts_core15.buildTooltip)(
2814
+ {
2815
+ trigger: "axis",
2816
+ axisPointer: {
2817
+ type: "cross"
2818
+ }
2819
+ },
2820
+ {
2821
+ show: showTooltip,
2822
+ formatter: tooltipFormatter,
2823
+ valueFormatter: tooltipValueFormatter
2824
+ }
2825
+ ),
2826
+ grid: gridArr,
2827
+ xAxis: xAxisArr,
2828
+ yAxis: yAxisArr,
2829
+ dataZoom: [
2830
+ {
2831
+ type: "inside",
2832
+ start: 80,
2833
+ end: 100
2834
+ },
2835
+ {
2836
+ show: true,
2837
+ type: "slider",
2838
+ top: "90%",
2839
+ start: 80,
2840
+ end: 100
2841
+ }
2842
+ ],
2843
+ series: seriesArr
2844
+ },
2845
+ optionOverride
2846
+ );
2847
+ }, [
2848
+ data,
2849
+ isEmpty,
2850
+ showTooltip,
2851
+ showVolume,
2852
+ title,
2853
+ chartColors,
2854
+ xAxisConfig,
2855
+ yAxisConfig,
2856
+ tooltipFormatter,
2857
+ tooltipValueFormatter,
2858
+ optionOverride
2859
+ ]);
2086
2860
  const latest = useLatestRef({ onClick, data });
2087
2861
  const { containerRef } = useChart({
2088
2862
  option,
2089
- theme: currentTheme === import_charts_core12.THEME_DARK ? "dark" : "light",
2863
+ theme: currentTheme === import_charts_core15.THEME_DARK ? "dark" : "light",
2090
2864
  animation,
2091
2865
  onReady: (chart) => {
2092
2866
  chart.on("click", (params) => {
@@ -2109,7 +2883,7 @@ var CandlestickChart = React16.forwardRef(
2109
2883
  }
2110
2884
  });
2111
2885
  const accessibilityDescription = description || (!isEmpty ? `${title}. Candlestick chart with ${data.dates.length} data points.` : void 0);
2112
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2886
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2113
2887
  ChartContainer,
2114
2888
  {
2115
2889
  ref,
@@ -2122,7 +2896,7 @@ var CandlestickChart = React16.forwardRef(
2122
2896
  emptyMessage,
2123
2897
  className: cn(className),
2124
2898
  ...props,
2125
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { ref: containerRef, className: "h-full w-full" })
2899
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { ref: containerRef, className: "h-full w-full" })
2126
2900
  }
2127
2901
  );
2128
2902
  }
@@ -2130,14 +2904,15 @@ var CandlestickChart = React16.forwardRef(
2130
2904
  CandlestickChart.displayName = "CandlestickChart";
2131
2905
 
2132
2906
  // src/components/extended/SankeyChart.tsx
2133
- var React17 = __toESM(require("react"), 1);
2907
+ var React18 = __toESM(require("react"), 1);
2134
2908
  var echarts15 = __toESM(require("echarts/core"), 1);
2135
2909
  var import_charts13 = require("echarts/charts");
2136
2910
  var import_components13 = require("echarts/components");
2137
2911
  var import_renderers13 = require("echarts/renderers");
2138
- var import_jsx_runtime17 = require("react/jsx-runtime");
2912
+ var import_charts_core16 = require("@classic-homes/charts-core");
2913
+ var import_jsx_runtime18 = require("react/jsx-runtime");
2139
2914
  echarts15.use([import_charts13.SankeyChart, import_components13.TooltipComponent, import_components13.TitleComponent, import_renderers13.CanvasRenderer]);
2140
- var SankeyChart = React17.forwardRef(
2915
+ var SankeyChart = React18.forwardRef(
2141
2916
  ({
2142
2917
  title,
2143
2918
  description,
@@ -2150,45 +2925,73 @@ var SankeyChart = React17.forwardRef(
2150
2925
  animation = true,
2151
2926
  showTooltip = true,
2152
2927
  orient = "horizontal",
2928
+ labelFormatter,
2929
+ tooltipFormatter,
2930
+ tooltipValueFormatter,
2931
+ option: optionOverride,
2153
2932
  onClick,
2154
2933
  class: className,
2155
2934
  ...props
2156
2935
  }, ref) => {
2157
2936
  const isEmpty = !data?.nodes?.length || !data?.links?.length;
2158
- const option = React17.useMemo(() => {
2937
+ const option = React18.useMemo(() => {
2159
2938
  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
2939
+ return (0, import_charts_core16.mergeChartOption)(
2940
+ {
2941
+ tooltip: (0, import_charts_core16.buildTooltip)(
2942
+ {
2943
+ trigger: "item",
2944
+ triggerOn: "mousemove"
2184
2945
  },
2185
- label: {
2186
- position: orient === "horizontal" ? "right" : "bottom"
2946
+ {
2947
+ show: showTooltip,
2948
+ formatter: tooltipFormatter,
2949
+ valueFormatter: tooltipValueFormatter
2187
2950
  }
2188
- }
2189
- ]
2190
- };
2191
- }, [data, isEmpty, showTooltip, orient]);
2951
+ ),
2952
+ series: [
2953
+ {
2954
+ type: "sankey",
2955
+ orient,
2956
+ emphasis: {
2957
+ focus: "adjacency"
2958
+ },
2959
+ data: data.nodes.map((node) => ({
2960
+ name: node.name,
2961
+ itemStyle: node.color ? { color: node.color } : void 0,
2962
+ label: (0, import_charts_core16.pointLabel)(node.label, labelFormatter)
2963
+ })),
2964
+ links: data.links.map((link) => ({
2965
+ source: link.source,
2966
+ target: link.target,
2967
+ value: link.value
2968
+ })),
2969
+ lineStyle: {
2970
+ color: "gradient",
2971
+ curveness: 0.5
2972
+ },
2973
+ label: (0, import_charts_core16.buildDataLabel)(
2974
+ {
2975
+ position: orient === "horizontal" ? "right" : "bottom"
2976
+ },
2977
+ void 0,
2978
+ { show: true, formatter: labelFormatter, getLabel: (0, import_charts_core16.nameLabelLookup)(data.nodes) }
2979
+ )
2980
+ }
2981
+ ]
2982
+ },
2983
+ optionOverride
2984
+ );
2985
+ }, [
2986
+ data,
2987
+ isEmpty,
2988
+ showTooltip,
2989
+ orient,
2990
+ labelFormatter,
2991
+ tooltipFormatter,
2992
+ tooltipValueFormatter,
2993
+ optionOverride
2994
+ ]);
2192
2995
  const latest = useLatestRef({ onClick });
2193
2996
  const { containerRef } = useChart({
2194
2997
  option,
@@ -2212,7 +3015,7 @@ var SankeyChart = React17.forwardRef(
2212
3015
  }
2213
3016
  });
2214
3017
  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)(
3018
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2216
3019
  ChartContainer,
2217
3020
  {
2218
3021
  ref,
@@ -2225,7 +3028,7 @@ var SankeyChart = React17.forwardRef(
2225
3028
  emptyMessage,
2226
3029
  className: cn(className),
2227
3030
  ...props,
2228
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { ref: containerRef, className: "h-full w-full" })
3031
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref: containerRef, className: "h-full w-full" })
2229
3032
  }
2230
3033
  );
2231
3034
  }
@@ -2239,6 +3042,7 @@ SankeyChart.displayName = "SankeyChart";
2239
3042
  ChartContainer,
2240
3043
  ChartEmpty,
2241
3044
  ChartError,
3045
+ ChartLegend,
2242
3046
  ChartSkeleton,
2243
3047
  DonutChart,
2244
3048
  FunnelChart,
@@ -2253,6 +3057,7 @@ SankeyChart.displayName = "SankeyChart";
2253
3057
  cn,
2254
3058
  registerClassicThemes,
2255
3059
  useChart,
3060
+ useChartLegend,
2256
3061
  useChartResize,
2257
3062
  useChartTheme,
2258
3063
  useReducedMotion