@classic-homes/charts-react 0.1.37 → 0.2.0

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