@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.js CHANGED
@@ -141,6 +141,30 @@ ChartEmpty.displayName = "ChartEmpty";
141
141
 
142
142
  // src/components/base/ChartContainer.tsx
143
143
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
144
+ var CHART_ONLY_PROPS = [
145
+ "minHeight",
146
+ "maxHeight",
147
+ "rowHeight",
148
+ "showLegend",
149
+ "showGrid",
150
+ "valueFormat",
151
+ "locale",
152
+ "onReady",
153
+ "events",
154
+ "showDataTable",
155
+ "exportable",
156
+ "patterns",
157
+ "referenceLines",
158
+ "referenceBands",
159
+ "dataZoom",
160
+ "connectNulls",
161
+ "zoomStart"
162
+ ];
163
+ function domProps(props) {
164
+ const rest = { ...props };
165
+ for (const key of CHART_ONLY_PROPS) delete rest[key];
166
+ return rest;
167
+ }
144
168
  var ChartContainer = React4.forwardRef(
145
169
  ({
146
170
  title,
@@ -153,6 +177,8 @@ var ChartContainer = React4.forwardRef(
153
177
  onRetry,
154
178
  className,
155
179
  wrapperClassName,
180
+ legend,
181
+ legendPosition = "bottom",
156
182
  children,
157
183
  ...props
158
184
  }, ref) => {
@@ -175,7 +201,7 @@ var ChartContainer = React4.forwardRef(
175
201
  if (empty) {
176
202
  return /* @__PURE__ */ jsx4(ChartEmpty, { message: emptyMessage, height, className: wrapperClassName });
177
203
  }
178
- return /* @__PURE__ */ jsxs4(
204
+ const chart = /* @__PURE__ */ jsxs4(
179
205
  "div",
180
206
  {
181
207
  ref,
@@ -183,20 +209,91 @@ var ChartContainer = React4.forwardRef(
183
209
  "aria-label": title,
184
210
  "aria-describedby": description ? descriptionId : void 0,
185
211
  "data-testid": "chart-container",
186
- className: cn("rounded-lg border border-border bg-card", wrapperClassName),
187
- ...props,
212
+ className: legend ? "min-h-0 min-w-0 flex-1" : cn("rounded-lg border border-border bg-card", wrapperClassName),
213
+ ...domProps(props),
188
214
  children: [
189
215
  description && /* @__PURE__ */ jsx4("span", { id: descriptionId, className: "sr-only", children: description }),
190
216
  /* @__PURE__ */ jsx4("div", { className: cn("h-full w-full", className), style: { height: heightStyle }, children })
191
217
  ]
192
218
  }
193
219
  );
220
+ if (!legend) return chart;
221
+ const side = legendPosition === "left" || legendPosition === "right";
222
+ const leading = legendPosition === "top" || legendPosition === "left";
223
+ const legendNode = /* @__PURE__ */ jsx4(
224
+ "div",
225
+ {
226
+ "data-testid": "chart-legend",
227
+ className: cn("flex shrink-0 p-2", side ? "items-center" : "justify-center"),
228
+ children: legend
229
+ }
230
+ );
231
+ return /* @__PURE__ */ jsxs4(
232
+ "div",
233
+ {
234
+ className: cn(
235
+ "flex rounded-lg border border-border bg-card",
236
+ side ? "flex-row" : "flex-col",
237
+ wrapperClassName
238
+ ),
239
+ children: [
240
+ leading && legendNode,
241
+ chart,
242
+ !leading && legendNode
243
+ ]
244
+ }
245
+ );
194
246
  }
195
247
  );
196
248
  ChartContainer.displayName = "ChartContainer";
197
249
 
198
- // src/components/core/LineChart.tsx
250
+ // src/components/base/ChartLegend.tsx
199
251
  import * as React5 from "react";
252
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
253
+ var ChartLegend = React5.forwardRef(
254
+ ({ items, toggle, position = "bottom", chartTitle, orientation, className }, ref) => {
255
+ const vertical = (orientation ?? (position === "left" || position === "right" ? "vertical" : "horizontal")) === "vertical";
256
+ return /* @__PURE__ */ jsx5(
257
+ "ul",
258
+ {
259
+ ref,
260
+ "aria-label": chartTitle ? `${chartTitle} legend` : "Chart legend",
261
+ className: cn(
262
+ "flex gap-1 text-sm",
263
+ vertical ? "flex-col" : "flex-wrap justify-center",
264
+ className
265
+ ),
266
+ children: items.map((item) => /* @__PURE__ */ jsx5("li", { children: /* @__PURE__ */ jsxs5(
267
+ "button",
268
+ {
269
+ type: "button",
270
+ "aria-pressed": item.visible,
271
+ onClick: () => toggle(item.name),
272
+ className: cn(
273
+ "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",
274
+ item.visible ? "text-foreground" : "text-muted-foreground line-through"
275
+ ),
276
+ children: [
277
+ /* @__PURE__ */ jsx5(
278
+ "span",
279
+ {
280
+ "aria-hidden": "true",
281
+ className: "h-3 w-3 shrink-0 rounded-sm",
282
+ style: { backgroundColor: item.color, opacity: item.visible ? 1 : 0.35 }
283
+ }
284
+ ),
285
+ item.name
286
+ ]
287
+ }
288
+ ) }, item.name))
289
+ }
290
+ );
291
+ }
292
+ );
293
+ ChartLegend.displayName = "ChartLegend";
294
+
295
+ // src/components/core/LineChart.tsx
296
+ import * as React6 from "react";
200
297
  import * as echarts3 from "echarts/core";
201
298
  import { LineChart as EChartsLineChart } from "echarts/charts";
202
299
  import {
@@ -207,9 +304,18 @@ import {
207
304
  } from "echarts/components";
208
305
  import { CanvasRenderer } from "echarts/renderers";
209
306
  import {
307
+ buildAxis,
308
+ buildDataLabel,
309
+ buildLegend,
310
+ buildTooltip,
210
311
  generateLineChartDescription,
211
312
  getChartColors,
212
- THEME_DARK as THEME_DARK2
313
+ hasPointLabels,
314
+ gridInset,
315
+ mergeChartOption,
316
+ seriesLabelLookup,
317
+ THEME_DARK as THEME_DARK2,
318
+ withPointLabels
213
319
  } from "@classic-homes/charts-core";
214
320
 
215
321
  // src/hooks/useChart.ts
@@ -366,6 +472,12 @@ function useChart({
366
472
  };
367
473
  }
368
474
 
475
+ // src/hooks/useChartLegend.ts
476
+ import { useCallback, useRef as useRef3, useState as useState4 } from "react";
477
+ import {
478
+ getLegendItems
479
+ } from "@classic-homes/charts-core";
480
+
369
481
  // src/hooks/useLatestRef.ts
370
482
  import { useRef as useRef2 } from "react";
371
483
  function useLatestRef(value) {
@@ -374,8 +486,62 @@ function useLatestRef(value) {
374
486
  return ref;
375
487
  }
376
488
 
489
+ // src/hooks/useChartLegend.ts
490
+ var LEGEND_EVENTS = [
491
+ "legendselectchanged",
492
+ "legendselected",
493
+ "legendunselected",
494
+ "legendselectall",
495
+ "legendinverseselect"
496
+ ];
497
+ function useChartLegend({
498
+ names,
499
+ colors,
500
+ palette,
501
+ config,
502
+ title,
503
+ onLegendChange
504
+ }) {
505
+ const chartRef = useRef3(null);
506
+ const [visible, setVisible] = useState4({});
507
+ const latest = useLatestRef({ onLegendChange });
508
+ const attach = useCallback(
509
+ (chart) => {
510
+ chartRef.current = chart;
511
+ setVisible({});
512
+ const handler = (params) => {
513
+ const selected = { ...params.selected };
514
+ setVisible(selected);
515
+ latest.current.onLegendChange?.(selected);
516
+ };
517
+ for (const event of LEGEND_EVENTS) chart.on(event, handler);
518
+ },
519
+ [latest]
520
+ );
521
+ const dispatch = (action) => {
522
+ const chart = chartRef.current;
523
+ if (chart && !chart.isDisposed()) chart.dispatchAction(action);
524
+ };
525
+ return {
526
+ attach,
527
+ renderProps: {
528
+ items: getLegendItems(names, palette, colors, visible),
529
+ position: config?.position ?? "bottom",
530
+ chartTitle: title,
531
+ toggle: (name) => dispatch({ type: "legendToggleSelect", name }),
532
+ setVisible: (name, show) => dispatch({ type: show ? "legendSelect" : "legendUnSelect", name }),
533
+ showOnly: (name) => {
534
+ for (const other of names) {
535
+ dispatch({ type: other === name ? "legendSelect" : "legendUnSelect", name: other });
536
+ }
537
+ },
538
+ showAll: () => dispatch({ type: "legendAllSelect" })
539
+ }
540
+ };
541
+ }
542
+
377
543
  // src/components/core/LineChart.tsx
378
- import { jsx as jsx5 } from "react/jsx-runtime";
544
+ import { jsx as jsx6 } from "react/jsx-runtime";
379
545
  echarts3.use([
380
546
  EChartsLineChart,
381
547
  GridComponent,
@@ -384,7 +550,7 @@ echarts3.use([
384
550
  TitleComponent,
385
551
  CanvasRenderer
386
552
  ]);
387
- var LineChart = React5.forwardRef(
553
+ var LineChart = React6.forwardRef(
388
554
  ({
389
555
  title,
390
556
  description,
@@ -402,6 +568,16 @@ var LineChart = React5.forwardRef(
402
568
  areaFilled = false,
403
569
  showDataPoints = false,
404
570
  stacked = false,
571
+ showLabels = false,
572
+ labelFormatter,
573
+ xAxisConfig,
574
+ yAxisConfig,
575
+ tooltipFormatter,
576
+ tooltipValueFormatter,
577
+ legend: legendConfig,
578
+ renderLegend,
579
+ onLegendChange,
580
+ option: optionOverride,
405
581
  onClick,
406
582
  onHover,
407
583
  class: className,
@@ -410,57 +586,92 @@ var LineChart = React5.forwardRef(
410
586
  const isEmpty = !data?.categories?.length || !data?.series?.length || data.series.every((s) => !s.data?.length);
411
587
  const currentTheme = useChartTheme(theme);
412
588
  const chartColors = getChartColors(currentTheme);
413
- const option = React5.useMemo(() => {
589
+ const legend = useChartLegend({
590
+ names: data?.series?.map((s) => s.name) ?? [],
591
+ colors: data?.series?.map((s) => s.color) ?? [],
592
+ palette: chartColors.series,
593
+ config: legendConfig,
594
+ title,
595
+ onLegendChange
596
+ });
597
+ const customLegend = renderLegend !== void 0;
598
+ const option = React6.useMemo(() => {
414
599
  if (isEmpty) return {};
415
- return {
416
- grid: {
417
- left: "3%",
418
- right: "4%",
419
- bottom: "3%",
420
- containLabel: true
421
- },
422
- tooltip: showTooltip ? {
423
- trigger: "axis",
424
- axisPointer: {
425
- type: "cross",
426
- label: {
427
- backgroundColor: chartColors.axisPointerLabel
600
+ const getLabel = seriesLabelLookup(data.series);
601
+ const showSymbol = showDataPoints || showLabels || hasPointLabels(data.series);
602
+ return mergeChartOption(
603
+ {
604
+ grid: {
605
+ left: "3%",
606
+ right: "4%",
607
+ bottom: "3%",
608
+ containLabel: true,
609
+ ...gridInset(legendConfig, showLegend && !customLegend, xAxisConfig, yAxisConfig)
610
+ },
611
+ tooltip: buildTooltip(
612
+ {
613
+ trigger: "axis",
614
+ axisPointer: {
615
+ type: "cross",
616
+ label: {
617
+ backgroundColor: chartColors.axisPointerLabel
618
+ }
619
+ }
620
+ },
621
+ {
622
+ show: showTooltip,
623
+ formatter: tooltipFormatter,
624
+ valueFormatter: tooltipValueFormatter,
625
+ getLabel
428
626
  }
429
- }
430
- } : void 0,
431
- legend: showLegend ? {
432
- data: data.series.map((s) => s.name),
433
- bottom: 0
434
- } : void 0,
435
- xAxis: {
436
- type: "category",
437
- boundaryGap: false,
438
- data: data.categories,
439
- splitLine: {
440
- show: showGrid
441
- }
442
- },
443
- yAxis: {
444
- type: "value",
445
- splitLine: {
446
- show: showGrid
447
- }
627
+ ),
628
+ legend: buildLegend(
629
+ data.series.map((s) => s.name),
630
+ legendConfig,
631
+ { show: showLegend, custom: customLegend }
632
+ ),
633
+ xAxis: buildAxis(
634
+ {
635
+ type: "category",
636
+ boundaryGap: false,
637
+ data: data.categories,
638
+ splitLine: {
639
+ show: showGrid
640
+ }
641
+ },
642
+ xAxisConfig
643
+ ),
644
+ yAxis: buildAxis(
645
+ {
646
+ type: "value",
647
+ splitLine: {
648
+ show: showGrid
649
+ }
650
+ },
651
+ yAxisConfig
652
+ ),
653
+ series: data.series.map((series) => ({
654
+ name: series.name,
655
+ type: "line",
656
+ data: withPointLabels(series.data, series.labels, labelFormatter, { position: "top" }),
657
+ smooth,
658
+ showSymbol,
659
+ symbolSize: showDataPoints ? 6 : 0,
660
+ stack: stacked ? "Total" : void 0,
661
+ areaStyle: areaFilled ? { opacity: 0.3 } : void 0,
662
+ itemStyle: series.color ? { color: series.color } : void 0,
663
+ label: buildDataLabel({ show: true, position: "top" }, void 0, {
664
+ show: showLabels,
665
+ formatter: labelFormatter,
666
+ getLabel
667
+ }),
668
+ emphasis: {
669
+ focus: "series"
670
+ }
671
+ }))
448
672
  },
449
- series: data.series.map((series) => ({
450
- name: series.name,
451
- type: "line",
452
- data: series.data,
453
- smooth,
454
- showSymbol: showDataPoints,
455
- symbolSize: showDataPoints ? 6 : 0,
456
- stack: stacked ? "Total" : void 0,
457
- areaStyle: areaFilled ? { opacity: 0.3 } : void 0,
458
- itemStyle: series.color ? { color: series.color } : void 0,
459
- emphasis: {
460
- focus: "series"
461
- }
462
- }))
463
- };
673
+ optionOverride
674
+ );
464
675
  }, [
465
676
  data,
466
677
  isEmpty,
@@ -471,7 +682,16 @@ var LineChart = React5.forwardRef(
471
682
  areaFilled,
472
683
  showDataPoints,
473
684
  stacked,
474
- chartColors
685
+ chartColors,
686
+ showLabels,
687
+ labelFormatter,
688
+ xAxisConfig,
689
+ yAxisConfig,
690
+ tooltipFormatter,
691
+ tooltipValueFormatter,
692
+ legendConfig,
693
+ customLegend,
694
+ optionOverride
475
695
  ]);
476
696
  const latest = useLatestRef({ onClick, onHover });
477
697
  const { containerRef } = useChart({
@@ -479,6 +699,7 @@ var LineChart = React5.forwardRef(
479
699
  theme: currentTheme === THEME_DARK2 ? "dark" : "light",
480
700
  animation,
481
701
  onReady: (chart) => {
702
+ legend.attach(chart);
482
703
  chart.on("click", (params) => {
483
704
  const { onClick: onClick2 } = latest.current;
484
705
  if (!onClick2) return;
@@ -510,7 +731,7 @@ var LineChart = React5.forwardRef(
510
731
  }
511
732
  });
512
733
  const accessibilityDescription = description || (!isEmpty ? generateLineChartDescription(title, data) : void 0);
513
- return /* @__PURE__ */ jsx5(
734
+ return /* @__PURE__ */ jsx6(
514
735
  ChartContainer,
515
736
  {
516
737
  ref,
@@ -522,8 +743,10 @@ var LineChart = React5.forwardRef(
522
743
  empty: isEmpty,
523
744
  emptyMessage,
524
745
  className: cn(className),
746
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
747
+ legendPosition: legendConfig?.position,
525
748
  ...props,
526
- children: /* @__PURE__ */ jsx5("div", { ref: containerRef, className: "h-full w-full" })
749
+ children: /* @__PURE__ */ jsx6("div", { ref: containerRef, className: "h-full w-full" })
527
750
  }
528
751
  );
529
752
  }
@@ -531,7 +754,7 @@ var LineChart = React5.forwardRef(
531
754
  LineChart.displayName = "LineChart";
532
755
 
533
756
  // src/components/core/BarChart.tsx
534
- import * as React6 from "react";
757
+ import * as React7 from "react";
535
758
  import * as echarts4 from "echarts/core";
536
759
  import { BarChart as EChartsBarChart } from "echarts/charts";
537
760
  import {
@@ -541,8 +764,19 @@ import {
541
764
  TitleComponent as TitleComponent2
542
765
  } from "echarts/components";
543
766
  import { CanvasRenderer as CanvasRenderer2 } from "echarts/renderers";
544
- import { generateBarChartDescription } from "@classic-homes/charts-core";
545
- import { jsx as jsx6 } from "react/jsx-runtime";
767
+ import {
768
+ buildAxis as buildAxis2,
769
+ buildDataLabel as buildDataLabel2,
770
+ buildLegend as buildLegend2,
771
+ buildTooltip as buildTooltip2,
772
+ generateBarChartDescription,
773
+ getChartColors as getChartColors2,
774
+ gridInset as gridInset2,
775
+ mergeChartOption as mergeChartOption2,
776
+ seriesLabelLookup as seriesLabelLookup2,
777
+ withPointLabels as withPointLabels2
778
+ } from "@classic-homes/charts-core";
779
+ import { jsx as jsx7 } from "react/jsx-runtime";
546
780
  echarts4.use([
547
781
  EChartsBarChart,
548
782
  GridComponent2,
@@ -551,7 +785,7 @@ echarts4.use([
551
785
  TitleComponent2,
552
786
  CanvasRenderer2
553
787
  ]);
554
- var BarChart = React6.forwardRef(
788
+ var BarChart = React7.forwardRef(
555
789
  ({
556
790
  title,
557
791
  description,
@@ -568,17 +802,38 @@ var BarChart = React6.forwardRef(
568
802
  orientation = "vertical",
569
803
  stacked = false,
570
804
  showValues = false,
805
+ labelFormatter,
806
+ xAxisConfig,
807
+ yAxisConfig,
808
+ tooltipFormatter,
809
+ tooltipValueFormatter,
810
+ legend: legendConfig,
811
+ renderLegend,
812
+ onLegendChange,
813
+ option: optionOverride,
571
814
  onClick,
572
815
  class: className,
573
816
  ...props
574
817
  }, ref) => {
575
818
  const isEmpty = !data?.categories?.length || !data?.series?.length || data.series.every((s) => !s.data?.length);
576
819
  const isHorizontal = orientation === "horizontal";
577
- const option = React6.useMemo(() => {
820
+ const currentTheme = useChartTheme(theme);
821
+ const legend = useChartLegend({
822
+ names: data?.series?.map((s) => s.name) ?? [],
823
+ colors: data?.series?.map((s) => s.color) ?? [],
824
+ palette: getChartColors2(currentTheme).series,
825
+ config: legendConfig,
826
+ title,
827
+ onLegendChange
828
+ });
829
+ const customLegend = renderLegend !== void 0;
830
+ const option = React7.useMemo(() => {
578
831
  if (isEmpty) return {};
579
832
  const categoryAxis = {
580
833
  type: "category",
581
834
  data: data.categories,
835
+ // Horizontal bars list the first category at the top
836
+ inverse: isHorizontal,
582
837
  axisTick: {
583
838
  alignWithLabel: true
584
839
  }
@@ -589,47 +844,85 @@ var BarChart = React6.forwardRef(
589
844
  show: showGrid
590
845
  }
591
846
  };
592
- return {
593
- grid: {
594
- left: "3%",
595
- right: "4%",
596
- bottom: "3%",
597
- containLabel: true
847
+ const getLabel = seriesLabelLookup2(data.series);
848
+ return mergeChartOption2(
849
+ {
850
+ grid: {
851
+ left: "3%",
852
+ right: "4%",
853
+ bottom: "3%",
854
+ containLabel: true,
855
+ ...gridInset2(legendConfig, showLegend && !customLegend, xAxisConfig, yAxisConfig)
856
+ },
857
+ tooltip: buildTooltip2(
858
+ {
859
+ trigger: "axis",
860
+ axisPointer: {
861
+ type: "shadow"
862
+ }
863
+ },
864
+ {
865
+ show: showTooltip,
866
+ formatter: tooltipFormatter,
867
+ valueFormatter: tooltipValueFormatter,
868
+ getLabel
869
+ }
870
+ ),
871
+ legend: buildLegend2(
872
+ data.series.map((s) => s.name),
873
+ legendConfig,
874
+ { show: showLegend, custom: customLegend }
875
+ ),
876
+ xAxis: buildAxis2(isHorizontal ? valueAxis : categoryAxis, xAxisConfig),
877
+ yAxis: buildAxis2(isHorizontal ? categoryAxis : valueAxis, yAxisConfig),
878
+ series: data.series.map((series) => ({
879
+ name: series.name,
880
+ type: "bar",
881
+ data: withPointLabels2(series.data, series.labels, labelFormatter, {
882
+ position: isHorizontal ? "right" : "top"
883
+ }),
884
+ stack: stacked ? "Total" : void 0,
885
+ itemStyle: series.color ? { color: series.color } : void 0,
886
+ label: buildDataLabel2(
887
+ {
888
+ show: true,
889
+ position: isHorizontal ? "right" : "top"
890
+ },
891
+ void 0,
892
+ { show: showValues, formatter: labelFormatter, getLabel }
893
+ ),
894
+ emphasis: {
895
+ focus: "series"
896
+ }
897
+ }))
598
898
  },
599
- tooltip: showTooltip ? {
600
- trigger: "axis",
601
- axisPointer: {
602
- type: "shadow"
603
- }
604
- } : void 0,
605
- legend: showLegend ? {
606
- data: data.series.map((s) => s.name),
607
- bottom: 0
608
- } : void 0,
609
- xAxis: isHorizontal ? valueAxis : categoryAxis,
610
- yAxis: isHorizontal ? categoryAxis : valueAxis,
611
- series: data.series.map((series) => ({
612
- name: series.name,
613
- type: "bar",
614
- data: series.data,
615
- stack: stacked ? "Total" : void 0,
616
- itemStyle: series.color ? { color: series.color } : void 0,
617
- label: showValues ? {
618
- show: true,
619
- position: isHorizontal ? "right" : "top"
620
- } : void 0,
621
- emphasis: {
622
- focus: "series"
623
- }
624
- }))
625
- };
626
- }, [data, isEmpty, showTooltip, showLegend, showGrid, isHorizontal, stacked, showValues]);
899
+ optionOverride
900
+ );
901
+ }, [
902
+ data,
903
+ isEmpty,
904
+ showTooltip,
905
+ showLegend,
906
+ showGrid,
907
+ isHorizontal,
908
+ stacked,
909
+ showValues,
910
+ labelFormatter,
911
+ xAxisConfig,
912
+ yAxisConfig,
913
+ tooltipFormatter,
914
+ tooltipValueFormatter,
915
+ legendConfig,
916
+ customLegend,
917
+ optionOverride
918
+ ]);
627
919
  const latest = useLatestRef({ onClick });
628
920
  const { containerRef } = useChart({
629
921
  option,
630
922
  theme,
631
923
  animation,
632
924
  onReady: (chart) => {
925
+ legend.attach(chart);
633
926
  chart.on("click", (params) => {
634
927
  const { onClick: onClick2 } = latest.current;
635
928
  if (!onClick2) return;
@@ -647,7 +940,7 @@ var BarChart = React6.forwardRef(
647
940
  }
648
941
  });
649
942
  const accessibilityDescription = description || (!isEmpty ? generateBarChartDescription(title, data) : void 0);
650
- return /* @__PURE__ */ jsx6(
943
+ return /* @__PURE__ */ jsx7(
651
944
  ChartContainer,
652
945
  {
653
946
  ref,
@@ -659,8 +952,10 @@ var BarChart = React6.forwardRef(
659
952
  empty: isEmpty,
660
953
  emptyMessage,
661
954
  className: cn(className),
955
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
956
+ legendPosition: legendConfig?.position,
662
957
  ...props,
663
- children: /* @__PURE__ */ jsx6("div", { ref: containerRef, className: "h-full w-full" })
958
+ children: /* @__PURE__ */ jsx7("div", { ref: containerRef, className: "h-full w-full" })
664
959
  }
665
960
  );
666
961
  }
@@ -668,15 +963,27 @@ var BarChart = React6.forwardRef(
668
963
  BarChart.displayName = "BarChart";
669
964
 
670
965
  // src/components/core/PieChart.tsx
671
- import * as React7 from "react";
966
+ import * as React8 from "react";
672
967
  import * as echarts5 from "echarts/core";
673
968
  import { PieChart as EChartsPieChart } from "echarts/charts";
674
969
  import { TooltipComponent as TooltipComponent3, LegendComponent as LegendComponent3, TitleComponent as TitleComponent3 } from "echarts/components";
675
970
  import { CanvasRenderer as CanvasRenderer3 } from "echarts/renderers";
676
- import { generatePieChartDescription } from "@classic-homes/charts-core";
677
- import { jsx as jsx7 } from "react/jsx-runtime";
971
+ import {
972
+ buildDataLabel as buildDataLabel3,
973
+ buildLegend as buildLegend3,
974
+ buildTooltip as buildTooltip3,
975
+ escapeHtml,
976
+ formatTooltipValue,
977
+ generatePieChartDescription,
978
+ getChartColors as getChartColors3,
979
+ itemLabelLookup,
980
+ legendCenter,
981
+ mergeChartOption as mergeChartOption3,
982
+ pointLabel
983
+ } from "@classic-homes/charts-core";
984
+ import { jsx as jsx8 } from "react/jsx-runtime";
678
985
  echarts5.use([EChartsPieChart, TooltipComponent3, LegendComponent3, TitleComponent3, CanvasRenderer3]);
679
- var PieChart = React7.forwardRef(
986
+ var PieChart = React8.forwardRef(
680
987
  ({
681
988
  title,
682
989
  description,
@@ -692,58 +999,107 @@ var PieChart = React7.forwardRef(
692
999
  showLabels = true,
693
1000
  labelPosition = "outside",
694
1001
  innerRadius = 0,
1002
+ labelFormatter,
1003
+ tooltipFormatter,
1004
+ tooltipValueFormatter,
1005
+ legend: legendConfig,
1006
+ renderLegend,
1007
+ onLegendChange,
1008
+ option: optionOverride,
695
1009
  onClick,
696
1010
  class: className,
697
1011
  ...props
698
1012
  }, ref) => {
699
1013
  const isEmpty = !data?.length;
700
- const option = React7.useMemo(() => {
1014
+ const currentTheme = useChartTheme(theme);
1015
+ const legend = useChartLegend({
1016
+ names: data?.map((d) => d.name) ?? [],
1017
+ colors: data?.map((d) => d.color) ?? [],
1018
+ palette: getChartColors3(currentTheme).series,
1019
+ config: legendConfig,
1020
+ title,
1021
+ onLegendChange
1022
+ });
1023
+ const customLegend = renderLegend !== void 0;
1024
+ const option = React8.useMemo(() => {
701
1025
  if (isEmpty) return {};
702
- return {
703
- tooltip: showTooltip ? {
704
- trigger: "item",
705
- formatter: "{a} <br/>{b}: {c} ({d}%)"
706
- } : void 0,
707
- legend: showLegend ? {
708
- orient: "horizontal",
709
- bottom: 0,
710
- data: data.map((d) => d.name)
711
- } : void 0,
712
- series: [
713
- {
714
- name: title,
715
- type: "pie",
716
- radius: innerRadius ? [innerRadius, "70%"] : "70%",
717
- center: ["50%", "45%"],
718
- data: data.map((item) => ({
719
- name: item.name,
720
- value: item.value,
721
- itemStyle: item.color ? { color: item.color } : void 0
722
- })),
723
- label: showLabels ? {
724
- show: true,
725
- position: labelPosition,
726
- formatter: labelPosition === "inside" ? "{d}%" : "{b}: {d}%"
727
- } : {
728
- show: false
729
- },
730
- emphasis: {
731
- itemStyle: {
732
- shadowBlur: 10,
733
- shadowOffsetX: 0,
734
- shadowColor: "rgba(0, 0, 0, 0.5)"
1026
+ const valueFormatter = tooltipFormatter === void 0 ? tooltipValueFormatter : void 0;
1027
+ const tooltipBase = {
1028
+ trigger: "item",
1029
+ formatter: valueFormatter ? (params) => {
1030
+ const p = params;
1031
+ return `${escapeHtml(p.seriesName)} <br/>${escapeHtml(p.name)}: ${escapeHtml(formatTooltipValue(p.value, valueFormatter))} (${p.percent}%)`;
1032
+ } : "{a} <br/>{b}: {c} ({d}%)"
1033
+ };
1034
+ return mergeChartOption3(
1035
+ {
1036
+ tooltip: buildTooltip3(tooltipBase, {
1037
+ show: showTooltip,
1038
+ formatter: tooltipFormatter,
1039
+ valueFormatter: tooltipValueFormatter,
1040
+ getLabel: itemLabelLookup(data)
1041
+ }),
1042
+ legend: buildLegend3(
1043
+ data.map((d) => d.name),
1044
+ legendConfig,
1045
+ { show: showLegend, custom: customLegend, defaults: { orient: "horizontal" } }
1046
+ ),
1047
+ series: [
1048
+ {
1049
+ name: title,
1050
+ type: "pie",
1051
+ radius: innerRadius ? [innerRadius, "70%"] : "70%",
1052
+ center: legendCenter(legendConfig, showLegend && !customLegend, ["50%", "45%"]),
1053
+ data: data.map((item) => ({
1054
+ name: item.name,
1055
+ value: item.value,
1056
+ itemStyle: item.color ? { color: item.color } : void 0,
1057
+ label: pointLabel(item.label, labelFormatter, { position: labelPosition })
1058
+ })),
1059
+ label: buildDataLabel3(
1060
+ {
1061
+ show: true,
1062
+ position: labelPosition,
1063
+ formatter: labelPosition === "inside" ? "{d}%" : "{b}: {d}%"
1064
+ },
1065
+ { show: false },
1066
+ { show: showLabels, formatter: labelFormatter, getLabel: itemLabelLookup(data) }
1067
+ ),
1068
+ emphasis: {
1069
+ itemStyle: {
1070
+ shadowBlur: 10,
1071
+ shadowOffsetX: 0,
1072
+ shadowColor: "rgba(0, 0, 0, 0.5)"
1073
+ }
735
1074
  }
736
1075
  }
737
- }
738
- ]
739
- };
740
- }, [data, isEmpty, showTooltip, showLegend, showLabels, labelPosition, innerRadius, title]);
1076
+ ]
1077
+ },
1078
+ optionOverride
1079
+ );
1080
+ }, [
1081
+ data,
1082
+ isEmpty,
1083
+ showTooltip,
1084
+ showLegend,
1085
+ showLabels,
1086
+ labelPosition,
1087
+ innerRadius,
1088
+ title,
1089
+ labelFormatter,
1090
+ tooltipFormatter,
1091
+ tooltipValueFormatter,
1092
+ legendConfig,
1093
+ customLegend,
1094
+ optionOverride
1095
+ ]);
741
1096
  const latest = useLatestRef({ onClick });
742
1097
  const { containerRef } = useChart({
743
1098
  option,
744
1099
  theme,
745
1100
  animation,
746
1101
  onReady: (chart) => {
1102
+ legend.attach(chart);
747
1103
  chart.on("click", (params) => {
748
1104
  const { onClick: onClick2 } = latest.current;
749
1105
  if (!onClick2) return;
@@ -759,7 +1115,7 @@ var PieChart = React7.forwardRef(
759
1115
  }
760
1116
  });
761
1117
  const accessibilityDescription = description || (!isEmpty ? generatePieChartDescription(title, data, false) : void 0);
762
- return /* @__PURE__ */ jsx7(
1118
+ return /* @__PURE__ */ jsx8(
763
1119
  ChartContainer,
764
1120
  {
765
1121
  ref,
@@ -771,8 +1127,10 @@ var PieChart = React7.forwardRef(
771
1127
  empty: isEmpty,
772
1128
  emptyMessage,
773
1129
  className: cn(className),
1130
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
1131
+ legendPosition: legendConfig?.position,
774
1132
  ...props,
775
- children: /* @__PURE__ */ jsx7("div", { ref: containerRef, className: "h-full w-full" })
1133
+ children: /* @__PURE__ */ jsx8("div", { ref: containerRef, className: "h-full w-full" })
776
1134
  }
777
1135
  );
778
1136
  }
@@ -780,7 +1138,7 @@ var PieChart = React7.forwardRef(
780
1138
  PieChart.displayName = "PieChart";
781
1139
 
782
1140
  // src/components/core/AreaChart.tsx
783
- import * as React8 from "react";
1141
+ import * as React9 from "react";
784
1142
  import * as echarts6 from "echarts/core";
785
1143
  import { LineChart as EChartsLineChart2 } from "echarts/charts";
786
1144
  import {
@@ -790,8 +1148,21 @@ import {
790
1148
  TitleComponent as TitleComponent4
791
1149
  } from "echarts/components";
792
1150
  import { CanvasRenderer as CanvasRenderer4 } from "echarts/renderers";
793
- import { colorWithAlpha, getChartColors as getChartColors2, THEME_DARK as THEME_DARK3 } from "@classic-homes/charts-core";
794
- import { jsx as jsx8 } from "react/jsx-runtime";
1151
+ import {
1152
+ buildAxis as buildAxis3,
1153
+ buildDataLabel as buildDataLabel4,
1154
+ buildLegend as buildLegend4,
1155
+ buildTooltip as buildTooltip4,
1156
+ colorWithAlpha,
1157
+ getChartColors as getChartColors4,
1158
+ hasPointLabels as hasPointLabels2,
1159
+ gridInset as gridInset3,
1160
+ mergeChartOption as mergeChartOption4,
1161
+ seriesLabelLookup as seriesLabelLookup3,
1162
+ THEME_DARK as THEME_DARK3,
1163
+ withPointLabels as withPointLabels3
1164
+ } from "@classic-homes/charts-core";
1165
+ import { jsx as jsx9 } from "react/jsx-runtime";
795
1166
  echarts6.use([
796
1167
  EChartsLineChart2,
797
1168
  GridComponent3,
@@ -800,7 +1171,7 @@ echarts6.use([
800
1171
  TitleComponent4,
801
1172
  CanvasRenderer4
802
1173
  ]);
803
- var AreaChart = React8.forwardRef(
1174
+ var AreaChart = React9.forwardRef(
804
1175
  ({
805
1176
  title,
806
1177
  description,
@@ -817,76 +1188,143 @@ var AreaChart = React8.forwardRef(
817
1188
  smooth = false,
818
1189
  stacked = false,
819
1190
  gradient = true,
1191
+ showLabels = false,
1192
+ labelFormatter,
1193
+ xAxisConfig,
1194
+ yAxisConfig,
1195
+ tooltipFormatter,
1196
+ tooltipValueFormatter,
1197
+ legend: legendConfig,
1198
+ renderLegend,
1199
+ onLegendChange,
1200
+ option: optionOverride,
820
1201
  onClick,
821
1202
  class: className,
822
1203
  ...props
823
1204
  }, ref) => {
824
1205
  const isEmpty = !data?.categories?.length || !data?.series?.length || data.series.every((s) => !s.data?.length);
825
1206
  const currentTheme = useChartTheme(theme);
826
- const chartColors = getChartColors2(currentTheme);
827
- const option = React8.useMemo(() => {
1207
+ const chartColors = getChartColors4(currentTheme);
1208
+ const legend = useChartLegend({
1209
+ names: data?.series?.map((s) => s.name) ?? [],
1210
+ colors: data?.series?.map((s) => s.color) ?? [],
1211
+ palette: chartColors.series,
1212
+ config: legendConfig,
1213
+ title,
1214
+ onLegendChange
1215
+ });
1216
+ const customLegend = renderLegend !== void 0;
1217
+ const option = React9.useMemo(() => {
828
1218
  if (isEmpty) return {};
829
- return {
830
- grid: {
831
- left: "3%",
832
- right: "4%",
833
- bottom: "3%",
834
- containLabel: true
835
- },
836
- tooltip: showTooltip ? {
837
- trigger: "axis",
838
- axisPointer: {
839
- type: "cross",
840
- label: {
841
- backgroundColor: chartColors.axisPointerLabel
1219
+ const getLabel = seriesLabelLookup3(data.series);
1220
+ const showSymbol = showLabels || hasPointLabels2(data.series);
1221
+ return mergeChartOption4(
1222
+ {
1223
+ grid: {
1224
+ left: "3%",
1225
+ right: "4%",
1226
+ bottom: "3%",
1227
+ containLabel: true,
1228
+ ...gridInset3(legendConfig, showLegend && !customLegend, xAxisConfig, yAxisConfig)
1229
+ },
1230
+ tooltip: buildTooltip4(
1231
+ {
1232
+ trigger: "axis",
1233
+ axisPointer: {
1234
+ type: "cross",
1235
+ label: {
1236
+ backgroundColor: chartColors.axisPointerLabel
1237
+ }
1238
+ }
1239
+ },
1240
+ {
1241
+ show: showTooltip,
1242
+ formatter: tooltipFormatter,
1243
+ valueFormatter: tooltipValueFormatter,
1244
+ getLabel
842
1245
  }
843
- }
844
- } : void 0,
845
- legend: showLegend ? {
846
- data: data.series.map((s) => s.name),
847
- bottom: 0
848
- } : void 0,
849
- xAxis: {
850
- type: "category",
851
- boundaryGap: false,
852
- data: data.categories
853
- },
854
- yAxis: {
855
- type: "value",
856
- splitLine: {
857
- show: showGrid
858
- }
859
- },
860
- series: data.series.map((series, index) => {
861
- const base = series.color || chartColors.series[index % chartColors.series.length];
862
- return {
863
- name: series.name,
864
- type: "line",
865
- data: series.data,
866
- smooth,
867
- stack: stacked ? "Total" : void 0,
868
- areaStyle: gradient ? {
869
- opacity: 0.8,
870
- color: new echarts6.graphic.LinearGradient(0, 0, 0, 1, [
871
- { offset: 0, color: colorWithAlpha(base, 0.8) },
872
- { offset: 1, color: colorWithAlpha(base, 0.1) }
873
- ])
874
- } : { opacity: 0.5 },
875
- itemStyle: series.color ? { color: series.color } : void 0,
876
- emphasis: {
877
- focus: "series"
1246
+ ),
1247
+ legend: buildLegend4(
1248
+ data.series.map((s) => s.name),
1249
+ legendConfig,
1250
+ { show: showLegend, custom: customLegend }
1251
+ ),
1252
+ xAxis: buildAxis3(
1253
+ {
1254
+ type: "category",
1255
+ boundaryGap: false,
1256
+ data: data.categories
878
1257
  },
879
- showSymbol: false
880
- };
881
- })
882
- };
883
- }, [data, isEmpty, showTooltip, showLegend, showGrid, smooth, stacked, gradient, chartColors]);
1258
+ xAxisConfig
1259
+ ),
1260
+ yAxis: buildAxis3(
1261
+ {
1262
+ type: "value",
1263
+ splitLine: {
1264
+ show: showGrid
1265
+ }
1266
+ },
1267
+ yAxisConfig
1268
+ ),
1269
+ series: data.series.map((series, index) => {
1270
+ const base = series.color || chartColors.series[index % chartColors.series.length];
1271
+ return {
1272
+ name: series.name,
1273
+ type: "line",
1274
+ data: withPointLabels3(series.data, series.labels, labelFormatter, {
1275
+ position: "top"
1276
+ }),
1277
+ smooth,
1278
+ stack: stacked ? "Total" : void 0,
1279
+ areaStyle: gradient ? {
1280
+ opacity: 0.8,
1281
+ color: new echarts6.graphic.LinearGradient(0, 0, 0, 1, [
1282
+ { offset: 0, color: colorWithAlpha(base, 0.8) },
1283
+ { offset: 1, color: colorWithAlpha(base, 0.1) }
1284
+ ])
1285
+ } : { opacity: 0.5 },
1286
+ itemStyle: series.color ? { color: series.color } : void 0,
1287
+ label: buildDataLabel4({ show: true, position: "top" }, void 0, {
1288
+ show: showLabels,
1289
+ formatter: labelFormatter,
1290
+ getLabel
1291
+ }),
1292
+ emphasis: {
1293
+ focus: "series"
1294
+ },
1295
+ showSymbol
1296
+ };
1297
+ })
1298
+ },
1299
+ optionOverride
1300
+ );
1301
+ }, [
1302
+ data,
1303
+ isEmpty,
1304
+ showTooltip,
1305
+ showLegend,
1306
+ showGrid,
1307
+ smooth,
1308
+ stacked,
1309
+ gradient,
1310
+ chartColors,
1311
+ showLabels,
1312
+ labelFormatter,
1313
+ xAxisConfig,
1314
+ yAxisConfig,
1315
+ tooltipFormatter,
1316
+ tooltipValueFormatter,
1317
+ legendConfig,
1318
+ customLegend,
1319
+ optionOverride
1320
+ ]);
884
1321
  const latest = useLatestRef({ onClick });
885
1322
  const { containerRef } = useChart({
886
1323
  option,
887
1324
  theme: currentTheme === THEME_DARK3 ? "dark" : "light",
888
1325
  animation,
889
1326
  onReady: (chart) => {
1327
+ legend.attach(chart);
890
1328
  chart.on("click", (params) => {
891
1329
  const { onClick: onClick2 } = latest.current;
892
1330
  if (!onClick2) return;
@@ -904,7 +1342,7 @@ var AreaChart = React8.forwardRef(
904
1342
  }
905
1343
  });
906
1344
  const accessibilityDescription = description || (!isEmpty ? `${title}. Area chart with ${data.categories.length} data points across ${data.series.length} series.` : void 0);
907
- return /* @__PURE__ */ jsx8(
1345
+ return /* @__PURE__ */ jsx9(
908
1346
  ChartContainer,
909
1347
  {
910
1348
  ref,
@@ -916,8 +1354,10 @@ var AreaChart = React8.forwardRef(
916
1354
  empty: isEmpty,
917
1355
  emptyMessage,
918
1356
  className: cn(className),
1357
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
1358
+ legendPosition: legendConfig?.position,
919
1359
  ...props,
920
- children: /* @__PURE__ */ jsx8("div", { ref: containerRef, className: "h-full w-full" })
1360
+ children: /* @__PURE__ */ jsx9("div", { ref: containerRef, className: "h-full w-full" })
921
1361
  }
922
1362
  );
923
1363
  }
@@ -925,7 +1365,7 @@ var AreaChart = React8.forwardRef(
925
1365
  AreaChart.displayName = "AreaChart";
926
1366
 
927
1367
  // src/components/core/ScatterChart.tsx
928
- import * as React9 from "react";
1368
+ import * as React10 from "react";
929
1369
  import * as echarts7 from "echarts/core";
930
1370
  import { ScatterChart as EChartsScatterChart, LineChart as EChartsLineChart3 } from "echarts/charts";
931
1371
  import {
@@ -935,8 +1375,21 @@ import {
935
1375
  TitleComponent as TitleComponent5
936
1376
  } from "echarts/components";
937
1377
  import { CanvasRenderer as CanvasRenderer5 } from "echarts/renderers";
938
- import { escapeHtml, generateScatterChartDescription } from "@classic-homes/charts-core";
939
- import { jsx as jsx9 } from "react/jsx-runtime";
1378
+ import {
1379
+ buildAxis as buildAxis4,
1380
+ buildDataLabel as buildDataLabel5,
1381
+ buildLegend as buildLegend5,
1382
+ buildTooltip as buildTooltip5,
1383
+ escapeHtml as escapeHtml2,
1384
+ formatTooltipValue as formatTooltipValue2,
1385
+ generateScatterChartDescription,
1386
+ getChartColors as getChartColors5,
1387
+ gridInset as gridInset4,
1388
+ mergeChartOption as mergeChartOption5,
1389
+ seriesLabelLookup as seriesLabelLookup4,
1390
+ withPointLabels as withPointLabels4
1391
+ } from "@classic-homes/charts-core";
1392
+ import { jsx as jsx10 } from "react/jsx-runtime";
940
1393
  echarts7.use([
941
1394
  EChartsScatterChart,
942
1395
  // Trend lines are line series
@@ -947,7 +1400,7 @@ echarts7.use([
947
1400
  TitleComponent5,
948
1401
  CanvasRenderer5
949
1402
  ]);
950
- var ScatterChart = React9.forwardRef(
1403
+ var ScatterChart = React10.forwardRef(
951
1404
  ({
952
1405
  title,
953
1406
  description,
@@ -963,19 +1416,45 @@ var ScatterChart = React9.forwardRef(
963
1416
  showGrid = true,
964
1417
  showTrendLine = false,
965
1418
  symbolSize = 10,
1419
+ showLabels = false,
1420
+ labelFormatter,
1421
+ xAxisConfig,
1422
+ yAxisConfig,
1423
+ tooltipFormatter,
1424
+ tooltipValueFormatter,
1425
+ legend: legendConfig,
1426
+ renderLegend,
1427
+ onLegendChange,
1428
+ option: optionOverride,
966
1429
  onClick,
967
1430
  class: className,
968
1431
  ...props
969
1432
  }, ref) => {
970
1433
  const isEmpty = !data?.series?.length || data.series.every((s) => !s.data?.length);
971
- const option = React9.useMemo(() => {
1434
+ const currentTheme = useChartTheme(theme);
1435
+ const legend = useChartLegend({
1436
+ names: data?.series?.map((s) => s.name) ?? [],
1437
+ colors: data?.series?.map((s) => s.color) ?? [],
1438
+ palette: getChartColors5(currentTheme).series,
1439
+ config: legendConfig,
1440
+ title,
1441
+ onLegendChange
1442
+ });
1443
+ const customLegend = renderLegend !== void 0;
1444
+ const option = React10.useMemo(() => {
972
1445
  if (isEmpty) return {};
1446
+ const getLabel = seriesLabelLookup4(data.series);
973
1447
  const series = data.series.map((s) => ({
974
1448
  name: s.name,
975
1449
  type: "scatter",
976
- data: s.data,
1450
+ data: withPointLabels4(s.data, s.labels, labelFormatter, { position: "top" }),
977
1451
  symbolSize: typeof symbolSize === "function" ? symbolSize : symbolSize,
978
1452
  itemStyle: s.color ? { color: s.color } : void 0,
1453
+ label: buildDataLabel5({ show: true, position: "top" }, void 0, {
1454
+ show: showLabels,
1455
+ formatter: labelFormatter,
1456
+ getLabel
1457
+ }),
979
1458
  emphasis: {
980
1459
  focus: "series",
981
1460
  itemStyle: {
@@ -1023,46 +1502,85 @@ var ScatterChart = React9.forwardRef(
1023
1502
  });
1024
1503
  });
1025
1504
  }
1026
- return {
1027
- grid: {
1028
- left: "3%",
1029
- right: "4%",
1030
- bottom: "3%",
1031
- containLabel: true
1032
- },
1033
- tooltip: showTooltip ? {
1034
- trigger: "item",
1035
- formatter: (params) => {
1036
- const p = params;
1037
- if (!p.value) return "";
1038
- return `${escapeHtml(p.seriesName)}<br/>X: ${escapeHtml(p.value[0])}<br/>Y: ${escapeHtml(p.value[1])}`;
1039
- }
1040
- } : void 0,
1041
- legend: showLegend ? {
1042
- data: data.series.map((s) => s.name),
1043
- bottom: 0
1044
- } : void 0,
1045
- xAxis: {
1046
- type: "value",
1047
- splitLine: {
1048
- show: showGrid
1049
- }
1050
- },
1051
- yAxis: {
1052
- type: "value",
1053
- splitLine: {
1054
- show: showGrid
1055
- }
1505
+ return mergeChartOption5(
1506
+ {
1507
+ grid: {
1508
+ left: "3%",
1509
+ right: "4%",
1510
+ bottom: "3%",
1511
+ containLabel: true,
1512
+ ...gridInset4(legendConfig, showLegend && !customLegend, xAxisConfig, yAxisConfig)
1513
+ },
1514
+ tooltip: buildTooltip5(
1515
+ {
1516
+ trigger: "item",
1517
+ formatter: (params) => {
1518
+ const p = params;
1519
+ if (!p.value) return "";
1520
+ const x = formatTooltipValue2(p.value[0], tooltipValueFormatter);
1521
+ const y = formatTooltipValue2(p.value[1], tooltipValueFormatter);
1522
+ return `${escapeHtml2(p.seriesName)}<br/>X: ${escapeHtml2(x)}<br/>Y: ${escapeHtml2(y)}`;
1523
+ }
1524
+ },
1525
+ {
1526
+ show: showTooltip,
1527
+ formatter: tooltipFormatter,
1528
+ valueFormatter: tooltipValueFormatter,
1529
+ getLabel
1530
+ }
1531
+ ),
1532
+ legend: buildLegend5(
1533
+ data.series.map((s) => s.name),
1534
+ legendConfig,
1535
+ { show: showLegend, custom: customLegend }
1536
+ ),
1537
+ xAxis: buildAxis4(
1538
+ {
1539
+ type: "value",
1540
+ splitLine: {
1541
+ show: showGrid
1542
+ }
1543
+ },
1544
+ xAxisConfig
1545
+ ),
1546
+ yAxis: buildAxis4(
1547
+ {
1548
+ type: "value",
1549
+ splitLine: {
1550
+ show: showGrid
1551
+ }
1552
+ },
1553
+ yAxisConfig
1554
+ ),
1555
+ series
1056
1556
  },
1057
- series
1058
- };
1059
- }, [data, isEmpty, showTooltip, showLegend, showGrid, showTrendLine, symbolSize]);
1557
+ optionOverride
1558
+ );
1559
+ }, [
1560
+ data,
1561
+ isEmpty,
1562
+ showTooltip,
1563
+ showLegend,
1564
+ showGrid,
1565
+ showTrendLine,
1566
+ symbolSize,
1567
+ showLabels,
1568
+ labelFormatter,
1569
+ xAxisConfig,
1570
+ yAxisConfig,
1571
+ tooltipFormatter,
1572
+ tooltipValueFormatter,
1573
+ legendConfig,
1574
+ customLegend,
1575
+ optionOverride
1576
+ ]);
1060
1577
  const latest = useLatestRef({ onClick });
1061
1578
  const { containerRef } = useChart({
1062
1579
  option,
1063
1580
  theme,
1064
1581
  animation,
1065
1582
  onReady: (chart) => {
1583
+ legend.attach(chart);
1066
1584
  chart.on("click", (params) => {
1067
1585
  const { onClick: onClick2 } = latest.current;
1068
1586
  if (!onClick2) return;
@@ -1080,7 +1598,7 @@ var ScatterChart = React9.forwardRef(
1080
1598
  }
1081
1599
  });
1082
1600
  const accessibilityDescription = description || (!isEmpty ? generateScatterChartDescription(title, data) : void 0);
1083
- return /* @__PURE__ */ jsx9(
1601
+ return /* @__PURE__ */ jsx10(
1084
1602
  ChartContainer,
1085
1603
  {
1086
1604
  ref,
@@ -1092,8 +1610,10 @@ var ScatterChart = React9.forwardRef(
1092
1610
  empty: isEmpty,
1093
1611
  emptyMessage,
1094
1612
  className: cn(className),
1613
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
1614
+ legendPosition: legendConfig?.position,
1095
1615
  ...props,
1096
- children: /* @__PURE__ */ jsx9("div", { ref: containerRef, className: "h-full w-full" })
1616
+ children: /* @__PURE__ */ jsx10("div", { ref: containerRef, className: "h-full w-full" })
1097
1617
  }
1098
1618
  );
1099
1619
  }
@@ -1101,19 +1621,29 @@ var ScatterChart = React9.forwardRef(
1101
1621
  ScatterChart.displayName = "ScatterChart";
1102
1622
 
1103
1623
  // src/components/core/DonutChart.tsx
1104
- import * as React10 from "react";
1624
+ import * as React11 from "react";
1105
1625
  import * as echarts8 from "echarts/core";
1106
1626
  import { PieChart as EChartsPieChart2 } from "echarts/charts";
1107
1627
  import { TooltipComponent as TooltipComponent6, LegendComponent as LegendComponent6, TitleComponent as TitleComponent6 } from "echarts/components";
1108
1628
  import { CanvasRenderer as CanvasRenderer6 } from "echarts/renderers";
1109
1629
  import {
1630
+ buildDataLabel as buildDataLabel6,
1631
+ buildLegend as buildLegend6,
1632
+ buildTooltip as buildTooltip6,
1633
+ escapeHtml as escapeHtml3,
1634
+ formatTooltipValue as formatTooltipValue3,
1110
1635
  generatePieChartDescription as generatePieChartDescription2,
1111
- getChartColors as getChartColors3,
1636
+ getChartColors as getChartColors6,
1637
+ itemLabelLookup as itemLabelLookup2,
1638
+ anchorCenterGraphic,
1639
+ legendCenter as legendCenter2,
1640
+ mergeChartOption as mergeChartOption6,
1641
+ pointLabel as pointLabel2,
1112
1642
  THEME_DARK as THEME_DARK4
1113
1643
  } from "@classic-homes/charts-core";
1114
- import { jsx as jsx10 } from "react/jsx-runtime";
1644
+ import { jsx as jsx11 } from "react/jsx-runtime";
1115
1645
  echarts8.use([EChartsPieChart2, TooltipComponent6, LegendComponent6, TitleComponent6, CanvasRenderer6]);
1116
- var DonutChart = React10.forwardRef(
1646
+ var DonutChart = React11.forwardRef(
1117
1647
  ({
1118
1648
  title,
1119
1649
  description,
@@ -1131,95 +1661,134 @@ var DonutChart = React10.forwardRef(
1131
1661
  innerRadius = "50%",
1132
1662
  centerLabel,
1133
1663
  centerValue,
1664
+ labelFormatter,
1665
+ tooltipFormatter,
1666
+ tooltipValueFormatter,
1667
+ legend: legendConfig,
1668
+ renderLegend,
1669
+ onLegendChange,
1670
+ option: optionOverride,
1134
1671
  onClick,
1135
1672
  class: className,
1136
1673
  ...props
1137
1674
  }, ref) => {
1138
1675
  const isEmpty = !data?.length;
1139
1676
  const currentTheme = useChartTheme(theme);
1140
- const chartColors = getChartColors3(currentTheme);
1141
- const option = React10.useMemo(() => {
1677
+ const chartColors = getChartColors6(currentTheme);
1678
+ const legend = useChartLegend({
1679
+ names: data?.map((d) => d.name) ?? [],
1680
+ colors: data?.map((d) => d.color) ?? [],
1681
+ palette: chartColors.series,
1682
+ config: legendConfig,
1683
+ title,
1684
+ onLegendChange
1685
+ });
1686
+ const customLegend = renderLegend !== void 0;
1687
+ const option = React11.useMemo(() => {
1142
1688
  if (isEmpty) return {};
1689
+ const pieCenter = legendCenter2(legendConfig, showLegend && !customLegend, ["50%", "45%"]);
1143
1690
  const graphicElements = [];
1144
1691
  if (centerLabel || centerValue) {
1145
- graphicElements.push({
1146
- type: "group",
1147
- left: "center",
1148
- top: "40%",
1149
- children: [
1150
- ...centerValue !== void 0 ? [
1151
- {
1152
- type: "text",
1153
- style: {
1154
- text: String(centerValue),
1155
- fontSize: 28,
1156
- fontWeight: "bold",
1157
- fill: chartColors.foreground,
1158
- textAlign: "center"
1159
- },
1160
- top: 0,
1161
- left: "center"
1162
- }
1163
- ] : [],
1164
- ...centerLabel ? [
1165
- {
1166
- type: "text",
1167
- style: {
1168
- text: centerLabel,
1169
- fontSize: 14,
1170
- fill: chartColors.mutedForeground,
1171
- textAlign: "center"
1172
- },
1173
- top: centerValue !== void 0 ? 35 : 0,
1174
- left: "center"
1175
- }
1176
- ] : []
1177
- ]
1178
- });
1179
- }
1180
- return {
1181
- tooltip: showTooltip ? {
1182
- trigger: "item",
1183
- formatter: "{a} <br/>{b}: {c} ({d}%)"
1184
- } : void 0,
1185
- legend: showLegend ? {
1186
- orient: "horizontal",
1187
- bottom: 0,
1188
- data: data.map((d) => d.name)
1189
- } : void 0,
1190
- graphic: graphicElements.length > 0 ? graphicElements : void 0,
1191
- series: [
1192
- {
1193
- name: title,
1194
- type: "pie",
1195
- radius: [innerRadius, "70%"],
1196
- center: ["50%", "45%"],
1197
- avoidLabelOverlap: true,
1198
- data: data.map((item) => ({
1199
- name: item.name,
1200
- value: item.value,
1201
- itemStyle: item.color ? { color: item.color } : void 0
1202
- })),
1203
- label: showLabels ? {
1204
- show: true,
1205
- position: labelPosition,
1206
- formatter: labelPosition === "inside" ? "{d}%" : "{b}: {d}%"
1207
- } : {
1208
- show: false
1209
- },
1210
- labelLine: {
1211
- show: showLabels && labelPosition === "outside"
1692
+ graphicElements.push(
1693
+ anchorCenterGraphic(
1694
+ {
1695
+ type: "group",
1696
+ left: "center",
1697
+ top: "40%",
1698
+ children: [
1699
+ ...centerValue !== void 0 ? [
1700
+ {
1701
+ type: "text",
1702
+ style: {
1703
+ text: String(centerValue),
1704
+ fontSize: 28,
1705
+ fontWeight: "bold",
1706
+ fill: chartColors.foreground,
1707
+ textAlign: "center"
1708
+ },
1709
+ top: 0,
1710
+ left: "center"
1711
+ }
1712
+ ] : [],
1713
+ ...centerLabel ? [
1714
+ {
1715
+ type: "text",
1716
+ style: {
1717
+ text: centerLabel,
1718
+ fontSize: 14,
1719
+ fill: chartColors.mutedForeground,
1720
+ textAlign: "center"
1721
+ },
1722
+ top: centerValue !== void 0 ? 35 : 0,
1723
+ left: "center"
1724
+ }
1725
+ ] : []
1726
+ ]
1212
1727
  },
1213
- emphasis: {
1214
- itemStyle: {
1215
- shadowBlur: 10,
1216
- shadowOffsetX: 0,
1217
- shadowColor: "rgba(0, 0, 0, 0.5)"
1728
+ pieCenter,
1729
+ ["50%", "45%"]
1730
+ )
1731
+ );
1732
+ }
1733
+ const valueFormatter = tooltipFormatter === void 0 ? tooltipValueFormatter : void 0;
1734
+ const tooltipBase = {
1735
+ trigger: "item",
1736
+ formatter: valueFormatter ? (params) => {
1737
+ const p = params;
1738
+ return `${escapeHtml3(p.seriesName)} <br/>${escapeHtml3(p.name)}: ${escapeHtml3(formatTooltipValue3(p.value, valueFormatter))} (${p.percent}%)`;
1739
+ } : "{a} <br/>{b}: {c} ({d}%)"
1740
+ };
1741
+ return mergeChartOption6(
1742
+ {
1743
+ tooltip: buildTooltip6(tooltipBase, {
1744
+ show: showTooltip,
1745
+ formatter: tooltipFormatter,
1746
+ valueFormatter: tooltipValueFormatter,
1747
+ getLabel: itemLabelLookup2(data)
1748
+ }),
1749
+ legend: buildLegend6(
1750
+ data.map((d) => d.name),
1751
+ legendConfig,
1752
+ { show: showLegend, custom: customLegend, defaults: { orient: "horizontal" } }
1753
+ ),
1754
+ graphic: graphicElements.length > 0 ? graphicElements : void 0,
1755
+ series: [
1756
+ {
1757
+ name: title,
1758
+ type: "pie",
1759
+ radius: [innerRadius, "70%"],
1760
+ center: pieCenter,
1761
+ avoidLabelOverlap: true,
1762
+ data: data.map((item) => ({
1763
+ name: item.name,
1764
+ value: item.value,
1765
+ itemStyle: item.color ? { color: item.color } : void 0,
1766
+ label: pointLabel2(item.label, labelFormatter, { position: labelPosition })
1767
+ })),
1768
+ label: buildDataLabel6(
1769
+ {
1770
+ show: true,
1771
+ position: labelPosition,
1772
+ formatter: labelPosition === "inside" ? "{d}%" : "{b}: {d}%"
1773
+ },
1774
+ { show: false },
1775
+ { show: showLabels, formatter: labelFormatter, getLabel: itemLabelLookup2(data) }
1776
+ ),
1777
+ labelLine: {
1778
+ show: showLabels && labelPosition === "outside"
1779
+ },
1780
+ emphasis: {
1781
+ itemStyle: {
1782
+ shadowBlur: 10,
1783
+ shadowOffsetX: 0,
1784
+ shadowColor: "rgba(0, 0, 0, 0.5)"
1785
+ }
1218
1786
  }
1219
1787
  }
1220
- }
1221
- ]
1222
- };
1788
+ ]
1789
+ },
1790
+ optionOverride
1791
+ );
1223
1792
  }, [
1224
1793
  data,
1225
1794
  isEmpty,
@@ -1231,7 +1800,13 @@ var DonutChart = React10.forwardRef(
1231
1800
  centerLabel,
1232
1801
  centerValue,
1233
1802
  title,
1234
- chartColors
1803
+ chartColors,
1804
+ labelFormatter,
1805
+ tooltipFormatter,
1806
+ tooltipValueFormatter,
1807
+ legendConfig,
1808
+ customLegend,
1809
+ optionOverride
1235
1810
  ]);
1236
1811
  const latest = useLatestRef({ onClick });
1237
1812
  const { containerRef } = useChart({
@@ -1239,6 +1814,7 @@ var DonutChart = React10.forwardRef(
1239
1814
  theme: currentTheme === THEME_DARK4 ? "dark" : "light",
1240
1815
  animation,
1241
1816
  onReady: (chart) => {
1817
+ legend.attach(chart);
1242
1818
  chart.on("click", (params) => {
1243
1819
  const { onClick: onClick2 } = latest.current;
1244
1820
  if (!onClick2) return;
@@ -1254,7 +1830,7 @@ var DonutChart = React10.forwardRef(
1254
1830
  }
1255
1831
  });
1256
1832
  const accessibilityDescription = description || (!isEmpty ? generatePieChartDescription2(title, data, true) : void 0);
1257
- return /* @__PURE__ */ jsx10(
1833
+ return /* @__PURE__ */ jsx11(
1258
1834
  ChartContainer,
1259
1835
  {
1260
1836
  ref,
@@ -1266,8 +1842,10 @@ var DonutChart = React10.forwardRef(
1266
1842
  empty: isEmpty,
1267
1843
  emptyMessage,
1268
1844
  className: cn(className),
1845
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
1846
+ legendPosition: legendConfig?.position,
1269
1847
  ...props,
1270
- children: /* @__PURE__ */ jsx10("div", { ref: containerRef, className: "h-full w-full" })
1848
+ children: /* @__PURE__ */ jsx11("div", { ref: containerRef, className: "h-full w-full" })
1271
1849
  }
1272
1850
  );
1273
1851
  }
@@ -1275,7 +1853,7 @@ var DonutChart = React10.forwardRef(
1275
1853
  DonutChart.displayName = "DonutChart";
1276
1854
 
1277
1855
  // src/components/extended/RadarChart.tsx
1278
- import * as React11 from "react";
1856
+ import * as React12 from "react";
1279
1857
  import * as echarts9 from "echarts/core";
1280
1858
  import { RadarChart as EChartsRadarChart } from "echarts/charts";
1281
1859
  import {
@@ -1285,7 +1863,15 @@ import {
1285
1863
  RadarComponent
1286
1864
  } from "echarts/components";
1287
1865
  import { CanvasRenderer as CanvasRenderer7 } from "echarts/renderers";
1288
- import { jsx as jsx11 } from "react/jsx-runtime";
1866
+ import {
1867
+ buildDataLabel as buildDataLabel7,
1868
+ buildLegend as buildLegend7,
1869
+ buildTooltip as buildTooltip7,
1870
+ getChartColors as getChartColors7,
1871
+ legendCenter as legendCenter3,
1872
+ mergeChartOption as mergeChartOption7
1873
+ } from "@classic-homes/charts-core";
1874
+ import { jsx as jsx12 } from "react/jsx-runtime";
1289
1875
  echarts9.use([
1290
1876
  EChartsRadarChart,
1291
1877
  TooltipComponent7,
@@ -1294,7 +1880,7 @@ echarts9.use([
1294
1880
  RadarComponent,
1295
1881
  CanvasRenderer7
1296
1882
  ]);
1297
- var RadarChart = React11.forwardRef(
1883
+ var RadarChart = React12.forwardRef(
1298
1884
  ({
1299
1885
  title,
1300
1886
  description,
@@ -1309,50 +1895,117 @@ var RadarChart = React11.forwardRef(
1309
1895
  showLegend = true,
1310
1896
  showTooltip = true,
1311
1897
  shape = "polygon",
1898
+ showLabels = false,
1899
+ labelFormatter,
1900
+ tooltipFormatter,
1901
+ tooltipValueFormatter,
1902
+ legend: legendConfig,
1903
+ renderLegend,
1904
+ onLegendChange,
1905
+ option: optionOverride,
1906
+ onClick,
1312
1907
  class: className,
1313
1908
  ...props
1314
1909
  }, ref) => {
1315
1910
  const isEmpty = !data?.series?.length || !indicators?.length || data.series.every((s) => !s.data?.length);
1316
- const option = React11.useMemo(() => {
1911
+ const currentTheme = useChartTheme(theme);
1912
+ const legend = useChartLegend({
1913
+ names: data?.series?.map((s) => s.name) ?? [],
1914
+ colors: data?.series?.map((s) => s.color) ?? [],
1915
+ palette: getChartColors7(currentTheme).series,
1916
+ config: legendConfig,
1917
+ title,
1918
+ onLegendChange
1919
+ });
1920
+ const customLegend = renderLegend !== void 0;
1921
+ const option = React12.useMemo(() => {
1317
1922
  if (isEmpty) return {};
1318
- return {
1319
- tooltip: showTooltip ? {
1320
- trigger: "item"
1321
- } : void 0,
1322
- legend: showLegend ? {
1323
- data: data.series.map((s) => s.name),
1324
- bottom: 0
1325
- } : void 0,
1326
- radar: {
1327
- indicator: indicators.map((ind) => ({
1328
- name: ind.name,
1329
- max: ind.max,
1330
- min: ind.min
1331
- })),
1332
- shape
1923
+ return mergeChartOption7(
1924
+ {
1925
+ tooltip: buildTooltip7(
1926
+ {
1927
+ trigger: "item"
1928
+ },
1929
+ {
1930
+ show: showTooltip,
1931
+ formatter: tooltipFormatter,
1932
+ valueFormatter: tooltipValueFormatter
1933
+ }
1934
+ ),
1935
+ legend: buildLegend7(
1936
+ data.series.map((s) => s.name),
1937
+ legendConfig,
1938
+ { show: showLegend, custom: customLegend }
1939
+ ),
1940
+ radar: {
1941
+ indicator: indicators.map((ind) => ({
1942
+ name: ind.name,
1943
+ max: ind.max,
1944
+ min: ind.min
1945
+ })),
1946
+ shape,
1947
+ center: legendCenter3(legendConfig, showLegend && !customLegend, ["50%", "50%"])
1948
+ },
1949
+ series: [
1950
+ {
1951
+ type: "radar",
1952
+ label: buildDataLabel7({ show: true }, void 0, {
1953
+ show: showLabels,
1954
+ formatter: labelFormatter
1955
+ }),
1956
+ data: data.series.map((series) => ({
1957
+ name: series.name,
1958
+ value: series.data,
1959
+ itemStyle: series.color ? { color: series.color } : void 0,
1960
+ areaStyle: {
1961
+ opacity: 0.2
1962
+ }
1963
+ }))
1964
+ }
1965
+ ]
1333
1966
  },
1334
- series: [
1335
- {
1336
- type: "radar",
1337
- data: data.series.map((series) => ({
1338
- name: series.name,
1339
- value: series.data,
1340
- itemStyle: series.color ? { color: series.color } : void 0,
1341
- areaStyle: {
1342
- opacity: 0.2
1343
- }
1344
- }))
1345
- }
1346
- ]
1347
- };
1348
- }, [data, indicators, isEmpty, showTooltip, showLegend, shape]);
1967
+ optionOverride
1968
+ );
1969
+ }, [
1970
+ data,
1971
+ indicators,
1972
+ isEmpty,
1973
+ showTooltip,
1974
+ showLegend,
1975
+ shape,
1976
+ showLabels,
1977
+ labelFormatter,
1978
+ tooltipFormatter,
1979
+ tooltipValueFormatter,
1980
+ legendConfig,
1981
+ customLegend,
1982
+ optionOverride
1983
+ ]);
1984
+ const latest = useLatestRef({ onClick });
1349
1985
  const { containerRef } = useChart({
1350
1986
  option,
1351
1987
  theme,
1352
- animation
1988
+ animation,
1989
+ onReady: (chart) => {
1990
+ legend.attach(chart);
1991
+ chart.on("click", (params) => {
1992
+ const { onClick: onClick2 } = latest.current;
1993
+ if (!onClick2) return;
1994
+ onClick2({
1995
+ type: params.type || "click",
1996
+ componentType: params.componentType || "series",
1997
+ seriesIndex: params.seriesIndex,
1998
+ seriesName: params.seriesName || "",
1999
+ dataIndex: params.dataIndex ?? 0,
2000
+ name: params.name || "",
2001
+ value: params.value,
2002
+ color: params.color
2003
+ });
2004
+ });
2005
+ }
1353
2006
  });
1354
2007
  const accessibilityDescription = description || (!isEmpty ? `${title}. Radar chart with ${indicators.length} dimensions comparing ${data.series.length} series.` : void 0);
1355
- return /* @__PURE__ */ jsx11(
2008
+ return /* @__PURE__ */ jsx12(
1356
2009
  ChartContainer,
1357
2010
  {
1358
2011
  ref,
@@ -1364,8 +2017,10 @@ var RadarChart = React11.forwardRef(
1364
2017
  empty: isEmpty,
1365
2018
  emptyMessage,
1366
2019
  className: cn(className),
2020
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
2021
+ legendPosition: legendConfig?.position,
1367
2022
  ...props,
1368
- children: /* @__PURE__ */ jsx11("div", { ref: containerRef, className: "h-full w-full" })
2023
+ children: /* @__PURE__ */ jsx12("div", { ref: containerRef, className: "h-full w-full" })
1369
2024
  }
1370
2025
  );
1371
2026
  }
@@ -1373,12 +2028,23 @@ var RadarChart = React11.forwardRef(
1373
2028
  RadarChart.displayName = "RadarChart";
1374
2029
 
1375
2030
  // src/components/extended/FunnelChart.tsx
1376
- import * as React12 from "react";
2031
+ import * as React13 from "react";
1377
2032
  import * as echarts10 from "echarts/core";
1378
2033
  import { FunnelChart as EChartsFunnelChart } from "echarts/charts";
1379
2034
  import { TooltipComponent as TooltipComponent8, LegendComponent as LegendComponent8, TitleComponent as TitleComponent8 } from "echarts/components";
1380
2035
  import { CanvasRenderer as CanvasRenderer8 } from "echarts/renderers";
1381
- import { jsx as jsx12 } from "react/jsx-runtime";
2036
+ import {
2037
+ buildDataLabel as buildDataLabel8,
2038
+ buildLegend as buildLegend8,
2039
+ buildTooltip as buildTooltip8,
2040
+ escapeHtml as escapeHtml4,
2041
+ formatTooltipValue as formatTooltipValue4,
2042
+ getChartColors as getChartColors8,
2043
+ itemLabelLookup as itemLabelLookup3,
2044
+ mergeChartOption as mergeChartOption8,
2045
+ pointLabel as pointLabel3
2046
+ } from "@classic-homes/charts-core";
2047
+ import { jsx as jsx13 } from "react/jsx-runtime";
1382
2048
  echarts10.use([
1383
2049
  EChartsFunnelChart,
1384
2050
  TooltipComponent8,
@@ -1386,7 +2052,7 @@ echarts10.use([
1386
2052
  TitleComponent8,
1387
2053
  CanvasRenderer8
1388
2054
  ]);
1389
- var FunnelChart = React12.forwardRef(
2055
+ var FunnelChart = React13.forwardRef(
1390
2056
  ({
1391
2057
  title,
1392
2058
  description,
@@ -1402,68 +2068,119 @@ var FunnelChart = React12.forwardRef(
1402
2068
  sort = "descending",
1403
2069
  orientation = "vertical",
1404
2070
  showLabels = true,
2071
+ labelFormatter,
2072
+ tooltipFormatter,
2073
+ tooltipValueFormatter,
2074
+ legend: legendConfig,
2075
+ renderLegend,
2076
+ onLegendChange,
2077
+ option: optionOverride,
1405
2078
  onClick,
1406
2079
  class: className,
1407
2080
  ...props
1408
2081
  }, ref) => {
1409
2082
  const isEmpty = !data?.length;
1410
- const option = React12.useMemo(() => {
2083
+ const currentTheme = useChartTheme(theme);
2084
+ const legend = useChartLegend({
2085
+ names: data?.map((d) => d.name) ?? [],
2086
+ colors: data?.map((d) => d.color) ?? [],
2087
+ palette: getChartColors8(currentTheme).series,
2088
+ config: legendConfig,
2089
+ title,
2090
+ onLegendChange
2091
+ });
2092
+ const customLegend = renderLegend !== void 0;
2093
+ const option = React13.useMemo(() => {
1411
2094
  if (isEmpty) return {};
1412
- return {
1413
- tooltip: showTooltip ? {
1414
- trigger: "item",
1415
- formatter: "{a} <br/>{b}: {c}"
1416
- } : void 0,
1417
- legend: showLegend ? {
1418
- data: data.map((d) => d.name),
1419
- bottom: 0
1420
- } : void 0,
1421
- series: [
1422
- {
1423
- name: title,
1424
- type: "funnel",
1425
- left: "10%",
1426
- top: 60,
1427
- bottom: 60,
1428
- width: "80%",
1429
- min: 0,
1430
- // reduce, not Math.max(...): spreading large arrays overflows the stack
1431
- max: data.reduce((m, d) => d.value > m ? d.value : m, -Infinity),
1432
- minSize: "0%",
1433
- maxSize: "100%",
1434
- sort,
1435
- orient: orientation,
1436
- gap: 2,
1437
- label: showLabels ? {
1438
- show: true,
1439
- position: "inside",
1440
- formatter: "{b}: {c}"
1441
- } : {
1442
- show: false
1443
- },
1444
- labelLine: {
1445
- show: false
1446
- },
1447
- emphasis: {
1448
- label: {
1449
- fontSize: 16
1450
- }
1451
- },
1452
- data: data.map((item) => ({
1453
- name: item.name,
1454
- value: item.value,
1455
- itemStyle: item.color ? { color: item.color } : void 0
1456
- }))
1457
- }
1458
- ]
2095
+ const getLabel = itemLabelLookup3(data);
2096
+ const valueFormatter = tooltipFormatter === void 0 ? tooltipValueFormatter : void 0;
2097
+ const tooltipBase = {
2098
+ trigger: "item",
2099
+ formatter: valueFormatter ? (params) => {
2100
+ const p = params;
2101
+ return `${escapeHtml4(p.seriesName)} <br/>${escapeHtml4(p.name)}: ${escapeHtml4(formatTooltipValue4(p.value, valueFormatter))}`;
2102
+ } : "{a} <br/>{b}: {c}"
1459
2103
  };
1460
- }, [data, isEmpty, showTooltip, showLegend, sort, orientation, showLabels, title]);
2104
+ return mergeChartOption8(
2105
+ {
2106
+ tooltip: buildTooltip8(tooltipBase, {
2107
+ show: showTooltip,
2108
+ formatter: tooltipFormatter,
2109
+ valueFormatter: tooltipValueFormatter,
2110
+ getLabel
2111
+ }),
2112
+ legend: buildLegend8(
2113
+ data.map((d) => d.name),
2114
+ legendConfig,
2115
+ { show: showLegend, custom: customLegend }
2116
+ ),
2117
+ series: [
2118
+ {
2119
+ name: title,
2120
+ type: "funnel",
2121
+ left: "10%",
2122
+ top: 60,
2123
+ bottom: 60,
2124
+ width: "80%",
2125
+ min: 0,
2126
+ // reduce, not Math.max(...): spreading large arrays overflows the stack
2127
+ max: data.reduce((m, d) => d.value > m ? d.value : m, -Infinity),
2128
+ minSize: "0%",
2129
+ maxSize: "100%",
2130
+ sort,
2131
+ orient: orientation,
2132
+ gap: 2,
2133
+ label: buildDataLabel8(
2134
+ {
2135
+ show: true,
2136
+ position: "inside",
2137
+ formatter: "{b}: {c}"
2138
+ },
2139
+ { show: false },
2140
+ { show: showLabels, formatter: labelFormatter, getLabel }
2141
+ ),
2142
+ labelLine: {
2143
+ show: false
2144
+ },
2145
+ emphasis: {
2146
+ label: {
2147
+ fontSize: 16
2148
+ }
2149
+ },
2150
+ data: data.map((item) => ({
2151
+ name: item.name,
2152
+ value: item.value,
2153
+ itemStyle: item.color ? { color: item.color } : void 0,
2154
+ label: pointLabel3(item.label, labelFormatter, { position: "inside" })
2155
+ }))
2156
+ }
2157
+ ]
2158
+ },
2159
+ optionOverride
2160
+ );
2161
+ }, [
2162
+ data,
2163
+ isEmpty,
2164
+ showTooltip,
2165
+ showLegend,
2166
+ sort,
2167
+ orientation,
2168
+ showLabels,
2169
+ title,
2170
+ labelFormatter,
2171
+ tooltipFormatter,
2172
+ tooltipValueFormatter,
2173
+ legendConfig,
2174
+ customLegend,
2175
+ optionOverride
2176
+ ]);
1461
2177
  const latest = useLatestRef({ onClick });
1462
2178
  const { containerRef } = useChart({
1463
2179
  option,
1464
2180
  theme,
1465
2181
  animation,
1466
2182
  onReady: (chart) => {
2183
+ legend.attach(chart);
1467
2184
  chart.on("click", (params) => {
1468
2185
  const { onClick: onClick2 } = latest.current;
1469
2186
  if (!onClick2) return;
@@ -1478,7 +2195,7 @@ var FunnelChart = React12.forwardRef(
1478
2195
  }
1479
2196
  });
1480
2197
  const accessibilityDescription = description || (!isEmpty ? `${title}. Funnel chart with ${data.length} stages.` : void 0);
1481
- return /* @__PURE__ */ jsx12(
2198
+ return /* @__PURE__ */ jsx13(
1482
2199
  ChartContainer,
1483
2200
  {
1484
2201
  ref,
@@ -1490,8 +2207,10 @@ var FunnelChart = React12.forwardRef(
1490
2207
  empty: isEmpty,
1491
2208
  emptyMessage,
1492
2209
  className: cn(className),
2210
+ legend: !isEmpty && renderLegend ? renderLegend(legend.renderProps) : void 0,
2211
+ legendPosition: legendConfig?.position,
1493
2212
  ...props,
1494
- children: /* @__PURE__ */ jsx12("div", { ref: containerRef, className: "h-full w-full" })
2213
+ children: /* @__PURE__ */ jsx13("div", { ref: containerRef, className: "h-full w-full" })
1495
2214
  }
1496
2215
  );
1497
2216
  }
@@ -1499,15 +2218,20 @@ var FunnelChart = React12.forwardRef(
1499
2218
  FunnelChart.displayName = "FunnelChart";
1500
2219
 
1501
2220
  // src/components/extended/GaugeChart.tsx
1502
- import * as React13 from "react";
2221
+ import * as React14 from "react";
1503
2222
  import * as echarts11 from "echarts/core";
1504
2223
  import { GaugeChart as EChartsGaugeChart } from "echarts/charts";
1505
- import { TitleComponent as TitleComponent9 } from "echarts/components";
2224
+ import { TitleComponent as TitleComponent9, TooltipComponent as TooltipComponent9 } from "echarts/components";
1506
2225
  import { CanvasRenderer as CanvasRenderer9 } from "echarts/renderers";
1507
- import { getChartColors as getChartColors4, THEME_DARK as THEME_DARK5 } from "@classic-homes/charts-core";
1508
- import { jsx as jsx13 } from "react/jsx-runtime";
1509
- echarts11.use([EChartsGaugeChart, TitleComponent9, CanvasRenderer9]);
1510
- var GaugeChart = React13.forwardRef(
2226
+ import {
2227
+ buildTooltip as buildTooltip9,
2228
+ getChartColors as getChartColors9,
2229
+ mergeChartOption as mergeChartOption9,
2230
+ THEME_DARK as THEME_DARK5
2231
+ } from "@classic-homes/charts-core";
2232
+ import { jsx as jsx14 } from "react/jsx-runtime";
2233
+ echarts11.use([EChartsGaugeChart, TitleComponent9, TooltipComponent9, CanvasRenderer9]);
2234
+ var GaugeChart = React14.forwardRef(
1511
2235
  ({
1512
2236
  title,
1513
2237
  description,
@@ -1523,84 +2247,101 @@ var GaugeChart = React13.forwardRef(
1523
2247
  splitNumber = 10,
1524
2248
  showProgress = true,
1525
2249
  formatValue,
2250
+ showTooltip = true,
2251
+ tooltipFormatter,
2252
+ tooltipValueFormatter,
2253
+ option: optionOverride,
2254
+ onClick,
1526
2255
  class: className,
1527
2256
  ...props
1528
2257
  }, ref) => {
1529
2258
  const currentTheme = useChartTheme(theme);
1530
- const chartColors = getChartColors4(currentTheme);
2259
+ const chartColors = getChartColors9(currentTheme);
1531
2260
  const reducedMotion = useReducedMotion();
1532
- const option = React13.useMemo(() => {
1533
- return {
1534
- series: [
1535
- {
1536
- type: "gauge",
1537
- center: ["50%", "60%"],
1538
- radius: "80%",
1539
- min,
1540
- max,
1541
- splitNumber,
1542
- progress: showProgress ? {
1543
- show: true,
1544
- width: 18
1545
- } : void 0,
1546
- axisLine: {
1547
- lineStyle: {
2261
+ const option = React14.useMemo(() => {
2262
+ return mergeChartOption9(
2263
+ {
2264
+ // No tooltip by default; one appears once its content is customised
2265
+ tooltip: tooltipFormatter || tooltipValueFormatter ? buildTooltip9(
2266
+ { trigger: "item" },
2267
+ {
2268
+ show: showTooltip,
2269
+ formatter: tooltipFormatter,
2270
+ valueFormatter: tooltipValueFormatter
2271
+ }
2272
+ ) : void 0,
2273
+ series: [
2274
+ {
2275
+ type: "gauge",
2276
+ center: ["50%", "60%"],
2277
+ radius: "80%",
2278
+ min,
2279
+ max,
2280
+ splitNumber,
2281
+ progress: showProgress ? {
2282
+ show: true,
1548
2283
  width: 18
1549
- }
1550
- },
1551
- axisTick: {
1552
- distance: -30,
1553
- splitNumber: 5,
1554
- lineStyle: {
1555
- width: 2,
1556
- color: chartColors.mutedForeground
1557
- }
1558
- },
1559
- splitLine: {
1560
- distance: -32,
1561
- length: 10,
1562
- lineStyle: {
1563
- width: 3,
1564
- color: chartColors.mutedForeground
1565
- }
1566
- },
1567
- axisLabel: {
1568
- distance: -20,
1569
- color: chartColors.mutedForeground,
1570
- fontSize: 12
1571
- },
1572
- anchor: {
1573
- show: true,
1574
- size: 20,
1575
- itemStyle: {
1576
- borderWidth: 2
1577
- }
1578
- },
1579
- pointer: {
1580
- width: 6,
1581
- length: "60%"
1582
- },
1583
- title: {
1584
- show: true,
1585
- offsetCenter: [0, "70%"],
1586
- fontSize: 14
1587
- },
1588
- detail: {
1589
- valueAnimation: animation && !reducedMotion,
1590
- fontSize: 28,
1591
- fontWeight: "bold",
1592
- offsetCenter: [0, "40%"],
1593
- formatter: formatValue || "{value}"
1594
- },
1595
- data: [
1596
- {
1597
- value,
1598
- name: title
1599
- }
1600
- ]
1601
- }
1602
- ]
1603
- };
2284
+ } : void 0,
2285
+ axisLine: {
2286
+ lineStyle: {
2287
+ width: 18
2288
+ }
2289
+ },
2290
+ axisTick: {
2291
+ distance: -30,
2292
+ splitNumber: 5,
2293
+ lineStyle: {
2294
+ width: 2,
2295
+ color: chartColors.mutedForeground
2296
+ }
2297
+ },
2298
+ splitLine: {
2299
+ distance: -32,
2300
+ length: 10,
2301
+ lineStyle: {
2302
+ width: 3,
2303
+ color: chartColors.mutedForeground
2304
+ }
2305
+ },
2306
+ axisLabel: {
2307
+ distance: -20,
2308
+ color: chartColors.mutedForeground,
2309
+ fontSize: 12
2310
+ },
2311
+ anchor: {
2312
+ show: true,
2313
+ size: 20,
2314
+ itemStyle: {
2315
+ borderWidth: 2
2316
+ }
2317
+ },
2318
+ pointer: {
2319
+ width: 6,
2320
+ length: "60%"
2321
+ },
2322
+ title: {
2323
+ show: true,
2324
+ offsetCenter: [0, "70%"],
2325
+ fontSize: 14
2326
+ },
2327
+ detail: {
2328
+ valueAnimation: animation && !reducedMotion,
2329
+ fontSize: 28,
2330
+ fontWeight: "bold",
2331
+ offsetCenter: [0, "40%"],
2332
+ formatter: formatValue || "{value}"
2333
+ },
2334
+ data: [
2335
+ {
2336
+ value,
2337
+ name: title
2338
+ }
2339
+ ]
2340
+ }
2341
+ ]
2342
+ },
2343
+ optionOverride
2344
+ );
1604
2345
  }, [
1605
2346
  value,
1606
2347
  min,
@@ -1611,15 +2352,36 @@ var GaugeChart = React13.forwardRef(
1611
2352
  title,
1612
2353
  animation,
1613
2354
  reducedMotion,
1614
- chartColors
2355
+ chartColors,
2356
+ showTooltip,
2357
+ tooltipFormatter,
2358
+ tooltipValueFormatter,
2359
+ optionOverride
1615
2360
  ]);
2361
+ const latest = useLatestRef({ onClick });
1616
2362
  const { containerRef } = useChart({
1617
2363
  option,
1618
2364
  theme: currentTheme === THEME_DARK5 ? "dark" : "light",
1619
- animation
2365
+ animation,
2366
+ onReady: (chart) => {
2367
+ chart.on("click", (params) => {
2368
+ const { onClick: onClick2 } = latest.current;
2369
+ if (!onClick2) return;
2370
+ onClick2({
2371
+ type: params.type || "click",
2372
+ componentType: params.componentType || "series",
2373
+ seriesIndex: params.seriesIndex,
2374
+ seriesName: params.seriesName || "",
2375
+ dataIndex: params.dataIndex ?? 0,
2376
+ name: params.name || "",
2377
+ value: params.value,
2378
+ color: params.color
2379
+ });
2380
+ });
2381
+ }
1620
2382
  });
1621
2383
  const accessibilityDescription = description || `${title}. Gauge showing value ${value} on scale from ${min} to ${max}.`;
1622
- return /* @__PURE__ */ jsx13(
2384
+ return /* @__PURE__ */ jsx14(
1623
2385
  ChartContainer,
1624
2386
  {
1625
2387
  ref,
@@ -1631,7 +2393,7 @@ var GaugeChart = React13.forwardRef(
1631
2393
  emptyMessage,
1632
2394
  className: cn(className),
1633
2395
  ...props,
1634
- children: /* @__PURE__ */ jsx13("div", { ref: containerRef, className: "h-full w-full" })
2396
+ children: /* @__PURE__ */ jsx14("div", { ref: containerRef, className: "h-full w-full" })
1635
2397
  }
1636
2398
  );
1637
2399
  }
@@ -1639,27 +2401,34 @@ var GaugeChart = React13.forwardRef(
1639
2401
  GaugeChart.displayName = "GaugeChart";
1640
2402
 
1641
2403
  // src/components/extended/HeatmapChart.tsx
1642
- import * as React14 from "react";
2404
+ import * as React15 from "react";
1643
2405
  import * as echarts12 from "echarts/core";
1644
2406
  import { HeatmapChart as EChartsHeatmapChart } from "echarts/charts";
1645
2407
  import {
1646
2408
  GridComponent as GridComponent5,
1647
- TooltipComponent as TooltipComponent9,
2409
+ TooltipComponent as TooltipComponent10,
1648
2410
  TitleComponent as TitleComponent10,
1649
2411
  VisualMapComponent
1650
2412
  } from "echarts/components";
1651
2413
  import { CanvasRenderer as CanvasRenderer10 } from "echarts/renderers";
1652
- import { escapeHtml as escapeHtml2 } from "@classic-homes/charts-core";
1653
- import { jsx as jsx14 } from "react/jsx-runtime";
2414
+ import {
2415
+ buildAxis as buildAxis5,
2416
+ buildDataLabel as buildDataLabel9,
2417
+ buildTooltip as buildTooltip10,
2418
+ escapeHtml as escapeHtml5,
2419
+ formatTooltipValue as formatTooltipValue5,
2420
+ mergeChartOption as mergeChartOption10
2421
+ } from "@classic-homes/charts-core";
2422
+ import { jsx as jsx15 } from "react/jsx-runtime";
1654
2423
  echarts12.use([
1655
2424
  EChartsHeatmapChart,
1656
2425
  GridComponent5,
1657
- TooltipComponent9,
2426
+ TooltipComponent10,
1658
2427
  TitleComponent10,
1659
2428
  VisualMapComponent,
1660
2429
  CanvasRenderer10
1661
2430
  ]);
1662
- var HeatmapChart = React14.forwardRef(
2431
+ var HeatmapChart = React15.forwardRef(
1663
2432
  ({
1664
2433
  title,
1665
2434
  description,
@@ -1674,6 +2443,12 @@ var HeatmapChart = React14.forwardRef(
1674
2443
  animation = true,
1675
2444
  showTooltip = true,
1676
2445
  showValues = false,
2446
+ labelFormatter,
2447
+ xAxisConfig,
2448
+ yAxisConfig,
2449
+ tooltipFormatter,
2450
+ tooltipValueFormatter,
2451
+ option: optionOverride,
1677
2452
  onClick,
1678
2453
  class: className,
1679
2454
  ...props
@@ -1687,64 +2462,97 @@ var HeatmapChart = React14.forwardRef(
1687
2462
  }
1688
2463
  const minValue = data?.min ?? dataMin;
1689
2464
  const maxValue = data?.max ?? dataMax;
1690
- const option = React14.useMemo(() => {
2465
+ const option = React15.useMemo(() => {
1691
2466
  if (isEmpty) return {};
1692
- return {
1693
- tooltip: showTooltip ? {
1694
- position: "top",
1695
- formatter: (params) => {
1696
- const p = params;
1697
- if (!p.value) return "";
1698
- const [xIdx, yIdx, value] = p.value;
1699
- return `${escapeHtml2(xAxis[xIdx])} / ${escapeHtml2(yAxis[yIdx])}: ${escapeHtml2(value)}`;
1700
- }
1701
- } : void 0,
1702
- grid: {
1703
- left: "3%",
1704
- right: "10%",
1705
- bottom: "3%",
1706
- containLabel: true
1707
- },
1708
- xAxis: {
1709
- type: "category",
1710
- data: xAxis,
1711
- splitArea: {
1712
- show: true
1713
- }
1714
- },
1715
- yAxis: {
1716
- type: "category",
1717
- data: yAxis,
1718
- splitArea: {
1719
- show: true
1720
- }
1721
- },
1722
- visualMap: {
1723
- min: minValue,
1724
- max: maxValue,
1725
- calculable: true,
1726
- orient: "vertical",
1727
- right: "0%",
1728
- top: "center"
1729
- },
1730
- series: [
1731
- {
1732
- name: title,
1733
- type: "heatmap",
1734
- data: data.data,
1735
- label: showValues ? {
1736
- show: true
1737
- } : void 0,
1738
- emphasis: {
1739
- itemStyle: {
1740
- shadowBlur: 10,
1741
- shadowColor: "rgba(0, 0, 0, 0.5)"
2467
+ return mergeChartOption10(
2468
+ {
2469
+ tooltip: buildTooltip10(
2470
+ {
2471
+ position: "top",
2472
+ formatter: (params) => {
2473
+ const p = params;
2474
+ if (!p.value) return "";
2475
+ const [xIdx, yIdx, value] = p.value;
2476
+ return `${escapeHtml5(xAxis[xIdx])} / ${escapeHtml5(yAxis[yIdx])}: ${escapeHtml5(formatTooltipValue5(value, tooltipValueFormatter))}`;
1742
2477
  }
2478
+ },
2479
+ {
2480
+ show: showTooltip,
2481
+ formatter: tooltipFormatter,
2482
+ valueFormatter: tooltipValueFormatter
1743
2483
  }
1744
- }
1745
- ]
1746
- };
1747
- }, [data, xAxis, yAxis, isEmpty, showTooltip, showValues, minValue, maxValue, title]);
2484
+ ),
2485
+ grid: {
2486
+ left: "3%",
2487
+ right: "10%",
2488
+ bottom: "3%",
2489
+ containLabel: true
2490
+ },
2491
+ xAxis: buildAxis5(
2492
+ {
2493
+ type: "category",
2494
+ data: xAxis,
2495
+ splitArea: {
2496
+ show: true
2497
+ }
2498
+ },
2499
+ xAxisConfig
2500
+ ),
2501
+ yAxis: buildAxis5(
2502
+ {
2503
+ type: "category",
2504
+ data: yAxis,
2505
+ splitArea: {
2506
+ show: true
2507
+ }
2508
+ },
2509
+ yAxisConfig
2510
+ ),
2511
+ visualMap: {
2512
+ min: minValue,
2513
+ max: maxValue,
2514
+ calculable: true,
2515
+ orient: "vertical",
2516
+ right: "0%",
2517
+ top: "center"
2518
+ },
2519
+ series: [
2520
+ {
2521
+ name: title,
2522
+ type: "heatmap",
2523
+ data: data.data,
2524
+ label: buildDataLabel9({ show: true }, void 0, {
2525
+ show: showValues,
2526
+ formatter: labelFormatter
2527
+ }),
2528
+ emphasis: {
2529
+ itemStyle: {
2530
+ shadowBlur: 10,
2531
+ shadowColor: "rgba(0, 0, 0, 0.5)"
2532
+ }
2533
+ }
2534
+ }
2535
+ ]
2536
+ },
2537
+ optionOverride
2538
+ );
2539
+ }, [
2540
+ data,
2541
+ xAxis,
2542
+ yAxis,
2543
+ isEmpty,
2544
+ showTooltip,
2545
+ showValues,
2546
+ minValue,
2547
+ maxValue,
2548
+ title,
2549
+ labelFormatter,
2550
+ xAxisConfig,
2551
+ yAxisConfig,
2552
+ tooltipFormatter,
2553
+ tooltipValueFormatter,
2554
+ optionOverride
2555
+ ]);
1748
2556
  const latest = useLatestRef({ onClick, xAxis, yAxis });
1749
2557
  const { containerRef } = useChart({
1750
2558
  option,
@@ -1768,7 +2576,7 @@ var HeatmapChart = React14.forwardRef(
1768
2576
  }
1769
2577
  });
1770
2578
  const accessibilityDescription = description || (!isEmpty ? `${title}. Heatmap with ${xAxis.length} columns and ${yAxis.length} rows.` : void 0);
1771
- return /* @__PURE__ */ jsx14(
2579
+ return /* @__PURE__ */ jsx15(
1772
2580
  ChartContainer,
1773
2581
  {
1774
2582
  ref,
@@ -1781,7 +2589,7 @@ var HeatmapChart = React14.forwardRef(
1781
2589
  emptyMessage,
1782
2590
  className: cn(className),
1783
2591
  ...props,
1784
- children: /* @__PURE__ */ jsx14("div", { ref: containerRef, className: "h-full w-full" })
2592
+ children: /* @__PURE__ */ jsx15("div", { ref: containerRef, className: "h-full w-full" })
1785
2593
  }
1786
2594
  );
1787
2595
  }
@@ -1789,15 +2597,25 @@ var HeatmapChart = React14.forwardRef(
1789
2597
  HeatmapChart.displayName = "HeatmapChart";
1790
2598
 
1791
2599
  // src/components/extended/TreemapChart.tsx
1792
- import * as React15 from "react";
2600
+ import * as React16 from "react";
1793
2601
  import * as echarts13 from "echarts/core";
1794
2602
  import { TreemapChart as EChartsTreemapChart } from "echarts/charts";
1795
- import { TooltipComponent as TooltipComponent10, TitleComponent as TitleComponent11 } from "echarts/components";
2603
+ import { TooltipComponent as TooltipComponent11, TitleComponent as TitleComponent11 } from "echarts/components";
1796
2604
  import { CanvasRenderer as CanvasRenderer11 } from "echarts/renderers";
1797
- import { escapeHtml as escapeHtml3, getChartColors as getChartColors5, THEME_DARK as THEME_DARK6 } from "@classic-homes/charts-core";
1798
- import { jsx as jsx15 } from "react/jsx-runtime";
1799
- echarts13.use([EChartsTreemapChart, TooltipComponent10, TitleComponent11, CanvasRenderer11]);
1800
- var TreemapChart = React15.forwardRef(
2605
+ import {
2606
+ buildDataLabel as buildDataLabel10,
2607
+ buildTooltip as buildTooltip11,
2608
+ escapeHtml as escapeHtml6,
2609
+ formatTooltipValue as formatTooltipValue6,
2610
+ getChartColors as getChartColors10,
2611
+ mergeChartOption as mergeChartOption11,
2612
+ nameLabelLookup,
2613
+ pointLabel as pointLabel4,
2614
+ THEME_DARK as THEME_DARK6
2615
+ } from "@classic-homes/charts-core";
2616
+ import { jsx as jsx16 } from "react/jsx-runtime";
2617
+ echarts13.use([EChartsTreemapChart, TooltipComponent11, TitleComponent11, CanvasRenderer11]);
2618
+ var TreemapChart = React16.forwardRef(
1801
2619
  ({
1802
2620
  title,
1803
2621
  description,
@@ -1810,69 +2628,99 @@ var TreemapChart = React15.forwardRef(
1810
2628
  theme = "auto",
1811
2629
  animation = true,
1812
2630
  showTooltip = true,
2631
+ labelFormatter,
2632
+ tooltipFormatter,
2633
+ tooltipValueFormatter,
2634
+ option: optionOverride,
1813
2635
  onClick,
1814
2636
  class: className,
1815
2637
  ...props
1816
2638
  }, ref) => {
1817
2639
  const isEmpty = !data?.length;
1818
2640
  const currentTheme = useChartTheme(theme);
1819
- const chartColors = getChartColors5(currentTheme);
1820
- const option = React15.useMemo(() => {
2641
+ const chartColors = getChartColors10(currentTheme);
2642
+ const option = React16.useMemo(() => {
1821
2643
  if (isEmpty) return {};
1822
- return {
1823
- tooltip: showTooltip ? {
1824
- formatter: (params) => {
1825
- const p = params;
1826
- return `${escapeHtml3(p.name)}: ${escapeHtml3(p.value)}`;
1827
- }
1828
- } : void 0,
1829
- series: [
1830
- {
1831
- type: "treemap",
1832
- data,
1833
- roam: false,
1834
- levels: levels || [
1835
- {
1836
- itemStyle: {
1837
- borderColor: chartColors.divider,
1838
- borderWidth: 4,
1839
- gapWidth: 4
1840
- }
1841
- },
1842
- {
1843
- colorSaturation: [0.3, 0.6],
1844
- itemStyle: {
1845
- borderColorSaturation: 0.7,
1846
- gapWidth: 2,
1847
- borderWidth: 2
1848
- }
1849
- },
1850
- {
1851
- colorSaturation: [0.3, 0.5],
1852
- itemStyle: {
1853
- borderColorSaturation: 0.6,
1854
- gapWidth: 1
2644
+ const toNode = (node) => ({
2645
+ ...node,
2646
+ children: node.children?.map(toNode),
2647
+ label: pointLabel4(node.label, labelFormatter)
2648
+ });
2649
+ return mergeChartOption11(
2650
+ {
2651
+ tooltip: buildTooltip11(
2652
+ {
2653
+ formatter: (params) => {
2654
+ const p = params;
2655
+ return `${escapeHtml6(p.name)}: ${escapeHtml6(formatTooltipValue6(p.value, tooltipValueFormatter))}`;
2656
+ }
2657
+ },
2658
+ {
2659
+ show: showTooltip,
2660
+ formatter: tooltipFormatter,
2661
+ valueFormatter: tooltipValueFormatter
2662
+ }
2663
+ ),
2664
+ series: [
2665
+ {
2666
+ type: "treemap",
2667
+ data: data.map(toNode),
2668
+ roam: false,
2669
+ levels: levels || [
2670
+ {
2671
+ itemStyle: {
2672
+ borderColor: chartColors.divider,
2673
+ borderWidth: 4,
2674
+ gapWidth: 4
2675
+ }
2676
+ },
2677
+ {
2678
+ colorSaturation: [0.3, 0.6],
2679
+ itemStyle: {
2680
+ borderColorSaturation: 0.7,
2681
+ gapWidth: 2,
2682
+ borderWidth: 2
2683
+ }
2684
+ },
2685
+ {
2686
+ colorSaturation: [0.3, 0.5],
2687
+ itemStyle: {
2688
+ borderColorSaturation: 0.6,
2689
+ gapWidth: 1
2690
+ }
2691
+ },
2692
+ {
2693
+ colorSaturation: [0.3, 0.5]
1855
2694
  }
2695
+ ],
2696
+ label: buildDataLabel10({ show: true, formatter: "{b}" }, void 0, {
2697
+ show: true,
2698
+ formatter: labelFormatter,
2699
+ getLabel: nameLabelLookup(data)
2700
+ }),
2701
+ upperLabel: {
2702
+ show: true,
2703
+ height: 30
1856
2704
  },
1857
- {
1858
- colorSaturation: [0.3, 0.5]
2705
+ breadcrumb: {
2706
+ show: true
1859
2707
  }
1860
- ],
1861
- label: {
1862
- show: true,
1863
- formatter: "{b}"
1864
- },
1865
- upperLabel: {
1866
- show: true,
1867
- height: 30
1868
- },
1869
- breadcrumb: {
1870
- show: true
1871
2708
  }
1872
- }
1873
- ]
1874
- };
1875
- }, [data, levels, isEmpty, showTooltip, chartColors]);
2709
+ ]
2710
+ },
2711
+ optionOverride
2712
+ );
2713
+ }, [
2714
+ data,
2715
+ levels,
2716
+ isEmpty,
2717
+ showTooltip,
2718
+ chartColors,
2719
+ labelFormatter,
2720
+ tooltipFormatter,
2721
+ tooltipValueFormatter,
2722
+ optionOverride
2723
+ ]);
1876
2724
  const latest = useLatestRef({ onClick });
1877
2725
  const { containerRef } = useChart({
1878
2726
  option,
@@ -1895,7 +2743,7 @@ var TreemapChart = React15.forwardRef(
1895
2743
  }
1896
2744
  });
1897
2745
  const accessibilityDescription = description || (!isEmpty ? `${title}. Treemap chart showing hierarchical data.` : void 0);
1898
- return /* @__PURE__ */ jsx15(
2746
+ return /* @__PURE__ */ jsx16(
1899
2747
  ChartContainer,
1900
2748
  {
1901
2749
  ref,
@@ -1908,7 +2756,7 @@ var TreemapChart = React15.forwardRef(
1908
2756
  emptyMessage,
1909
2757
  className: cn(className),
1910
2758
  ...props,
1911
- children: /* @__PURE__ */ jsx15("div", { ref: containerRef, className: "h-full w-full" })
2759
+ children: /* @__PURE__ */ jsx16("div", { ref: containerRef, className: "h-full w-full" })
1912
2760
  }
1913
2761
  );
1914
2762
  }
@@ -1916,7 +2764,7 @@ var TreemapChart = React15.forwardRef(
1916
2764
  TreemapChart.displayName = "TreemapChart";
1917
2765
 
1918
2766
  // src/components/extended/CandlestickChart.tsx
1919
- import * as React16 from "react";
2767
+ import * as React17 from "react";
1920
2768
  import * as echarts14 from "echarts/core";
1921
2769
  import {
1922
2770
  CandlestickChart as EChartsCandlestickChart,
@@ -1924,23 +2772,29 @@ import {
1924
2772
  } from "echarts/charts";
1925
2773
  import {
1926
2774
  GridComponent as GridComponent6,
1927
- TooltipComponent as TooltipComponent11,
2775
+ TooltipComponent as TooltipComponent12,
1928
2776
  TitleComponent as TitleComponent12,
1929
2777
  DataZoomComponent
1930
2778
  } from "echarts/components";
1931
2779
  import { CanvasRenderer as CanvasRenderer12 } from "echarts/renderers";
1932
- import { getChartColors as getChartColors6, THEME_DARK as THEME_DARK7 } from "@classic-homes/charts-core";
1933
- import { jsx as jsx16 } from "react/jsx-runtime";
2780
+ import {
2781
+ buildAxis as buildAxis6,
2782
+ buildTooltip as buildTooltip12,
2783
+ getChartColors as getChartColors11,
2784
+ mergeChartOption as mergeChartOption12,
2785
+ THEME_DARK as THEME_DARK7
2786
+ } from "@classic-homes/charts-core";
2787
+ import { jsx as jsx17 } from "react/jsx-runtime";
1934
2788
  echarts14.use([
1935
2789
  EChartsCandlestickChart,
1936
2790
  EChartsBarChart2,
1937
2791
  GridComponent6,
1938
- TooltipComponent11,
2792
+ TooltipComponent12,
1939
2793
  TitleComponent12,
1940
2794
  DataZoomComponent,
1941
2795
  CanvasRenderer12
1942
2796
  ]);
1943
- var CandlestickChart = React16.forwardRef(
2797
+ var CandlestickChart = React17.forwardRef(
1944
2798
  ({
1945
2799
  title,
1946
2800
  description,
@@ -1953,14 +2807,19 @@ var CandlestickChart = React16.forwardRef(
1953
2807
  animation = true,
1954
2808
  showTooltip = true,
1955
2809
  showVolume = false,
2810
+ xAxisConfig,
2811
+ yAxisConfig,
2812
+ tooltipFormatter,
2813
+ tooltipValueFormatter,
2814
+ option: optionOverride,
1956
2815
  onClick,
1957
2816
  class: className,
1958
2817
  ...props
1959
2818
  }, ref) => {
1960
2819
  const isEmpty = !data?.dates?.length || !data?.data?.length;
1961
2820
  const currentTheme = useChartTheme(theme);
1962
- const chartColors = getChartColors6(currentTheme);
1963
- const option = React16.useMemo(() => {
2821
+ const chartColors = getChartColors11(currentTheme);
2822
+ const option = React17.useMemo(() => {
1964
2823
  if (isEmpty) return {};
1965
2824
  const candlestickData = data.data.map((item) => [item.open, item.close, item.low, item.high]);
1966
2825
  const seriesArr = [
@@ -1987,23 +2846,29 @@ var CandlestickChart = React16.forwardRef(
1987
2846
  }
1988
2847
  ];
1989
2848
  const xAxisArr = [
1990
- {
1991
- type: "category",
1992
- data: data.dates,
1993
- boundaryGap: false,
1994
- axisLine: { onZero: false },
1995
- splitLine: { show: false },
1996
- min: "dataMin",
1997
- max: "dataMax"
1998
- }
2849
+ buildAxis6(
2850
+ {
2851
+ type: "category",
2852
+ data: data.dates,
2853
+ boundaryGap: false,
2854
+ axisLine: { onZero: false },
2855
+ splitLine: { show: false },
2856
+ min: "dataMin",
2857
+ max: "dataMax"
2858
+ },
2859
+ xAxisConfig
2860
+ )
1999
2861
  ];
2000
2862
  const yAxisArr = [
2001
- {
2002
- scale: true,
2003
- splitArea: {
2004
- show: true
2005
- }
2006
- }
2863
+ buildAxis6(
2864
+ {
2865
+ scale: true,
2866
+ splitArea: {
2867
+ show: true
2868
+ }
2869
+ },
2870
+ yAxisConfig
2871
+ )
2007
2872
  ];
2008
2873
  if (showVolume && data.volume) {
2009
2874
  gridArr.push({
@@ -2044,33 +2909,55 @@ var CandlestickChart = React16.forwardRef(
2044
2909
  }
2045
2910
  });
2046
2911
  }
2047
- return {
2048
- tooltip: showTooltip ? {
2049
- trigger: "axis",
2050
- axisPointer: {
2051
- type: "cross"
2052
- }
2053
- } : void 0,
2054
- grid: gridArr,
2055
- xAxis: xAxisArr,
2056
- yAxis: yAxisArr,
2057
- dataZoom: [
2058
- {
2059
- type: "inside",
2060
- start: 80,
2061
- end: 100
2062
- },
2063
- {
2064
- show: true,
2065
- type: "slider",
2066
- top: "90%",
2067
- start: 80,
2068
- end: 100
2069
- }
2070
- ],
2071
- series: seriesArr
2072
- };
2073
- }, [data, isEmpty, showTooltip, showVolume, title, chartColors]);
2912
+ return mergeChartOption12(
2913
+ {
2914
+ tooltip: buildTooltip12(
2915
+ {
2916
+ trigger: "axis",
2917
+ axisPointer: {
2918
+ type: "cross"
2919
+ }
2920
+ },
2921
+ {
2922
+ show: showTooltip,
2923
+ formatter: tooltipFormatter,
2924
+ valueFormatter: tooltipValueFormatter
2925
+ }
2926
+ ),
2927
+ grid: gridArr,
2928
+ xAxis: xAxisArr,
2929
+ yAxis: yAxisArr,
2930
+ dataZoom: [
2931
+ {
2932
+ type: "inside",
2933
+ start: 80,
2934
+ end: 100
2935
+ },
2936
+ {
2937
+ show: true,
2938
+ type: "slider",
2939
+ top: "90%",
2940
+ start: 80,
2941
+ end: 100
2942
+ }
2943
+ ],
2944
+ series: seriesArr
2945
+ },
2946
+ optionOverride
2947
+ );
2948
+ }, [
2949
+ data,
2950
+ isEmpty,
2951
+ showTooltip,
2952
+ showVolume,
2953
+ title,
2954
+ chartColors,
2955
+ xAxisConfig,
2956
+ yAxisConfig,
2957
+ tooltipFormatter,
2958
+ tooltipValueFormatter,
2959
+ optionOverride
2960
+ ]);
2074
2961
  const latest = useLatestRef({ onClick, data });
2075
2962
  const { containerRef } = useChart({
2076
2963
  option,
@@ -2097,7 +2984,7 @@ var CandlestickChart = React16.forwardRef(
2097
2984
  }
2098
2985
  });
2099
2986
  const accessibilityDescription = description || (!isEmpty ? `${title}. Candlestick chart with ${data.dates.length} data points.` : void 0);
2100
- return /* @__PURE__ */ jsx16(
2987
+ return /* @__PURE__ */ jsx17(
2101
2988
  ChartContainer,
2102
2989
  {
2103
2990
  ref,
@@ -2110,7 +2997,7 @@ var CandlestickChart = React16.forwardRef(
2110
2997
  emptyMessage,
2111
2998
  className: cn(className),
2112
2999
  ...props,
2113
- children: /* @__PURE__ */ jsx16("div", { ref: containerRef, className: "h-full w-full" })
3000
+ children: /* @__PURE__ */ jsx17("div", { ref: containerRef, className: "h-full w-full" })
2114
3001
  }
2115
3002
  );
2116
3003
  }
@@ -2118,14 +3005,21 @@ var CandlestickChart = React16.forwardRef(
2118
3005
  CandlestickChart.displayName = "CandlestickChart";
2119
3006
 
2120
3007
  // src/components/extended/SankeyChart.tsx
2121
- import * as React17 from "react";
3008
+ import * as React18 from "react";
2122
3009
  import * as echarts15 from "echarts/core";
2123
3010
  import { SankeyChart as EChartsSankeyChart } from "echarts/charts";
2124
- import { TooltipComponent as TooltipComponent12, TitleComponent as TitleComponent13 } from "echarts/components";
3011
+ import { TooltipComponent as TooltipComponent13, TitleComponent as TitleComponent13 } from "echarts/components";
2125
3012
  import { CanvasRenderer as CanvasRenderer13 } from "echarts/renderers";
2126
- import { jsx as jsx17 } from "react/jsx-runtime";
2127
- echarts15.use([EChartsSankeyChart, TooltipComponent12, TitleComponent13, CanvasRenderer13]);
2128
- var SankeyChart = React17.forwardRef(
3013
+ import {
3014
+ buildDataLabel as buildDataLabel11,
3015
+ buildTooltip as buildTooltip13,
3016
+ mergeChartOption as mergeChartOption13,
3017
+ nameLabelLookup as nameLabelLookup2,
3018
+ pointLabel as pointLabel5
3019
+ } from "@classic-homes/charts-core";
3020
+ import { jsx as jsx18 } from "react/jsx-runtime";
3021
+ echarts15.use([EChartsSankeyChart, TooltipComponent13, TitleComponent13, CanvasRenderer13]);
3022
+ var SankeyChart = React18.forwardRef(
2129
3023
  ({
2130
3024
  title,
2131
3025
  description,
@@ -2138,45 +3032,73 @@ var SankeyChart = React17.forwardRef(
2138
3032
  animation = true,
2139
3033
  showTooltip = true,
2140
3034
  orient = "horizontal",
3035
+ labelFormatter,
3036
+ tooltipFormatter,
3037
+ tooltipValueFormatter,
3038
+ option: optionOverride,
2141
3039
  onClick,
2142
3040
  class: className,
2143
3041
  ...props
2144
3042
  }, ref) => {
2145
3043
  const isEmpty = !data?.nodes?.length || !data?.links?.length;
2146
- const option = React17.useMemo(() => {
3044
+ const option = React18.useMemo(() => {
2147
3045
  if (isEmpty) return {};
2148
- return {
2149
- tooltip: showTooltip ? {
2150
- trigger: "item",
2151
- triggerOn: "mousemove"
2152
- } : void 0,
2153
- series: [
2154
- {
2155
- type: "sankey",
2156
- orient,
2157
- emphasis: {
2158
- focus: "adjacency"
2159
- },
2160
- data: data.nodes.map((node) => ({
2161
- name: node.name,
2162
- itemStyle: node.color ? { color: node.color } : void 0
2163
- })),
2164
- links: data.links.map((link) => ({
2165
- source: link.source,
2166
- target: link.target,
2167
- value: link.value
2168
- })),
2169
- lineStyle: {
2170
- color: "gradient",
2171
- curveness: 0.5
3046
+ return mergeChartOption13(
3047
+ {
3048
+ tooltip: buildTooltip13(
3049
+ {
3050
+ trigger: "item",
3051
+ triggerOn: "mousemove"
2172
3052
  },
2173
- label: {
2174
- position: orient === "horizontal" ? "right" : "bottom"
3053
+ {
3054
+ show: showTooltip,
3055
+ formatter: tooltipFormatter,
3056
+ valueFormatter: tooltipValueFormatter
2175
3057
  }
2176
- }
2177
- ]
2178
- };
2179
- }, [data, isEmpty, showTooltip, orient]);
3058
+ ),
3059
+ series: [
3060
+ {
3061
+ type: "sankey",
3062
+ orient,
3063
+ emphasis: {
3064
+ focus: "adjacency"
3065
+ },
3066
+ data: data.nodes.map((node) => ({
3067
+ name: node.name,
3068
+ itemStyle: node.color ? { color: node.color } : void 0,
3069
+ label: pointLabel5(node.label, labelFormatter)
3070
+ })),
3071
+ links: data.links.map((link) => ({
3072
+ source: link.source,
3073
+ target: link.target,
3074
+ value: link.value
3075
+ })),
3076
+ lineStyle: {
3077
+ color: "gradient",
3078
+ curveness: 0.5
3079
+ },
3080
+ label: buildDataLabel11(
3081
+ {
3082
+ position: orient === "horizontal" ? "right" : "bottom"
3083
+ },
3084
+ void 0,
3085
+ { show: true, formatter: labelFormatter, getLabel: nameLabelLookup2(data.nodes) }
3086
+ )
3087
+ }
3088
+ ]
3089
+ },
3090
+ optionOverride
3091
+ );
3092
+ }, [
3093
+ data,
3094
+ isEmpty,
3095
+ showTooltip,
3096
+ orient,
3097
+ labelFormatter,
3098
+ tooltipFormatter,
3099
+ tooltipValueFormatter,
3100
+ optionOverride
3101
+ ]);
2180
3102
  const latest = useLatestRef({ onClick });
2181
3103
  const { containerRef } = useChart({
2182
3104
  option,
@@ -2200,7 +3122,7 @@ var SankeyChart = React17.forwardRef(
2200
3122
  }
2201
3123
  });
2202
3124
  const accessibilityDescription = description || (!isEmpty ? `${title}. Sankey diagram with ${data.nodes.length} nodes and ${data.links.length} links.` : void 0);
2203
- return /* @__PURE__ */ jsx17(
3125
+ return /* @__PURE__ */ jsx18(
2204
3126
  ChartContainer,
2205
3127
  {
2206
3128
  ref,
@@ -2213,7 +3135,7 @@ var SankeyChart = React17.forwardRef(
2213
3135
  emptyMessage,
2214
3136
  className: cn(className),
2215
3137
  ...props,
2216
- children: /* @__PURE__ */ jsx17("div", { ref: containerRef, className: "h-full w-full" })
3138
+ children: /* @__PURE__ */ jsx18("div", { ref: containerRef, className: "h-full w-full" })
2217
3139
  }
2218
3140
  );
2219
3141
  }
@@ -2226,6 +3148,7 @@ export {
2226
3148
  ChartContainer,
2227
3149
  ChartEmpty,
2228
3150
  ChartError,
3151
+ ChartLegend,
2229
3152
  ChartSkeleton,
2230
3153
  DonutChart,
2231
3154
  FunnelChart,
@@ -2240,6 +3163,7 @@ export {
2240
3163
  cn,
2241
3164
  registerClassicThemes,
2242
3165
  useChart,
3166
+ useChartLegend,
2243
3167
  useChartResize,
2244
3168
  useChartTheme,
2245
3169
  useReducedMotion