bruv-ui 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/area-chart.d.ts +84 -0
  2. package/dist/area-chart.js +2002 -0
  3. package/dist/area-chart.js.map +1 -0
  4. package/dist/bar-chart.d.ts +80 -0
  5. package/dist/bar-chart.js +2251 -0
  6. package/dist/bar-chart.js.map +1 -0
  7. package/dist/chart-background-BK77UXhl.d.ts +9 -0
  8. package/dist/chart-brush-BoxY9aDm.d.ts +72 -0
  9. package/dist/chart-dot-8H287EDv.d.ts +17 -0
  10. package/dist/chart-legend-Dv9pqes-.d.ts +16 -0
  11. package/dist/chart-tooltip-DajpzJRy.d.ts +68 -0
  12. package/dist/charts.d.ts +17 -0
  13. package/dist/charts.js +5097 -0
  14. package/dist/charts.js.map +1 -0
  15. package/dist/composed-chart.d.ts +93 -0
  16. package/dist/composed-chart.js +2338 -0
  17. package/dist/composed-chart.js.map +1 -0
  18. package/dist/form-dK_DJRTw.d.ts +43 -0
  19. package/dist/form-rhf.d.ts +26 -0
  20. package/dist/form-rhf.js +268 -0
  21. package/dist/form-rhf.js.map +1 -0
  22. package/dist/index.d.ts +2881 -0
  23. package/dist/index.js +9368 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/line-chart.d.ts +81 -0
  26. package/dist/line-chart.js +1879 -0
  27. package/dist/line-chart.js.map +1 -0
  28. package/dist/pie-chart.d.ts +64 -0
  29. package/dist/pie-chart.js +1094 -0
  30. package/dist/pie-chart.js.map +1 -0
  31. package/dist/radar-chart.d.ts +67 -0
  32. package/dist/radar-chart.js +1318 -0
  33. package/dist/radar-chart.js.map +1 -0
  34. package/dist/radial-chart.d.ts +56 -0
  35. package/dist/radial-chart.js +1051 -0
  36. package/dist/radial-chart.js.map +1 -0
  37. package/dist/sankey-chart.d.ts +58 -0
  38. package/dist/sankey-chart.js +1179 -0
  39. package/dist/sankey-chart.js.map +1 -0
  40. package/package.json +135 -0
  41. package/src/scales.css +288 -0
  42. package/src/shiki.css +37 -0
  43. package/src/styles.css +23 -0
  44. package/src/theme.css +659 -0
@@ -0,0 +1,1879 @@
1
+ // src/components/charts/chart.tsx
2
+ import * as RechartsPrimitive from "recharts";
3
+
4
+ // src/lib/cn.ts
5
+ import { clsx } from "clsx";
6
+ import { extendTailwindMerge } from "tailwind-merge";
7
+ var twMerge = extendTailwindMerge({
8
+ extend: {
9
+ classGroups: {
10
+ "font-size": [{ "text-cui": ["sm", "base", "lg", "xl"] }],
11
+ "text-color": [
12
+ {
13
+ "text-cui": [
14
+ "primary",
15
+ "secondary",
16
+ "tertiary",
17
+ "inverse",
18
+ "accent",
19
+ "accent-on",
20
+ "danger",
21
+ "danger-on",
22
+ "warn",
23
+ "warn-on",
24
+ "success",
25
+ "success-on"
26
+ ]
27
+ }
28
+ ]
29
+ }
30
+ }
31
+ });
32
+ function cn(...inputs) {
33
+ return twMerge(clsx(inputs));
34
+ }
35
+
36
+ // src/components/charts/chart.tsx
37
+ import {
38
+ forwardRef,
39
+ useId,
40
+ createContext,
41
+ useContext
42
+ } from "react";
43
+ import { jsx, jsxs } from "react/jsx-runtime";
44
+ var THEMES = { light: "", dark: '[data-theme="dark"]' };
45
+ var VALID_THEME_KEYS = Object.keys(THEMES);
46
+ var DEFAULT_PALETTE = [
47
+ "var(--color-bruv-chart-1)",
48
+ "var(--color-bruv-chart-2)",
49
+ "var(--color-bruv-chart-3)",
50
+ "var(--color-bruv-chart-4)",
51
+ "var(--color-bruv-chart-5)",
52
+ "var(--color-bruv-chart-6)"
53
+ ];
54
+ var ChartContext = createContext(null);
55
+ function useChart() {
56
+ const context = useContext(ChartContext);
57
+ if (!context) {
58
+ throw new Error("useChart must be used within a <Chart />");
59
+ }
60
+ return context;
61
+ }
62
+ function validateChartConfigColors(config) {
63
+ for (const [key, value] of Object.entries(config)) {
64
+ if (value.colors) {
65
+ const hasValidThemeKey = VALID_THEME_KEYS.some(
66
+ (themeKey) => value.colors?.[themeKey] !== void 0
67
+ );
68
+ if (!hasValidThemeKey) {
69
+ throw new Error(
70
+ `[BruvCharts] Invalid chart config for "${key}": colors object must have at least one theme key (${VALID_THEME_KEYS.join(", ")}).`
71
+ );
72
+ }
73
+ }
74
+ }
75
+ }
76
+ function applyDefaultChartColors(config) {
77
+ const entries = Object.entries(config);
78
+ return Object.fromEntries(
79
+ entries.map(([key, item], index) => {
80
+ if (item.colors || item.color) {
81
+ if (item.color && !item.colors) {
82
+ return [
83
+ key,
84
+ {
85
+ ...item,
86
+ colors: {
87
+ light: [item.color],
88
+ dark: [item.color]
89
+ }
90
+ }
91
+ ];
92
+ }
93
+ return [key, item];
94
+ }
95
+ const paletteColor = DEFAULT_PALETTE[index % DEFAULT_PALETTE.length] ?? DEFAULT_PALETTE[0];
96
+ return [
97
+ key,
98
+ {
99
+ ...item,
100
+ colors: {
101
+ light: [paletteColor],
102
+ dark: [paletteColor]
103
+ }
104
+ }
105
+ ];
106
+ })
107
+ );
108
+ }
109
+ var Chart = forwardRef(function Chart2({
110
+ id,
111
+ config,
112
+ initialDimension = { width: 320, height: 200 },
113
+ className,
114
+ children,
115
+ footer,
116
+ ...props
117
+ }, ref) {
118
+ const uniqueId = useId();
119
+ const chartId = `chart-${id ?? uniqueId.replace(/:/g, "")}`;
120
+ const resolvedConfig = applyDefaultChartColors(config);
121
+ validateChartConfigColors(resolvedConfig);
122
+ return /* @__PURE__ */ jsx(ChartContext.Provider, { value: { config: resolvedConfig }, children: /* @__PURE__ */ jsxs(
123
+ "div",
124
+ {
125
+ ref,
126
+ "data-slot": "chart",
127
+ "data-chart": chartId,
128
+ className: cn(
129
+ "bruv-chart relative flex min-h-0 w-full flex-1 flex-col justify-center text-bruv-sm text-bruv-secondary",
130
+ "[&_.recharts-cartesian-axis-tick_text]:fill-bruv-chart-axis [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-bruv-chart-grid/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-bruv-chart-cursor [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-bruv-chart-grid [&_.recharts-radial-bar-background-sector]:fill-bruv-subtle [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-bruv-subtle [&_.recharts-reference-line_[stroke='#ccc']]:stroke-bruv-chart-grid [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
131
+ !footer && "aspect-video",
132
+ className
133
+ ),
134
+ ...props,
135
+ children: [
136
+ /* @__PURE__ */ jsx(ChartStyle, { id: chartId, config: resolvedConfig }),
137
+ /* @__PURE__ */ jsx(
138
+ RechartsPrimitive.ResponsiveContainer,
139
+ {
140
+ className: "min-h-0 w-full flex-1",
141
+ initialDimension,
142
+ children
143
+ }
144
+ ),
145
+ footer
146
+ ]
147
+ }
148
+ ) });
149
+ });
150
+ function LoadingIndicator({ isLoading }) {
151
+ if (!isLoading) return null;
152
+ return /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 z-20 flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "border-bruv-neutral bg-bruv-base-2 text-bruv-sm text-bruv-primary rounded-bruv-md flex items-center justify-center gap-2 border px-2 py-0.5", children: [
153
+ /* @__PURE__ */ jsx("div", { className: "border-bruv-neutral border-t-bruv-accent size-3 animate-spin rounded-full border" }),
154
+ /* @__PURE__ */ jsx("span", { children: "Loading" })
155
+ ] }) });
156
+ }
157
+ function distributeColors(colorsArray, maxCount) {
158
+ const availableCount = colorsArray.length;
159
+ if (availableCount >= maxCount) {
160
+ return colorsArray.slice(0, maxCount);
161
+ }
162
+ const result = [];
163
+ const baseSlots = Math.floor(maxCount / availableCount);
164
+ const extraSlots = maxCount % availableCount;
165
+ for (let colorIdx = 0; colorIdx < availableCount; colorIdx++) {
166
+ const isExtraColor = colorIdx >= availableCount - extraSlots;
167
+ const slotsForThisColor = baseSlots + (isExtraColor ? 1 : 0);
168
+ for (let j = 0; j < slotsForThisColor; j++) {
169
+ const color = colorsArray[colorIdx];
170
+ if (color) result.push(color);
171
+ }
172
+ }
173
+ return result;
174
+ }
175
+ function ChartStyle({
176
+ id,
177
+ config
178
+ }) {
179
+ const colorConfig = Object.entries(config).filter(([, item]) => item.colors);
180
+ if (!colorConfig.length) return null;
181
+ const generateCssVars = (theme) => colorConfig.flatMap(([key, itemConfig]) => {
182
+ const colorsArray = itemConfig.colors?.[theme];
183
+ if (!colorsArray?.length) return [];
184
+ const maxCount = getColorsCount(itemConfig);
185
+ const distributedColors = distributeColors(colorsArray, maxCount);
186
+ return distributedColors.map(
187
+ (color, index) => ` --color-${key}-${index}: ${color};`
188
+ );
189
+ }).filter(Boolean).join("\n");
190
+ const css = Object.entries(THEMES).map(
191
+ ([theme, prefix]) => `${prefix} [data-chart=${id}] {
192
+ ${generateCssVars(theme)}
193
+ }`
194
+ ).join("\n");
195
+ return /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: css } });
196
+ }
197
+ function getPayloadConfigFromPayload(config, payload, key) {
198
+ if (typeof payload !== "object" || payload === null) return void 0;
199
+ const payloadPayload = "payload" in payload && typeof payload.payload === "object" && payload.payload !== null ? payload.payload : void 0;
200
+ let configLabelKey = key;
201
+ if (key in payload && typeof payload[key] === "string") {
202
+ configLabelKey = payload[key];
203
+ } else if (payloadPayload && key in payloadPayload && typeof payloadPayload[key] === "string") {
204
+ configLabelKey = payloadPayload[key];
205
+ }
206
+ return configLabelKey in config ? config[configLabelKey] : config[key];
207
+ }
208
+ function getColorsCount(config) {
209
+ if (!config.colors) return 1;
210
+ const counts = VALID_THEME_KEYS.map(
211
+ (theme) => config.colors?.[theme]?.length ?? 0
212
+ );
213
+ return Math.max(...counts, 1);
214
+ }
215
+ var getLoadingData = (points = 10, min = 0, max = 70) => {
216
+ const range = max - min;
217
+ return Array.from({ length: points }, () => ({
218
+ loading: Math.floor(Math.random() * range) + min
219
+ }));
220
+ };
221
+
222
+ // src/components/charts/line-chart.tsx
223
+ import {
224
+ CartesianGrid,
225
+ Curve,
226
+ Line as RechartsLine,
227
+ LineChart as RechartsLineChart,
228
+ XAxis as RechartsXAxis,
229
+ YAxis as RechartsYAxis
230
+ } from "recharts";
231
+
232
+ // src/components/charts/chart-tooltip.tsx
233
+ import * as RechartsPrimitive2 from "recharts";
234
+ import * as React from "react";
235
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
236
+ var roundnessMap = {
237
+ sm: "rounded-bruv-sm",
238
+ md: "rounded-bruv-md",
239
+ lg: "rounded-bruv-lg",
240
+ xl: "rounded-bruv-xl"
241
+ };
242
+ var variantMap = {
243
+ default: "bg-bruv-base-2",
244
+ "frosted-glass": "bg-bruv-base-2/70 backdrop-blur-sm"
245
+ };
246
+ function ChartTooltipContent({
247
+ active,
248
+ payload,
249
+ className,
250
+ indicator = "dot",
251
+ hideLabel = false,
252
+ hideIndicator = false,
253
+ label,
254
+ labelFormatter,
255
+ labelClassName,
256
+ formatter,
257
+ nameKey,
258
+ labelKey,
259
+ selected,
260
+ roundness = "lg",
261
+ variant = "default"
262
+ }) {
263
+ const { config } = useChart();
264
+ const tooltipLabel = React.useMemo(() => {
265
+ if (hideLabel || !payload?.length) {
266
+ return null;
267
+ }
268
+ const [item] = payload;
269
+ const key = `${labelKey ?? item?.dataKey ?? item?.name ?? "value"}`;
270
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
271
+ const value = !labelKey && typeof label === "string" ? config[label]?.label ?? label : itemConfig?.label;
272
+ if (labelFormatter) {
273
+ return /* @__PURE__ */ jsx2("div", { className: cn("font-medium", labelClassName), children: labelFormatter(value, payload) });
274
+ }
275
+ if (!value) {
276
+ return null;
277
+ }
278
+ return /* @__PURE__ */ jsx2("div", { className: cn("font-medium", labelClassName), children: value });
279
+ }, [
280
+ label,
281
+ labelFormatter,
282
+ payload,
283
+ hideLabel,
284
+ labelClassName,
285
+ config,
286
+ labelKey
287
+ ]);
288
+ if (!active || !payload?.length) {
289
+ return /* @__PURE__ */ jsx2("span", { className: "p-4" });
290
+ }
291
+ const nestLabel = payload.length === 1 && indicator !== "dot";
292
+ return /* @__PURE__ */ jsxs2(
293
+ "div",
294
+ {
295
+ className: cn(
296
+ "border-bruv-neutral/50 grid min-w-32 items-start gap-1.5 border px-2.5 py-1.5 text-xs shadow-xl",
297
+ roundnessMap[roundness],
298
+ variantMap[variant],
299
+ className
300
+ ),
301
+ children: [
302
+ !nestLabel ? tooltipLabel : null,
303
+ /* @__PURE__ */ jsx2("div", { className: "grid gap-1.5", children: payload.filter((item) => item.type !== "none").map((item, index) => {
304
+ const payloadName = nameKey && item.payload ? item.payload[nameKey] : void 0;
305
+ const key = `${payloadName ?? item.name ?? item.dataKey ?? "value"}`;
306
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
307
+ const colorsCount = itemConfig ? getColorsCount(itemConfig) : 1;
308
+ return /* @__PURE__ */ jsx2(
309
+ "div",
310
+ {
311
+ className: cn(
312
+ "[&>svg]:text-bruv-secondary flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
313
+ indicator === "dot" && "items-center",
314
+ selected != null && selected !== item.dataKey && "opacity-30"
315
+ ),
316
+ children: formatter && item?.value !== void 0 && item.name ? formatter(item.value, item.name, item, index, item.payload) : /* @__PURE__ */ jsxs2(Fragment, { children: [
317
+ itemConfig?.icon ? /* @__PURE__ */ jsx2(itemConfig.icon, {}) : !hideIndicator && /* @__PURE__ */ jsx2(
318
+ "div",
319
+ {
320
+ className: cn("shrink-0 rounded-[2px]", {
321
+ "h-2.5 w-2.5": indicator === "dot",
322
+ "w-1": indicator === "line",
323
+ "w-0 border-[1.5px] border-dashed bg-transparent!": indicator === "dashed",
324
+ "my-0.5": nestLabel && indicator === "dashed"
325
+ }),
326
+ style: getIndicatorColorStyle(key, colorsCount)
327
+ }
328
+ ),
329
+ /* @__PURE__ */ jsxs2(
330
+ "div",
331
+ {
332
+ className: cn(
333
+ "flex flex-1 justify-between gap-4 leading-none",
334
+ nestLabel ? "items-end" : "items-center"
335
+ ),
336
+ children: [
337
+ /* @__PURE__ */ jsxs2("div", { className: "grid gap-1.5", children: [
338
+ nestLabel ? tooltipLabel : null,
339
+ /* @__PURE__ */ jsx2("span", { className: "text-bruv-secondary", children: itemConfig?.label ?? item.name })
340
+ ] }),
341
+ item.value != null && /* @__PURE__ */ jsx2("span", { className: "text-bruv-primary font-mono font-medium tabular-nums", children: typeof item.value === "number" ? item.value.toLocaleString() : String(item.value) })
342
+ ]
343
+ }
344
+ )
345
+ ] })
346
+ },
347
+ index
348
+ );
349
+ }) })
350
+ ]
351
+ }
352
+ );
353
+ }
354
+ function getIndicatorColorStyle(dataKey, colorsCount) {
355
+ if (colorsCount <= 1) {
356
+ return { background: `var(--color-${dataKey}-0)` };
357
+ }
358
+ const stops = Array.from({ length: colorsCount }, (_, index) => {
359
+ const offset = index / (colorsCount - 1) * 100;
360
+ return `var(--color-${dataKey}-${index}) ${offset}%`;
361
+ }).join(", ");
362
+ return { background: `linear-gradient(to right, ${stops})` };
363
+ }
364
+ var ChartTooltip = ({
365
+ animationDuration = 200,
366
+ ...props
367
+ }) => /* @__PURE__ */ jsx2(RechartsPrimitive2.Tooltip, { animationDuration, ...props });
368
+
369
+ // src/components/charts/chart-brush.tsx
370
+ import {
371
+ motion,
372
+ useMotionValue,
373
+ useMotionValueEvent,
374
+ useSpring,
375
+ useTransform
376
+ } from "motion/react";
377
+ import {
378
+ ResponsiveContainer as ResponsiveContainer2,
379
+ AreaChart,
380
+ Area,
381
+ LineChart,
382
+ Line,
383
+ BarChart,
384
+ Bar
385
+ } from "recharts";
386
+ import { useCallback, useEffect } from "react";
387
+ import * as React2 from "react";
388
+ import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
389
+ var SPRING_CONFIG = { stiffness: 300, damping: 35, mass: 0.8 };
390
+ function useBrushDrag({
391
+ range,
392
+ totalPoints,
393
+ containerRef,
394
+ commit
395
+ }) {
396
+ const dragRef = React2.useRef(null);
397
+ const [isDragging, setIsDragging] = React2.useState(false);
398
+ const toIndexDelta = useCallback(
399
+ (px) => {
400
+ if (!containerRef.current || totalPoints <= 1) return 0;
401
+ return Math.round(
402
+ px / containerRef.current.getBoundingClientRect().width * (totalPoints - 1)
403
+ );
404
+ },
405
+ [totalPoints, containerRef]
406
+ );
407
+ const onPointerDown = useCallback(
408
+ (e, type) => {
409
+ e.preventDefault();
410
+ e.target.setPointerCapture(e.pointerId);
411
+ dragRef.current = { type, originX: e.clientX, originRange: { ...range } };
412
+ setIsDragging(true);
413
+ },
414
+ [range]
415
+ );
416
+ const onPointerMove = useCallback(
417
+ (e) => {
418
+ const d = dragRef.current;
419
+ if (!d) return;
420
+ const delta = toIndexDelta(e.clientX - d.originX);
421
+ const { type, originRange: o } = d;
422
+ if (type === "left") {
423
+ commit(
424
+ { startIndex: o.startIndex + delta, endIndex: o.endIndex },
425
+ "left"
426
+ );
427
+ } else if (type === "right") {
428
+ commit(
429
+ { startIndex: o.startIndex, endIndex: o.endIndex + delta },
430
+ "right"
431
+ );
432
+ } else {
433
+ const span = o.endIndex - o.startIndex;
434
+ let s = o.startIndex + delta;
435
+ let e2 = s + span;
436
+ if (s < 0) {
437
+ s = 0;
438
+ e2 = span;
439
+ }
440
+ if (e2 > totalPoints - 1) {
441
+ e2 = totalPoints - 1;
442
+ s = Math.max(0, e2 - span);
443
+ }
444
+ commit({ startIndex: s, endIndex: e2 }, "middle");
445
+ }
446
+ },
447
+ [toIndexDelta, totalPoints, commit]
448
+ );
449
+ const onPointerUp = useCallback((e) => {
450
+ ;
451
+ e.target.releasePointerCapture(e.pointerId);
452
+ dragRef.current = null;
453
+ setIsDragging(false);
454
+ }, []);
455
+ const bind = useCallback(
456
+ (type) => ({
457
+ onPointerDown: (e) => onPointerDown(e, type),
458
+ onPointerMove,
459
+ onPointerUp
460
+ }),
461
+ [onPointerDown, onPointerMove, onPointerUp]
462
+ );
463
+ return { isDragging, bind };
464
+ }
465
+ function Brush({
466
+ data,
467
+ chartConfig,
468
+ dataKeys,
469
+ xDataKey,
470
+ variant = "area",
471
+ height = 56,
472
+ className,
473
+ stacked = false,
474
+ strokeVariant = "solid",
475
+ connectNulls = false,
476
+ barRadius,
477
+ startIndex: controlledStart,
478
+ endIndex: controlledEnd,
479
+ defaultStartIndex = 0,
480
+ defaultEndIndex,
481
+ onChange,
482
+ formatLabel,
483
+ curveType = "monotone",
484
+ minSpan = 2,
485
+ showLabels = true,
486
+ skipStyle = false
487
+ }) {
488
+ const containerRef = React2.useRef(null);
489
+ const keys = React2.useMemo(
490
+ () => dataKeys ?? Object.keys(chartConfig),
491
+ [dataKeys, chartConfig]
492
+ );
493
+ const totalPoints = data.length;
494
+ const chartId = React2.useId().replace(/:/g, "");
495
+ const isControlled = controlledStart !== void 0 && controlledEnd !== void 0;
496
+ const [internalRange, setInternalRange] = React2.useState(() => ({
497
+ startIndex: Math.max(0, Math.min(defaultStartIndex, totalPoints - 1)),
498
+ endIndex: Math.max(
499
+ 0,
500
+ Math.min(defaultEndIndex ?? totalPoints - 1, totalPoints - 1)
501
+ )
502
+ }));
503
+ const lastCommittedRef = React2.useRef(internalRange);
504
+ useEffect(() => {
505
+ if (!isControlled) {
506
+ setInternalRange((prev) => {
507
+ const adjusted = {
508
+ startIndex: Math.min(prev.startIndex, Math.max(0, totalPoints - 1)),
509
+ endIndex: Math.min(prev.endIndex, Math.max(0, totalPoints - 1))
510
+ };
511
+ lastCommittedRef.current = adjusted;
512
+ return adjusted;
513
+ });
514
+ }
515
+ }, [totalPoints, isControlled]);
516
+ const clampRange = useCallback(
517
+ (range2, mode) => {
518
+ let { startIndex, endIndex } = range2;
519
+ const maxIndex = Math.max(0, totalPoints - 1);
520
+ startIndex = Math.max(0, Math.min(startIndex, maxIndex));
521
+ endIndex = Math.max(0, Math.min(endIndex, maxIndex));
522
+ if (mode === "left") {
523
+ const maxStart = Math.max(0, endIndex - minSpan);
524
+ startIndex = Math.min(startIndex, maxStart);
525
+ return { startIndex, endIndex };
526
+ }
527
+ if (mode === "right") {
528
+ const minEnd = Math.min(maxIndex, startIndex + minSpan);
529
+ endIndex = Math.max(endIndex, minEnd);
530
+ return { startIndex, endIndex };
531
+ }
532
+ if (endIndex - startIndex < minSpan) {
533
+ endIndex = Math.min(startIndex + minSpan, maxIndex);
534
+ if (endIndex - startIndex < minSpan) {
535
+ startIndex = Math.max(0, endIndex - minSpan);
536
+ }
537
+ }
538
+ return { startIndex, endIndex };
539
+ },
540
+ [totalPoints, minSpan]
541
+ );
542
+ const commit = useCallback(
543
+ (next, mode) => {
544
+ const clamped = clampRange(next, mode);
545
+ const last = lastCommittedRef.current;
546
+ if (last.startIndex === clamped.startIndex && last.endIndex === clamped.endIndex) {
547
+ return;
548
+ }
549
+ lastCommittedRef.current = clamped;
550
+ setInternalRange(clamped);
551
+ React2.startTransition(() => {
552
+ onChange?.(clamped);
553
+ });
554
+ },
555
+ [clampRange, onChange]
556
+ );
557
+ const { isDragging, bind } = useBrushDrag({
558
+ range: internalRange,
559
+ totalPoints,
560
+ containerRef,
561
+ commit
562
+ });
563
+ const range = internalRange;
564
+ useEffect(() => {
565
+ if (isControlled && !isDragging) {
566
+ const syncedRange = {
567
+ startIndex: controlledStart,
568
+ endIndex: controlledEnd
569
+ };
570
+ setInternalRange(syncedRange);
571
+ lastCommittedRef.current = syncedRange;
572
+ }
573
+ }, [isControlled, controlledStart, controlledEnd, isDragging]);
574
+ const leftPct = totalPoints > 1 ? range.startIndex / (totalPoints - 1) * 100 : 0;
575
+ const rightPct = totalPoints > 1 ? range.endIndex / (totalPoints - 1) * 100 : 100;
576
+ const leftTarget = useMotionValue(leftPct);
577
+ const rightTarget = useMotionValue(rightPct);
578
+ if (leftTarget.get() !== leftPct) leftTarget.set(leftPct);
579
+ if (rightTarget.get() !== rightPct) rightTarget.set(rightPct);
580
+ const leftSpring = useSpring(leftTarget, SPRING_CONFIG);
581
+ const rightSpring = useSpring(rightTarget, SPRING_CONFIG);
582
+ const leftPosition = useTransform(leftSpring, (v) => `${v}%`);
583
+ const rightPosition = useTransform(rightSpring, (v) => `${v}%`);
584
+ const leftOverlayWidth = useTransform(leftSpring, (v) => `${v}%`);
585
+ const rightOverlayWidth = useTransform(
586
+ rightSpring,
587
+ (v) => `${Math.max(0, 100 - v)}%`
588
+ );
589
+ const selectedWidth = useMotionValue(`${Math.max(0, rightPct - leftPct)}%`);
590
+ const updateSelectedWidth = useCallback(() => {
591
+ selectedWidth.set(`${Math.max(0, rightSpring.get() - leftSpring.get())}%`);
592
+ }, [leftSpring, rightSpring, selectedWidth]);
593
+ useMotionValueEvent(leftSpring, "change", updateSelectedWidth);
594
+ useMotionValueEvent(rightSpring, "change", updateSelectedWidth);
595
+ const getLabel = useCallback(
596
+ (idx) => {
597
+ if (!xDataKey) return String(idx);
598
+ const v = data[idx]?.[xDataKey];
599
+ return formatLabel ? formatLabel(v, idx) : String(v ?? idx);
600
+ },
601
+ [data, xDataKey, formatLabel]
602
+ );
603
+ if (totalPoints === 0) return null;
604
+ return /* @__PURE__ */ jsxs3(
605
+ "div",
606
+ {
607
+ ref: containerRef,
608
+ "data-chart": skipStyle ? void 0 : chartId,
609
+ className: cn("group relative select-none", className),
610
+ style: { height },
611
+ children: [
612
+ !skipStyle && /* @__PURE__ */ jsx3(ChartStyle, { id: chartId, config: chartConfig }),
613
+ /* @__PURE__ */ jsx3("div", { className: "rounded-bruv-md absolute inset-0 overflow-hidden", children: /* @__PURE__ */ jsx3(
614
+ MiniChart,
615
+ {
616
+ data,
617
+ keys,
618
+ chartConfig,
619
+ variant,
620
+ curveType,
621
+ chartId,
622
+ stacked,
623
+ strokeVariant: strokeVariant === "animated-dashed" ? "dashed" : strokeVariant,
624
+ connectNulls,
625
+ barRadius
626
+ }
627
+ ) }),
628
+ /* @__PURE__ */ jsx3(
629
+ motion.div,
630
+ {
631
+ className: "bg-bruv-base-2/70 pointer-events-none absolute inset-y-0 left-0 rounded-l-md backdrop-blur-[2px]",
632
+ style: { width: leftOverlayWidth }
633
+ }
634
+ ),
635
+ /* @__PURE__ */ jsx3(
636
+ motion.div,
637
+ {
638
+ className: "bg-bruv-base-2/70 pointer-events-none absolute inset-y-0 right-0 rounded-r-md backdrop-blur-[2px]",
639
+ style: { width: rightOverlayWidth }
640
+ }
641
+ ),
642
+ /* @__PURE__ */ jsx3(
643
+ motion.div,
644
+ {
645
+ className: "rounded-bruv-sm absolute inset-y-0 cursor-grab touch-none border active:cursor-grabbing",
646
+ style: { left: leftPosition, width: selectedWidth },
647
+ ...bind("middle")
648
+ }
649
+ ),
650
+ /* @__PURE__ */ jsx3(
651
+ BrushHandle,
652
+ {
653
+ side: "left",
654
+ position: leftPosition,
655
+ label: showLabels ? getLabel(range.startIndex) : void 0,
656
+ bind: bind("left")
657
+ }
658
+ ),
659
+ /* @__PURE__ */ jsx3(
660
+ BrushHandle,
661
+ {
662
+ side: "right",
663
+ position: rightPosition,
664
+ label: showLabels ? getLabel(range.endIndex) : void 0,
665
+ bind: bind("right")
666
+ }
667
+ )
668
+ ]
669
+ }
670
+ );
671
+ }
672
+ function BrushHandle({
673
+ side,
674
+ position,
675
+ label,
676
+ bind
677
+ }) {
678
+ const isLeft = side === "left";
679
+ return /* @__PURE__ */ jsxs3(motion.div, { className: "absolute inset-y-0 z-10", style: { left: position }, children: [
680
+ /* @__PURE__ */ jsx3(
681
+ "div",
682
+ {
683
+ className: cn(
684
+ "group absolute inset-y-0 flex w-3 cursor-ew-resize touch-none items-center justify-center after:absolute after:inset-y-0 after:-left-4 after:w-11 after:content-['']",
685
+ isLeft ? "" : "-translate-x-full"
686
+ ),
687
+ ...bind,
688
+ children: /* @__PURE__ */ jsx3(
689
+ "div",
690
+ {
691
+ className: cn(
692
+ "bg-muted-foreground group-hover:bg-foreground relative flex h-4 w-1.5 items-center justify-center rounded-bruv-md transition-colors",
693
+ isLeft ? "-left-[5.5px]" : "-right-[5.5px]"
694
+ ),
695
+ children: /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-[2px]", children: [
696
+ /* @__PURE__ */ jsx3("div", { className: "bg-bruv-base-2/70 h-[2px] w-[2px] rounded-full" }),
697
+ /* @__PURE__ */ jsx3("div", { className: "bg-bruv-base-2/70 h-[2px] w-[2px] rounded-full" }),
698
+ /* @__PURE__ */ jsx3("div", { className: "bg-bruv-base-2/70 h-[2px] w-[2px] rounded-full" })
699
+ ] })
700
+ }
701
+ )
702
+ }
703
+ ),
704
+ label && /* @__PURE__ */ jsx3(
705
+ "div",
706
+ {
707
+ className: cn(
708
+ "bg-foreground text-bruv-base-1 pointer-events-none absolute -bottom-3 -translate-y-1/2 rounded-[3px] px-1 py-px text-[8px] leading-tight font-medium whitespace-nowrap opacity-0 group-hover:opacity-100",
709
+ isLeft ? "left-1.5" : "right-1.5"
710
+ ),
711
+ children: label
712
+ }
713
+ )
714
+ ] });
715
+ }
716
+ function MiniChart({
717
+ data,
718
+ keys,
719
+ chartConfig,
720
+ variant,
721
+ curveType,
722
+ chartId,
723
+ stacked,
724
+ strokeVariant = "solid",
725
+ connectNulls = false,
726
+ barRadius
727
+ }) {
728
+ const gradients = React2.useMemo(
729
+ () => Object.entries(chartConfig).filter(([key]) => keys.includes(key)).map(([dataKey, config]) => ({
730
+ dataKey,
731
+ colorsCount: getColorsCount(config)
732
+ })),
733
+ [chartConfig, keys]
734
+ );
735
+ const dashArray = strokeVariant === "dashed" || strokeVariant === "animated-dashed" ? "4 4" : void 0;
736
+ const defsContent = /* @__PURE__ */ jsxs3(Fragment3, { children: [
737
+ variant === "area" && /* @__PURE__ */ jsxs3(
738
+ "linearGradient",
739
+ {
740
+ id: `${chartId}-zm-vertical-fade`,
741
+ x1: "0",
742
+ y1: "0",
743
+ x2: "0",
744
+ y2: "1",
745
+ children: [
746
+ /* @__PURE__ */ jsx3("stop", { offset: "0%", stopColor: "white", stopOpacity: 0.15 }),
747
+ /* @__PURE__ */ jsx3("stop", { offset: "100%", stopColor: "white", stopOpacity: 0 })
748
+ ]
749
+ }
750
+ ),
751
+ gradients.map(({ dataKey, colorsCount }) => {
752
+ const colorStops = colorsCount === 1 ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
753
+ /* @__PURE__ */ jsx3("stop", { offset: "0%", stopColor: `var(--color-${dataKey}-0)` }),
754
+ /* @__PURE__ */ jsx3("stop", { offset: "100%", stopColor: `var(--color-${dataKey}-0)` })
755
+ ] }) : Array.from({ length: colorsCount }, (_, i) => /* @__PURE__ */ jsx3(
756
+ "stop",
757
+ {
758
+ offset: `${i / (colorsCount - 1) * 100}%`,
759
+ stopColor: `var(--color-${dataKey}-${i}, var(--color-${dataKey}-0))`
760
+ },
761
+ i
762
+ ));
763
+ return /* @__PURE__ */ jsxs3(React2.Fragment, { children: [
764
+ /* @__PURE__ */ jsx3(
765
+ "linearGradient",
766
+ {
767
+ id: `${chartId}-zm-${dataKey}`,
768
+ x1: "0",
769
+ y1: "0",
770
+ x2: "0",
771
+ y2: "1",
772
+ children: colorStops
773
+ }
774
+ ),
775
+ variant === "area" && /* @__PURE__ */ jsxs3(Fragment3, { children: [
776
+ /* @__PURE__ */ jsx3("mask", { id: `${chartId}-zm-fill-mask-${dataKey}`, children: /* @__PURE__ */ jsx3(
777
+ "rect",
778
+ {
779
+ width: "100%",
780
+ height: "100%",
781
+ fill: `url(#${chartId}-zm-vertical-fade)`
782
+ }
783
+ ) }),
784
+ /* @__PURE__ */ jsx3(
785
+ "pattern",
786
+ {
787
+ id: `${chartId}-zm-fill-${dataKey}`,
788
+ patternUnits: "userSpaceOnUse",
789
+ width: "100%",
790
+ height: "100%",
791
+ children: /* @__PURE__ */ jsx3(
792
+ "rect",
793
+ {
794
+ width: "100%",
795
+ height: "100%",
796
+ fill: `url(#${chartId}-zm-${dataKey})`,
797
+ mask: `url(#${chartId}-zm-fill-mask-${dataKey})`
798
+ }
799
+ )
800
+ }
801
+ )
802
+ ] })
803
+ ] }, dataKey);
804
+ })
805
+ ] });
806
+ if (variant === "line") {
807
+ return /* @__PURE__ */ jsx3(ResponsiveContainer2, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs3(
808
+ LineChart,
809
+ {
810
+ data,
811
+ margin: { top: 4, right: 0, bottom: 0, left: 0 },
812
+ children: [
813
+ /* @__PURE__ */ jsx3("defs", { children: defsContent }),
814
+ keys.map((dk) => /* @__PURE__ */ jsx3(
815
+ Line,
816
+ {
817
+ type: curveType,
818
+ dataKey: dk,
819
+ stroke: `url(#${chartId}-zm-${dk})`,
820
+ strokeWidth: 1,
821
+ strokeOpacity: 0.5,
822
+ strokeDasharray: dashArray,
823
+ connectNulls,
824
+ dot: false,
825
+ activeDot: false,
826
+ isAnimationActive: false
827
+ },
828
+ dk
829
+ ))
830
+ ]
831
+ }
832
+ ) });
833
+ }
834
+ if (variant === "bar") {
835
+ const r = barRadius ?? 3;
836
+ return /* @__PURE__ */ jsx3(ResponsiveContainer2, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs3(
837
+ BarChart,
838
+ {
839
+ data,
840
+ margin: { top: 2, right: 0, bottom: 0, left: 0 },
841
+ barGap: 2,
842
+ barSize: 14,
843
+ children: [
844
+ /* @__PURE__ */ jsx3("defs", { children: defsContent }),
845
+ keys.map((dk) => /* @__PURE__ */ jsx3(
846
+ Bar,
847
+ {
848
+ dataKey: dk,
849
+ fill: `url(#${chartId}-zm-${dk})`,
850
+ fillOpacity: 0.35,
851
+ stackId: stacked ? "zm-stack" : void 0,
852
+ isAnimationActive: false,
853
+ radius: [r, r, r, r]
854
+ },
855
+ dk
856
+ ))
857
+ ]
858
+ }
859
+ ) });
860
+ }
861
+ return /* @__PURE__ */ jsx3(ResponsiveContainer2, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs3(AreaChart, { data, margin: { top: 4, right: 0, bottom: 0, left: 0 }, children: [
862
+ /* @__PURE__ */ jsx3("defs", { children: defsContent }),
863
+ keys.map((dk) => /* @__PURE__ */ jsx3(
864
+ Area,
865
+ {
866
+ type: curveType,
867
+ dataKey: dk,
868
+ stroke: `url(#${chartId}-zm-${dk})`,
869
+ fill: `url(#${chartId}-zm-fill-${dk})`,
870
+ strokeWidth: 1,
871
+ strokeOpacity: 0.5,
872
+ strokeDasharray: dashArray,
873
+ connectNulls,
874
+ fillOpacity: 1,
875
+ stackId: stacked ? "zm-stack" : void 0,
876
+ dot: false,
877
+ activeDot: false,
878
+ isAnimationActive: false
879
+ },
880
+ dk
881
+ ))
882
+ ] }) });
883
+ }
884
+ function useBrush({
885
+ data,
886
+ defaultStartIndex = 0,
887
+ defaultEndIndex
888
+ }) {
889
+ const [range, setRange] = React2.useState({
890
+ startIndex: defaultStartIndex,
891
+ endIndex: defaultEndIndex ?? Math.max(0, data.length - 1)
892
+ });
893
+ const deferredRange = React2.useDeferredValue(range);
894
+ useEffect(() => {
895
+ setRange({
896
+ startIndex: 0,
897
+ endIndex: Math.max(0, data.length - 1)
898
+ });
899
+ }, [data.length]);
900
+ const visibleData = React2.useMemo(
901
+ () => data.slice(deferredRange.startIndex, deferredRange.endIndex + 1),
902
+ [data, deferredRange.startIndex, deferredRange.endIndex]
903
+ );
904
+ return {
905
+ range,
906
+ visibleData,
907
+ brushProps: {
908
+ startIndex: range.startIndex,
909
+ endIndex: range.endIndex,
910
+ onChange: setRange
911
+ }
912
+ };
913
+ }
914
+
915
+ // src/components/charts/chart-legend.tsx
916
+ import * as RechartsPrimitive3 from "recharts";
917
+ import "react";
918
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
919
+ function ChartLegendContent({
920
+ className,
921
+ hideIcon = false,
922
+ nameKey,
923
+ payload,
924
+ verticalAlign,
925
+ align = "right",
926
+ selected,
927
+ onSelectChange,
928
+ isClickable,
929
+ variant = "rounded-square"
930
+ }) {
931
+ const { config } = useChart();
932
+ if (!payload?.length) {
933
+ return null;
934
+ }
935
+ return /* @__PURE__ */ jsx4(
936
+ "div",
937
+ {
938
+ className: cn(
939
+ "flex items-center gap-4 select-none",
940
+ align === "left" && "justify-start",
941
+ align === "center" && "justify-center",
942
+ align === "right" && "justify-end",
943
+ verticalAlign === "top" ? "pb-4" : "pt-4",
944
+ className
945
+ ),
946
+ children: payload.filter((item) => item.type !== "none").map((item) => {
947
+ const payloadName = nameKey && item.payload ? item.payload[nameKey] : void 0;
948
+ const key = `${payloadName ?? item.value ?? item.dataKey ?? "value"}`;
949
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
950
+ const isSelected = selected === null || selected === key;
951
+ const colorsCount = itemConfig ? getColorsCount(itemConfig) : 1;
952
+ return /* @__PURE__ */ jsxs4(
953
+ "div",
954
+ {
955
+ className: cn(
956
+ "[&>svg]:text-bruv-secondary flex items-center gap-1.5 transition-opacity [&>svg]:h-3 [&>svg]:w-3",
957
+ !isSelected && "opacity-30",
958
+ isClickable && "cursor-pointer"
959
+ ),
960
+ onClick: () => {
961
+ if (!isClickable) return;
962
+ onSelectChange?.(selected === key ? null : key);
963
+ },
964
+ children: [
965
+ itemConfig?.icon && !hideIcon ? /* @__PURE__ */ jsx4(itemConfig.icon, {}) : /* @__PURE__ */ jsx4(
966
+ LegendIndicator,
967
+ {
968
+ variant,
969
+ dataKey: key,
970
+ colorsCount
971
+ }
972
+ ),
973
+ itemConfig?.label
974
+ ]
975
+ },
976
+ key
977
+ );
978
+ })
979
+ }
980
+ );
981
+ }
982
+ function LegendIndicator({
983
+ variant,
984
+ dataKey,
985
+ colorsCount
986
+ }) {
987
+ const fillStyle = getLegendFillStyle(dataKey, colorsCount);
988
+ const outlineStyle = getLegendOutlineStyle(dataKey, colorsCount);
989
+ switch (variant) {
990
+ case "square":
991
+ return /* @__PURE__ */ jsx4("div", { className: "h-2 w-2 shrink-0", style: fillStyle });
992
+ case "circle":
993
+ return /* @__PURE__ */ jsx4("div", { className: "h-2 w-2 shrink-0 rounded-full", style: fillStyle });
994
+ case "circle-outline":
995
+ return /* @__PURE__ */ jsx4(
996
+ "div",
997
+ {
998
+ className: "h-2.5 w-2.5 shrink-0 rounded-full p-[1.5px]",
999
+ style: outlineStyle
1000
+ }
1001
+ );
1002
+ case "vertical-bar":
1003
+ return /* @__PURE__ */ jsx4("div", { className: "h-3 w-1 shrink-0 rounded-[2px]", style: fillStyle });
1004
+ case "horizontal-bar":
1005
+ return /* @__PURE__ */ jsx4("div", { className: "h-1 w-3 shrink-0 rounded-[2px]", style: fillStyle });
1006
+ case "rounded-square-outline":
1007
+ return /* @__PURE__ */ jsx4(
1008
+ "div",
1009
+ {
1010
+ className: "h-2.5 w-2.5 shrink-0 rounded-[3px] p-[1.5px]",
1011
+ style: outlineStyle
1012
+ }
1013
+ );
1014
+ case "rounded-square":
1015
+ default:
1016
+ return /* @__PURE__ */ jsx4("div", { className: "h-2 w-2 shrink-0 rounded-[2px]", style: fillStyle });
1017
+ }
1018
+ }
1019
+ function getLegendFillStyle(dataKey, colorsCount) {
1020
+ if (colorsCount <= 1) {
1021
+ return { backgroundColor: `var(--color-${dataKey}-0)` };
1022
+ }
1023
+ const stops = Array.from({ length: colorsCount }, (_, i) => {
1024
+ const offset = i / (colorsCount - 1) * 100;
1025
+ return `var(--color-${dataKey}-${i}) ${offset}%`;
1026
+ }).join(", ");
1027
+ return { background: `linear-gradient(to right, ${stops})` };
1028
+ }
1029
+ function getLegendOutlineStyle(dataKey, colorsCount) {
1030
+ const maskStyle = {
1031
+ WebkitMask: "linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)",
1032
+ WebkitMaskComposite: "xor",
1033
+ mask: "linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)",
1034
+ maskComposite: "exclude"
1035
+ };
1036
+ if (colorsCount <= 1) {
1037
+ return {
1038
+ backgroundColor: `var(--color-${dataKey}-0)`,
1039
+ ...maskStyle
1040
+ };
1041
+ }
1042
+ const stops = Array.from({ length: colorsCount }, (_, i) => {
1043
+ const offset = i / (colorsCount - 1) * 100;
1044
+ return `var(--color-${dataKey}-${i}) ${offset}%`;
1045
+ }).join(", ");
1046
+ return {
1047
+ background: `linear-gradient(to right, ${stops})`,
1048
+ ...maskStyle
1049
+ };
1050
+ }
1051
+ var ChartLegend = RechartsPrimitive3.Legend;
1052
+
1053
+ // src/components/charts/chart-dot.tsx
1054
+ import * as React4 from "react";
1055
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1056
+ var ChartDot = React4.memo(function ChartDot2({
1057
+ cx,
1058
+ cy,
1059
+ dataKey,
1060
+ chartId,
1061
+ className,
1062
+ fillOpacity = 1,
1063
+ type = "default",
1064
+ maskId
1065
+ }) {
1066
+ const dotId = React4.useId().replace(/:/g, "");
1067
+ const gradientUrl = `url(#${chartId}-colors-${String(dataKey)})`;
1068
+ if (cx === void 0 || cy === void 0) return null;
1069
+ switch (type) {
1070
+ case "border":
1071
+ return /* @__PURE__ */ jsx5(
1072
+ PrimaryBorderDot,
1073
+ {
1074
+ cx,
1075
+ cy,
1076
+ dotId,
1077
+ fillOpacity,
1078
+ gradientUrl,
1079
+ className,
1080
+ maskId
1081
+ }
1082
+ );
1083
+ case "colored-border":
1084
+ return /* @__PURE__ */ jsx5(
1085
+ ColoredBorderDot,
1086
+ {
1087
+ cx,
1088
+ cy,
1089
+ dotId,
1090
+ fillOpacity,
1091
+ gradientUrl,
1092
+ className,
1093
+ maskId
1094
+ }
1095
+ );
1096
+ default:
1097
+ return /* @__PURE__ */ jsx5(
1098
+ DefaultDot,
1099
+ {
1100
+ cx,
1101
+ cy,
1102
+ dotId,
1103
+ fillOpacity,
1104
+ gradientUrl,
1105
+ className,
1106
+ maskId
1107
+ }
1108
+ );
1109
+ }
1110
+ });
1111
+ var DefaultDot = React4.memo(
1112
+ ({
1113
+ cx,
1114
+ cy,
1115
+ dotId,
1116
+ fillOpacity,
1117
+ gradientUrl,
1118
+ className,
1119
+ maskId
1120
+ }) => {
1121
+ const r = 3;
1122
+ return /* @__PURE__ */ jsxs5("g", { className, mask: maskId ? `url(#${maskId})` : void 0, children: [
1123
+ /* @__PURE__ */ jsx5("defs", { children: /* @__PURE__ */ jsx5("clipPath", { id: `dot-clip-${dotId}`, children: /* @__PURE__ */ jsx5("circle", { cx, cy, r }) }) }),
1124
+ /* @__PURE__ */ jsx5(
1125
+ "rect",
1126
+ {
1127
+ x: "0",
1128
+ y: cy - r,
1129
+ width: "100%",
1130
+ height: r * 2,
1131
+ fill: gradientUrl,
1132
+ fillOpacity,
1133
+ clipPath: `url(#dot-clip-${dotId})`
1134
+ }
1135
+ )
1136
+ ] });
1137
+ }
1138
+ );
1139
+ DefaultDot.displayName = "DefaultDot";
1140
+ var PrimaryBorderDot = React4.memo(
1141
+ ({
1142
+ cx,
1143
+ cy,
1144
+ dotId,
1145
+ fillOpacity,
1146
+ gradientUrl,
1147
+ className,
1148
+ maskId
1149
+ }) => {
1150
+ const r = 6;
1151
+ const strokeWidth = 5;
1152
+ return /* @__PURE__ */ jsxs5(
1153
+ "g",
1154
+ {
1155
+ className: cn(className, "text-bruv-base-1"),
1156
+ mask: maskId ? `url(#${maskId})` : void 0,
1157
+ children: [
1158
+ /* @__PURE__ */ jsx5("defs", { children: /* @__PURE__ */ jsx5("clipPath", { id: `dot-clip-${dotId}`, children: /* @__PURE__ */ jsx5("circle", { cx, cy, r }) }) }),
1159
+ /* @__PURE__ */ jsx5("circle", { cx, cy, r, fill: "currentColor" }),
1160
+ /* @__PURE__ */ jsx5(
1161
+ "rect",
1162
+ {
1163
+ x: "0",
1164
+ y: cy - (r - strokeWidth / 2),
1165
+ width: "100%",
1166
+ height: (r - strokeWidth / 2) * 2,
1167
+ fill: gradientUrl,
1168
+ fillOpacity,
1169
+ clipPath: `url(#dot-clip-inner-${dotId})`
1170
+ }
1171
+ ),
1172
+ /* @__PURE__ */ jsx5("defs", { children: /* @__PURE__ */ jsx5("clipPath", { id: `dot-clip-inner-${dotId}`, children: /* @__PURE__ */ jsx5("circle", { cx, cy, r: r - strokeWidth / 2 }) }) })
1173
+ ]
1174
+ }
1175
+ );
1176
+ }
1177
+ );
1178
+ PrimaryBorderDot.displayName = "PrimaryBorderDot";
1179
+ var ColoredBorderDot = React4.memo(
1180
+ ({
1181
+ cx,
1182
+ cy,
1183
+ dotId,
1184
+ fillOpacity,
1185
+ gradientUrl,
1186
+ className,
1187
+ maskId
1188
+ }) => {
1189
+ const r = 3;
1190
+ const strokeWidth = 1;
1191
+ return /* @__PURE__ */ jsxs5(
1192
+ "g",
1193
+ {
1194
+ className: cn(className, "text-bruv-base-1"),
1195
+ mask: maskId ? `url(#${maskId})` : void 0,
1196
+ children: [
1197
+ /* @__PURE__ */ jsx5("defs", { children: /* @__PURE__ */ jsx5("clipPath", { id: `dot-clip-${dotId}`, children: /* @__PURE__ */ jsx5("circle", { cx, cy, r: r + strokeWidth / 2 }) }) }),
1198
+ /* @__PURE__ */ jsx5(
1199
+ "rect",
1200
+ {
1201
+ x: "0",
1202
+ y: cy - r - strokeWidth / 2,
1203
+ width: "100%",
1204
+ height: (r + strokeWidth / 2) * 2,
1205
+ fill: gradientUrl,
1206
+ fillOpacity,
1207
+ clipPath: `url(#dot-clip-${dotId})`
1208
+ }
1209
+ ),
1210
+ /* @__PURE__ */ jsx5("circle", { cx, cy, r: r - strokeWidth / 2, fill: "currentColor" })
1211
+ ]
1212
+ }
1213
+ );
1214
+ }
1215
+ );
1216
+ ColoredBorderDot.displayName = "ColoredBorderDot";
1217
+
1218
+ // src/components/charts/line-chart.tsx
1219
+ import {
1220
+ Children,
1221
+ createContext as createContext2,
1222
+ isValidElement,
1223
+ use,
1224
+ useCallback as useCallback2,
1225
+ useId as useId4,
1226
+ useMemo as useMemo3,
1227
+ useRef as useRef2,
1228
+ useState as useState2
1229
+ } from "react";
1230
+ import { motion as motion2, useReducedMotion } from "motion/react";
1231
+ import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1232
+ var STROKE_WIDTH = 1;
1233
+ var LOADING_LINE_DATA_KEY = "loading";
1234
+ var LOADING_ANIMATION_DURATION = 2e3;
1235
+ var REVEAL_DURATION = 1;
1236
+ var REVEAL_EASE = [0, 0.7, 0.5, 1];
1237
+ var LineChartContext = createContext2(null);
1238
+ function useLineChart() {
1239
+ const context = use(LineChartContext);
1240
+ if (!context) {
1241
+ throw new Error(
1242
+ "Line chart parts (<Line />, <XAxis />, \u2026) must be used within <LineChart />"
1243
+ );
1244
+ }
1245
+ return context;
1246
+ }
1247
+ function LineChart2({
1248
+ config,
1249
+ data,
1250
+ children,
1251
+ className,
1252
+ chartProps,
1253
+ curveType = "linear",
1254
+ animationType = "left-to-right",
1255
+ defaultSelectedDataKey = null,
1256
+ onSelectionChange,
1257
+ isLoading = false,
1258
+ loadingPoints,
1259
+ showBrush = false,
1260
+ xDataKey,
1261
+ brushHeight,
1262
+ brushFormatLabel,
1263
+ onBrushChange
1264
+ }) {
1265
+ const chartId = useId4().replace(/:/g, "");
1266
+ const [selectedDataKey, setSelectedDataKey] = useState2(
1267
+ defaultSelectedDataKey
1268
+ );
1269
+ const { loadingData, onShimmerExit } = useLoadingData(
1270
+ isLoading,
1271
+ loadingPoints
1272
+ );
1273
+ const { visibleData, brushProps } = useBrush({ data });
1274
+ const displayData = showBrush && !isLoading ? visibleData : data;
1275
+ const selectDataKey = useCallback2(
1276
+ (newSelectedDataKey) => {
1277
+ setSelectedDataKey(newSelectedDataKey);
1278
+ onSelectionChange?.(newSelectedDataKey);
1279
+ },
1280
+ [onSelectionChange]
1281
+ );
1282
+ const contextValue = useMemo3(
1283
+ () => ({
1284
+ config,
1285
+ curveType,
1286
+ animationType,
1287
+ isLoading,
1288
+ selectedDataKey,
1289
+ selectDataKey
1290
+ }),
1291
+ [
1292
+ config,
1293
+ curveType,
1294
+ animationType,
1295
+ isLoading,
1296
+ selectedDataKey,
1297
+ selectDataKey
1298
+ ]
1299
+ );
1300
+ return /* @__PURE__ */ jsx6(LineChartContext, { value: contextValue, children: /* @__PURE__ */ jsxs6(
1301
+ Chart,
1302
+ {
1303
+ className,
1304
+ config,
1305
+ footer: showBrush && !isLoading && /* @__PURE__ */ jsx6(
1306
+ Brush,
1307
+ {
1308
+ data,
1309
+ chartConfig: config,
1310
+ xDataKey,
1311
+ variant: "line",
1312
+ curveType,
1313
+ height: brushHeight,
1314
+ formatLabel: brushFormatLabel,
1315
+ skipStyle: true,
1316
+ className: "mt-1",
1317
+ ...brushProps,
1318
+ onChange: (range) => {
1319
+ brushProps.onChange(range);
1320
+ onBrushChange?.(range);
1321
+ }
1322
+ }
1323
+ ),
1324
+ children: [
1325
+ /* @__PURE__ */ jsx6(LoadingIndicator, { isLoading }),
1326
+ /* @__PURE__ */ jsxs6(
1327
+ RechartsLineChart,
1328
+ {
1329
+ id: chartId,
1330
+ accessibilityLayer: true,
1331
+ data: isLoading ? loadingData : displayData,
1332
+ ...chartProps,
1333
+ children: [
1334
+ children,
1335
+ isLoading && /* @__PURE__ */ jsx6(
1336
+ LoadingLine,
1337
+ {
1338
+ chartId,
1339
+ curveType,
1340
+ onShimmerExit
1341
+ }
1342
+ )
1343
+ ]
1344
+ }
1345
+ )
1346
+ ]
1347
+ }
1348
+ ) });
1349
+ }
1350
+ function Line2({
1351
+ dataKey,
1352
+ strokeVariant = "solid",
1353
+ curveType,
1354
+ animationType,
1355
+ connectNulls = false,
1356
+ isClickable = false,
1357
+ enableBufferLine = false,
1358
+ children,
1359
+ lineProps
1360
+ }) {
1361
+ const {
1362
+ config,
1363
+ curveType: defaultCurve,
1364
+ animationType: defaultAnimation,
1365
+ isLoading,
1366
+ selectedDataKey,
1367
+ selectDataKey
1368
+ } = useLineChart();
1369
+ const id = useId4().replace(/:/g, "");
1370
+ const shouldReduceMotion = useReducedMotion();
1371
+ if (isLoading) return null;
1372
+ const resolvedCurve = curveType ?? defaultCurve;
1373
+ const revealType = shouldReduceMotion ? "none" : animationType ?? defaultAnimation;
1374
+ const maskId = revealType === "none" ? void 0 : `${id}-reveal-mask`;
1375
+ const isSelected = selectedDataKey === dataKey;
1376
+ const hasSelection = selectedDataKey !== null;
1377
+ const opacity = getOpacity(selectedDataKey, dataKey);
1378
+ const { dot, activeDot } = resolveDots(
1379
+ children,
1380
+ id,
1381
+ dataKey,
1382
+ opacity.dot,
1383
+ maskId
1384
+ );
1385
+ const isAnimatedDashed = strokeVariant === "animated-dashed";
1386
+ const isDashed = strokeVariant === "dashed" || isAnimatedDashed;
1387
+ return /* @__PURE__ */ jsxs6(Fragment4, { children: [
1388
+ /* @__PURE__ */ jsxs6("g", { children: [
1389
+ isClickable && /* @__PURE__ */ jsx6(
1390
+ RechartsLine,
1391
+ {
1392
+ type: resolvedCurve,
1393
+ dataKey,
1394
+ connectNulls,
1395
+ stroke: "transparent",
1396
+ strokeWidth: 15,
1397
+ dot: false,
1398
+ activeDot: false,
1399
+ isAnimationActive: false,
1400
+ legendType: "none",
1401
+ tooltipType: "none",
1402
+ style: { cursor: "pointer" },
1403
+ onClick: () => selectDataKey(isSelected ? null : dataKey)
1404
+ }
1405
+ ),
1406
+ /* @__PURE__ */ jsx6(
1407
+ RechartsLine,
1408
+ {
1409
+ type: resolvedCurve,
1410
+ dataKey,
1411
+ connectNulls,
1412
+ strokeOpacity: opacity.stroke,
1413
+ stroke: `url(#${id}-colors-${dataKey})`,
1414
+ dot,
1415
+ activeDot,
1416
+ strokeWidth: STROKE_WIDTH,
1417
+ strokeDasharray: getStrokeDasharray(enableBufferLine, isDashed),
1418
+ shape: enableBufferLine ? bufferLineShape : void 0,
1419
+ isAnimationActive: false,
1420
+ style: {
1421
+ ...maskId ? { mask: `url(#${maskId})` } : {},
1422
+ ...isClickable ? { cursor: "pointer" } : {}
1423
+ },
1424
+ onClick: () => {
1425
+ if (!isClickable) return;
1426
+ selectDataKey(isSelected ? null : dataKey);
1427
+ },
1428
+ ...lineProps,
1429
+ children: isAnimatedDashed && !hasSelection && /* @__PURE__ */ jsx6(AnimatedDashedStroke, {})
1430
+ }
1431
+ )
1432
+ ] }, dataKey),
1433
+ /* @__PURE__ */ jsxs6("defs", { children: [
1434
+ revealType !== "none" && /* @__PURE__ */ jsx6(RevealMask, { id, type: revealType }),
1435
+ /* @__PURE__ */ jsx6(ColorGradient, { id, dataKey, config })
1436
+ ] })
1437
+ ] });
1438
+ }
1439
+ var Dot = () => null;
1440
+ var ActiveDot = () => null;
1441
+ function XAxis({
1442
+ tickLine = false,
1443
+ axisLine = false,
1444
+ tickMargin = 8,
1445
+ minTickGap = 8,
1446
+ ...props
1447
+ }) {
1448
+ const { isLoading } = useLineChart();
1449
+ if (isLoading) return null;
1450
+ return /* @__PURE__ */ jsx6(
1451
+ RechartsXAxis,
1452
+ {
1453
+ tickLine,
1454
+ axisLine,
1455
+ tickMargin,
1456
+ minTickGap,
1457
+ ...props
1458
+ }
1459
+ );
1460
+ }
1461
+ function YAxis({
1462
+ tickLine = false,
1463
+ axisLine = false,
1464
+ tickMargin = 8,
1465
+ minTickGap = 8,
1466
+ width = "auto",
1467
+ ...props
1468
+ }) {
1469
+ const { isLoading } = useLineChart();
1470
+ if (isLoading) return null;
1471
+ return /* @__PURE__ */ jsx6(
1472
+ RechartsYAxis,
1473
+ {
1474
+ tickLine,
1475
+ axisLine,
1476
+ tickMargin,
1477
+ minTickGap,
1478
+ width,
1479
+ ...props
1480
+ }
1481
+ );
1482
+ }
1483
+ function Grid({
1484
+ vertical = false,
1485
+ strokeDasharray = "3 3",
1486
+ ...props
1487
+ }) {
1488
+ return /* @__PURE__ */ jsx6(
1489
+ CartesianGrid,
1490
+ {
1491
+ vertical,
1492
+ strokeDasharray,
1493
+ ...props
1494
+ }
1495
+ );
1496
+ }
1497
+ function Tooltip2({
1498
+ variant,
1499
+ roundness,
1500
+ defaultIndex,
1501
+ cursor = true
1502
+ }) {
1503
+ const { isLoading, selectedDataKey } = useLineChart();
1504
+ if (isLoading) return null;
1505
+ return /* @__PURE__ */ jsx6(
1506
+ ChartTooltip,
1507
+ {
1508
+ defaultIndex,
1509
+ cursor: cursor ? { strokeDasharray: "3 3", strokeWidth: STROKE_WIDTH } : false,
1510
+ content: /* @__PURE__ */ jsx6(
1511
+ ChartTooltipContent,
1512
+ {
1513
+ selected: selectedDataKey,
1514
+ roundness,
1515
+ variant
1516
+ }
1517
+ )
1518
+ }
1519
+ );
1520
+ }
1521
+ function Legend2({
1522
+ variant,
1523
+ align = "right",
1524
+ verticalAlign = "top",
1525
+ isClickable = false
1526
+ }) {
1527
+ const { selectedDataKey, selectDataKey } = useLineChart();
1528
+ return /* @__PURE__ */ jsx6(
1529
+ ChartLegend,
1530
+ {
1531
+ verticalAlign,
1532
+ align,
1533
+ content: /* @__PURE__ */ jsx6(
1534
+ ChartLegendContent,
1535
+ {
1536
+ selected: selectedDataKey,
1537
+ onSelectChange: selectDataKey,
1538
+ isClickable,
1539
+ variant
1540
+ }
1541
+ )
1542
+ }
1543
+ );
1544
+ }
1545
+ var getOpacity = (selectedDataKey, dataKey) => {
1546
+ if (selectedDataKey === null) {
1547
+ return { stroke: 1, dot: 1 };
1548
+ }
1549
+ return selectedDataKey === dataKey ? { stroke: 1, dot: 1 } : { stroke: 0.3, dot: 0.3 };
1550
+ };
1551
+ var getStrokeDasharray = (enableBufferLine, isDashed) => {
1552
+ if (enableBufferLine) return void 0;
1553
+ return isDashed ? "5 5" : void 0;
1554
+ };
1555
+ var resolveDots = (children, id, dataKey, dotOpacity, maskId) => {
1556
+ let dot = false;
1557
+ let activeDot = false;
1558
+ Children.forEach(children, (child) => {
1559
+ if (!isValidElement(child)) return;
1560
+ if (child.type === Dot) {
1561
+ const { variant } = child.props;
1562
+ dot = /* @__PURE__ */ jsx6(
1563
+ ChartDot,
1564
+ {
1565
+ type: variant,
1566
+ dataKey,
1567
+ chartId: id,
1568
+ fillOpacity: dotOpacity,
1569
+ maskId
1570
+ }
1571
+ );
1572
+ }
1573
+ if (child.type === ActiveDot) {
1574
+ const { variant } = child.props;
1575
+ activeDot = /* @__PURE__ */ jsx6(
1576
+ ChartDot,
1577
+ {
1578
+ type: variant,
1579
+ dataKey,
1580
+ chartId: id,
1581
+ fillOpacity: dotOpacity
1582
+ }
1583
+ );
1584
+ }
1585
+ });
1586
+ return { dot, activeDot };
1587
+ };
1588
+ var isDrawableCurvePoint = (point) => {
1589
+ return typeof point.x === "number" && typeof point.y === "number";
1590
+ };
1591
+ var BUFFER_DASH_SIZE = 4;
1592
+ var BUFFER_GAP_SIZE = 3;
1593
+ var findLengthAtX = (path, totalLength, targetX) => {
1594
+ let lo = 0;
1595
+ let hi = totalLength;
1596
+ while (hi - lo > 0.5) {
1597
+ const mid = (lo + hi) / 2;
1598
+ const pt = path.getPointAtLength(mid);
1599
+ if (pt.x < targetX) lo = mid;
1600
+ else hi = mid;
1601
+ }
1602
+ return (lo + hi) / 2;
1603
+ };
1604
+ var bufferLineShape = (props) => {
1605
+ const { points, ...rest } = props;
1606
+ if (!points || points.length < 2) {
1607
+ return /* @__PURE__ */ jsx6(Curve, { ...props });
1608
+ }
1609
+ const drawablePoints = points.filter(isDrawableCurvePoint);
1610
+ if (drawablePoints.length < 2) {
1611
+ return /* @__PURE__ */ jsx6(Curve, { ...props });
1612
+ }
1613
+ const splitX = drawablePoints[drawablePoints.length - 2].x;
1614
+ const gRef = (g) => {
1615
+ if (!g) return;
1616
+ const path = g.querySelector("path");
1617
+ if (!path) return;
1618
+ const totalLength = path.getTotalLength();
1619
+ const solidLength = findLengthAtX(path, totalLength, splitX);
1620
+ const lastSegmentLength = totalLength - solidLength;
1621
+ const reps = Math.ceil(lastSegmentLength / (BUFFER_DASH_SIZE + BUFFER_GAP_SIZE)) + 1;
1622
+ const dashedPart = Array.from(
1623
+ { length: reps },
1624
+ () => `${BUFFER_DASH_SIZE} ${BUFFER_GAP_SIZE}`
1625
+ ).join(" ");
1626
+ path.setAttribute("stroke-dasharray", `${solidLength} 0 ${dashedPart}`);
1627
+ };
1628
+ return /* @__PURE__ */ jsx6("g", { ref: gRef, children: /* @__PURE__ */ jsx6(Curve, { ...rest, points: drawablePoints }) });
1629
+ };
1630
+ var AnimatedDashedStroke = () => {
1631
+ return /* @__PURE__ */ jsxs6(Fragment4, { children: [
1632
+ /* @__PURE__ */ jsx6(
1633
+ "animate",
1634
+ {
1635
+ attributeName: "stroke-dasharray",
1636
+ values: "5 5; 0 5; 5 5",
1637
+ dur: "1s",
1638
+ repeatCount: "indefinite",
1639
+ keyTimes: "0;0.5;1"
1640
+ }
1641
+ ),
1642
+ /* @__PURE__ */ jsx6(
1643
+ "animate",
1644
+ {
1645
+ attributeName: "stroke-dashoffset",
1646
+ values: "0; -10",
1647
+ dur: "1s",
1648
+ repeatCount: "indefinite",
1649
+ keyTimes: "0;1"
1650
+ }
1651
+ )
1652
+ ] });
1653
+ };
1654
+ var SINGLE_REVEAL_ORIGIN = {
1655
+ "left-to-right": 0,
1656
+ "right-to-left": 1,
1657
+ "center-out": 0.5
1658
+ };
1659
+ var RevealMask = ({
1660
+ id,
1661
+ type
1662
+ }) => {
1663
+ const reveal = {
1664
+ initial: { scaleX: 0 },
1665
+ animate: { scaleX: 1 },
1666
+ transition: { duration: REVEAL_DURATION, ease: REVEAL_EASE }
1667
+ };
1668
+ return /* @__PURE__ */ jsx6(
1669
+ "mask",
1670
+ {
1671
+ id: `${id}-reveal-mask`,
1672
+ maskUnits: "userSpaceOnUse",
1673
+ maskContentUnits: "userSpaceOnUse",
1674
+ x: "0",
1675
+ y: "0",
1676
+ width: "100%",
1677
+ height: "100%",
1678
+ children: type === "edges-in" ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
1679
+ /* @__PURE__ */ jsx6(
1680
+ motion2.rect,
1681
+ {
1682
+ ...reveal,
1683
+ x: "0",
1684
+ y: "0",
1685
+ width: "50%",
1686
+ height: "100%",
1687
+ fill: "white",
1688
+ style: { originX: 0 }
1689
+ }
1690
+ ),
1691
+ /* @__PURE__ */ jsx6(
1692
+ motion2.rect,
1693
+ {
1694
+ ...reveal,
1695
+ x: "50%",
1696
+ y: "0",
1697
+ width: "50%",
1698
+ height: "100%",
1699
+ fill: "white",
1700
+ style: { originX: 1 }
1701
+ }
1702
+ )
1703
+ ] }) : /* @__PURE__ */ jsx6(
1704
+ motion2.rect,
1705
+ {
1706
+ ...reveal,
1707
+ x: "0",
1708
+ y: "0",
1709
+ width: "100%",
1710
+ height: "100%",
1711
+ fill: "white",
1712
+ style: { originX: SINGLE_REVEAL_ORIGIN[type] }
1713
+ }
1714
+ )
1715
+ }
1716
+ );
1717
+ };
1718
+ var ColorGradient = ({
1719
+ id,
1720
+ dataKey,
1721
+ config
1722
+ }) => {
1723
+ const colorsCount = getColorsCount(config[dataKey] ?? {});
1724
+ return /* @__PURE__ */ jsx6("linearGradient", { id: `${id}-colors-${dataKey}`, x1: "0", y1: "0", x2: "1", y2: "0", children: colorsCount === 1 ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
1725
+ /* @__PURE__ */ jsx6("stop", { offset: "0%", stopColor: `var(--color-${dataKey}-0)` }),
1726
+ /* @__PURE__ */ jsx6("stop", { offset: "100%", stopColor: `var(--color-${dataKey}-0)` })
1727
+ ] }) : Array.from({ length: colorsCount }, (_, index) => {
1728
+ const offset = `${index / (colorsCount - 1) * 100}%`;
1729
+ return /* @__PURE__ */ jsx6(
1730
+ "stop",
1731
+ {
1732
+ offset,
1733
+ stopColor: `var(--color-${dataKey}-${index}, var(--color-${dataKey}-0))`
1734
+ },
1735
+ offset
1736
+ );
1737
+ }) });
1738
+ };
1739
+ var generateEasedGradientStops = (steps = 17, minOpacity = 0.05, maxOpacity = 0.9) => {
1740
+ return Array.from({ length: steps }, (_, i) => {
1741
+ const t = i / (steps - 1);
1742
+ const eased = Math.sin(t * Math.PI) ** 2;
1743
+ const opacity = minOpacity + eased * (maxOpacity - minOpacity);
1744
+ return {
1745
+ offset: `${(t * 100).toFixed(0)}%`,
1746
+ opacity: Number(opacity.toFixed(3))
1747
+ };
1748
+ });
1749
+ };
1750
+ function useLoadingData(isLoading, loadingPoints = 14) {
1751
+ const [loadingDataKey, setLoadingDataKey] = useState2(false);
1752
+ const onShimmerExit = useCallback2(() => {
1753
+ if (isLoading) {
1754
+ setLoadingDataKey((prev) => !prev);
1755
+ }
1756
+ }, [isLoading]);
1757
+ const loadingData = useMemo3(
1758
+ () => getLoadingData(loadingPoints),
1759
+ [loadingPoints, loadingDataKey]
1760
+ );
1761
+ return { loadingData, onShimmerExit };
1762
+ }
1763
+ var LoadingLine = ({
1764
+ chartId,
1765
+ curveType,
1766
+ onShimmerExit
1767
+ }) => {
1768
+ return /* @__PURE__ */ jsxs6(Fragment4, { children: [
1769
+ /* @__PURE__ */ jsx6(
1770
+ RechartsLine,
1771
+ {
1772
+ type: curveType,
1773
+ dataKey: LOADING_LINE_DATA_KEY,
1774
+ min: 0,
1775
+ max: 100,
1776
+ stroke: "currentColor",
1777
+ strokeOpacity: 0.5,
1778
+ isAnimationActive: false,
1779
+ legendType: "none",
1780
+ tooltipType: "none",
1781
+ activeDot: false,
1782
+ dot: false,
1783
+ strokeWidth: STROKE_WIDTH,
1784
+ style: { mask: `url(#${chartId}-loading-mask)` }
1785
+ }
1786
+ ),
1787
+ /* @__PURE__ */ jsx6("defs", { children: /* @__PURE__ */ jsx6(LoadingPattern, { chartId, onShimmerExit }) })
1788
+ ] });
1789
+ };
1790
+ var LoadingPattern = ({
1791
+ chartId,
1792
+ onShimmerExit
1793
+ }) => {
1794
+ const gradientStops = generateEasedGradientStops();
1795
+ const patternWidth = 3;
1796
+ const startX = -1;
1797
+ const endX = 2;
1798
+ const lastXRef = useRef2(startX);
1799
+ return /* @__PURE__ */ jsxs6(Fragment4, { children: [
1800
+ /* @__PURE__ */ jsx6(
1801
+ "linearGradient",
1802
+ {
1803
+ id: `${chartId}-loading-gradient`,
1804
+ x1: "0",
1805
+ y1: "0",
1806
+ x2: "1",
1807
+ y2: "0",
1808
+ children: gradientStops.map(({ offset, opacity }) => /* @__PURE__ */ jsx6(
1809
+ "stop",
1810
+ {
1811
+ offset,
1812
+ stopColor: "white",
1813
+ stopOpacity: opacity
1814
+ },
1815
+ offset
1816
+ ))
1817
+ }
1818
+ ),
1819
+ /* @__PURE__ */ jsx6(
1820
+ "pattern",
1821
+ {
1822
+ id: `${chartId}-loading-pattern`,
1823
+ patternUnits: "objectBoundingBox",
1824
+ patternContentUnits: "objectBoundingBox",
1825
+ patternTransform: "rotate(25)",
1826
+ width: patternWidth,
1827
+ height: "1",
1828
+ x: "0",
1829
+ y: "0",
1830
+ children: /* @__PURE__ */ jsx6(
1831
+ motion2.rect,
1832
+ {
1833
+ y: "0",
1834
+ width: "1",
1835
+ height: "1",
1836
+ fill: `url(#${chartId}-loading-gradient)`,
1837
+ initial: { x: startX },
1838
+ animate: { x: endX },
1839
+ transition: {
1840
+ duration: LOADING_ANIMATION_DURATION / 1e3,
1841
+ ease: "linear",
1842
+ repeat: Infinity,
1843
+ repeatType: "loop"
1844
+ },
1845
+ onUpdate: (latest) => {
1846
+ const xValue = typeof latest.x === "number" ? latest.x : startX;
1847
+ const lastX = lastXRef.current;
1848
+ if (xValue >= 1 && lastX < 1) {
1849
+ onShimmerExit();
1850
+ }
1851
+ lastXRef.current = xValue;
1852
+ }
1853
+ }
1854
+ )
1855
+ }
1856
+ ),
1857
+ /* @__PURE__ */ jsx6("mask", { id: `${chartId}-loading-mask`, maskUnits: "userSpaceOnUse", children: /* @__PURE__ */ jsx6(
1858
+ "rect",
1859
+ {
1860
+ width: "100%",
1861
+ height: "100%",
1862
+ fill: `url(#${chartId}-loading-pattern)`
1863
+ }
1864
+ ) })
1865
+ ] });
1866
+ };
1867
+ export {
1868
+ ActiveDot,
1869
+ Dot,
1870
+ Grid,
1871
+ Legend2 as Legend,
1872
+ Line2 as Line,
1873
+ LineChart2 as LineChart,
1874
+ Tooltip2 as Tooltip,
1875
+ XAxis,
1876
+ YAxis,
1877
+ useLoadingData
1878
+ };
1879
+ //# sourceMappingURL=line-chart.js.map