@optilogic/charts 1.0.0-beta.8 → 1.0.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.
Files changed (37) hide show
  1. package/dist/index.cjs +3034 -174
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +489 -192
  4. package/dist/index.d.ts +489 -192
  5. package/dist/index.js +3006 -175
  6. package/dist/index.js.map +1 -1
  7. package/package.json +2 -2
  8. package/src/cartesian/area-chart.tsx +177 -0
  9. package/src/cartesian/bar-chart.tsx +217 -0
  10. package/src/cartesian/composed-chart.tsx +222 -0
  11. package/src/cartesian/line-chart.tsx +159 -0
  12. package/src/cartesian/scatter-chart.tsx +158 -0
  13. package/src/cartesian/waterfall-chart.tsx +171 -0
  14. package/src/dashboard/chart-builder.tsx +310 -0
  15. package/src/dashboard/chart-renderer.tsx +250 -0
  16. package/src/dashboard/kpi-card.tsx +121 -0
  17. package/src/dashboard/scenario-comparison.tsx +235 -0
  18. package/src/dashboard/sparkline.tsx +86 -0
  19. package/src/index.ts +50 -19
  20. package/src/radial/donut-chart.tsx +135 -0
  21. package/src/radial/pie-chart.tsx +153 -0
  22. package/src/radial/radar-chart.tsx +111 -0
  23. package/src/radial/radial-bar-chart.tsx +115 -0
  24. package/src/shared/chart-container.tsx +104 -0
  25. package/src/shared/chart-legend.tsx +57 -0
  26. package/src/shared/chart-tooltip.tsx +159 -0
  27. package/src/shared/colors.ts +37 -0
  28. package/src/shared/formatters.ts +51 -0
  29. package/src/shared/types.ts +66 -0
  30. package/src/shared/use-live-data.ts +83 -0
  31. package/src/specialized/funnel-chart.tsx +93 -0
  32. package/src/specialized/gantt-chart.tsx +416 -0
  33. package/src/specialized/heatmap-chart.tsx +250 -0
  34. package/src/specialized/sankey-chart.tsx +155 -0
  35. package/src/specialized/treemap-chart.tsx +121 -0
  36. package/src/bar-chart.tsx +0 -337
  37. package/src/line-chart.tsx +0 -266
package/dist/index.js CHANGED
@@ -1,21 +1,389 @@
1
- import * as React from 'react';
2
- import { ResponsiveContainer, LineChart as LineChart$1, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Line, BarChart as BarChart$1, Bar } from 'recharts';
1
+ import * as React17 from 'react';
2
+ import { useState, useRef, useCallback, useEffect } from 'react';
3
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
+ import { ResponsiveContainer, LineChart as LineChart$1, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Line, BarChart as BarChart$1, Bar, AreaChart as AreaChart$1, Area, ScatterChart as ScatterChart$1, ZAxis, Scatter, ComposedChart as ComposedChart$1, ReferenceLine, Cell, PieChart as PieChart$1, Pie, RadarChart as RadarChart$1, PolarGrid, PolarAngleAxis, PolarRadiusAxis, Radar, RadialBarChart as RadialBarChart$1, RadialBar, Sankey, Treemap, FunnelChart as FunnelChart$1, Funnel, LabelList, Layer, Rectangle } from 'recharts';
3
5
  import { cn } from '@optilogic/core';
4
- import { jsx, jsxs } from 'react/jsx-runtime';
5
6
 
6
- // src/line-chart.tsx
7
+ // src/shared/colors.ts
7
8
  var CHART_COLORS = [
8
9
  "hsl(var(--chart-1))",
9
10
  "hsl(var(--chart-2))",
10
11
  "hsl(var(--chart-3))",
11
12
  "hsl(var(--chart-4))",
12
- "hsl(var(--chart-5))"
13
+ "hsl(var(--chart-5))",
14
+ "hsl(var(--chart-6))",
15
+ "hsl(var(--chart-7))",
16
+ "hsl(var(--chart-8))",
17
+ "hsl(var(--chart-9))",
18
+ "hsl(var(--chart-10))",
19
+ "hsl(var(--chart-11))",
20
+ "hsl(var(--chart-12))"
13
21
  ];
14
22
  function getChartColor(index, custom) {
15
23
  if (custom) return custom;
16
24
  return CHART_COLORS[index % CHART_COLORS.length] ?? "hsl(var(--chart-1))";
17
25
  }
18
- var LineChart = React.forwardRef(
26
+ var SEMANTIC_COLORS = {
27
+ positive: "hsl(var(--success))",
28
+ negative: "hsl(var(--destructive))",
29
+ neutral: "hsl(var(--muted-foreground))",
30
+ warning: "hsl(var(--warning))"
31
+ };
32
+ function getSemanticColor(name) {
33
+ return SEMANTIC_COLORS[name];
34
+ }
35
+ var ChartTooltipContent = React17.memo(function ChartTooltipContent2({
36
+ active,
37
+ payload,
38
+ label,
39
+ config
40
+ }) {
41
+ if (!active || !payload?.length) return null;
42
+ const items = payload;
43
+ const formattedLabel = config?.labelFormatter ? config.labelFormatter(String(label)) : String(label ?? "");
44
+ const total = config?.showTotal ? items.reduce((sum, item) => sum + (Number(item.value) || 0), 0) : null;
45
+ return /* @__PURE__ */ jsxs(
46
+ "div",
47
+ {
48
+ style: {
49
+ background: "hsl(var(--card))",
50
+ border: "1px solid hsl(var(--border))",
51
+ borderRadius: 6,
52
+ padding: "8px 10px",
53
+ fontSize: 11,
54
+ lineHeight: 1.5,
55
+ boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
56
+ minWidth: 120
57
+ },
58
+ children: [
59
+ formattedLabel && /* @__PURE__ */ jsx(
60
+ "div",
61
+ {
62
+ style: {
63
+ color: "hsl(var(--foreground))",
64
+ fontWeight: 600,
65
+ marginBottom: 4,
66
+ borderBottom: "1px solid hsl(var(--border))",
67
+ paddingBottom: 4
68
+ },
69
+ children: formattedLabel
70
+ }
71
+ ),
72
+ items.map((item, i) => {
73
+ const formatted = config?.formatter ? config.formatter(Number(item.value), item.name) : String(item.value);
74
+ const displayValue = Array.isArray(formatted) ? formatted[0] : formatted;
75
+ const displayName = Array.isArray(formatted) ? formatted[1] : item.name;
76
+ let delta = null;
77
+ if (config?.showDelta && config.baseline) {
78
+ const baselineValue = Number(
79
+ item.payload?.[config.baseline] ?? 0
80
+ );
81
+ if (baselineValue !== 0) {
82
+ const diff = Number(item.value) - baselineValue;
83
+ const pct = (diff / baselineValue * 100).toFixed(1);
84
+ const sign = diff >= 0 ? "+" : "";
85
+ const color = diff > 0 ? "hsl(var(--success))" : diff < 0 ? "hsl(var(--destructive))" : "hsl(var(--muted-foreground))";
86
+ delta = /* @__PURE__ */ jsxs("span", { style: { color, marginLeft: 4, fontSize: 10 }, children: [
87
+ diff >= 0 ? "\u25B2" : "\u25BC",
88
+ " ",
89
+ sign,
90
+ pct,
91
+ "%"
92
+ ] });
93
+ }
94
+ }
95
+ return /* @__PURE__ */ jsxs(
96
+ "div",
97
+ {
98
+ style: {
99
+ display: "flex",
100
+ alignItems: "center",
101
+ justifyContent: "space-between",
102
+ gap: 12,
103
+ padding: "1px 0"
104
+ },
105
+ children: [
106
+ /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
107
+ /* @__PURE__ */ jsx(
108
+ "span",
109
+ {
110
+ style: {
111
+ width: 8,
112
+ height: 8,
113
+ borderRadius: "50%",
114
+ background: item.color,
115
+ flexShrink: 0
116
+ }
117
+ }
118
+ ),
119
+ /* @__PURE__ */ jsx("span", { style: { color: "hsl(var(--muted-foreground))" }, children: displayName })
120
+ ] }),
121
+ /* @__PURE__ */ jsxs(
122
+ "span",
123
+ {
124
+ style: {
125
+ fontWeight: 600,
126
+ color: "hsl(var(--foreground))",
127
+ fontVariantNumeric: "tabular-nums"
128
+ },
129
+ children: [
130
+ displayValue,
131
+ delta
132
+ ]
133
+ }
134
+ )
135
+ ]
136
+ },
137
+ item.dataKey ?? i
138
+ );
139
+ }),
140
+ total != null && /* @__PURE__ */ jsxs(
141
+ "div",
142
+ {
143
+ style: {
144
+ borderTop: "1px solid hsl(var(--border))",
145
+ marginTop: 4,
146
+ paddingTop: 4,
147
+ display: "flex",
148
+ justifyContent: "space-between",
149
+ fontWeight: 600,
150
+ color: "hsl(var(--foreground))"
151
+ },
152
+ children: [
153
+ /* @__PURE__ */ jsx("span", { children: "Total" }),
154
+ /* @__PURE__ */ jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: config?.formatter ? config.formatter(total, "Total") : total })
155
+ ]
156
+ }
157
+ )
158
+ ]
159
+ }
160
+ );
161
+ });
162
+ function resolveTooltipProps(tooltip) {
163
+ if (tooltip === false || tooltip === void 0) return null;
164
+ if (tooltip === true) return { content: /* @__PURE__ */ jsx(ChartTooltipContent, {}) };
165
+ return {
166
+ content: /* @__PURE__ */ jsx(ChartTooltipContent, { config: tooltip })
167
+ };
168
+ }
169
+ var ChartLegendContent = React17.memo(function ChartLegendContent2({
170
+ payload
171
+ }) {
172
+ if (!payload?.length) return null;
173
+ return /* @__PURE__ */ jsx(
174
+ "div",
175
+ {
176
+ style: {
177
+ display: "flex",
178
+ flexWrap: "wrap",
179
+ gap: "8px 16px",
180
+ fontSize: 11,
181
+ lineHeight: 1,
182
+ padding: "0 0 4px"
183
+ },
184
+ children: payload.map((entry, i) => /* @__PURE__ */ jsxs(
185
+ "span",
186
+ {
187
+ style: { display: "flex", alignItems: "center", gap: 5 },
188
+ children: [
189
+ /* @__PURE__ */ jsx(
190
+ "span",
191
+ {
192
+ style: {
193
+ width: 8,
194
+ height: 8,
195
+ borderRadius: "50%",
196
+ background: entry.color,
197
+ flexShrink: 0
198
+ }
199
+ }
200
+ ),
201
+ /* @__PURE__ */ jsx("span", { style: { color: "hsl(var(--muted-foreground))" }, children: entry.value })
202
+ ]
203
+ },
204
+ entry.dataKey ?? i
205
+ ))
206
+ }
207
+ );
208
+ });
209
+ function resolveLegendConfig(legend) {
210
+ if (legend === false || legend === void 0) return null;
211
+ if (legend === true) return { position: "top", align: "right" };
212
+ return legend;
213
+ }
214
+ var ChartContainer = React17.forwardRef(function ChartContainer2({
215
+ children,
216
+ className,
217
+ height = "100%",
218
+ loading,
219
+ empty,
220
+ emptyMessage = "No data available",
221
+ title,
222
+ description
223
+ }, ref) {
224
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("w-full", className), style: { height }, children: [
225
+ (title || description) && /* @__PURE__ */ jsxs("div", { style: { marginBottom: 8 }, children: [
226
+ title && /* @__PURE__ */ jsx(
227
+ "div",
228
+ {
229
+ style: {
230
+ fontSize: 13,
231
+ fontWeight: 600,
232
+ color: "hsl(var(--foreground))"
233
+ },
234
+ children: title
235
+ }
236
+ ),
237
+ description && /* @__PURE__ */ jsx(
238
+ "div",
239
+ {
240
+ style: {
241
+ fontSize: 11,
242
+ color: "hsl(var(--muted-foreground))",
243
+ marginTop: 2
244
+ },
245
+ children: description
246
+ }
247
+ )
248
+ ] }),
249
+ loading ? /* @__PURE__ */ jsx(
250
+ "div",
251
+ {
252
+ style: {
253
+ width: "100%",
254
+ height: title || description ? "calc(100% - 40px)" : "100%",
255
+ display: "flex",
256
+ alignItems: "center",
257
+ justifyContent: "center"
258
+ },
259
+ children: /* @__PURE__ */ jsx(
260
+ "div",
261
+ {
262
+ className: "animate-pulse",
263
+ style: {
264
+ width: "80%",
265
+ height: "60%",
266
+ borderRadius: 6,
267
+ background: "hsl(var(--muted))"
268
+ }
269
+ }
270
+ )
271
+ }
272
+ ) : empty ? /* @__PURE__ */ jsx(
273
+ "div",
274
+ {
275
+ style: {
276
+ width: "100%",
277
+ height: title || description ? "calc(100% - 40px)" : "100%",
278
+ display: "flex",
279
+ alignItems: "center",
280
+ justifyContent: "center",
281
+ color: "hsl(var(--muted-foreground))",
282
+ fontSize: 12
283
+ },
284
+ children: emptyMessage
285
+ }
286
+ ) : /* @__PURE__ */ jsx(
287
+ ResponsiveContainer,
288
+ {
289
+ width: "100%",
290
+ height: title || description ? "calc(100% - 40px)" : "100%",
291
+ children
292
+ }
293
+ )
294
+ ] });
295
+ });
296
+ function useLiveData(config = {}) {
297
+ const {
298
+ initial = [],
299
+ maxPoints = 200,
300
+ interval = 2e3,
301
+ fetcher,
302
+ paused: initialPaused = false
303
+ } = config;
304
+ const [data, setData] = useState(initial);
305
+ const [isPaused, setIsPaused] = useState(initialPaused);
306
+ const bufferRef = useRef(initial);
307
+ const push = useCallback(
308
+ (...items) => {
309
+ bufferRef.current = [...bufferRef.current, ...items].slice(-maxPoints);
310
+ setData(bufferRef.current);
311
+ },
312
+ [maxPoints]
313
+ );
314
+ const replace = useCallback((items) => {
315
+ bufferRef.current = items;
316
+ setData(items);
317
+ }, []);
318
+ const pause = useCallback(() => setIsPaused(true), []);
319
+ const resume = useCallback(() => setIsPaused(false), []);
320
+ useEffect(() => {
321
+ if (!fetcher || isPaused) return;
322
+ let cancelled = false;
323
+ const tick = async () => {
324
+ try {
325
+ const result = await fetcher();
326
+ if (!cancelled) {
327
+ bufferRef.current = result.slice(-maxPoints);
328
+ setData(bufferRef.current);
329
+ }
330
+ } catch {
331
+ }
332
+ };
333
+ tick();
334
+ const id = setInterval(tick, interval);
335
+ return () => {
336
+ cancelled = true;
337
+ clearInterval(id);
338
+ };
339
+ }, [fetcher, interval, isPaused, maxPoints]);
340
+ return { data, push, replace, isPaused, pause, resume };
341
+ }
342
+
343
+ // src/shared/formatters.ts
344
+ var compactThresholds = [
345
+ [1e9, "B"],
346
+ [1e6, "M"],
347
+ [1e3, "K"]
348
+ ];
349
+ function formatCompact(value, decimals = 1) {
350
+ for (const [threshold, suffix] of compactThresholds) {
351
+ if (Math.abs(value) >= threshold) {
352
+ return `${(value / threshold).toFixed(decimals)}${suffix}`;
353
+ }
354
+ }
355
+ return value.toLocaleString();
356
+ }
357
+ function formatCurrency(value, currency = "USD", decimals) {
358
+ if (Math.abs(value) >= 1e6) {
359
+ return `$${formatCompact(value, decimals ?? 1)}`;
360
+ }
361
+ return value.toLocaleString("en-US", {
362
+ style: "currency",
363
+ currency,
364
+ minimumFractionDigits: decimals ?? 0,
365
+ maximumFractionDigits: decimals ?? 0
366
+ });
367
+ }
368
+ function formatPercent(value, decimals = 1) {
369
+ return `${value.toFixed(decimals)}%`;
370
+ }
371
+ function formatUnit(value, unit, decimals = 1) {
372
+ return `${value.toLocaleString(void 0, { maximumFractionDigits: decimals })} ${unit}`;
373
+ }
374
+ function formatDuration(seconds) {
375
+ if (seconds < 60) return `${Math.round(seconds)}s`;
376
+ if (seconds < 3600) return `${Math.round(seconds / 60)}m`;
377
+ if (seconds < 86400) {
378
+ const h2 = Math.floor(seconds / 3600);
379
+ const m = Math.round(seconds % 3600 / 60);
380
+ return m > 0 ? `${h2}h ${m}m` : `${h2}h`;
381
+ }
382
+ const d = Math.floor(seconds / 86400);
383
+ const h = Math.round(seconds % 86400 / 3600);
384
+ return h > 0 ? `${d}d ${h}h` : `${d}d`;
385
+ }
386
+ var LineChart = React17.forwardRef(
19
387
  ({
20
388
  data,
21
389
  series,
@@ -25,112 +393,105 @@ var LineChart = React.forwardRef(
25
393
  tooltip = true,
26
394
  legend = false,
27
395
  animate = false,
396
+ live = false,
28
397
  className,
29
398
  height = "100%",
30
- margin = { top: 8, right: 12, left: 0, bottom: 4 }
399
+ margin = { top: 8, right: 12, left: 0, bottom: 4 },
400
+ loading
31
401
  }, ref) => {
32
- const gridConfig = React.useMemo(() => {
402
+ const gridConfig = React17.useMemo(() => {
33
403
  if (grid === false) return null;
34
404
  if (grid === true) return { vertical: false, horizontal: true };
35
405
  return grid;
36
406
  }, [grid]);
37
- const legendConfig = React.useMemo(() => {
38
- if (legend === false) return null;
39
- if (legend === true)
40
- return { position: "top", align: "right" };
41
- return legend;
42
- }, [legend]);
43
- return /* @__PURE__ */ jsx("div", { ref, className: cn("w-full", className), style: { height }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(LineChart$1, { data, margin, children: [
44
- gridConfig && /* @__PURE__ */ jsx(
45
- CartesianGrid,
46
- {
47
- strokeDasharray: "3 3",
48
- stroke: "hsl(var(--divider))",
49
- vertical: gridConfig.vertical ?? false,
50
- horizontal: gridConfig.horizontal ?? true
51
- }
52
- ),
53
- /* @__PURE__ */ jsx(
54
- XAxis,
55
- {
56
- dataKey: xAxis.dataKey,
57
- tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
58
- tickLine: false,
59
- axisLine: false,
60
- minTickGap: xAxis.minTickGap ?? 24,
61
- tickFormatter: xAxis.tickFormatter,
62
- label: xAxis.label ? {
63
- value: xAxis.label,
64
- position: "insideBottom",
65
- offset: -4,
66
- fontSize: 11,
67
- fill: "hsl(var(--muted-foreground))"
68
- } : void 0
69
- }
70
- ),
71
- /* @__PURE__ */ jsx(
72
- YAxis,
73
- {
74
- domain: yAxis?.domain ?? ["auto", "auto"],
75
- tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
76
- tickLine: false,
77
- axisLine: false,
78
- width: yAxis?.width ?? 30,
79
- tickFormatter: yAxis?.tickFormatter,
80
- label: yAxis?.label ? {
81
- value: yAxis.label,
82
- angle: -90,
83
- position: "insideLeft",
84
- fontSize: 11,
85
- fill: "hsl(var(--muted-foreground))"
86
- } : void 0
87
- }
88
- ),
89
- tooltip && /* @__PURE__ */ jsx(
90
- Tooltip,
91
- {
92
- contentStyle: {
93
- background: "hsl(var(--card))",
94
- border: "1px solid hsl(var(--border))",
95
- borderRadius: 6,
96
- fontSize: 11
97
- },
98
- labelStyle: {
99
- color: "hsl(var(--foreground))",
100
- fontWeight: 500,
101
- marginBottom: 4
102
- },
103
- itemStyle: {
104
- color: "hsl(var(--foreground))"
105
- }
106
- }
107
- ),
108
- legendConfig && /* @__PURE__ */ jsx(
109
- Legend,
110
- {
111
- verticalAlign: legendConfig.position ?? "top",
112
- align: legendConfig.align ?? "right",
113
- wrapperStyle: { fontSize: 11, paddingBottom: 4 }
114
- }
115
- ),
116
- series.map((s, index) => /* @__PURE__ */ jsx(
117
- Line,
118
- {
119
- type: s.type ?? "monotone",
120
- dataKey: s.dataKey,
121
- name: s.name,
122
- stroke: getChartColor(index, s.color),
123
- strokeWidth: s.strokeWidth ?? 2,
124
- dot: s.dot ?? false,
125
- isAnimationActive: animate
126
- },
127
- s.dataKey
128
- ))
129
- ] }) }) });
407
+ const legendConfig = resolveLegendConfig(legend);
408
+ const tooltipProps = resolveTooltipProps(tooltip);
409
+ const isAnimated = live ? false : animate;
410
+ return /* @__PURE__ */ jsx(
411
+ ChartContainer,
412
+ {
413
+ ref,
414
+ className,
415
+ height,
416
+ loading,
417
+ empty: !data?.length,
418
+ children: /* @__PURE__ */ jsxs(LineChart$1, { data, margin, children: [
419
+ gridConfig && /* @__PURE__ */ jsx(
420
+ CartesianGrid,
421
+ {
422
+ strokeDasharray: "3 3",
423
+ stroke: "hsl(var(--divider))",
424
+ vertical: gridConfig.vertical ?? false,
425
+ horizontal: gridConfig.horizontal ?? true
426
+ }
427
+ ),
428
+ /* @__PURE__ */ jsx(
429
+ XAxis,
430
+ {
431
+ dataKey: xAxis.dataKey,
432
+ tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
433
+ tickLine: false,
434
+ axisLine: false,
435
+ minTickGap: xAxis.minTickGap ?? 24,
436
+ tickFormatter: xAxis.tickFormatter,
437
+ label: xAxis.label ? {
438
+ value: xAxis.label,
439
+ position: "insideBottom",
440
+ offset: -4,
441
+ fontSize: 11,
442
+ fill: "hsl(var(--muted-foreground))"
443
+ } : void 0
444
+ }
445
+ ),
446
+ /* @__PURE__ */ jsx(
447
+ YAxis,
448
+ {
449
+ domain: yAxis?.domain ?? ["auto", "auto"],
450
+ tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
451
+ tickLine: false,
452
+ axisLine: false,
453
+ width: yAxis?.width ?? 30,
454
+ tickFormatter: yAxis?.tickFormatter,
455
+ label: yAxis?.label ? {
456
+ value: yAxis.label,
457
+ angle: -90,
458
+ position: "insideLeft",
459
+ fontSize: 11,
460
+ fill: "hsl(var(--muted-foreground))"
461
+ } : void 0
462
+ }
463
+ ),
464
+ tooltipProps && /* @__PURE__ */ jsx(Tooltip, { ...tooltipProps }),
465
+ legendConfig && /* @__PURE__ */ jsx(
466
+ Legend,
467
+ {
468
+ verticalAlign: legendConfig.position ?? "top",
469
+ align: legendConfig.align ?? "right",
470
+ content: /* @__PURE__ */ jsx(ChartLegendContent, {})
471
+ }
472
+ ),
473
+ series.map((s, index) => /* @__PURE__ */ jsx(
474
+ Line,
475
+ {
476
+ type: s.type ?? "monotone",
477
+ dataKey: s.dataKey,
478
+ name: s.name,
479
+ stroke: getChartColor(index, s.color),
480
+ strokeWidth: s.strokeWidth ?? 2,
481
+ strokeDasharray: s.strokeDasharray,
482
+ dot: s.dot ?? false,
483
+ isAnimationActive: isAnimated,
484
+ animationDuration: isAnimated ? 300 : 0
485
+ },
486
+ s.dataKey
487
+ ))
488
+ ] })
489
+ }
490
+ );
130
491
  }
131
492
  );
132
493
  LineChart.displayName = "LineChart";
133
- var BarChart = React.forwardRef(
494
+ var BarChart = React17.forwardRef(
134
495
  ({
135
496
  data,
136
497
  series,
@@ -141,105 +502,351 @@ var BarChart = React.forwardRef(
141
502
  tooltip = true,
142
503
  legend = false,
143
504
  animate = true,
505
+ live = false,
144
506
  barSize,
145
507
  barGap,
146
508
  barCategoryGap,
147
509
  className,
148
510
  height = "100%",
149
- margin = { top: 8, right: 12, left: 0, bottom: 4 }
511
+ margin = { top: 8, right: 12, left: 0, bottom: 4 },
512
+ loading
150
513
  }, ref) => {
151
- const gridConfig = React.useMemo(() => {
514
+ const gridConfig = React17.useMemo(() => {
152
515
  if (grid === false) return null;
153
516
  if (grid === true) return { vertical: false, horizontal: true };
154
517
  return grid;
155
518
  }, [grid]);
156
- const legendConfig = React.useMemo(() => {
157
- if (legend === false) return null;
158
- if (legend === true)
159
- return { position: "top", align: "right" };
160
- return legend;
161
- }, [legend]);
519
+ const legendConfig = resolveLegendConfig(legend);
520
+ const tooltipProps = resolveTooltipProps(tooltip);
521
+ const isAnimated = live ? false : animate;
162
522
  const isHorizontal = layout === "horizontal";
163
- return /* @__PURE__ */ jsx("div", { ref, className: cn("w-full", className), style: { height }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(
164
- BarChart$1,
523
+ return /* @__PURE__ */ jsx(
524
+ ChartContainer,
165
525
  {
166
- data,
167
- layout: isHorizontal ? "vertical" : "horizontal",
168
- margin,
169
- barSize,
170
- barGap,
171
- barCategoryGap,
172
- children: [
526
+ ref,
527
+ className,
528
+ height,
529
+ loading,
530
+ empty: !data?.length,
531
+ children: /* @__PURE__ */ jsxs(
532
+ BarChart$1,
533
+ {
534
+ data,
535
+ layout: isHorizontal ? "vertical" : "horizontal",
536
+ margin,
537
+ barSize,
538
+ barGap,
539
+ barCategoryGap,
540
+ children: [
541
+ gridConfig && /* @__PURE__ */ jsx(
542
+ CartesianGrid,
543
+ {
544
+ strokeDasharray: "3 3",
545
+ stroke: "hsl(var(--divider))",
546
+ vertical: isHorizontal ? gridConfig.horizontal ?? true : gridConfig.vertical ?? false,
547
+ horizontal: isHorizontal ? gridConfig.vertical ?? false : gridConfig.horizontal ?? true
548
+ }
549
+ ),
550
+ isHorizontal ? /* @__PURE__ */ jsx(
551
+ XAxis,
552
+ {
553
+ type: "number",
554
+ domain: yAxis?.domain ?? ["auto", "auto"],
555
+ tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
556
+ tickLine: false,
557
+ axisLine: false,
558
+ tickFormatter: yAxis?.tickFormatter,
559
+ label: yAxis?.label ? {
560
+ value: yAxis.label,
561
+ position: "insideBottom",
562
+ offset: -4,
563
+ fontSize: 11,
564
+ fill: "hsl(var(--muted-foreground))"
565
+ } : void 0
566
+ }
567
+ ) : /* @__PURE__ */ jsx(
568
+ XAxis,
569
+ {
570
+ dataKey: xAxis.dataKey,
571
+ tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
572
+ tickLine: false,
573
+ axisLine: false,
574
+ minTickGap: xAxis.minTickGap ?? 24,
575
+ tickFormatter: xAxis.tickFormatter,
576
+ label: xAxis.label ? {
577
+ value: xAxis.label,
578
+ position: "insideBottom",
579
+ offset: -4,
580
+ fontSize: 11,
581
+ fill: "hsl(var(--muted-foreground))"
582
+ } : void 0
583
+ }
584
+ ),
585
+ isHorizontal ? /* @__PURE__ */ jsx(
586
+ YAxis,
587
+ {
588
+ type: "category",
589
+ dataKey: xAxis.dataKey,
590
+ tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
591
+ tickLine: false,
592
+ axisLine: false,
593
+ width: yAxis?.width ?? 80,
594
+ tickFormatter: xAxis.tickFormatter
595
+ }
596
+ ) : /* @__PURE__ */ jsx(
597
+ YAxis,
598
+ {
599
+ domain: yAxis?.domain ?? ["auto", "auto"],
600
+ tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
601
+ tickLine: false,
602
+ axisLine: false,
603
+ width: yAxis?.width ?? 30,
604
+ tickFormatter: yAxis?.tickFormatter,
605
+ label: yAxis?.label ? {
606
+ value: yAxis.label,
607
+ angle: -90,
608
+ position: "insideLeft",
609
+ fontSize: 11,
610
+ fill: "hsl(var(--muted-foreground))"
611
+ } : void 0
612
+ }
613
+ ),
614
+ tooltipProps && /* @__PURE__ */ jsx(
615
+ Tooltip,
616
+ {
617
+ ...tooltipProps,
618
+ cursor: { fill: "hsl(var(--accent))", fillOpacity: 0.3 }
619
+ }
620
+ ),
621
+ legendConfig && /* @__PURE__ */ jsx(
622
+ Legend,
623
+ {
624
+ verticalAlign: legendConfig.position ?? "top",
625
+ align: legendConfig.align ?? "right",
626
+ content: /* @__PURE__ */ jsx(ChartLegendContent, {})
627
+ }
628
+ ),
629
+ series.map((s, index) => /* @__PURE__ */ jsx(
630
+ Bar,
631
+ {
632
+ dataKey: s.dataKey,
633
+ name: s.name,
634
+ fill: getChartColor(index, s.color),
635
+ stackId: s.stackId,
636
+ radius: s.radius ?? 0,
637
+ isAnimationActive: isAnimated,
638
+ animationDuration: isAnimated ? 300 : 0
639
+ },
640
+ s.dataKey
641
+ ))
642
+ ]
643
+ }
644
+ )
645
+ }
646
+ );
647
+ }
648
+ );
649
+ BarChart.displayName = "BarChart";
650
+ var AreaChart = React17.forwardRef(
651
+ ({
652
+ data,
653
+ series,
654
+ xAxis,
655
+ yAxis,
656
+ grid = true,
657
+ tooltip = true,
658
+ legend = false,
659
+ animate = true,
660
+ live = false,
661
+ className,
662
+ height = "100%",
663
+ margin = { top: 8, right: 12, left: 0, bottom: 4 },
664
+ loading
665
+ }, ref) => {
666
+ const gridConfig = React17.useMemo(() => {
667
+ if (grid === false) return null;
668
+ if (grid === true) return { vertical: false, horizontal: true };
669
+ return grid;
670
+ }, [grid]);
671
+ const legendConfig = resolveLegendConfig(legend);
672
+ const tooltipProps = resolveTooltipProps(tooltip);
673
+ const isAnimated = live ? false : animate;
674
+ return /* @__PURE__ */ jsx(
675
+ ChartContainer,
676
+ {
677
+ ref,
678
+ className,
679
+ height,
680
+ loading,
681
+ empty: !data?.length,
682
+ children: /* @__PURE__ */ jsxs(AreaChart$1, { data, margin, children: [
683
+ /* @__PURE__ */ jsx("defs", { children: series.map((s, index) => {
684
+ const color = getChartColor(index, s.color);
685
+ return /* @__PURE__ */ jsxs(
686
+ "linearGradient",
687
+ {
688
+ id: `area-gradient-${s.dataKey}`,
689
+ x1: "0",
690
+ y1: "0",
691
+ x2: "0",
692
+ y2: "1",
693
+ children: [
694
+ /* @__PURE__ */ jsx("stop", { offset: "5%", stopColor: color, stopOpacity: 0.3 }),
695
+ /* @__PURE__ */ jsx("stop", { offset: "95%", stopColor: color, stopOpacity: 0.05 })
696
+ ]
697
+ },
698
+ s.dataKey
699
+ );
700
+ }) }),
173
701
  gridConfig && /* @__PURE__ */ jsx(
174
702
  CartesianGrid,
175
703
  {
176
704
  strokeDasharray: "3 3",
177
705
  stroke: "hsl(var(--divider))",
178
- vertical: isHorizontal ? gridConfig.horizontal ?? true : gridConfig.vertical ?? false,
179
- horizontal: isHorizontal ? gridConfig.vertical ?? false : gridConfig.horizontal ?? true
706
+ vertical: gridConfig.vertical ?? false,
707
+ horizontal: gridConfig.horizontal ?? true
180
708
  }
181
709
  ),
182
- isHorizontal ? /* @__PURE__ */ jsx(
710
+ /* @__PURE__ */ jsx(
183
711
  XAxis,
184
712
  {
185
- type: "number",
186
- domain: yAxis?.domain ?? ["auto", "auto"],
713
+ dataKey: xAxis.dataKey,
187
714
  tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
188
715
  tickLine: false,
189
716
  axisLine: false,
190
- tickFormatter: yAxis?.tickFormatter,
191
- label: yAxis?.label ? {
192
- value: yAxis.label,
717
+ minTickGap: xAxis.minTickGap ?? 24,
718
+ tickFormatter: xAxis.tickFormatter,
719
+ label: xAxis.label ? {
720
+ value: xAxis.label,
193
721
  position: "insideBottom",
194
722
  offset: -4,
195
723
  fontSize: 11,
196
724
  fill: "hsl(var(--muted-foreground))"
197
725
  } : void 0
198
726
  }
199
- ) : /* @__PURE__ */ jsx(
200
- XAxis,
727
+ ),
728
+ /* @__PURE__ */ jsx(
729
+ YAxis,
201
730
  {
202
- dataKey: xAxis.dataKey,
731
+ domain: yAxis?.domain ?? ["auto", "auto"],
203
732
  tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
204
733
  tickLine: false,
205
734
  axisLine: false,
206
- minTickGap: xAxis.minTickGap ?? 24,
207
- tickFormatter: xAxis.tickFormatter,
208
- label: xAxis.label ? {
209
- value: xAxis.label,
210
- position: "insideBottom",
211
- offset: -4,
735
+ width: yAxis?.width ?? 30,
736
+ tickFormatter: yAxis?.tickFormatter,
737
+ label: yAxis?.label ? {
738
+ value: yAxis.label,
739
+ angle: -90,
740
+ position: "insideLeft",
212
741
  fontSize: 11,
213
742
  fill: "hsl(var(--muted-foreground))"
214
743
  } : void 0
215
744
  }
216
745
  ),
217
- isHorizontal ? /* @__PURE__ */ jsx(
218
- YAxis,
746
+ tooltipProps && /* @__PURE__ */ jsx(Tooltip, { ...tooltipProps }),
747
+ legendConfig && /* @__PURE__ */ jsx(
748
+ Legend,
749
+ {
750
+ verticalAlign: legendConfig.position ?? "top",
751
+ align: legendConfig.align ?? "right",
752
+ content: /* @__PURE__ */ jsx(ChartLegendContent, {})
753
+ }
754
+ ),
755
+ series.map((s, index) => /* @__PURE__ */ jsx(
756
+ Area,
757
+ {
758
+ type: s.type ?? "monotone",
759
+ dataKey: s.dataKey,
760
+ name: s.name,
761
+ stroke: getChartColor(index, s.color),
762
+ strokeWidth: s.strokeWidth ?? 2,
763
+ fill: `url(#area-gradient-${s.dataKey})`,
764
+ fillOpacity: s.fillOpacity ?? 1,
765
+ stackId: s.stackId,
766
+ isAnimationActive: isAnimated,
767
+ animationDuration: isAnimated ? 300 : 0
768
+ },
769
+ s.dataKey
770
+ ))
771
+ ] })
772
+ }
773
+ );
774
+ }
775
+ );
776
+ AreaChart.displayName = "AreaChart";
777
+ var ScatterChart = React17.forwardRef(
778
+ ({
779
+ series,
780
+ xAxis,
781
+ yAxis,
782
+ zAxis,
783
+ grid = true,
784
+ tooltip = true,
785
+ legend = false,
786
+ animate = true,
787
+ live = false,
788
+ className,
789
+ height = "100%",
790
+ margin = { top: 8, right: 12, left: 0, bottom: 4 },
791
+ loading
792
+ }, ref) => {
793
+ const gridConfig = React17.useMemo(() => {
794
+ if (grid === false) return null;
795
+ if (grid === true) return { vertical: true, horizontal: true };
796
+ return grid;
797
+ }, [grid]);
798
+ const legendConfig = resolveLegendConfig(legend);
799
+ const tooltipProps = resolveTooltipProps(tooltip);
800
+ const isAnimated = live ? false : animate;
801
+ const hasZ = series.some((s) => s.zDataKey);
802
+ return /* @__PURE__ */ jsx(
803
+ ChartContainer,
804
+ {
805
+ ref,
806
+ className,
807
+ height,
808
+ loading,
809
+ empty: !series.some((s) => s.data?.length),
810
+ children: /* @__PURE__ */ jsxs(ScatterChart$1, { margin, children: [
811
+ gridConfig && /* @__PURE__ */ jsx(
812
+ CartesianGrid,
813
+ {
814
+ strokeDasharray: "3 3",
815
+ stroke: "hsl(var(--divider))",
816
+ vertical: gridConfig.vertical ?? true,
817
+ horizontal: gridConfig.horizontal ?? true
818
+ }
819
+ ),
820
+ /* @__PURE__ */ jsx(
821
+ XAxis,
219
822
  {
220
- type: "category",
221
823
  dataKey: xAxis.dataKey,
824
+ name: xAxis.name ?? xAxis.dataKey,
825
+ type: "number",
222
826
  tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
223
827
  tickLine: false,
224
828
  axisLine: false,
225
- width: yAxis?.width ?? 80,
226
829
  tickFormatter: xAxis.tickFormatter,
227
830
  label: xAxis.label ? {
228
831
  value: xAxis.label,
229
- angle: -90,
230
- position: "insideLeft",
832
+ position: "insideBottom",
833
+ offset: -4,
231
834
  fontSize: 11,
232
835
  fill: "hsl(var(--muted-foreground))"
233
836
  } : void 0
234
837
  }
235
- ) : /* @__PURE__ */ jsx(
838
+ ),
839
+ /* @__PURE__ */ jsx(
236
840
  YAxis,
237
841
  {
842
+ dataKey: yAxis?.domain ? void 0 : "y",
843
+ name: yAxis?.name,
844
+ type: "number",
238
845
  domain: yAxis?.domain ?? ["auto", "auto"],
239
846
  tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
240
847
  tickLine: false,
241
848
  axisLine: false,
242
- width: yAxis?.width ?? 30,
849
+ width: yAxis?.width ?? 40,
243
850
  tickFormatter: yAxis?.tickFormatter,
244
851
  label: yAxis?.label ? {
245
852
  value: yAxis.label,
@@ -250,53 +857,2277 @@ var BarChart = React.forwardRef(
250
857
  } : void 0
251
858
  }
252
859
  ),
253
- tooltip && /* @__PURE__ */ jsx(
254
- Tooltip,
255
- {
256
- contentStyle: {
257
- background: "hsl(var(--card))",
258
- border: "1px solid hsl(var(--border))",
259
- borderRadius: 6,
260
- fontSize: 11
261
- },
262
- labelStyle: {
263
- color: "hsl(var(--foreground))",
264
- fontWeight: 500,
265
- marginBottom: 4
266
- },
267
- itemStyle: {
268
- color: "hsl(var(--foreground))"
269
- },
270
- cursor: { fill: "hsl(var(--accent))", fillOpacity: 0.3 }
271
- }
272
- ),
860
+ hasZ && /* @__PURE__ */ jsx(ZAxis, { range: zAxis?.range ?? [40, 400] }),
861
+ tooltipProps && /* @__PURE__ */ jsx(Tooltip, { ...tooltipProps }),
273
862
  legendConfig && /* @__PURE__ */ jsx(
274
863
  Legend,
275
864
  {
276
865
  verticalAlign: legendConfig.position ?? "top",
277
866
  align: legendConfig.align ?? "right",
278
- wrapperStyle: { fontSize: 11, paddingBottom: 4 }
867
+ content: /* @__PURE__ */ jsx(ChartLegendContent, {})
279
868
  }
280
869
  ),
281
870
  series.map((s, index) => /* @__PURE__ */ jsx(
282
- Bar,
871
+ Scatter,
283
872
  {
284
- dataKey: s.dataKey,
285
873
  name: s.name,
874
+ data: s.data,
286
875
  fill: getChartColor(index, s.color),
287
- stackId: s.stackId,
288
- radius: s.radius ?? 0,
289
- isAnimationActive: animate
876
+ isAnimationActive: isAnimated
290
877
  },
291
- s.dataKey
878
+ s.name
292
879
  ))
293
- ]
880
+ ] })
294
881
  }
295
- ) }) });
882
+ );
296
883
  }
297
884
  );
298
- BarChart.displayName = "BarChart";
885
+ ScatterChart.displayName = "ScatterChart";
886
+ var ComposedChart = React17.forwardRef(
887
+ ({
888
+ data,
889
+ series,
890
+ xAxis,
891
+ yAxis,
892
+ yAxisRight,
893
+ grid = true,
894
+ tooltip = true,
895
+ legend = false,
896
+ animate = true,
897
+ live = false,
898
+ className,
899
+ height = "100%",
900
+ margin = { top: 8, right: 12, left: 0, bottom: 4 },
901
+ loading
902
+ }, ref) => {
903
+ const gridConfig = React17.useMemo(() => {
904
+ if (grid === false) return null;
905
+ if (grid === true) return { vertical: false, horizontal: true };
906
+ return grid;
907
+ }, [grid]);
908
+ const legendConfig = resolveLegendConfig(legend);
909
+ const tooltipProps = resolveTooltipProps(tooltip);
910
+ const isAnimated = live ? false : animate;
911
+ return /* @__PURE__ */ jsx(
912
+ ChartContainer,
913
+ {
914
+ ref,
915
+ className,
916
+ height,
917
+ loading,
918
+ empty: !data?.length,
919
+ children: /* @__PURE__ */ jsxs(ComposedChart$1, { data, margin, children: [
920
+ gridConfig && /* @__PURE__ */ jsx(
921
+ CartesianGrid,
922
+ {
923
+ strokeDasharray: "3 3",
924
+ stroke: "hsl(var(--divider))",
925
+ vertical: gridConfig.vertical ?? false,
926
+ horizontal: gridConfig.horizontal ?? true
927
+ }
928
+ ),
929
+ /* @__PURE__ */ jsx(
930
+ XAxis,
931
+ {
932
+ dataKey: xAxis.dataKey,
933
+ tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
934
+ tickLine: false,
935
+ axisLine: false,
936
+ minTickGap: xAxis.minTickGap ?? 24,
937
+ tickFormatter: xAxis.tickFormatter,
938
+ label: xAxis.label ? {
939
+ value: xAxis.label,
940
+ position: "insideBottom",
941
+ offset: -4,
942
+ fontSize: 11,
943
+ fill: "hsl(var(--muted-foreground))"
944
+ } : void 0
945
+ }
946
+ ),
947
+ /* @__PURE__ */ jsx(
948
+ YAxis,
949
+ {
950
+ yAxisId: "left",
951
+ domain: yAxis?.domain ?? ["auto", "auto"],
952
+ tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
953
+ tickLine: false,
954
+ axisLine: false,
955
+ width: yAxis?.width ?? 40,
956
+ tickFormatter: yAxis?.tickFormatter,
957
+ label: yAxis?.label ? {
958
+ value: yAxis.label,
959
+ angle: -90,
960
+ position: "insideLeft",
961
+ fontSize: 11,
962
+ fill: "hsl(var(--muted-foreground))"
963
+ } : void 0
964
+ }
965
+ ),
966
+ yAxisRight && /* @__PURE__ */ jsx(
967
+ YAxis,
968
+ {
969
+ yAxisId: yAxisRight.id,
970
+ orientation: "right",
971
+ domain: yAxisRight.domain ?? ["auto", "auto"],
972
+ tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
973
+ tickLine: false,
974
+ axisLine: false,
975
+ width: yAxisRight.width ?? 40,
976
+ tickFormatter: yAxisRight.tickFormatter,
977
+ label: yAxisRight.label ? {
978
+ value: yAxisRight.label,
979
+ angle: 90,
980
+ position: "insideRight",
981
+ fontSize: 11,
982
+ fill: "hsl(var(--muted-foreground))"
983
+ } : void 0
984
+ }
985
+ ),
986
+ tooltipProps && /* @__PURE__ */ jsx(Tooltip, { ...tooltipProps }),
987
+ legendConfig && /* @__PURE__ */ jsx(
988
+ Legend,
989
+ {
990
+ verticalAlign: legendConfig.position ?? "top",
991
+ align: legendConfig.align ?? "right",
992
+ content: /* @__PURE__ */ jsx(ChartLegendContent, {})
993
+ }
994
+ ),
995
+ series.map((s, index) => {
996
+ const color = getChartColor(index, s.color);
997
+ const yAxisId = s.yAxisId ?? "left";
998
+ const props = {
999
+ key: s.dataKey,
1000
+ dataKey: s.dataKey,
1001
+ name: s.name,
1002
+ yAxisId,
1003
+ isAnimationActive: isAnimated,
1004
+ animationDuration: isAnimated ? 300 : 0
1005
+ };
1006
+ switch (s.type) {
1007
+ case "line":
1008
+ return /* @__PURE__ */ jsx(
1009
+ Line,
1010
+ {
1011
+ ...props,
1012
+ type: "monotone",
1013
+ stroke: color,
1014
+ strokeWidth: s.strokeWidth ?? 2,
1015
+ strokeDasharray: s.strokeDasharray,
1016
+ dot: false
1017
+ }
1018
+ );
1019
+ case "bar":
1020
+ return /* @__PURE__ */ jsx(
1021
+ Bar,
1022
+ {
1023
+ ...props,
1024
+ fill: color,
1025
+ stackId: s.stackId,
1026
+ radius: s.radius ?? 0
1027
+ }
1028
+ );
1029
+ case "area":
1030
+ return /* @__PURE__ */ jsx(
1031
+ Area,
1032
+ {
1033
+ ...props,
1034
+ type: "monotone",
1035
+ stroke: color,
1036
+ fill: color,
1037
+ fillOpacity: s.fillOpacity ?? 0.15,
1038
+ strokeWidth: s.strokeWidth ?? 2,
1039
+ stackId: s.stackId
1040
+ }
1041
+ );
1042
+ }
1043
+ })
1044
+ ] })
1045
+ }
1046
+ );
1047
+ }
1048
+ );
1049
+ ComposedChart.displayName = "ComposedChart";
1050
+ function buildWaterfallData(items) {
1051
+ let running = 0;
1052
+ return items.map((item) => {
1053
+ if (item.isTotal) {
1054
+ const row = {
1055
+ name: item.name,
1056
+ invisible: 0,
1057
+ delta: running,
1058
+ isTotal: true,
1059
+ rawValue: running
1060
+ };
1061
+ return row;
1062
+ }
1063
+ const start = running;
1064
+ running += item.value;
1065
+ const base = item.value >= 0 ? start : running;
1066
+ return {
1067
+ name: item.name,
1068
+ invisible: base,
1069
+ delta: Math.abs(item.value),
1070
+ isTotal: false,
1071
+ rawValue: item.value
1072
+ };
1073
+ });
1074
+ }
1075
+ var WaterfallChart = React17.forwardRef(
1076
+ ({
1077
+ data,
1078
+ xAxis,
1079
+ yAxis,
1080
+ tooltip = true,
1081
+ positiveColor,
1082
+ negativeColor,
1083
+ totalColor,
1084
+ animate = true,
1085
+ live = false,
1086
+ className,
1087
+ height = "100%",
1088
+ margin = { top: 8, right: 12, left: 0, bottom: 4 },
1089
+ loading
1090
+ }, ref) => {
1091
+ const rows = React17.useMemo(() => buildWaterfallData(data), [data]);
1092
+ const tooltipProps = resolveTooltipProps(tooltip);
1093
+ const isAnimated = live ? false : animate;
1094
+ const posColor = positiveColor ?? getSemanticColor("positive");
1095
+ const negColor = negativeColor ?? getSemanticColor("negative");
1096
+ const totColor = totalColor ?? "hsl(var(--chart-3))";
1097
+ return /* @__PURE__ */ jsx(
1098
+ ChartContainer,
1099
+ {
1100
+ ref,
1101
+ className,
1102
+ height,
1103
+ loading,
1104
+ empty: !data?.length,
1105
+ children: /* @__PURE__ */ jsxs(BarChart$1, { data: rows, margin, children: [
1106
+ /* @__PURE__ */ jsx(
1107
+ CartesianGrid,
1108
+ {
1109
+ strokeDasharray: "3 3",
1110
+ stroke: "hsl(var(--divider))",
1111
+ vertical: false
1112
+ }
1113
+ ),
1114
+ /* @__PURE__ */ jsx(
1115
+ XAxis,
1116
+ {
1117
+ dataKey: "name",
1118
+ tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
1119
+ tickLine: false,
1120
+ axisLine: false,
1121
+ tickFormatter: xAxis?.tickFormatter
1122
+ }
1123
+ ),
1124
+ /* @__PURE__ */ jsx(
1125
+ YAxis,
1126
+ {
1127
+ domain: yAxis?.domain ?? ["auto", "auto"],
1128
+ tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
1129
+ tickLine: false,
1130
+ axisLine: false,
1131
+ width: yAxis?.width ?? 40,
1132
+ tickFormatter: yAxis?.tickFormatter,
1133
+ label: yAxis?.label ? {
1134
+ value: yAxis.label,
1135
+ angle: -90,
1136
+ position: "insideLeft",
1137
+ fontSize: 11,
1138
+ fill: "hsl(var(--muted-foreground))"
1139
+ } : void 0
1140
+ }
1141
+ ),
1142
+ /* @__PURE__ */ jsx(ReferenceLine, { y: 0, stroke: "hsl(var(--divider))" }),
1143
+ tooltipProps && /* @__PURE__ */ jsx(Tooltip, { ...tooltipProps }),
1144
+ /* @__PURE__ */ jsx(
1145
+ Bar,
1146
+ {
1147
+ dataKey: "invisible",
1148
+ stackId: "waterfall",
1149
+ fill: "transparent",
1150
+ isAnimationActive: false
1151
+ }
1152
+ ),
1153
+ /* @__PURE__ */ jsx(
1154
+ Bar,
1155
+ {
1156
+ dataKey: "delta",
1157
+ stackId: "waterfall",
1158
+ isAnimationActive: isAnimated,
1159
+ animationDuration: isAnimated ? 300 : 0,
1160
+ radius: [2, 2, 0, 0],
1161
+ children: rows.map((row, i) => /* @__PURE__ */ jsx(
1162
+ Cell,
1163
+ {
1164
+ fill: row.isTotal ? totColor : row.rawValue >= 0 ? posColor : negColor
1165
+ },
1166
+ i
1167
+ ))
1168
+ }
1169
+ )
1170
+ ] })
1171
+ }
1172
+ );
1173
+ }
1174
+ );
1175
+ WaterfallChart.displayName = "WaterfallChart";
1176
+ var RADIAN = Math.PI / 180;
1177
+ function defaultRenderLabel({
1178
+ cx,
1179
+ cy,
1180
+ midAngle,
1181
+ innerRadius,
1182
+ outerRadius,
1183
+ percent
1184
+ }) {
1185
+ const radius = innerRadius + (outerRadius - innerRadius) * 0.5;
1186
+ const x = cx + radius * Math.cos(-midAngle * RADIAN);
1187
+ const y = cy + radius * Math.sin(-midAngle * RADIAN);
1188
+ if (percent < 0.04) return null;
1189
+ return /* @__PURE__ */ jsx(
1190
+ "text",
1191
+ {
1192
+ x,
1193
+ y,
1194
+ fill: "hsl(var(--card-foreground))",
1195
+ textAnchor: "middle",
1196
+ dominantBaseline: "central",
1197
+ fontSize: 10,
1198
+ fontWeight: 600,
1199
+ children: `${(percent * 100).toFixed(0)}%`
1200
+ }
1201
+ );
1202
+ }
1203
+ var PieChart = React17.forwardRef(
1204
+ ({
1205
+ data,
1206
+ innerRadius = 0,
1207
+ outerRadius = "80%",
1208
+ paddingAngle = 2,
1209
+ startAngle = 90,
1210
+ endAngle = -270,
1211
+ legend = false,
1212
+ tooltip = true,
1213
+ label = false,
1214
+ animate = true,
1215
+ live = false,
1216
+ className,
1217
+ height = "100%",
1218
+ margin = { top: 8, right: 8, left: 8, bottom: 8 },
1219
+ loading
1220
+ }, ref) => {
1221
+ const legendConfig = resolveLegendConfig(legend);
1222
+ const tooltipProps = resolveTooltipProps(tooltip);
1223
+ const isAnimated = live ? false : animate;
1224
+ const renderLabel = label === true ? defaultRenderLabel : typeof label === "function" ? (entry) => label(entry) : void 0;
1225
+ return /* @__PURE__ */ jsx(
1226
+ ChartContainer,
1227
+ {
1228
+ ref,
1229
+ className,
1230
+ height,
1231
+ loading,
1232
+ empty: !data?.length,
1233
+ children: /* @__PURE__ */ jsxs(PieChart$1, { margin, children: [
1234
+ /* @__PURE__ */ jsx(
1235
+ Pie,
1236
+ {
1237
+ data,
1238
+ dataKey: "value",
1239
+ nameKey: "name",
1240
+ cx: "50%",
1241
+ cy: "50%",
1242
+ innerRadius,
1243
+ outerRadius,
1244
+ paddingAngle,
1245
+ startAngle,
1246
+ endAngle,
1247
+ isAnimationActive: isAnimated,
1248
+ animationDuration: isAnimated ? 400 : 0,
1249
+ label: renderLabel,
1250
+ labelLine: false,
1251
+ stroke: "hsl(var(--card))",
1252
+ strokeWidth: 2,
1253
+ children: data.map((entry, index) => /* @__PURE__ */ jsx(
1254
+ Cell,
1255
+ {
1256
+ fill: getChartColor(index, entry.color)
1257
+ },
1258
+ entry.name
1259
+ ))
1260
+ }
1261
+ ),
1262
+ tooltipProps && /* @__PURE__ */ jsx(Tooltip, { ...tooltipProps }),
1263
+ legendConfig && /* @__PURE__ */ jsx(
1264
+ Legend,
1265
+ {
1266
+ verticalAlign: legendConfig.position ?? "bottom",
1267
+ align: legendConfig.align ?? "center",
1268
+ content: /* @__PURE__ */ jsx(ChartLegendContent, {})
1269
+ }
1270
+ )
1271
+ ] })
1272
+ }
1273
+ );
1274
+ }
1275
+ );
1276
+ PieChart.displayName = "PieChart";
1277
+ var DonutChart = React17.forwardRef(
1278
+ ({
1279
+ data,
1280
+ innerRadius = "60%",
1281
+ outerRadius = "80%",
1282
+ paddingAngle = 2,
1283
+ legend = false,
1284
+ tooltip = true,
1285
+ centerLabel,
1286
+ centerValue,
1287
+ animate = true,
1288
+ live = false,
1289
+ className,
1290
+ height = "100%",
1291
+ margin = { top: 8, right: 8, left: 8, bottom: 8 },
1292
+ loading
1293
+ }, ref) => {
1294
+ const legendConfig = resolveLegendConfig(legend);
1295
+ const tooltipProps = resolveTooltipProps(tooltip);
1296
+ const isAnimated = live ? false : animate;
1297
+ return /* @__PURE__ */ jsx(
1298
+ ChartContainer,
1299
+ {
1300
+ ref,
1301
+ className,
1302
+ height,
1303
+ loading,
1304
+ empty: !data?.length,
1305
+ children: /* @__PURE__ */ jsxs(PieChart$1, { margin, children: [
1306
+ /* @__PURE__ */ jsx(
1307
+ Pie,
1308
+ {
1309
+ data,
1310
+ dataKey: "value",
1311
+ nameKey: "name",
1312
+ cx: "50%",
1313
+ cy: "50%",
1314
+ innerRadius,
1315
+ outerRadius,
1316
+ paddingAngle,
1317
+ startAngle: 90,
1318
+ endAngle: -270,
1319
+ isAnimationActive: isAnimated,
1320
+ animationDuration: isAnimated ? 400 : 0,
1321
+ stroke: "hsl(var(--card))",
1322
+ strokeWidth: 2,
1323
+ children: data.map((entry, index) => /* @__PURE__ */ jsx(
1324
+ Cell,
1325
+ {
1326
+ fill: getChartColor(index, entry.color)
1327
+ },
1328
+ entry.name
1329
+ ))
1330
+ }
1331
+ ),
1332
+ (centerLabel || centerValue) && /* @__PURE__ */ jsxs(
1333
+ "text",
1334
+ {
1335
+ x: "50%",
1336
+ y: "50%",
1337
+ textAnchor: "middle",
1338
+ dominantBaseline: "central",
1339
+ children: [
1340
+ centerValue && /* @__PURE__ */ jsx(
1341
+ "tspan",
1342
+ {
1343
+ x: "50%",
1344
+ dy: centerLabel ? "-0.5em" : "0",
1345
+ fontSize: 20,
1346
+ fontWeight: 700,
1347
+ fill: "hsl(var(--foreground))",
1348
+ children: centerValue
1349
+ }
1350
+ ),
1351
+ centerLabel && /* @__PURE__ */ jsx(
1352
+ "tspan",
1353
+ {
1354
+ x: "50%",
1355
+ dy: centerValue ? "1.4em" : "0",
1356
+ fontSize: 11,
1357
+ fill: "hsl(var(--muted-foreground))",
1358
+ children: centerLabel
1359
+ }
1360
+ )
1361
+ ]
1362
+ }
1363
+ ),
1364
+ tooltipProps && /* @__PURE__ */ jsx(Tooltip, { ...tooltipProps }),
1365
+ legendConfig && /* @__PURE__ */ jsx(
1366
+ Legend,
1367
+ {
1368
+ verticalAlign: legendConfig.position ?? "bottom",
1369
+ align: legendConfig.align ?? "center",
1370
+ content: /* @__PURE__ */ jsx(ChartLegendContent, {})
1371
+ }
1372
+ )
1373
+ ] })
1374
+ }
1375
+ );
1376
+ }
1377
+ );
1378
+ DonutChart.displayName = "DonutChart";
1379
+ var RadarChart = React17.forwardRef(
1380
+ ({
1381
+ data,
1382
+ series,
1383
+ categoryKey,
1384
+ domain,
1385
+ legend = false,
1386
+ tooltip = true,
1387
+ animate = true,
1388
+ live = false,
1389
+ className,
1390
+ height = "100%",
1391
+ margin = { top: 8, right: 8, left: 8, bottom: 8 },
1392
+ loading
1393
+ }, ref) => {
1394
+ const legendConfig = resolveLegendConfig(legend);
1395
+ const tooltipProps = resolveTooltipProps(tooltip);
1396
+ const isAnimated = live ? false : animate;
1397
+ return /* @__PURE__ */ jsx(
1398
+ ChartContainer,
1399
+ {
1400
+ ref,
1401
+ className,
1402
+ height,
1403
+ loading,
1404
+ empty: !data?.length,
1405
+ children: /* @__PURE__ */ jsxs(RadarChart$1, { data, margin, cx: "50%", cy: "50%", children: [
1406
+ /* @__PURE__ */ jsx(PolarGrid, { stroke: "hsl(var(--divider))" }),
1407
+ /* @__PURE__ */ jsx(
1408
+ PolarAngleAxis,
1409
+ {
1410
+ dataKey: categoryKey,
1411
+ tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" }
1412
+ }
1413
+ ),
1414
+ /* @__PURE__ */ jsx(
1415
+ PolarRadiusAxis,
1416
+ {
1417
+ domain,
1418
+ tick: { fontSize: 9, fill: "hsl(var(--muted-foreground))" },
1419
+ axisLine: false
1420
+ }
1421
+ ),
1422
+ tooltipProps && /* @__PURE__ */ jsx(Tooltip, { ...tooltipProps }),
1423
+ legendConfig && /* @__PURE__ */ jsx(
1424
+ Legend,
1425
+ {
1426
+ verticalAlign: legendConfig.position ?? "bottom",
1427
+ align: legendConfig.align ?? "center",
1428
+ content: /* @__PURE__ */ jsx(ChartLegendContent, {})
1429
+ }
1430
+ ),
1431
+ series.map((s, index) => {
1432
+ const color = getChartColor(index, s.color);
1433
+ return /* @__PURE__ */ jsx(
1434
+ Radar,
1435
+ {
1436
+ name: s.name,
1437
+ dataKey: s.dataKey,
1438
+ stroke: color,
1439
+ strokeWidth: s.strokeWidth ?? 2,
1440
+ strokeDasharray: s.strokeDasharray,
1441
+ fill: color,
1442
+ fillOpacity: s.fillOpacity ?? 0.15,
1443
+ isAnimationActive: isAnimated,
1444
+ animationDuration: isAnimated ? 400 : 0
1445
+ },
1446
+ s.dataKey
1447
+ );
1448
+ })
1449
+ ] })
1450
+ }
1451
+ );
1452
+ }
1453
+ );
1454
+ RadarChart.displayName = "RadarChart";
1455
+ var RadialBarChart = React17.forwardRef(
1456
+ ({
1457
+ data,
1458
+ innerRadius = "30%",
1459
+ outerRadius = "90%",
1460
+ startAngle = 180,
1461
+ endAngle = 0,
1462
+ legend = false,
1463
+ tooltip = true,
1464
+ showLabels = true,
1465
+ animate = true,
1466
+ live = false,
1467
+ className,
1468
+ height = "100%",
1469
+ margin = { top: 8, right: 8, left: 8, bottom: 8 },
1470
+ loading
1471
+ }, ref) => {
1472
+ const legendConfig = resolveLegendConfig(legend);
1473
+ const tooltipProps = resolveTooltipProps(tooltip);
1474
+ const isAnimated = live ? false : animate;
1475
+ const coloredData = React17.useMemo(
1476
+ () => data.map((d, i) => ({
1477
+ ...d,
1478
+ fill: d.fill ?? getChartColor(i)
1479
+ })),
1480
+ [data]
1481
+ );
1482
+ return /* @__PURE__ */ jsx(
1483
+ ChartContainer,
1484
+ {
1485
+ ref,
1486
+ className,
1487
+ height,
1488
+ loading,
1489
+ empty: !data?.length,
1490
+ children: /* @__PURE__ */ jsxs(
1491
+ RadialBarChart$1,
1492
+ {
1493
+ data: coloredData,
1494
+ innerRadius,
1495
+ outerRadius,
1496
+ startAngle,
1497
+ endAngle,
1498
+ margin,
1499
+ cx: "50%",
1500
+ cy: "50%",
1501
+ children: [
1502
+ /* @__PURE__ */ jsx(
1503
+ RadialBar,
1504
+ {
1505
+ dataKey: "value",
1506
+ background: { fill: "hsl(var(--muted))" },
1507
+ isAnimationActive: isAnimated,
1508
+ animationDuration: isAnimated ? 400 : 0,
1509
+ label: showLabels ? {
1510
+ position: "insideStart",
1511
+ fill: "hsl(var(--foreground))",
1512
+ fontSize: 10,
1513
+ fontWeight: 600
1514
+ } : false
1515
+ }
1516
+ ),
1517
+ tooltipProps && /* @__PURE__ */ jsx(Tooltip, { ...tooltipProps }),
1518
+ legendConfig && /* @__PURE__ */ jsx(
1519
+ Legend,
1520
+ {
1521
+ verticalAlign: legendConfig.position ?? "bottom",
1522
+ align: legendConfig.align ?? "center",
1523
+ content: /* @__PURE__ */ jsx(ChartLegendContent, {})
1524
+ }
1525
+ )
1526
+ ]
1527
+ }
1528
+ )
1529
+ }
1530
+ );
1531
+ }
1532
+ );
1533
+ RadialBarChart.displayName = "RadialBarChart";
1534
+ function SankeyNodeRenderer({
1535
+ x,
1536
+ y,
1537
+ width,
1538
+ height,
1539
+ index,
1540
+ payload
1541
+ }) {
1542
+ const fill = payload.color ?? getChartColor(index);
1543
+ return /* @__PURE__ */ jsxs(Layer, { children: [
1544
+ /* @__PURE__ */ jsx(
1545
+ Rectangle,
1546
+ {
1547
+ x,
1548
+ y,
1549
+ width,
1550
+ height,
1551
+ fill,
1552
+ fillOpacity: 0.9
1553
+ }
1554
+ ),
1555
+ /* @__PURE__ */ jsx(
1556
+ "text",
1557
+ {
1558
+ x: x + width + 6,
1559
+ y: y + height / 2,
1560
+ textAnchor: "start",
1561
+ dominantBaseline: "central",
1562
+ fontSize: 10,
1563
+ fill: "hsl(var(--foreground))",
1564
+ children: payload.name
1565
+ }
1566
+ )
1567
+ ] }, `node-${index}`);
1568
+ }
1569
+ function SankeyLinkRenderer({
1570
+ sourceX,
1571
+ sourceY,
1572
+ sourceControlX,
1573
+ targetX,
1574
+ targetY,
1575
+ targetControlX,
1576
+ linkWidth,
1577
+ index,
1578
+ payload
1579
+ }) {
1580
+ const sourceColor = payload.source.color ?? getChartColor(index);
1581
+ return /* @__PURE__ */ jsx(Layer, { children: /* @__PURE__ */ jsx(
1582
+ "path",
1583
+ {
1584
+ d: `
1585
+ M${sourceX},${sourceY}
1586
+ C${sourceControlX},${sourceY} ${targetControlX},${targetY} ${targetX},${targetY}
1587
+ `,
1588
+ fill: "none",
1589
+ stroke: sourceColor,
1590
+ strokeWidth: linkWidth,
1591
+ strokeOpacity: 0.3
1592
+ }
1593
+ ) }, `link-${index}`);
1594
+ }
1595
+ var SankeyChart = React17.forwardRef(
1596
+ ({
1597
+ data,
1598
+ nodeWidth = 10,
1599
+ nodePadding = 20,
1600
+ tooltip = true,
1601
+ className,
1602
+ height = "100%",
1603
+ margin = { top: 8, right: 80, left: 8, bottom: 8 },
1604
+ loading
1605
+ }, ref) => {
1606
+ const tooltipProps = resolveTooltipProps(tooltip);
1607
+ return /* @__PURE__ */ jsx(
1608
+ ChartContainer,
1609
+ {
1610
+ ref,
1611
+ className,
1612
+ height,
1613
+ loading,
1614
+ empty: !data?.nodes?.length,
1615
+ children: /* @__PURE__ */ jsx(
1616
+ Sankey,
1617
+ {
1618
+ data,
1619
+ nodeWidth,
1620
+ nodePadding,
1621
+ margin,
1622
+ node: SankeyNodeRenderer,
1623
+ link: SankeyLinkRenderer,
1624
+ children: tooltipProps && /* @__PURE__ */ jsx(Tooltip, { ...tooltipProps })
1625
+ }
1626
+ )
1627
+ }
1628
+ );
1629
+ }
1630
+ );
1631
+ SankeyChart.displayName = "SankeyChart";
1632
+ function TreemapContent({
1633
+ x,
1634
+ y,
1635
+ width,
1636
+ height,
1637
+ index,
1638
+ name,
1639
+ depth,
1640
+ color
1641
+ }) {
1642
+ const fill = color ?? getChartColor(index);
1643
+ const showLabel = width > 40 && height > 20;
1644
+ return /* @__PURE__ */ jsxs("g", { children: [
1645
+ /* @__PURE__ */ jsx(
1646
+ "rect",
1647
+ {
1648
+ x,
1649
+ y,
1650
+ width,
1651
+ height,
1652
+ fill,
1653
+ fillOpacity: depth === 1 ? 0.85 : 0.6,
1654
+ stroke: "hsl(var(--card))",
1655
+ strokeWidth: 2,
1656
+ rx: 2
1657
+ }
1658
+ ),
1659
+ showLabel && /* @__PURE__ */ jsx(
1660
+ "text",
1661
+ {
1662
+ x: x + width / 2,
1663
+ y: y + height / 2,
1664
+ textAnchor: "middle",
1665
+ dominantBaseline: "central",
1666
+ fontSize: Math.min(11, width / 6),
1667
+ fill: "hsl(var(--card-foreground))",
1668
+ fontWeight: 500,
1669
+ style: { pointerEvents: "none" },
1670
+ children: name.length > width / 7 ? `${name.slice(0, Math.floor(width / 7))}\u2026` : name
1671
+ }
1672
+ )
1673
+ ] });
1674
+ }
1675
+ var TreemapChart = React17.forwardRef(
1676
+ ({
1677
+ data,
1678
+ dataKey = "value",
1679
+ aspectRatio = 4 / 3,
1680
+ tooltip = true,
1681
+ animate = true,
1682
+ live = false,
1683
+ className,
1684
+ height = "100%",
1685
+ margin,
1686
+ loading
1687
+ }, ref) => {
1688
+ const tooltipProps = resolveTooltipProps(tooltip);
1689
+ const isAnimated = live ? false : animate;
1690
+ return /* @__PURE__ */ jsx(
1691
+ ChartContainer,
1692
+ {
1693
+ ref,
1694
+ className,
1695
+ height,
1696
+ loading,
1697
+ empty: !data?.length,
1698
+ children: /* @__PURE__ */ jsx(
1699
+ Treemap,
1700
+ {
1701
+ data,
1702
+ dataKey,
1703
+ aspectRatio,
1704
+ isAnimationActive: isAnimated,
1705
+ animationDuration: isAnimated ? 400 : 0,
1706
+ content: /* @__PURE__ */ jsx(TreemapContent, { x: 0, y: 0, width: 0, height: 0, index: 0, name: "", depth: 1 }),
1707
+ children: tooltipProps && /* @__PURE__ */ jsx(Tooltip, { ...tooltipProps })
1708
+ }
1709
+ )
1710
+ }
1711
+ );
1712
+ }
1713
+ );
1714
+ TreemapChart.displayName = "TreemapChart";
1715
+ var FunnelChart = React17.forwardRef(
1716
+ ({
1717
+ data,
1718
+ tooltip = true,
1719
+ showLabels = true,
1720
+ animate = true,
1721
+ live = false,
1722
+ className,
1723
+ height = "100%",
1724
+ margin = { top: 8, right: 8, left: 8, bottom: 8 },
1725
+ loading
1726
+ }, ref) => {
1727
+ const tooltipProps = resolveTooltipProps(tooltip);
1728
+ const isAnimated = live ? false : animate;
1729
+ return /* @__PURE__ */ jsx(
1730
+ ChartContainer,
1731
+ {
1732
+ ref,
1733
+ className,
1734
+ height,
1735
+ loading,
1736
+ empty: !data?.length,
1737
+ children: /* @__PURE__ */ jsxs(FunnelChart$1, { margin, children: [
1738
+ /* @__PURE__ */ jsxs(
1739
+ Funnel,
1740
+ {
1741
+ dataKey: "value",
1742
+ data,
1743
+ isAnimationActive: isAnimated,
1744
+ animationDuration: isAnimated ? 400 : 0,
1745
+ children: [
1746
+ data.map((entry, index) => /* @__PURE__ */ jsx(
1747
+ Cell,
1748
+ {
1749
+ fill: getChartColor(index, entry.color),
1750
+ stroke: "hsl(var(--card))",
1751
+ strokeWidth: 2
1752
+ },
1753
+ entry.name
1754
+ )),
1755
+ showLabels && /* @__PURE__ */ jsx(
1756
+ LabelList,
1757
+ {
1758
+ dataKey: "name",
1759
+ position: "center",
1760
+ fill: "#ffffff",
1761
+ fontSize: 13,
1762
+ fontWeight: 600,
1763
+ style: {
1764
+ textShadow: "0 1px 3px rgba(0,0,0,0.4)",
1765
+ paintOrder: "stroke",
1766
+ stroke: "rgba(0,0,0,0.2)",
1767
+ strokeWidth: 2,
1768
+ strokeLinejoin: "round"
1769
+ }
1770
+ }
1771
+ )
1772
+ ]
1773
+ }
1774
+ ),
1775
+ tooltipProps && /* @__PURE__ */ jsx(Tooltip, { ...tooltipProps })
1776
+ ] })
1777
+ }
1778
+ );
1779
+ }
1780
+ );
1781
+ FunnelChart.displayName = "FunnelChart";
1782
+ var ROW_HEIGHT_DEFAULT = 36;
1783
+ var LABEL_WIDTH_DEFAULT = 160;
1784
+ var HEADER_HEIGHT = 32;
1785
+ function getTimeBounds(tasks, milestones) {
1786
+ let min = Infinity;
1787
+ let max = -Infinity;
1788
+ for (const t of tasks) {
1789
+ if (t.start.getTime() < min) min = t.start.getTime();
1790
+ if (t.end.getTime() > max) max = t.end.getTime();
1791
+ }
1792
+ for (const m of milestones) {
1793
+ if (m.date.getTime() < min) min = m.date.getTime();
1794
+ if (m.date.getTime() > max) max = m.date.getTime();
1795
+ }
1796
+ const pad = (max - min) * 0.05 || 864e5;
1797
+ return { min: min - pad, max: max + pad, span: max - min + 2 * pad };
1798
+ }
1799
+ function generateTicks(min, max, scale) {
1800
+ const ticks = [];
1801
+ const d = new Date(min);
1802
+ if (scale === "day") {
1803
+ d.setHours(0, 0, 0, 0);
1804
+ while (d.getTime() <= max) {
1805
+ ticks.push({
1806
+ pos: d.getTime(),
1807
+ label: d.toLocaleDateString(void 0, { month: "short", day: "numeric" })
1808
+ });
1809
+ d.setDate(d.getDate() + 1);
1810
+ }
1811
+ } else if (scale === "week") {
1812
+ d.setHours(0, 0, 0, 0);
1813
+ d.setDate(d.getDate() - d.getDay());
1814
+ while (d.getTime() <= max) {
1815
+ ticks.push({
1816
+ pos: d.getTime(),
1817
+ label: d.toLocaleDateString(void 0, { month: "short", day: "numeric" })
1818
+ });
1819
+ d.setDate(d.getDate() + 7);
1820
+ }
1821
+ } else {
1822
+ d.setDate(1);
1823
+ d.setHours(0, 0, 0, 0);
1824
+ while (d.getTime() <= max) {
1825
+ ticks.push({
1826
+ pos: d.getTime(),
1827
+ label: d.toLocaleDateString(void 0, { month: "short", year: "2-digit" })
1828
+ });
1829
+ d.setMonth(d.getMonth() + 1);
1830
+ }
1831
+ }
1832
+ return ticks;
1833
+ }
1834
+ var GanttChart = React17.forwardRef(
1835
+ function GanttChart2({
1836
+ tasks,
1837
+ milestones = [],
1838
+ timeScale = "day",
1839
+ className,
1840
+ height = 400,
1841
+ rowHeight = ROW_HEIGHT_DEFAULT,
1842
+ labelWidth = LABEL_WIDTH_DEFAULT,
1843
+ onTaskClick
1844
+ }, ref) {
1845
+ const containerRef = React17.useRef(null);
1846
+ const [hoveredId, setHoveredId] = React17.useState(null);
1847
+ const [tooltipInfo, setTooltipInfo] = React17.useState(null);
1848
+ const groups = React17.useMemo(() => {
1849
+ const map = /* @__PURE__ */ new Map();
1850
+ for (const t of tasks) {
1851
+ const g = t.group ?? "";
1852
+ if (!map.has(g)) map.set(g, []);
1853
+ map.get(g).push(t);
1854
+ }
1855
+ return map;
1856
+ }, [tasks]);
1857
+ const orderedTasks = React17.useMemo(() => {
1858
+ const result = [];
1859
+ for (const group of groups.values()) {
1860
+ result.push(...group.sort((a, b) => a.start.getTime() - b.start.getTime()));
1861
+ }
1862
+ return result;
1863
+ }, [groups]);
1864
+ const taskIndex = React17.useMemo(() => {
1865
+ const m = /* @__PURE__ */ new Map();
1866
+ orderedTasks.forEach((t, i) => m.set(t.id, i));
1867
+ return m;
1868
+ }, [orderedTasks]);
1869
+ const bounds = React17.useMemo(
1870
+ () => getTimeBounds(tasks, milestones),
1871
+ [tasks, milestones]
1872
+ );
1873
+ const ticks = React17.useMemo(
1874
+ () => generateTicks(bounds.min, bounds.max, timeScale),
1875
+ [bounds, timeScale]
1876
+ );
1877
+ const chartHeight = typeof height === "number" ? height : void 0;
1878
+ const svgHeight = HEADER_HEIGHT + orderedTasks.length * rowHeight + 8;
1879
+ function xOf(time, chartWidth) {
1880
+ return (time - bounds.min) / bounds.span * chartWidth;
1881
+ }
1882
+ return /* @__PURE__ */ jsxs(
1883
+ "div",
1884
+ {
1885
+ ref: (node) => {
1886
+ containerRef.current = node;
1887
+ if (typeof ref === "function") ref(node);
1888
+ else if (ref) ref.current = node;
1889
+ },
1890
+ className: cn("w-full overflow-auto relative", className),
1891
+ style: { height },
1892
+ children: [
1893
+ /* @__PURE__ */ jsxs(
1894
+ "svg",
1895
+ {
1896
+ width: "100%",
1897
+ height: Math.max(svgHeight, chartHeight ?? svgHeight),
1898
+ style: { minWidth: labelWidth + 400 },
1899
+ children: [
1900
+ /* @__PURE__ */ jsx(
1901
+ "rect",
1902
+ {
1903
+ x: 0,
1904
+ y: 0,
1905
+ width: labelWidth,
1906
+ height: svgHeight,
1907
+ fill: "hsl(var(--card))"
1908
+ }
1909
+ ),
1910
+ /* @__PURE__ */ jsx(
1911
+ "line",
1912
+ {
1913
+ x1: labelWidth,
1914
+ y1: 0,
1915
+ x2: labelWidth,
1916
+ y2: svgHeight,
1917
+ stroke: "hsl(var(--border))"
1918
+ }
1919
+ ),
1920
+ /* @__PURE__ */ jsx("g", { children: ticks.map((tick, i) => {
1921
+ const x = labelWidth + xOf(tick.pos, 1e3);
1922
+ return /* @__PURE__ */ jsxs("g", { children: [
1923
+ /* @__PURE__ */ jsx(
1924
+ "line",
1925
+ {
1926
+ x1: x,
1927
+ y1: HEADER_HEIGHT,
1928
+ x2: x,
1929
+ y2: svgHeight,
1930
+ stroke: "hsl(var(--divider))",
1931
+ strokeDasharray: "2 4"
1932
+ }
1933
+ ),
1934
+ /* @__PURE__ */ jsx(
1935
+ "text",
1936
+ {
1937
+ x: x + 4,
1938
+ y: HEADER_HEIGHT - 10,
1939
+ fontSize: 9,
1940
+ fill: "hsl(var(--muted-foreground))",
1941
+ children: tick.label
1942
+ }
1943
+ )
1944
+ ] }, i);
1945
+ }) }),
1946
+ orderedTasks.map((task, i) => {
1947
+ const y = HEADER_HEIGHT + i * rowHeight;
1948
+ const barX = labelWidth + xOf(task.start.getTime(), 1e3);
1949
+ const barW = xOf(task.end.getTime(), 1e3) - xOf(task.start.getTime(), 1e3);
1950
+ const barH = rowHeight * 0.55;
1951
+ const barY = y + (rowHeight - barH) / 2;
1952
+ const color = task.color ?? getChartColor(i);
1953
+ const isHovered = hoveredId === task.id;
1954
+ const progress = task.progress ?? 0;
1955
+ return /* @__PURE__ */ jsxs(
1956
+ "g",
1957
+ {
1958
+ onMouseEnter: (e) => {
1959
+ setHoveredId(task.id);
1960
+ const rect = containerRef.current?.getBoundingClientRect();
1961
+ setTooltipInfo({
1962
+ task,
1963
+ x: e.clientX - (rect?.left ?? 0) + (containerRef.current?.scrollLeft ?? 0),
1964
+ y: e.clientY - (rect?.top ?? 0) + (containerRef.current?.scrollTop ?? 0)
1965
+ });
1966
+ },
1967
+ onMouseLeave: () => {
1968
+ setHoveredId(null);
1969
+ setTooltipInfo(null);
1970
+ },
1971
+ onClick: () => onTaskClick?.(task),
1972
+ style: { cursor: onTaskClick ? "pointer" : "default" },
1973
+ children: [
1974
+ i % 2 === 0 && /* @__PURE__ */ jsx(
1975
+ "rect",
1976
+ {
1977
+ x: 0,
1978
+ y,
1979
+ width: "100%",
1980
+ height: rowHeight,
1981
+ fill: "hsl(var(--muted))",
1982
+ fillOpacity: 0.3
1983
+ }
1984
+ ),
1985
+ /* @__PURE__ */ jsx(
1986
+ "text",
1987
+ {
1988
+ x: 12,
1989
+ y: y + rowHeight / 2,
1990
+ dominantBaseline: "central",
1991
+ fontSize: 11,
1992
+ fill: "hsl(var(--foreground))",
1993
+ fontWeight: isHovered ? 600 : 400,
1994
+ children: task.name.length > labelWidth / 8 ? `${task.name.slice(0, Math.floor(labelWidth / 8))}\u2026` : task.name
1995
+ }
1996
+ ),
1997
+ /* @__PURE__ */ jsx(
1998
+ "rect",
1999
+ {
2000
+ x: barX,
2001
+ y: barY,
2002
+ width: Math.max(barW, 2),
2003
+ height: barH,
2004
+ rx: 3,
2005
+ fill: color,
2006
+ fillOpacity: 0.25,
2007
+ stroke: isHovered ? color : "none",
2008
+ strokeWidth: isHovered ? 1.5 : 0
2009
+ }
2010
+ ),
2011
+ progress > 0 && /* @__PURE__ */ jsx(
2012
+ "rect",
2013
+ {
2014
+ x: barX,
2015
+ y: barY,
2016
+ width: Math.max(barW * progress / 100, 2),
2017
+ height: barH,
2018
+ rx: 3,
2019
+ fill: color,
2020
+ fillOpacity: 0.85
2021
+ }
2022
+ )
2023
+ ]
2024
+ },
2025
+ task.id
2026
+ );
2027
+ }),
2028
+ orderedTasks.map(
2029
+ (task) => (task.dependencies ?? []).map((depId) => {
2030
+ const fromIdx = taskIndex.get(depId);
2031
+ if (fromIdx === void 0) return null;
2032
+ const toIdx = taskIndex.get(task.id);
2033
+ const fromTask = orderedTasks[fromIdx];
2034
+ const fromEndX = labelWidth + xOf(fromTask.end.getTime(), 1e3);
2035
+ const fromY = HEADER_HEIGHT + fromIdx * rowHeight + rowHeight / 2;
2036
+ const toStartX = labelWidth + xOf(task.start.getTime(), 1e3);
2037
+ const toY = HEADER_HEIGHT + toIdx * rowHeight + rowHeight / 2;
2038
+ const midX = (fromEndX + toStartX) / 2;
2039
+ return /* @__PURE__ */ jsx("g", { children: /* @__PURE__ */ jsx(
2040
+ "path",
2041
+ {
2042
+ d: `M${fromEndX},${fromY} C${midX},${fromY} ${midX},${toY} ${toStartX},${toY}`,
2043
+ fill: "none",
2044
+ stroke: "hsl(var(--muted-foreground))",
2045
+ strokeWidth: 1,
2046
+ strokeDasharray: "3 2",
2047
+ markerEnd: "url(#arrow)"
2048
+ }
2049
+ ) }, `${depId}-${task.id}`);
2050
+ })
2051
+ ),
2052
+ milestones.map((m, i) => {
2053
+ const x = labelWidth + xOf(m.date.getTime(), 1e3);
2054
+ return /* @__PURE__ */ jsxs("g", { children: [
2055
+ /* @__PURE__ */ jsx(
2056
+ "line",
2057
+ {
2058
+ x1: x,
2059
+ y1: HEADER_HEIGHT,
2060
+ x2: x,
2061
+ y2: svgHeight,
2062
+ stroke: m.color ?? "hsl(var(--warning))",
2063
+ strokeWidth: 1.5,
2064
+ strokeDasharray: "4 3"
2065
+ }
2066
+ ),
2067
+ /* @__PURE__ */ jsx(
2068
+ "polygon",
2069
+ {
2070
+ points: `${x},${HEADER_HEIGHT - 2} ${x + 5},${HEADER_HEIGHT + 6} ${x},${HEADER_HEIGHT + 14} ${x - 5},${HEADER_HEIGHT + 6}`,
2071
+ fill: m.color ?? "hsl(var(--warning))"
2072
+ }
2073
+ )
2074
+ ] }, m.id);
2075
+ }),
2076
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
2077
+ "marker",
2078
+ {
2079
+ id: "arrow",
2080
+ viewBox: "0 0 6 6",
2081
+ refX: 6,
2082
+ refY: 3,
2083
+ markerWidth: 6,
2084
+ markerHeight: 6,
2085
+ orient: "auto-start-reverse",
2086
+ children: /* @__PURE__ */ jsx(
2087
+ "path",
2088
+ {
2089
+ d: "M 0 0 L 6 3 L 0 6 z",
2090
+ fill: "hsl(var(--muted-foreground))"
2091
+ }
2092
+ )
2093
+ }
2094
+ ) })
2095
+ ]
2096
+ }
2097
+ ),
2098
+ tooltipInfo && /* @__PURE__ */ jsxs(
2099
+ "div",
2100
+ {
2101
+ style: {
2102
+ position: "absolute",
2103
+ left: tooltipInfo.x + 12,
2104
+ top: tooltipInfo.y - 10,
2105
+ background: "hsl(var(--card))",
2106
+ border: "1px solid hsl(var(--border))",
2107
+ borderRadius: 6,
2108
+ padding: "6px 10px",
2109
+ fontSize: 11,
2110
+ lineHeight: 1.5,
2111
+ boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
2112
+ pointerEvents: "none",
2113
+ zIndex: 50,
2114
+ maxWidth: 220
2115
+ },
2116
+ children: [
2117
+ /* @__PURE__ */ jsx(
2118
+ "div",
2119
+ {
2120
+ style: {
2121
+ fontWeight: 600,
2122
+ color: "hsl(var(--foreground))",
2123
+ marginBottom: 2
2124
+ },
2125
+ children: tooltipInfo.task.name
2126
+ }
2127
+ ),
2128
+ /* @__PURE__ */ jsxs("div", { style: { color: "hsl(var(--muted-foreground))" }, children: [
2129
+ tooltipInfo.task.start.toLocaleDateString(),
2130
+ " \u2013",
2131
+ " ",
2132
+ tooltipInfo.task.end.toLocaleDateString()
2133
+ ] }),
2134
+ tooltipInfo.task.progress != null && /* @__PURE__ */ jsxs("div", { style: { color: "hsl(var(--muted-foreground))" }, children: [
2135
+ "Progress: ",
2136
+ tooltipInfo.task.progress,
2137
+ "%"
2138
+ ] })
2139
+ ]
2140
+ }
2141
+ )
2142
+ ]
2143
+ }
2144
+ );
2145
+ }
2146
+ );
2147
+ GanttChart.displayName = "GanttChart";
2148
+ var DEFAULT_SCALE = {
2149
+ min: "hsl(var(--chart-9))",
2150
+ mid: "hsl(var(--chart-4))",
2151
+ max: "hsl(var(--chart-7))"
2152
+ };
2153
+ function interpolateColor(t, scale) {
2154
+ if (!scale.mid) {
2155
+ return `color-mix(in srgb, ${scale.min} ${(1 - t) * 100}%, ${scale.max})`;
2156
+ }
2157
+ if (t <= 0.5) {
2158
+ const t22 = t * 2;
2159
+ return `color-mix(in srgb, ${scale.min} ${(1 - t22) * 100}%, ${scale.mid})`;
2160
+ }
2161
+ const t2 = (t - 0.5) * 2;
2162
+ return `color-mix(in srgb, ${scale.mid} ${(1 - t2) * 100}%, ${scale.max})`;
2163
+ }
2164
+ var HeatmapChart = React17.forwardRef(
2165
+ function HeatmapChart2({
2166
+ data,
2167
+ xLabels,
2168
+ yLabels,
2169
+ colorScale = DEFAULT_SCALE,
2170
+ cellSize = 40,
2171
+ showValues = true,
2172
+ valueFormatter,
2173
+ className,
2174
+ height,
2175
+ loading,
2176
+ onCellClick
2177
+ }, ref) {
2178
+ const containerRef = React17.useRef(null);
2179
+ const [hoveredCell, setHoveredCell] = React17.useState(null);
2180
+ const [tooltipPos, setTooltipPos] = React17.useState({ x: 0, y: 0 });
2181
+ const { valueMap, minVal, maxVal } = React17.useMemo(() => {
2182
+ const map = /* @__PURE__ */ new Map();
2183
+ let lo = Infinity;
2184
+ let hi = -Infinity;
2185
+ for (const cell of data) {
2186
+ map.set(`${cell.x}|${cell.y}`, cell.value);
2187
+ if (cell.value < lo) lo = cell.value;
2188
+ if (cell.value > hi) hi = cell.value;
2189
+ }
2190
+ return { valueMap: map, minVal: lo, maxVal: hi };
2191
+ }, [data]);
2192
+ const normalize = (v) => maxVal === minVal ? 0.5 : (v - minVal) / (maxVal - minVal);
2193
+ const labelColWidth = 80;
2194
+ const svgWidth = labelColWidth + xLabels.length * cellSize;
2195
+ const svgHeight = 28 + yLabels.length * cellSize;
2196
+ if (loading) {
2197
+ return /* @__PURE__ */ jsx(
2198
+ "div",
2199
+ {
2200
+ ref,
2201
+ className: cn("w-full flex items-center justify-center", className),
2202
+ style: { height: height ?? svgHeight },
2203
+ children: /* @__PURE__ */ jsx(
2204
+ "div",
2205
+ {
2206
+ className: "animate-pulse",
2207
+ style: {
2208
+ width: "80%",
2209
+ height: "60%",
2210
+ borderRadius: 6,
2211
+ background: "hsl(var(--muted))"
2212
+ }
2213
+ }
2214
+ )
2215
+ }
2216
+ );
2217
+ }
2218
+ return /* @__PURE__ */ jsxs(
2219
+ "div",
2220
+ {
2221
+ ref: (node) => {
2222
+ containerRef.current = node;
2223
+ if (typeof ref === "function") ref(node);
2224
+ else if (ref) ref.current = node;
2225
+ },
2226
+ className: cn("w-full overflow-auto relative", className),
2227
+ style: { height },
2228
+ children: [
2229
+ /* @__PURE__ */ jsxs("svg", { width: svgWidth, height: svgHeight, children: [
2230
+ xLabels.map((label, i) => /* @__PURE__ */ jsx(
2231
+ "text",
2232
+ {
2233
+ x: labelColWidth + i * cellSize + cellSize / 2,
2234
+ y: 14,
2235
+ textAnchor: "middle",
2236
+ fontSize: 9,
2237
+ fill: "hsl(var(--muted-foreground))",
2238
+ children: label
2239
+ },
2240
+ `x-${i}`
2241
+ )),
2242
+ yLabels.map((yLabel, yi) => /* @__PURE__ */ jsxs("g", { children: [
2243
+ /* @__PURE__ */ jsx(
2244
+ "text",
2245
+ {
2246
+ x: labelColWidth - 8,
2247
+ y: 28 + yi * cellSize + cellSize / 2,
2248
+ textAnchor: "end",
2249
+ dominantBaseline: "central",
2250
+ fontSize: 9,
2251
+ fill: "hsl(var(--muted-foreground))",
2252
+ children: yLabel
2253
+ }
2254
+ ),
2255
+ xLabels.map((xLabel, xi) => {
2256
+ const key = `${xLabel}|${yLabel}`;
2257
+ const val = valueMap.get(key);
2258
+ const t = val != null ? normalize(val) : 0;
2259
+ const cx = labelColWidth + xi * cellSize;
2260
+ const cy = 28 + yi * cellSize;
2261
+ const cellData = { x: xLabel, y: yLabel, value: val ?? 0 };
2262
+ return /* @__PURE__ */ jsxs(
2263
+ "g",
2264
+ {
2265
+ onMouseEnter: (e) => {
2266
+ setHoveredCell(cellData);
2267
+ const rect = containerRef.current?.getBoundingClientRect();
2268
+ setTooltipPos({
2269
+ x: e.clientX - (rect?.left ?? 0) + (containerRef.current?.scrollLeft ?? 0),
2270
+ y: e.clientY - (rect?.top ?? 0) + (containerRef.current?.scrollTop ?? 0)
2271
+ });
2272
+ },
2273
+ onMouseLeave: () => setHoveredCell(null),
2274
+ onClick: () => onCellClick?.(cellData),
2275
+ style: { cursor: onCellClick ? "pointer" : "default" },
2276
+ children: [
2277
+ /* @__PURE__ */ jsx(
2278
+ "rect",
2279
+ {
2280
+ x: cx + 1,
2281
+ y: cy + 1,
2282
+ width: cellSize - 2,
2283
+ height: cellSize - 2,
2284
+ rx: 3,
2285
+ fill: val != null ? interpolateColor(t, colorScale) : "hsl(var(--muted))",
2286
+ fillOpacity: val != null ? 0.85 : 0.3,
2287
+ stroke: hoveredCell?.x === xLabel && hoveredCell?.y === yLabel ? "hsl(var(--foreground))" : "none",
2288
+ strokeWidth: 1.5
2289
+ }
2290
+ ),
2291
+ showValues && val != null && cellSize >= 32 && /* @__PURE__ */ jsx(
2292
+ "text",
2293
+ {
2294
+ x: cx + cellSize / 2,
2295
+ y: cy + cellSize / 2,
2296
+ textAnchor: "middle",
2297
+ dominantBaseline: "central",
2298
+ fontSize: Math.min(10, cellSize / 4.5),
2299
+ fill: "hsl(var(--foreground))",
2300
+ fontWeight: 500,
2301
+ style: { pointerEvents: "none" },
2302
+ children: valueFormatter ? valueFormatter(val) : val.toLocaleString()
2303
+ }
2304
+ )
2305
+ ]
2306
+ },
2307
+ key
2308
+ );
2309
+ })
2310
+ ] }, `row-${yi}`))
2311
+ ] }),
2312
+ hoveredCell && /* @__PURE__ */ jsxs(
2313
+ "div",
2314
+ {
2315
+ style: {
2316
+ position: "absolute",
2317
+ left: tooltipPos.x + 12,
2318
+ top: tooltipPos.y - 10,
2319
+ background: "hsl(var(--card))",
2320
+ border: "1px solid hsl(var(--border))",
2321
+ borderRadius: 6,
2322
+ padding: "6px 10px",
2323
+ fontSize: 11,
2324
+ lineHeight: 1.5,
2325
+ boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
2326
+ pointerEvents: "none",
2327
+ zIndex: 50,
2328
+ maxWidth: 200
2329
+ },
2330
+ children: [
2331
+ /* @__PURE__ */ jsxs("div", { style: { color: "hsl(var(--muted-foreground))" }, children: [
2332
+ String(hoveredCell.x),
2333
+ " / ",
2334
+ String(hoveredCell.y)
2335
+ ] }),
2336
+ /* @__PURE__ */ jsx(
2337
+ "div",
2338
+ {
2339
+ style: {
2340
+ fontWeight: 600,
2341
+ color: "hsl(var(--foreground))",
2342
+ fontVariantNumeric: "tabular-nums"
2343
+ },
2344
+ children: valueFormatter ? valueFormatter(hoveredCell.value) : hoveredCell.value.toLocaleString()
2345
+ }
2346
+ )
2347
+ ]
2348
+ }
2349
+ )
2350
+ ]
2351
+ }
2352
+ );
2353
+ }
2354
+ );
2355
+ HeatmapChart.displayName = "HeatmapChart";
2356
+ var Sparkline = React17.memo(function Sparkline2({
2357
+ data,
2358
+ variant = "line",
2359
+ color,
2360
+ width = "100%",
2361
+ height = 24,
2362
+ strokeWidth = 1.5,
2363
+ className
2364
+ }) {
2365
+ const chartColor = color ?? getChartColor(0);
2366
+ const points = React17.useMemo(
2367
+ () => data.map((v, i) => ({ i, v })),
2368
+ [data]
2369
+ );
2370
+ if (!points.length) return null;
2371
+ return /* @__PURE__ */ jsx("div", { className, style: { width, height }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: variant === "bar" ? /* @__PURE__ */ jsx(BarChart$1, { data: points, margin: { top: 0, right: 0, left: 0, bottom: 0 }, children: /* @__PURE__ */ jsx(Bar, { dataKey: "v", fill: chartColor, fillOpacity: 0.7, isAnimationActive: false }) }) : variant === "area" ? /* @__PURE__ */ jsxs(AreaChart$1, { data: points, margin: { top: 0, right: 0, left: 0, bottom: 0 }, children: [
2372
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: "sparkArea", x1: "0", y1: "0", x2: "0", y2: "1", children: [
2373
+ /* @__PURE__ */ jsx("stop", { offset: "5%", stopColor: chartColor, stopOpacity: 0.3 }),
2374
+ /* @__PURE__ */ jsx("stop", { offset: "95%", stopColor: chartColor, stopOpacity: 0.05 })
2375
+ ] }) }),
2376
+ /* @__PURE__ */ jsx(
2377
+ Area,
2378
+ {
2379
+ type: "monotone",
2380
+ dataKey: "v",
2381
+ stroke: chartColor,
2382
+ strokeWidth,
2383
+ fill: "url(#sparkArea)",
2384
+ isAnimationActive: false,
2385
+ dot: false
2386
+ }
2387
+ )
2388
+ ] }) : /* @__PURE__ */ jsx(LineChart$1, { data: points, margin: { top: 0, right: 0, left: 0, bottom: 0 }, children: /* @__PURE__ */ jsx(
2389
+ Line,
2390
+ {
2391
+ type: "monotone",
2392
+ dataKey: "v",
2393
+ stroke: chartColor,
2394
+ strokeWidth,
2395
+ dot: false,
2396
+ isAnimationActive: false
2397
+ }
2398
+ ) }) }) });
2399
+ });
2400
+ Sparkline.displayName = "Sparkline";
2401
+ var KPICard = React17.memo(function KPICard2({
2402
+ label,
2403
+ value,
2404
+ previousValue,
2405
+ changeLabel,
2406
+ sparklineData,
2407
+ sparklineVariant = "area",
2408
+ sparklineColor,
2409
+ icon,
2410
+ className
2411
+ }) {
2412
+ const currentNum = typeof value === "number" ? value : parseFloat(String(value));
2413
+ const hasDelta = previousValue != null && !isNaN(currentNum) && previousValue !== 0;
2414
+ const delta = hasDelta ? (currentNum - previousValue) / previousValue * 100 : null;
2415
+ return /* @__PURE__ */ jsxs(
2416
+ "div",
2417
+ {
2418
+ className: cn(
2419
+ "rounded-lg border p-4 flex flex-col gap-2",
2420
+ className
2421
+ ),
2422
+ style: {
2423
+ background: "hsl(var(--card))",
2424
+ borderColor: "hsl(var(--border))"
2425
+ },
2426
+ children: [
2427
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
2428
+ /* @__PURE__ */ jsx(
2429
+ "span",
2430
+ {
2431
+ style: {
2432
+ fontSize: 12,
2433
+ color: "hsl(var(--muted-foreground))",
2434
+ fontWeight: 500
2435
+ },
2436
+ children: label
2437
+ }
2438
+ ),
2439
+ icon && /* @__PURE__ */ jsx("span", { style: { color: "hsl(var(--muted-foreground))" }, children: icon })
2440
+ ] }),
2441
+ /* @__PURE__ */ jsxs("div", { className: "flex items-end justify-between gap-3", children: [
2442
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
2443
+ /* @__PURE__ */ jsx(
2444
+ "span",
2445
+ {
2446
+ style: {
2447
+ fontSize: 22,
2448
+ fontWeight: 700,
2449
+ color: "hsl(var(--foreground))",
2450
+ lineHeight: 1,
2451
+ fontVariantNumeric: "tabular-nums"
2452
+ },
2453
+ children: value
2454
+ }
2455
+ ),
2456
+ delta != null && /* @__PURE__ */ jsxs(
2457
+ "span",
2458
+ {
2459
+ style: {
2460
+ fontSize: 11,
2461
+ fontWeight: 500,
2462
+ color: delta > 0 ? "hsl(var(--success))" : delta < 0 ? "hsl(var(--destructive))" : "hsl(var(--muted-foreground))",
2463
+ display: "flex",
2464
+ alignItems: "center",
2465
+ gap: 2
2466
+ },
2467
+ children: [
2468
+ delta >= 0 ? "\u25B2" : "\u25BC",
2469
+ " ",
2470
+ delta >= 0 ? "+" : "",
2471
+ delta.toFixed(1),
2472
+ "%",
2473
+ changeLabel && /* @__PURE__ */ jsx("span", { style: { color: "hsl(var(--muted-foreground))", marginLeft: 4 }, children: changeLabel })
2474
+ ]
2475
+ }
2476
+ )
2477
+ ] }),
2478
+ sparklineData && sparklineData.length > 1 && /* @__PURE__ */ jsx(
2479
+ Sparkline,
2480
+ {
2481
+ data: sparklineData,
2482
+ variant: sparklineVariant,
2483
+ color: sparklineColor ?? (delta != null ? delta >= 0 ? "hsl(var(--success))" : "hsl(var(--destructive))" : void 0),
2484
+ width: 80,
2485
+ height: 32
2486
+ }
2487
+ )
2488
+ ] })
2489
+ ]
2490
+ }
2491
+ );
2492
+ });
2493
+ KPICard.displayName = "KPICard";
2494
+ function buildOverlayData(scenarios, categoryKey, metrics) {
2495
+ const merged = /* @__PURE__ */ new Map();
2496
+ for (const scenario of scenarios) {
2497
+ for (const row of scenario.data) {
2498
+ const key = String(row[categoryKey]);
2499
+ if (!merged.has(key)) merged.set(key, { [categoryKey]: row[categoryKey] });
2500
+ const target = merged.get(key);
2501
+ for (const m of metrics) {
2502
+ target[`${scenario.name}_${m}`] = row[m];
2503
+ }
2504
+ }
2505
+ }
2506
+ const data = Array.from(merged.values());
2507
+ const series = scenarios.flatMap(
2508
+ (s, si) => metrics.map((m) => ({
2509
+ dataKey: `${s.name}_${m}`,
2510
+ name: `${s.name} \u2013 ${m}`,
2511
+ color: s.color,
2512
+ strokeDasharray: si > 0 ? "5 3" : void 0
2513
+ }))
2514
+ );
2515
+ return { data, series };
2516
+ }
2517
+ function buildDeltaData(scenarios, baselineIndex, categoryKey, metrics) {
2518
+ const baseline = scenarios[baselineIndex];
2519
+ if (!baseline) return { data: [], series: [] };
2520
+ const baseMap = /* @__PURE__ */ new Map();
2521
+ for (const row of baseline.data) {
2522
+ baseMap.set(String(row[categoryKey]), row);
2523
+ }
2524
+ const comparisons = scenarios.filter((_, i) => i !== baselineIndex);
2525
+ const merged = /* @__PURE__ */ new Map();
2526
+ for (const scenario of comparisons) {
2527
+ for (const row of scenario.data) {
2528
+ const key = String(row[categoryKey]);
2529
+ if (!merged.has(key)) merged.set(key, { [categoryKey]: row[categoryKey] });
2530
+ const target = merged.get(key);
2531
+ const baseRow = baseMap.get(key);
2532
+ for (const m of metrics) {
2533
+ const base = Number(baseRow?.[m] ?? 0);
2534
+ const val = Number(row[m] ?? 0);
2535
+ target[`${scenario.name}_${m}`] = val - base;
2536
+ }
2537
+ }
2538
+ }
2539
+ const data = Array.from(merged.values());
2540
+ const series = comparisons.flatMap(
2541
+ (s) => metrics.map((m) => ({
2542
+ dataKey: `${s.name}_${m}`,
2543
+ name: `${s.name} \u0394 ${m}`
2544
+ }))
2545
+ );
2546
+ return { data, series };
2547
+ }
2548
+ function buildScorecardData(scenarios, metrics) {
2549
+ const data = metrics.map((m) => {
2550
+ const row = { metric: m };
2551
+ for (const s of scenarios) {
2552
+ const values = s.data.map((d) => Number(d[m] ?? 0));
2553
+ const avg = values.length ? values.reduce((a, b) => a + b, 0) / values.length : 0;
2554
+ row[s.name] = Math.round(avg * 10) / 10;
2555
+ }
2556
+ return row;
2557
+ });
2558
+ const series = scenarios.map((s, i) => ({
2559
+ dataKey: s.name,
2560
+ name: s.name,
2561
+ color: s.color,
2562
+ strokeDasharray: i > 0 ? "5 3" : void 0
2563
+ }));
2564
+ return { data, series };
2565
+ }
2566
+ var ScenarioComparison = React17.memo(function ScenarioComparison2({
2567
+ scenarios,
2568
+ metrics,
2569
+ categoryKey,
2570
+ mode = "overlay",
2571
+ baselineIndex = 0,
2572
+ tooltip = true,
2573
+ className,
2574
+ height = 350
2575
+ }) {
2576
+ if (!scenarios.length || !metrics.length) return null;
2577
+ if (mode === "side-by-side") {
2578
+ return /* @__PURE__ */ jsx(
2579
+ "div",
2580
+ {
2581
+ className: cn("grid gap-4", className),
2582
+ style: {
2583
+ gridTemplateColumns: `repeat(${scenarios.length}, 1fr)`
2584
+ },
2585
+ children: scenarios.map((scenario) => /* @__PURE__ */ jsxs("div", { style: { height }, children: [
2586
+ /* @__PURE__ */ jsx(
2587
+ "div",
2588
+ {
2589
+ style: {
2590
+ fontSize: 12,
2591
+ fontWeight: 600,
2592
+ color: "hsl(var(--foreground))",
2593
+ marginBottom: 6
2594
+ },
2595
+ children: scenario.name
2596
+ }
2597
+ ),
2598
+ /* @__PURE__ */ jsx(
2599
+ LineChart,
2600
+ {
2601
+ data: scenario.data,
2602
+ series: metrics.map((m) => ({
2603
+ dataKey: m,
2604
+ name: m,
2605
+ color: scenario.color
2606
+ })),
2607
+ xAxis: { dataKey: categoryKey },
2608
+ tooltip,
2609
+ legend: metrics.length > 1,
2610
+ height: "calc(100% - 24px)"
2611
+ }
2612
+ )
2613
+ ] }, scenario.name))
2614
+ }
2615
+ );
2616
+ }
2617
+ if (mode === "overlay") {
2618
+ const { data, series } = buildOverlayData(scenarios, categoryKey, metrics);
2619
+ return /* @__PURE__ */ jsx("div", { className, style: { height }, children: /* @__PURE__ */ jsx(
2620
+ LineChart,
2621
+ {
2622
+ data,
2623
+ series,
2624
+ xAxis: { dataKey: categoryKey },
2625
+ tooltip,
2626
+ legend: true,
2627
+ height: "100%"
2628
+ }
2629
+ ) });
2630
+ }
2631
+ if (mode === "delta") {
2632
+ const { data, series } = buildDeltaData(
2633
+ scenarios,
2634
+ baselineIndex,
2635
+ categoryKey,
2636
+ metrics
2637
+ );
2638
+ return /* @__PURE__ */ jsx("div", { className, style: { height }, children: /* @__PURE__ */ jsx(
2639
+ BarChart,
2640
+ {
2641
+ data,
2642
+ series: series.map((s) => ({ ...s })),
2643
+ xAxis: { dataKey: categoryKey },
2644
+ tooltip,
2645
+ legend: true,
2646
+ height: "100%"
2647
+ }
2648
+ ) });
2649
+ }
2650
+ if (mode === "scorecard") {
2651
+ const { data, series } = buildScorecardData(scenarios, metrics);
2652
+ return /* @__PURE__ */ jsx("div", { className, style: { height }, children: /* @__PURE__ */ jsx(
2653
+ RadarChart,
2654
+ {
2655
+ data,
2656
+ series: series.map((s) => ({
2657
+ ...s,
2658
+ fillOpacity: 0.1
2659
+ })),
2660
+ categoryKey: "metric",
2661
+ tooltip,
2662
+ legend: true,
2663
+ height: "100%"
2664
+ }
2665
+ ) });
2666
+ }
2667
+ return null;
2668
+ });
2669
+ ScenarioComparison.displayName = "ScenarioComparison";
2670
+ function resolveLegendFromString(val) {
2671
+ if (!val || val === "none") return false;
2672
+ const parts = val.split("-");
2673
+ return {
2674
+ position: parts[0] ?? "top",
2675
+ align: parts[1] ?? "right"
2676
+ };
2677
+ }
2678
+ var ChartRenderer = React17.memo(function ChartRenderer2({
2679
+ config,
2680
+ data,
2681
+ height = "100%",
2682
+ className
2683
+ }) {
2684
+ if (!data?.length || !config.series.length) {
2685
+ return /* @__PURE__ */ jsx(
2686
+ "div",
2687
+ {
2688
+ className,
2689
+ style: {
2690
+ height,
2691
+ display: "flex",
2692
+ alignItems: "center",
2693
+ justifyContent: "center",
2694
+ color: "hsl(var(--muted-foreground))",
2695
+ fontSize: 12
2696
+ },
2697
+ children: "Configure a chart to preview"
2698
+ }
2699
+ );
2700
+ }
2701
+ const legend = resolveLegendFromString(config.legend);
2702
+ const common = {
2703
+ className,
2704
+ height,
2705
+ grid: config.grid ?? true,
2706
+ animate: config.animate ?? true,
2707
+ tooltip: config.tooltip ?? true,
2708
+ legend
2709
+ };
2710
+ const xAxis = { dataKey: config.categoryKey };
2711
+ const series = config.series;
2712
+ switch (config.type) {
2713
+ case "line":
2714
+ return /* @__PURE__ */ jsx(LineChart, { data, series, xAxis, ...common });
2715
+ case "bar":
2716
+ return /* @__PURE__ */ jsx(BarChart, { data, series, xAxis, ...common });
2717
+ case "area":
2718
+ return /* @__PURE__ */ jsx(AreaChart, { data, series, xAxis, ...common });
2719
+ case "pie":
2720
+ return /* @__PURE__ */ jsx(
2721
+ PieChart,
2722
+ {
2723
+ data: data.map((d) => ({
2724
+ name: String(d[config.categoryKey]),
2725
+ value: Number(d[series[0].dataKey] ?? 0)
2726
+ })),
2727
+ label: true,
2728
+ ...common
2729
+ }
2730
+ );
2731
+ case "donut":
2732
+ return /* @__PURE__ */ jsx(
2733
+ DonutChart,
2734
+ {
2735
+ data: data.map((d) => ({
2736
+ name: String(d[config.categoryKey]),
2737
+ value: Number(d[series[0].dataKey] ?? 0)
2738
+ })),
2739
+ ...common
2740
+ }
2741
+ );
2742
+ case "scatter":
2743
+ return /* @__PURE__ */ jsx(
2744
+ ScatterChart,
2745
+ {
2746
+ series: [
2747
+ {
2748
+ name: series[0]?.name ?? "Data",
2749
+ data
2750
+ }
2751
+ ],
2752
+ xAxis: { ...xAxis, name: config.categoryKey },
2753
+ ...common
2754
+ }
2755
+ );
2756
+ case "radar":
2757
+ return /* @__PURE__ */ jsx(
2758
+ RadarChart,
2759
+ {
2760
+ data,
2761
+ series,
2762
+ categoryKey: config.categoryKey,
2763
+ ...common
2764
+ }
2765
+ );
2766
+ case "composed":
2767
+ return /* @__PURE__ */ jsx(
2768
+ ComposedChart,
2769
+ {
2770
+ data,
2771
+ series: series.map((s) => ({
2772
+ type: s.composedType ?? "bar",
2773
+ dataKey: s.dataKey,
2774
+ name: s.name,
2775
+ color: s.color
2776
+ })),
2777
+ xAxis,
2778
+ ...common
2779
+ }
2780
+ );
2781
+ case "waterfall":
2782
+ return /* @__PURE__ */ jsx(
2783
+ WaterfallChart,
2784
+ {
2785
+ data: data.map((d) => ({
2786
+ name: String(d[config.categoryKey]),
2787
+ value: Number(d[series[0]?.dataKey] ?? 0)
2788
+ })),
2789
+ ...common
2790
+ }
2791
+ );
2792
+ case "funnel":
2793
+ return /* @__PURE__ */ jsx(
2794
+ FunnelChart,
2795
+ {
2796
+ data: data.map((d) => ({
2797
+ name: String(d[config.categoryKey]),
2798
+ value: Number(d[series[0]?.dataKey] ?? 0)
2799
+ })),
2800
+ ...common
2801
+ }
2802
+ );
2803
+ case "treemap":
2804
+ return /* @__PURE__ */ jsx(
2805
+ TreemapChart,
2806
+ {
2807
+ data: data.map((d) => ({
2808
+ name: String(d[config.categoryKey]),
2809
+ value: Number(d[series[0]?.dataKey] ?? 0)
2810
+ })),
2811
+ ...common
2812
+ }
2813
+ );
2814
+ case "radialBar":
2815
+ return /* @__PURE__ */ jsx(
2816
+ RadialBarChart,
2817
+ {
2818
+ data: data.map((d) => ({
2819
+ name: String(d[config.categoryKey]),
2820
+ value: Number(d[series[0]?.dataKey] ?? 0)
2821
+ })),
2822
+ ...common
2823
+ }
2824
+ );
2825
+ case "heatmap": {
2826
+ if (series.length < 2) {
2827
+ return /* @__PURE__ */ jsx(
2828
+ "div",
2829
+ {
2830
+ style: {
2831
+ height,
2832
+ display: "flex",
2833
+ alignItems: "center",
2834
+ justifyContent: "center",
2835
+ color: "hsl(var(--muted-foreground))",
2836
+ fontSize: 12
2837
+ },
2838
+ children: "Heatmap requires at least 2 series (x, value)"
2839
+ }
2840
+ );
2841
+ }
2842
+ const xLabels = Array.from(
2843
+ new Set(data.map((d) => String(d[config.categoryKey])))
2844
+ );
2845
+ const yLabels = Array.from(
2846
+ new Set(data.map((d) => String(d[series[0].dataKey])))
2847
+ );
2848
+ const cells = data.map((d) => ({
2849
+ x: String(d[config.categoryKey]),
2850
+ y: String(d[series[0].dataKey]),
2851
+ value: Number(d[series[1].dataKey] ?? 0)
2852
+ }));
2853
+ return /* @__PURE__ */ jsx(
2854
+ HeatmapChart,
2855
+ {
2856
+ data: cells,
2857
+ xLabels,
2858
+ yLabels,
2859
+ height,
2860
+ className
2861
+ }
2862
+ );
2863
+ }
2864
+ default:
2865
+ return null;
2866
+ }
2867
+ });
2868
+ ChartRenderer.displayName = "ChartRenderer";
2869
+ var CHART_TYPE_OPTIONS = [
2870
+ { value: "line", label: "Line" },
2871
+ { value: "bar", label: "Bar" },
2872
+ { value: "area", label: "Area" },
2873
+ { value: "pie", label: "Pie" },
2874
+ { value: "donut", label: "Donut" },
2875
+ { value: "scatter", label: "Scatter" },
2876
+ { value: "radar", label: "Radar" },
2877
+ { value: "composed", label: "Composed" },
2878
+ { value: "waterfall", label: "Waterfall" },
2879
+ { value: "funnel", label: "Funnel" },
2880
+ { value: "treemap", label: "Treemap" },
2881
+ { value: "radialBar", label: "Radial Bar" },
2882
+ { value: "heatmap", label: "Heatmap" }
2883
+ ];
2884
+ var LEGEND_POSITIONS = [
2885
+ { value: "none", label: "None" },
2886
+ { value: "top-left", label: "Top Left" },
2887
+ { value: "top-center", label: "Top Center" },
2888
+ { value: "top-right", label: "Top Right" },
2889
+ { value: "bottom-center", label: "Bottom Center" }
2890
+ ];
2891
+ function useControlledConfig(initial, columns) {
2892
+ const defaultConfig = {
2893
+ type: "bar",
2894
+ categoryKey: columns[0] ?? "",
2895
+ series: columns.length > 1 ? [{ dataKey: columns[1], name: columns[1] }] : [],
2896
+ legend: "none",
2897
+ grid: true,
2898
+ animate: true
2899
+ };
2900
+ return React17.useState({ ...defaultConfig, ...initial });
2901
+ }
2902
+ var selectStyle = {
2903
+ width: "100%",
2904
+ padding: "5px 8px",
2905
+ fontSize: 12,
2906
+ borderRadius: 4,
2907
+ border: "1px solid hsl(var(--border))",
2908
+ background: "hsl(var(--card))",
2909
+ color: "hsl(var(--foreground))"
2910
+ };
2911
+ var labelStyle = {
2912
+ fontSize: 11,
2913
+ fontWeight: 500,
2914
+ color: "hsl(var(--muted-foreground))",
2915
+ marginBottom: 3,
2916
+ display: "block"
2917
+ };
2918
+ var ChartBuilder = React17.memo(function ChartBuilder2({
2919
+ columns,
2920
+ data,
2921
+ onChange,
2922
+ onSave,
2923
+ initialConfig,
2924
+ className
2925
+ }) {
2926
+ const [config, setConfig] = useControlledConfig(initialConfig, columns);
2927
+ React17.useEffect(() => {
2928
+ onChange?.(config);
2929
+ }, [config, onChange]);
2930
+ const addSeries = () => {
2931
+ const unused = columns.find(
2932
+ (c) => c !== config.categoryKey && !config.series.some((s) => s.dataKey === c)
2933
+ );
2934
+ if (!unused) return;
2935
+ setConfig((prev) => ({
2936
+ ...prev,
2937
+ series: [...prev.series, { dataKey: unused, name: unused }]
2938
+ }));
2939
+ };
2940
+ const removeSeries = (index) => {
2941
+ setConfig((prev) => ({
2942
+ ...prev,
2943
+ series: prev.series.filter((_, i) => i !== index)
2944
+ }));
2945
+ };
2946
+ const updateSeries = (index, field, value) => {
2947
+ setConfig((prev) => ({
2948
+ ...prev,
2949
+ series: prev.series.map(
2950
+ (s, i) => i === index ? { ...s, [field]: value } : s
2951
+ )
2952
+ }));
2953
+ };
2954
+ return /* @__PURE__ */ jsxs(
2955
+ "div",
2956
+ {
2957
+ className: cn("flex gap-4", className),
2958
+ style: { minHeight: 400 },
2959
+ children: [
2960
+ /* @__PURE__ */ jsxs(
2961
+ "div",
2962
+ {
2963
+ className: "flex flex-col gap-3 shrink-0 overflow-y-auto",
2964
+ style: {
2965
+ width: 240,
2966
+ padding: 12,
2967
+ background: "hsl(var(--card))",
2968
+ border: "1px solid hsl(var(--border))",
2969
+ borderRadius: 8
2970
+ },
2971
+ children: [
2972
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 13, fontWeight: 600, color: "hsl(var(--foreground))" }, children: "Chart Builder" }),
2973
+ /* @__PURE__ */ jsxs("div", { children: [
2974
+ /* @__PURE__ */ jsx("label", { style: labelStyle, children: "Chart Type" }),
2975
+ /* @__PURE__ */ jsx(
2976
+ "select",
2977
+ {
2978
+ style: selectStyle,
2979
+ value: config.type,
2980
+ onChange: (e) => setConfig((prev) => ({ ...prev, type: e.target.value })),
2981
+ children: CHART_TYPE_OPTIONS.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
2982
+ }
2983
+ )
2984
+ ] }),
2985
+ /* @__PURE__ */ jsxs("div", { children: [
2986
+ /* @__PURE__ */ jsx("label", { style: labelStyle, children: "Category Axis" }),
2987
+ /* @__PURE__ */ jsx(
2988
+ "select",
2989
+ {
2990
+ style: selectStyle,
2991
+ value: config.categoryKey,
2992
+ onChange: (e) => setConfig((prev) => ({ ...prev, categoryKey: e.target.value })),
2993
+ children: columns.map((c) => /* @__PURE__ */ jsx("option", { value: c, children: c }, c))
2994
+ }
2995
+ )
2996
+ ] }),
2997
+ /* @__PURE__ */ jsxs("div", { children: [
2998
+ /* @__PURE__ */ jsx("label", { style: labelStyle, children: "Series" }),
2999
+ config.series.map((s, i) => /* @__PURE__ */ jsxs(
3000
+ "div",
3001
+ {
3002
+ className: "flex gap-1 items-center",
3003
+ style: { marginBottom: 4 },
3004
+ children: [
3005
+ /* @__PURE__ */ jsx(
3006
+ "select",
3007
+ {
3008
+ style: { ...selectStyle, flex: 1 },
3009
+ value: s.dataKey,
3010
+ onChange: (e) => updateSeries(i, "dataKey", e.target.value),
3011
+ children: columns.filter((c) => c !== config.categoryKey).map((c) => /* @__PURE__ */ jsx("option", { value: c, children: c }, c))
3012
+ }
3013
+ ),
3014
+ /* @__PURE__ */ jsx(
3015
+ "button",
3016
+ {
3017
+ onClick: () => removeSeries(i),
3018
+ style: {
3019
+ padding: "2px 6px",
3020
+ fontSize: 14,
3021
+ lineHeight: 1,
3022
+ border: "1px solid hsl(var(--border))",
3023
+ borderRadius: 4,
3024
+ background: "hsl(var(--card))",
3025
+ color: "hsl(var(--muted-foreground))",
3026
+ cursor: "pointer"
3027
+ },
3028
+ children: "\xD7"
3029
+ }
3030
+ )
3031
+ ]
3032
+ },
3033
+ i
3034
+ )),
3035
+ /* @__PURE__ */ jsx(
3036
+ "button",
3037
+ {
3038
+ onClick: addSeries,
3039
+ style: {
3040
+ width: "100%",
3041
+ padding: "4px 8px",
3042
+ fontSize: 11,
3043
+ border: "1px dashed hsl(var(--border))",
3044
+ borderRadius: 4,
3045
+ background: "transparent",
3046
+ color: "hsl(var(--muted-foreground))",
3047
+ cursor: "pointer",
3048
+ marginTop: 2
3049
+ },
3050
+ children: "+ Add Series"
3051
+ }
3052
+ )
3053
+ ] }),
3054
+ /* @__PURE__ */ jsxs("div", { children: [
3055
+ /* @__PURE__ */ jsx("label", { style: labelStyle, children: "Legend" }),
3056
+ /* @__PURE__ */ jsx(
3057
+ "select",
3058
+ {
3059
+ style: selectStyle,
3060
+ value: config.legend,
3061
+ onChange: (e) => setConfig((prev) => ({ ...prev, legend: e.target.value })),
3062
+ children: LEGEND_POSITIONS.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
3063
+ }
3064
+ )
3065
+ ] }),
3066
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3067
+ /* @__PURE__ */ jsx(
3068
+ "input",
3069
+ {
3070
+ type: "checkbox",
3071
+ id: "grid-toggle",
3072
+ checked: config.grid,
3073
+ onChange: (e) => setConfig((prev) => ({ ...prev, grid: e.target.checked }))
3074
+ }
3075
+ ),
3076
+ /* @__PURE__ */ jsx("label", { htmlFor: "grid-toggle", style: { ...labelStyle, margin: 0 }, children: "Show Grid" })
3077
+ ] }),
3078
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3079
+ /* @__PURE__ */ jsx(
3080
+ "input",
3081
+ {
3082
+ type: "checkbox",
3083
+ id: "animate-toggle",
3084
+ checked: config.animate,
3085
+ onChange: (e) => setConfig((prev) => ({ ...prev, animate: e.target.checked }))
3086
+ }
3087
+ ),
3088
+ /* @__PURE__ */ jsx("label", { htmlFor: "animate-toggle", style: { ...labelStyle, margin: 0 }, children: "Animate" })
3089
+ ] }),
3090
+ onSave && /* @__PURE__ */ jsx(
3091
+ "button",
3092
+ {
3093
+ onClick: () => onSave(config),
3094
+ style: {
3095
+ marginTop: 8,
3096
+ padding: "6px 12px",
3097
+ fontSize: 12,
3098
+ fontWeight: 600,
3099
+ borderRadius: 6,
3100
+ border: "none",
3101
+ background: "hsl(var(--primary))",
3102
+ color: "hsl(var(--primary-foreground))",
3103
+ cursor: "pointer"
3104
+ },
3105
+ children: "Save Chart"
3106
+ }
3107
+ )
3108
+ ]
3109
+ }
3110
+ ),
3111
+ /* @__PURE__ */ jsx(
3112
+ "div",
3113
+ {
3114
+ className: "flex-1",
3115
+ style: {
3116
+ border: "1px solid hsl(var(--border))",
3117
+ borderRadius: 8,
3118
+ padding: 12,
3119
+ background: "hsl(var(--background))",
3120
+ minHeight: 300
3121
+ },
3122
+ children: /* @__PURE__ */ jsx(ChartRenderer, { config, data, height: "100%" })
3123
+ }
3124
+ )
3125
+ ]
3126
+ }
3127
+ );
3128
+ });
3129
+ ChartBuilder.displayName = "ChartBuilder";
299
3130
 
300
- export { BarChart, CHART_COLORS, LineChart, getChartColor };
3131
+ export { AreaChart, BarChart, CHART_COLORS, ChartBuilder, ChartContainer, ChartLegendContent, ChartRenderer, ChartTooltipContent, ComposedChart, DonutChart, FunnelChart, GanttChart, HeatmapChart, KPICard, LineChart, PieChart, RadarChart, RadialBarChart, SankeyChart, ScatterChart, ScenarioComparison, Sparkline, TreemapChart, WaterfallChart, formatCompact, formatCurrency, formatDuration, formatPercent, formatUnit, getChartColor, getSemanticColor, resolveLegendConfig, resolveTooltipProps, useLiveData };
301
3132
  //# sourceMappingURL=index.js.map
302
3133
  //# sourceMappingURL=index.js.map