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.
- package/dist/area-chart.d.ts +84 -0
- package/dist/area-chart.js +2002 -0
- package/dist/area-chart.js.map +1 -0
- package/dist/bar-chart.d.ts +80 -0
- package/dist/bar-chart.js +2251 -0
- package/dist/bar-chart.js.map +1 -0
- package/dist/chart-background-BK77UXhl.d.ts +9 -0
- package/dist/chart-brush-BoxY9aDm.d.ts +72 -0
- package/dist/chart-dot-8H287EDv.d.ts +17 -0
- package/dist/chart-legend-Dv9pqes-.d.ts +16 -0
- package/dist/chart-tooltip-DajpzJRy.d.ts +68 -0
- package/dist/charts.d.ts +17 -0
- package/dist/charts.js +5097 -0
- package/dist/charts.js.map +1 -0
- package/dist/composed-chart.d.ts +93 -0
- package/dist/composed-chart.js +2338 -0
- package/dist/composed-chart.js.map +1 -0
- package/dist/form-dK_DJRTw.d.ts +43 -0
- package/dist/form-rhf.d.ts +26 -0
- package/dist/form-rhf.js +268 -0
- package/dist/form-rhf.js.map +1 -0
- package/dist/index.d.ts +2881 -0
- package/dist/index.js +9368 -0
- package/dist/index.js.map +1 -0
- package/dist/line-chart.d.ts +81 -0
- package/dist/line-chart.js +1879 -0
- package/dist/line-chart.js.map +1 -0
- package/dist/pie-chart.d.ts +64 -0
- package/dist/pie-chart.js +1094 -0
- package/dist/pie-chart.js.map +1 -0
- package/dist/radar-chart.d.ts +67 -0
- package/dist/radar-chart.js +1318 -0
- package/dist/radar-chart.js.map +1 -0
- package/dist/radial-chart.d.ts +56 -0
- package/dist/radial-chart.js +1051 -0
- package/dist/radial-chart.js.map +1 -0
- package/dist/sankey-chart.d.ts +58 -0
- package/dist/sankey-chart.js +1179 -0
- package/dist/sankey-chart.js.map +1 -0
- package/package.json +135 -0
- package/src/scales.css +288 -0
- package/src/shiki.css +37 -0
- package/src/styles.css +23 -0
- package/src/theme.css +659 -0
|
@@ -0,0 +1,1179 @@
|
|
|
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
|
+
|
|
216
|
+
// src/components/charts/chart-tooltip.tsx
|
|
217
|
+
import * as RechartsPrimitive2 from "recharts";
|
|
218
|
+
import * as React from "react";
|
|
219
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
220
|
+
var roundnessMap = {
|
|
221
|
+
sm: "rounded-bruv-sm",
|
|
222
|
+
md: "rounded-bruv-md",
|
|
223
|
+
lg: "rounded-bruv-lg",
|
|
224
|
+
xl: "rounded-bruv-xl"
|
|
225
|
+
};
|
|
226
|
+
var variantMap = {
|
|
227
|
+
default: "bg-bruv-base-2",
|
|
228
|
+
"frosted-glass": "bg-bruv-base-2/70 backdrop-blur-sm"
|
|
229
|
+
};
|
|
230
|
+
function ChartTooltipContent({
|
|
231
|
+
active,
|
|
232
|
+
payload,
|
|
233
|
+
className,
|
|
234
|
+
indicator = "dot",
|
|
235
|
+
hideLabel = false,
|
|
236
|
+
hideIndicator = false,
|
|
237
|
+
label,
|
|
238
|
+
labelFormatter,
|
|
239
|
+
labelClassName,
|
|
240
|
+
formatter,
|
|
241
|
+
nameKey,
|
|
242
|
+
labelKey,
|
|
243
|
+
selected,
|
|
244
|
+
roundness = "lg",
|
|
245
|
+
variant = "default"
|
|
246
|
+
}) {
|
|
247
|
+
const { config } = useChart();
|
|
248
|
+
const tooltipLabel = React.useMemo(() => {
|
|
249
|
+
if (hideLabel || !payload?.length) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const [item] = payload;
|
|
253
|
+
const key = `${labelKey ?? item?.dataKey ?? item?.name ?? "value"}`;
|
|
254
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
255
|
+
const value = !labelKey && typeof label === "string" ? config[label]?.label ?? label : itemConfig?.label;
|
|
256
|
+
if (labelFormatter) {
|
|
257
|
+
return /* @__PURE__ */ jsx2("div", { className: cn("font-medium", labelClassName), children: labelFormatter(value, payload) });
|
|
258
|
+
}
|
|
259
|
+
if (!value) {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
return /* @__PURE__ */ jsx2("div", { className: cn("font-medium", labelClassName), children: value });
|
|
263
|
+
}, [
|
|
264
|
+
label,
|
|
265
|
+
labelFormatter,
|
|
266
|
+
payload,
|
|
267
|
+
hideLabel,
|
|
268
|
+
labelClassName,
|
|
269
|
+
config,
|
|
270
|
+
labelKey
|
|
271
|
+
]);
|
|
272
|
+
if (!active || !payload?.length) {
|
|
273
|
+
return /* @__PURE__ */ jsx2("span", { className: "p-4" });
|
|
274
|
+
}
|
|
275
|
+
const nestLabel = payload.length === 1 && indicator !== "dot";
|
|
276
|
+
return /* @__PURE__ */ jsxs2(
|
|
277
|
+
"div",
|
|
278
|
+
{
|
|
279
|
+
className: cn(
|
|
280
|
+
"border-bruv-neutral/50 grid min-w-32 items-start gap-1.5 border px-2.5 py-1.5 text-xs shadow-xl",
|
|
281
|
+
roundnessMap[roundness],
|
|
282
|
+
variantMap[variant],
|
|
283
|
+
className
|
|
284
|
+
),
|
|
285
|
+
children: [
|
|
286
|
+
!nestLabel ? tooltipLabel : null,
|
|
287
|
+
/* @__PURE__ */ jsx2("div", { className: "grid gap-1.5", children: payload.filter((item) => item.type !== "none").map((item, index) => {
|
|
288
|
+
const payloadName = nameKey && item.payload ? item.payload[nameKey] : void 0;
|
|
289
|
+
const key = `${payloadName ?? item.name ?? item.dataKey ?? "value"}`;
|
|
290
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
291
|
+
const colorsCount = itemConfig ? getColorsCount(itemConfig) : 1;
|
|
292
|
+
return /* @__PURE__ */ jsx2(
|
|
293
|
+
"div",
|
|
294
|
+
{
|
|
295
|
+
className: cn(
|
|
296
|
+
"[&>svg]:text-bruv-secondary flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
|
|
297
|
+
indicator === "dot" && "items-center",
|
|
298
|
+
selected != null && selected !== item.dataKey && "opacity-30"
|
|
299
|
+
),
|
|
300
|
+
children: formatter && item?.value !== void 0 && item.name ? formatter(item.value, item.name, item, index, item.payload) : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
301
|
+
itemConfig?.icon ? /* @__PURE__ */ jsx2(itemConfig.icon, {}) : !hideIndicator && /* @__PURE__ */ jsx2(
|
|
302
|
+
"div",
|
|
303
|
+
{
|
|
304
|
+
className: cn("shrink-0 rounded-[2px]", {
|
|
305
|
+
"h-2.5 w-2.5": indicator === "dot",
|
|
306
|
+
"w-1": indicator === "line",
|
|
307
|
+
"w-0 border-[1.5px] border-dashed bg-transparent!": indicator === "dashed",
|
|
308
|
+
"my-0.5": nestLabel && indicator === "dashed"
|
|
309
|
+
}),
|
|
310
|
+
style: getIndicatorColorStyle(key, colorsCount)
|
|
311
|
+
}
|
|
312
|
+
),
|
|
313
|
+
/* @__PURE__ */ jsxs2(
|
|
314
|
+
"div",
|
|
315
|
+
{
|
|
316
|
+
className: cn(
|
|
317
|
+
"flex flex-1 justify-between gap-4 leading-none",
|
|
318
|
+
nestLabel ? "items-end" : "items-center"
|
|
319
|
+
),
|
|
320
|
+
children: [
|
|
321
|
+
/* @__PURE__ */ jsxs2("div", { className: "grid gap-1.5", children: [
|
|
322
|
+
nestLabel ? tooltipLabel : null,
|
|
323
|
+
/* @__PURE__ */ jsx2("span", { className: "text-bruv-secondary", children: itemConfig?.label ?? item.name })
|
|
324
|
+
] }),
|
|
325
|
+
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) })
|
|
326
|
+
]
|
|
327
|
+
}
|
|
328
|
+
)
|
|
329
|
+
] })
|
|
330
|
+
},
|
|
331
|
+
index
|
|
332
|
+
);
|
|
333
|
+
}) })
|
|
334
|
+
]
|
|
335
|
+
}
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
function getIndicatorColorStyle(dataKey, colorsCount) {
|
|
339
|
+
if (colorsCount <= 1) {
|
|
340
|
+
return { background: `var(--color-${dataKey}-0)` };
|
|
341
|
+
}
|
|
342
|
+
const stops = Array.from({ length: colorsCount }, (_, index) => {
|
|
343
|
+
const offset = index / (colorsCount - 1) * 100;
|
|
344
|
+
return `var(--color-${dataKey}-${index}) ${offset}%`;
|
|
345
|
+
}).join(", ");
|
|
346
|
+
return { background: `linear-gradient(to right, ${stops})` };
|
|
347
|
+
}
|
|
348
|
+
var ChartTooltip = ({
|
|
349
|
+
animationDuration = 200,
|
|
350
|
+
...props
|
|
351
|
+
}) => /* @__PURE__ */ jsx2(RechartsPrimitive2.Tooltip, { animationDuration, ...props });
|
|
352
|
+
|
|
353
|
+
// src/components/charts/chart-background.tsx
|
|
354
|
+
import { ZIndexLayer } from "recharts";
|
|
355
|
+
import { useId as useId2 } from "react";
|
|
356
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
357
|
+
var DotsPattern = ({ id }) => /* @__PURE__ */ jsx3(
|
|
358
|
+
"pattern",
|
|
359
|
+
{
|
|
360
|
+
id,
|
|
361
|
+
x: "0",
|
|
362
|
+
y: "0",
|
|
363
|
+
width: "20",
|
|
364
|
+
height: "20",
|
|
365
|
+
patternUnits: "userSpaceOnUse",
|
|
366
|
+
children: /* @__PURE__ */ jsx3(
|
|
367
|
+
"circle",
|
|
368
|
+
{
|
|
369
|
+
className: "text-bruv-chart-grid text-bruv-chart-grid",
|
|
370
|
+
cx: "2",
|
|
371
|
+
cy: "2",
|
|
372
|
+
r: "1",
|
|
373
|
+
fill: "currentColor"
|
|
374
|
+
}
|
|
375
|
+
)
|
|
376
|
+
}
|
|
377
|
+
);
|
|
378
|
+
var GridPattern = ({ id }) => /* @__PURE__ */ jsx3(
|
|
379
|
+
"pattern",
|
|
380
|
+
{
|
|
381
|
+
id,
|
|
382
|
+
x: "0",
|
|
383
|
+
y: "0",
|
|
384
|
+
width: "20",
|
|
385
|
+
height: "20",
|
|
386
|
+
patternUnits: "userSpaceOnUse",
|
|
387
|
+
children: /* @__PURE__ */ jsx3(
|
|
388
|
+
"path",
|
|
389
|
+
{
|
|
390
|
+
className: "text-bruv-chart-grid text-bruv-chart-grid",
|
|
391
|
+
d: "M 20 0 L 0 0 0 20",
|
|
392
|
+
fill: "none",
|
|
393
|
+
stroke: "currentColor",
|
|
394
|
+
strokeWidth: "0.5"
|
|
395
|
+
}
|
|
396
|
+
)
|
|
397
|
+
}
|
|
398
|
+
);
|
|
399
|
+
var CrossHatchPattern = ({ id }) => /* @__PURE__ */ jsx3(
|
|
400
|
+
"pattern",
|
|
401
|
+
{
|
|
402
|
+
id,
|
|
403
|
+
x: "0",
|
|
404
|
+
y: "0",
|
|
405
|
+
width: "20",
|
|
406
|
+
height: "20",
|
|
407
|
+
patternUnits: "userSpaceOnUse",
|
|
408
|
+
children: /* @__PURE__ */ jsx3(
|
|
409
|
+
"path",
|
|
410
|
+
{
|
|
411
|
+
className: "text-bruv-chart-grid/60 text-bruv-chart-grid/50",
|
|
412
|
+
d: "M 0 0 L 20 20 M 20 0 L 0 20",
|
|
413
|
+
fill: "none",
|
|
414
|
+
stroke: "currentColor",
|
|
415
|
+
strokeWidth: "0.5"
|
|
416
|
+
}
|
|
417
|
+
)
|
|
418
|
+
}
|
|
419
|
+
);
|
|
420
|
+
var DiagonalLinesPattern = ({ id }) => /* @__PURE__ */ jsx3(
|
|
421
|
+
"pattern",
|
|
422
|
+
{
|
|
423
|
+
id,
|
|
424
|
+
x: "0",
|
|
425
|
+
y: "0",
|
|
426
|
+
width: "6",
|
|
427
|
+
height: "6",
|
|
428
|
+
patternUnits: "userSpaceOnUse",
|
|
429
|
+
patternTransform: "rotate(45)",
|
|
430
|
+
children: /* @__PURE__ */ jsx3(
|
|
431
|
+
"line",
|
|
432
|
+
{
|
|
433
|
+
className: "text-bruv-chart-grid text-bruv-chart-grid",
|
|
434
|
+
x1: "0",
|
|
435
|
+
y1: "0",
|
|
436
|
+
x2: "0",
|
|
437
|
+
y2: "6",
|
|
438
|
+
stroke: "currentColor",
|
|
439
|
+
strokeWidth: "0.5"
|
|
440
|
+
}
|
|
441
|
+
)
|
|
442
|
+
}
|
|
443
|
+
);
|
|
444
|
+
var PlusPattern = ({ id }) => /* @__PURE__ */ jsx3(
|
|
445
|
+
"pattern",
|
|
446
|
+
{
|
|
447
|
+
id,
|
|
448
|
+
x: "0",
|
|
449
|
+
y: "0",
|
|
450
|
+
width: "16",
|
|
451
|
+
height: "16",
|
|
452
|
+
patternUnits: "userSpaceOnUse",
|
|
453
|
+
children: /* @__PURE__ */ jsx3(
|
|
454
|
+
"path",
|
|
455
|
+
{
|
|
456
|
+
className: "text-bruv-chart-grid text-bruv-chart-grid",
|
|
457
|
+
d: "M 8 4 L 8 12 M 4 8 L 12 8",
|
|
458
|
+
fill: "none",
|
|
459
|
+
stroke: "currentColor",
|
|
460
|
+
strokeWidth: "0.5",
|
|
461
|
+
strokeLinecap: "round"
|
|
462
|
+
}
|
|
463
|
+
)
|
|
464
|
+
}
|
|
465
|
+
);
|
|
466
|
+
var FallingTrianglesPattern = ({ id }) => /* @__PURE__ */ jsx3(
|
|
467
|
+
"pattern",
|
|
468
|
+
{
|
|
469
|
+
id,
|
|
470
|
+
x: "0",
|
|
471
|
+
y: "0",
|
|
472
|
+
width: "18",
|
|
473
|
+
height: "36",
|
|
474
|
+
patternUnits: "userSpaceOnUse",
|
|
475
|
+
children: /* @__PURE__ */ jsx3(
|
|
476
|
+
"path",
|
|
477
|
+
{
|
|
478
|
+
className: "text-bruv-chart-grid text-bruv-chart-grid",
|
|
479
|
+
d: "M2 6h12L8 18 2 6zm18 36h12l-6 12-6-12z",
|
|
480
|
+
transform: "scale(0.5)",
|
|
481
|
+
fill: "currentColor",
|
|
482
|
+
fillOpacity: "0.4"
|
|
483
|
+
}
|
|
484
|
+
)
|
|
485
|
+
}
|
|
486
|
+
);
|
|
487
|
+
var FourPointedStarPattern = ({ id }) => /* @__PURE__ */ jsx3(
|
|
488
|
+
"pattern",
|
|
489
|
+
{
|
|
490
|
+
id,
|
|
491
|
+
x: "0",
|
|
492
|
+
y: "0",
|
|
493
|
+
width: "16",
|
|
494
|
+
height: "16",
|
|
495
|
+
patternUnits: "userSpaceOnUse",
|
|
496
|
+
children: /* @__PURE__ */ jsx3(
|
|
497
|
+
"polygon",
|
|
498
|
+
{
|
|
499
|
+
className: "text-bruv-chart-grid text-bruv-chart-grid",
|
|
500
|
+
fillRule: "evenodd",
|
|
501
|
+
points: "5 3 8 4 5 5 4 8 3 5 0 4 3 3 4 0 5 3",
|
|
502
|
+
fill: "currentColor",
|
|
503
|
+
fillOpacity: "0.4"
|
|
504
|
+
}
|
|
505
|
+
)
|
|
506
|
+
}
|
|
507
|
+
);
|
|
508
|
+
var TinyCheckersPattern = ({ id }) => /* @__PURE__ */ jsx3(
|
|
509
|
+
"pattern",
|
|
510
|
+
{
|
|
511
|
+
id,
|
|
512
|
+
x: "0",
|
|
513
|
+
y: "0",
|
|
514
|
+
width: "8",
|
|
515
|
+
height: "8",
|
|
516
|
+
patternUnits: "userSpaceOnUse",
|
|
517
|
+
children: /* @__PURE__ */ jsx3(
|
|
518
|
+
"path",
|
|
519
|
+
{
|
|
520
|
+
className: "text-bruv-chart-grid text-bruv-chart-grid",
|
|
521
|
+
fillRule: "evenodd",
|
|
522
|
+
d: "M0 0h4v4H0V0zm4 4h4v4H4V4z",
|
|
523
|
+
fill: "currentColor",
|
|
524
|
+
fillOpacity: "0.2"
|
|
525
|
+
}
|
|
526
|
+
)
|
|
527
|
+
}
|
|
528
|
+
);
|
|
529
|
+
var OverlappingCirclesPattern = ({ id }) => /* @__PURE__ */ jsx3(
|
|
530
|
+
"pattern",
|
|
531
|
+
{
|
|
532
|
+
id,
|
|
533
|
+
x: "0",
|
|
534
|
+
y: "0",
|
|
535
|
+
width: "40",
|
|
536
|
+
height: "40",
|
|
537
|
+
patternUnits: "userSpaceOnUse",
|
|
538
|
+
children: /* @__PURE__ */ jsx3(
|
|
539
|
+
"path",
|
|
540
|
+
{
|
|
541
|
+
className: "text-bruv-chart-grid text-bruv-chart-grid",
|
|
542
|
+
fillRule: "evenodd",
|
|
543
|
+
d: "M25 25c0-2.762 2.238-5 5-5s5 2.238 5 5-2.238 5-5 5c0 2.762-2.238 5-5 5s-5-2.238-5-5 2.238-5 5-5zM5 5c0-2.762 2.238-5 5-5s5 2.238 5 5-2.238 5-5 5c0 2.762-2.238 5-5 5S0 12.762 0 10s2.238-5 5-5zm5 4c2.209 0 4-1.791 4-4s-1.791-4-4-4-4 1.791-4 4 1.791 4 4 4zm20 20c2.209 0 4-1.791 4-4s-1.791-4-4-4-4 1.791-4 4 1.791 4 4 4z",
|
|
544
|
+
fill: "currentColor",
|
|
545
|
+
fillOpacity: "0.4"
|
|
546
|
+
}
|
|
547
|
+
)
|
|
548
|
+
}
|
|
549
|
+
);
|
|
550
|
+
var WiggleLinesPattern = ({ id }) => /* @__PURE__ */ jsx3(
|
|
551
|
+
"pattern",
|
|
552
|
+
{
|
|
553
|
+
id,
|
|
554
|
+
x: "0",
|
|
555
|
+
y: "0",
|
|
556
|
+
width: "52",
|
|
557
|
+
height: "26",
|
|
558
|
+
patternUnits: "userSpaceOnUse",
|
|
559
|
+
patternTransform: "scale(0.6)",
|
|
560
|
+
children: /* @__PURE__ */ jsx3(
|
|
561
|
+
"path",
|
|
562
|
+
{
|
|
563
|
+
className: "text-bruv-chart-grid text-bruv-chart-grid",
|
|
564
|
+
d: "M10 10c0-2.21-1.79-4-4-4-3.314 0-6-2.686-6-6h2c0 2.21 1.79 4 4 4 3.314 0 6 2.686 6 6 0 2.21 1.79 4 4 4 3.314 0 6 2.686 6 6 0 2.21 1.79 4 4 4v2c-3.314 0-6-2.686-6-6 0-2.21-1.79-4-4-4-3.314 0-6-2.686-6-6zm25.464-1.95l8.486 8.486-1.414 1.414-8.486-8.486 1.414-1.414z",
|
|
565
|
+
fill: "currentColor",
|
|
566
|
+
fillOpacity: "0.4"
|
|
567
|
+
}
|
|
568
|
+
)
|
|
569
|
+
}
|
|
570
|
+
);
|
|
571
|
+
var BubblesPattern = ({ id }) => /* @__PURE__ */ jsx3(
|
|
572
|
+
"pattern",
|
|
573
|
+
{
|
|
574
|
+
id,
|
|
575
|
+
x: "0",
|
|
576
|
+
y: "0",
|
|
577
|
+
width: "100",
|
|
578
|
+
height: "100",
|
|
579
|
+
patternUnits: "userSpaceOnUse",
|
|
580
|
+
patternTransform: "scale(0.6667)",
|
|
581
|
+
children: /* @__PURE__ */ jsx3(
|
|
582
|
+
"path",
|
|
583
|
+
{
|
|
584
|
+
className: "text-bruv-chart-grid text-bruv-chart-grid",
|
|
585
|
+
d: "M11 18c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm48 25c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm-43-7c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm63 31c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM34 90c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm56-76c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM12 86c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm28-65c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm23-11c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-6 60c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm29 22c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zM32 63c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm57-13c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-9-21c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM60 91c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM35 41c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM12 60c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2z",
|
|
586
|
+
fill: "currentColor",
|
|
587
|
+
fillOpacity: "0.4",
|
|
588
|
+
fillRule: "evenodd"
|
|
589
|
+
}
|
|
590
|
+
)
|
|
591
|
+
}
|
|
592
|
+
);
|
|
593
|
+
var PATTERN_MAP = {
|
|
594
|
+
dots: DotsPattern,
|
|
595
|
+
grid: GridPattern,
|
|
596
|
+
plus: PlusPattern,
|
|
597
|
+
bubbles: BubblesPattern,
|
|
598
|
+
"cross-hatch": CrossHatchPattern,
|
|
599
|
+
"diagonal-lines": DiagonalLinesPattern,
|
|
600
|
+
"falling-triangles": FallingTrianglesPattern,
|
|
601
|
+
"4-pointed-star": FourPointedStarPattern,
|
|
602
|
+
"tiny-checkers": TinyCheckersPattern,
|
|
603
|
+
"overlapping-circles": OverlappingCirclesPattern,
|
|
604
|
+
"wiggle-lines": WiggleLinesPattern
|
|
605
|
+
};
|
|
606
|
+
function ChartBackground({ variant }) {
|
|
607
|
+
const baseId = useId2().replace(/:/g, "");
|
|
608
|
+
const patternId = `${baseId}-bg-${variant}`;
|
|
609
|
+
const maskId = `${baseId}-bg-edge-fade`;
|
|
610
|
+
const filterId = `${baseId}-bg-blur`;
|
|
611
|
+
const PatternComponent = PATTERN_MAP[variant];
|
|
612
|
+
return /* @__PURE__ */ jsxs3(ZIndexLayer, { zIndex: -1, children: [
|
|
613
|
+
/* @__PURE__ */ jsxs3("defs", { children: [
|
|
614
|
+
/* @__PURE__ */ jsx3(PatternComponent, { id: patternId }),
|
|
615
|
+
/* @__PURE__ */ jsx3("filter", { id: filterId, children: /* @__PURE__ */ jsx3("feGaussianBlur", { stdDeviation: "25" }) }),
|
|
616
|
+
/* @__PURE__ */ jsx3("mask", { id: maskId, maskUnits: "userSpaceOnUse", children: /* @__PURE__ */ jsx3(
|
|
617
|
+
"rect",
|
|
618
|
+
{
|
|
619
|
+
x: "8%",
|
|
620
|
+
y: "20%",
|
|
621
|
+
width: "85%",
|
|
622
|
+
height: "60%",
|
|
623
|
+
fill: "white",
|
|
624
|
+
filter: `url(#${filterId})`
|
|
625
|
+
}
|
|
626
|
+
) })
|
|
627
|
+
] }),
|
|
628
|
+
/* @__PURE__ */ jsx3(
|
|
629
|
+
"rect",
|
|
630
|
+
{
|
|
631
|
+
width: "100%",
|
|
632
|
+
height: "100%",
|
|
633
|
+
fill: `url(#${patternId})`,
|
|
634
|
+
mask: `url(#${maskId})`
|
|
635
|
+
}
|
|
636
|
+
)
|
|
637
|
+
] });
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// src/components/charts/sankey-chart.tsx
|
|
641
|
+
import {
|
|
642
|
+
Children,
|
|
643
|
+
createContext as createContext2,
|
|
644
|
+
isValidElement,
|
|
645
|
+
use,
|
|
646
|
+
useCallback,
|
|
647
|
+
useId as useId3,
|
|
648
|
+
useMemo as useMemo2,
|
|
649
|
+
useState
|
|
650
|
+
} from "react";
|
|
651
|
+
import {
|
|
652
|
+
Sankey as RechartsSankey,
|
|
653
|
+
Layer
|
|
654
|
+
} from "recharts";
|
|
655
|
+
import { motion } from "motion/react";
|
|
656
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
657
|
+
var LOADING_ANIMATION_DURATION = 2e3;
|
|
658
|
+
var DEFAULT_NODE_WIDTH = 10;
|
|
659
|
+
var DEFAULT_NODE_PADDING = 10;
|
|
660
|
+
var DEFAULT_LINK_CURVATURE = 0.5;
|
|
661
|
+
var DEFAULT_ITERATIONS = 32;
|
|
662
|
+
var SankeyChartContext = createContext2(null);
|
|
663
|
+
function useSankeyChart() {
|
|
664
|
+
const context = use(SankeyChartContext);
|
|
665
|
+
if (!context) {
|
|
666
|
+
throw new Error(
|
|
667
|
+
"Sankey chart parts (<Node />, <Link />, <Tooltip />, \u2026) must be used within <SankeyChart />"
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
return context;
|
|
671
|
+
}
|
|
672
|
+
function SankeyChart({
|
|
673
|
+
data,
|
|
674
|
+
config,
|
|
675
|
+
children,
|
|
676
|
+
className,
|
|
677
|
+
sankeyProps,
|
|
678
|
+
nodeWidth = DEFAULT_NODE_WIDTH,
|
|
679
|
+
nodePadding = DEFAULT_NODE_PADDING,
|
|
680
|
+
linkCurvature = DEFAULT_LINK_CURVATURE,
|
|
681
|
+
iterations = DEFAULT_ITERATIONS,
|
|
682
|
+
sort = true,
|
|
683
|
+
align = "justify",
|
|
684
|
+
verticalAlign = "justify",
|
|
685
|
+
backgroundVariant,
|
|
686
|
+
defaultSelectedNode = null,
|
|
687
|
+
onSelectionChange,
|
|
688
|
+
isLoading = false
|
|
689
|
+
}) {
|
|
690
|
+
const chartId = useId3().replace(/:/g, "");
|
|
691
|
+
const [selectedNode, setSelectedNode] = useState(
|
|
692
|
+
defaultSelectedNode
|
|
693
|
+
);
|
|
694
|
+
const selectNode = useCallback(
|
|
695
|
+
(nodeName) => {
|
|
696
|
+
setSelectedNode(nodeName);
|
|
697
|
+
if (!onSelectionChange) return;
|
|
698
|
+
if (nodeName === null) {
|
|
699
|
+
onSelectionChange(null);
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
onSelectionChange({
|
|
703
|
+
dataKey: nodeName,
|
|
704
|
+
value: getNodeValue(data, nodeName)
|
|
705
|
+
});
|
|
706
|
+
},
|
|
707
|
+
[onSelectionChange, data]
|
|
708
|
+
);
|
|
709
|
+
const contextValue = useMemo2(
|
|
710
|
+
() => ({ data, config, chartId, isLoading, selectedNode, selectNode }),
|
|
711
|
+
[data, config, chartId, isLoading, selectedNode, selectNode]
|
|
712
|
+
);
|
|
713
|
+
return /* @__PURE__ */ jsx4(SankeyChartContext, { value: contextValue, children: /* @__PURE__ */ jsxs4(Chart, { className, config, children: [
|
|
714
|
+
/* @__PURE__ */ jsx4(LoadingIndicator, { isLoading }),
|
|
715
|
+
backgroundVariant && /* @__PURE__ */ jsx4(ChartBackground, { variant: backgroundVariant }),
|
|
716
|
+
!isLoading && /* @__PURE__ */ jsxs4(
|
|
717
|
+
RechartsSankey,
|
|
718
|
+
{
|
|
719
|
+
id: chartId,
|
|
720
|
+
data,
|
|
721
|
+
nodeWidth,
|
|
722
|
+
nodePadding,
|
|
723
|
+
linkCurvature,
|
|
724
|
+
iterations,
|
|
725
|
+
sort,
|
|
726
|
+
align,
|
|
727
|
+
verticalAlign,
|
|
728
|
+
...resolveSankeyRenderers(children),
|
|
729
|
+
...sankeyProps,
|
|
730
|
+
children: [
|
|
731
|
+
children,
|
|
732
|
+
/* @__PURE__ */ jsx4("defs", { children: /* @__PURE__ */ jsx4(NodeColorGradients, { config, chartId }) })
|
|
733
|
+
]
|
|
734
|
+
}
|
|
735
|
+
),
|
|
736
|
+
isLoading && /* @__PURE__ */ jsx4(
|
|
737
|
+
"svg",
|
|
738
|
+
{
|
|
739
|
+
viewBox: "0 0 500 250",
|
|
740
|
+
preserveAspectRatio: "xMidYMid meet",
|
|
741
|
+
width: "100%",
|
|
742
|
+
height: "100%",
|
|
743
|
+
className: "absolute inset-0",
|
|
744
|
+
children: /* @__PURE__ */ jsx4(LoadingSankey, {})
|
|
745
|
+
}
|
|
746
|
+
)
|
|
747
|
+
] }) });
|
|
748
|
+
}
|
|
749
|
+
var Node = () => null;
|
|
750
|
+
var NodeLabel = () => null;
|
|
751
|
+
var Link = () => null;
|
|
752
|
+
function Tooltip2({ variant, roundness, defaultIndex }) {
|
|
753
|
+
const { isLoading } = useSankeyChart();
|
|
754
|
+
if (isLoading) return null;
|
|
755
|
+
return /* @__PURE__ */ jsx4(
|
|
756
|
+
ChartTooltip,
|
|
757
|
+
{
|
|
758
|
+
defaultIndex,
|
|
759
|
+
content: /* @__PURE__ */ jsx4(
|
|
760
|
+
ChartTooltipContent,
|
|
761
|
+
{
|
|
762
|
+
nameKey: "name",
|
|
763
|
+
hideLabel: true,
|
|
764
|
+
roundness,
|
|
765
|
+
variant
|
|
766
|
+
}
|
|
767
|
+
)
|
|
768
|
+
}
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
var getNodeValue = (data, nodeName) => {
|
|
772
|
+
const nodeIndex = data.nodes.findIndex((node) => node.name === nodeName);
|
|
773
|
+
if (nodeIndex === -1) return 0;
|
|
774
|
+
const outgoing = data.links.filter((link) => link.source === nodeIndex).reduce((sum, link) => sum + link.value, 0);
|
|
775
|
+
const incoming = data.links.filter((link) => link.target === nodeIndex).reduce((sum, link) => sum + link.value, 0);
|
|
776
|
+
return outgoing > 0 ? outgoing : incoming;
|
|
777
|
+
};
|
|
778
|
+
var resolveSankeyRenderers = (children) => {
|
|
779
|
+
let nodeProps = null;
|
|
780
|
+
let linkProps = null;
|
|
781
|
+
Children.forEach(children, (child) => {
|
|
782
|
+
if (!isValidElement(child)) return;
|
|
783
|
+
if (child.type === Node) {
|
|
784
|
+
nodeProps = child.props;
|
|
785
|
+
}
|
|
786
|
+
if (child.type === Link) {
|
|
787
|
+
linkProps = child.props;
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
return {
|
|
791
|
+
node: (props) => /* @__PURE__ */ jsx4(SankeyNode, { ...props, nodeConfig: nodeProps }),
|
|
792
|
+
link: (props) => /* @__PURE__ */ jsx4(SankeyLink, { ...props, linkConfig: linkProps })
|
|
793
|
+
};
|
|
794
|
+
};
|
|
795
|
+
var resolveNodeLabel = (children) => {
|
|
796
|
+
let label = null;
|
|
797
|
+
Children.forEach(children, (child) => {
|
|
798
|
+
if (isValidElement(child) && child.type === NodeLabel) {
|
|
799
|
+
label = child.props;
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
return label;
|
|
803
|
+
};
|
|
804
|
+
var SankeyNode = ({
|
|
805
|
+
x,
|
|
806
|
+
y,
|
|
807
|
+
width,
|
|
808
|
+
height,
|
|
809
|
+
payload,
|
|
810
|
+
nodeConfig
|
|
811
|
+
}) => {
|
|
812
|
+
const { config, chartId, data, selectedNode, selectNode } = useSankeyChart();
|
|
813
|
+
const radius = nodeConfig?.radius ?? 0;
|
|
814
|
+
const isClickable = nodeConfig?.isClickable ?? false;
|
|
815
|
+
const label = resolveNodeLabel(nodeConfig?.children);
|
|
816
|
+
const nodeName = payload.name;
|
|
817
|
+
const nodeValue = payload.value;
|
|
818
|
+
const nodeIcon = payload.icon;
|
|
819
|
+
const isHighlighted = isNodeConnected(data, selectedNode, nodeName);
|
|
820
|
+
const hasConfigColor = nodeName in config;
|
|
821
|
+
const configLabel = config[nodeName]?.label ?? nodeName;
|
|
822
|
+
const dimmed = isClickable && !isHighlighted;
|
|
823
|
+
const valueFormatter = label?.valueFormatter ?? ((value) => value.toLocaleString());
|
|
824
|
+
const showValues = label?.showValues ?? false;
|
|
825
|
+
const labelX = x + width / 2;
|
|
826
|
+
const labelY = showValues ? y + height / 2 - 8 : y + height / 2;
|
|
827
|
+
const valueY = y + height / 2 + 8;
|
|
828
|
+
const outsideLabelX = x + width + 8;
|
|
829
|
+
const outsideLabelY = y + height / 2;
|
|
830
|
+
return /* @__PURE__ */ jsxs4(Layer, { children: [
|
|
831
|
+
/* @__PURE__ */ jsx4(
|
|
832
|
+
"rect",
|
|
833
|
+
{
|
|
834
|
+
x,
|
|
835
|
+
y,
|
|
836
|
+
width,
|
|
837
|
+
height,
|
|
838
|
+
rx: radius,
|
|
839
|
+
ry: radius,
|
|
840
|
+
fill: hasConfigColor ? `url(#${chartId}-sankey-colors-${nodeName})` : "currentColor",
|
|
841
|
+
fillOpacity: dimmed ? 0.3 : 0.9,
|
|
842
|
+
className: "transition-opacity duration-200",
|
|
843
|
+
style: isClickable ? { cursor: "pointer" } : void 0,
|
|
844
|
+
onClick: () => {
|
|
845
|
+
if (!isClickable) return;
|
|
846
|
+
selectNode(selectedNode === nodeName ? null : nodeName);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
),
|
|
850
|
+
label?.position === "inside" && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
851
|
+
/* @__PURE__ */ jsx4(
|
|
852
|
+
"rect",
|
|
853
|
+
{
|
|
854
|
+
x: x + 1,
|
|
855
|
+
y: y + 1,
|
|
856
|
+
width: width - 2,
|
|
857
|
+
height: height - 2,
|
|
858
|
+
rx: Math.max(0, radius - 1),
|
|
859
|
+
ry: Math.max(0, radius - 1),
|
|
860
|
+
opacity: dimmed ? 0.3 : 1,
|
|
861
|
+
className: "fill-white/50 transition-opacity duration-200 dark:fill-black/60",
|
|
862
|
+
style: { pointerEvents: "none" }
|
|
863
|
+
}
|
|
864
|
+
),
|
|
865
|
+
nodeIcon && /* @__PURE__ */ jsx4(
|
|
866
|
+
"foreignObject",
|
|
867
|
+
{
|
|
868
|
+
x: labelX - 8,
|
|
869
|
+
y: labelY - 30,
|
|
870
|
+
width: 16,
|
|
871
|
+
height: 16,
|
|
872
|
+
opacity: dimmed ? 0.3 : 1,
|
|
873
|
+
className: "transition-opacity duration-200",
|
|
874
|
+
style: { pointerEvents: "none" },
|
|
875
|
+
children: /* @__PURE__ */ jsx4("div", { className: "text-bruv-primary/80 flex items-center justify-center dark:text-white/80", children: nodeIcon })
|
|
876
|
+
}
|
|
877
|
+
),
|
|
878
|
+
/* @__PURE__ */ jsx4(
|
|
879
|
+
"text",
|
|
880
|
+
{
|
|
881
|
+
x: labelX,
|
|
882
|
+
y: nodeIcon ? labelY - 4 : labelY,
|
|
883
|
+
textAnchor: "middle",
|
|
884
|
+
dominantBaseline: "middle",
|
|
885
|
+
className: "fill-foreground text-[10px] font-medium transition-opacity duration-200 dark:fill-white",
|
|
886
|
+
opacity: dimmed ? 0.3 : 1,
|
|
887
|
+
style: { pointerEvents: "none" },
|
|
888
|
+
children: configLabel
|
|
889
|
+
}
|
|
890
|
+
),
|
|
891
|
+
showValues && /* @__PURE__ */ jsx4(
|
|
892
|
+
"text",
|
|
893
|
+
{
|
|
894
|
+
x: labelX,
|
|
895
|
+
y: valueY,
|
|
896
|
+
textAnchor: "middle",
|
|
897
|
+
dominantBaseline: "middle",
|
|
898
|
+
className: "fill-foreground/60 font-mono text-xs font-medium tabular-nums transition-opacity duration-200 dark:fill-white",
|
|
899
|
+
opacity: dimmed ? 0.3 : 0.6,
|
|
900
|
+
style: { pointerEvents: "none" },
|
|
901
|
+
children: valueFormatter(nodeValue)
|
|
902
|
+
}
|
|
903
|
+
)
|
|
904
|
+
] }),
|
|
905
|
+
label?.position === "outside" && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
906
|
+
/* @__PURE__ */ jsx4(
|
|
907
|
+
"text",
|
|
908
|
+
{
|
|
909
|
+
x: outsideLabelX,
|
|
910
|
+
y: outsideLabelY - (showValues ? 8 : 0),
|
|
911
|
+
textAnchor: "start",
|
|
912
|
+
dominantBaseline: "middle",
|
|
913
|
+
className: "fill-foreground text-xs",
|
|
914
|
+
style: { pointerEvents: "none" },
|
|
915
|
+
children: configLabel
|
|
916
|
+
}
|
|
917
|
+
),
|
|
918
|
+
showValues && /* @__PURE__ */ jsx4(
|
|
919
|
+
"text",
|
|
920
|
+
{
|
|
921
|
+
x: outsideLabelX,
|
|
922
|
+
y: outsideLabelY + 8,
|
|
923
|
+
textAnchor: "start",
|
|
924
|
+
dominantBaseline: "middle",
|
|
925
|
+
opacity: 0.5,
|
|
926
|
+
className: "fill-foreground font-mono text-xs tabular-nums dark:fill-white",
|
|
927
|
+
style: { pointerEvents: "none" },
|
|
928
|
+
children: valueFormatter(nodeValue)
|
|
929
|
+
}
|
|
930
|
+
)
|
|
931
|
+
] })
|
|
932
|
+
] });
|
|
933
|
+
};
|
|
934
|
+
var SankeyLink = ({
|
|
935
|
+
sourceX,
|
|
936
|
+
targetX,
|
|
937
|
+
sourceY,
|
|
938
|
+
targetY,
|
|
939
|
+
sourceControlX,
|
|
940
|
+
targetControlX,
|
|
941
|
+
linkWidth,
|
|
942
|
+
index,
|
|
943
|
+
payload,
|
|
944
|
+
linkConfig
|
|
945
|
+
}) => {
|
|
946
|
+
const { config, chartId, selectedNode } = useSankeyChart();
|
|
947
|
+
const variant = linkConfig?.variant ?? "gradient";
|
|
948
|
+
const verticalPadding = linkConfig?.verticalPadding ?? 0;
|
|
949
|
+
const sourceName = payload.source.name;
|
|
950
|
+
const targetName = payload.target.name;
|
|
951
|
+
const isConnected = selectedNode === null || selectedNode === sourceName || selectedNode === targetName;
|
|
952
|
+
const paddedLinkWidth = Math.max(1, linkWidth - verticalPadding);
|
|
953
|
+
const halfWidth = paddedLinkWidth / 2;
|
|
954
|
+
const linkAreaPath = `M${sourceX},${sourceY - halfWidth}
|
|
955
|
+
C${sourceControlX},${sourceY - halfWidth} ${targetControlX},${targetY - halfWidth} ${targetX},${targetY - halfWidth}
|
|
956
|
+
L${targetX},${targetY + halfWidth}
|
|
957
|
+
C${targetControlX},${targetY + halfWidth} ${sourceControlX},${sourceY + halfWidth} ${sourceX},${sourceY + halfWidth}
|
|
958
|
+
Z`;
|
|
959
|
+
return /* @__PURE__ */ jsxs4(Layer, { children: [
|
|
960
|
+
/* @__PURE__ */ jsxs4("defs", { children: [
|
|
961
|
+
variant === "gradient" && /* @__PURE__ */ jsx4(
|
|
962
|
+
LinkGradient,
|
|
963
|
+
{
|
|
964
|
+
chartId,
|
|
965
|
+
index,
|
|
966
|
+
config,
|
|
967
|
+
sourceName,
|
|
968
|
+
targetName
|
|
969
|
+
}
|
|
970
|
+
),
|
|
971
|
+
/* @__PURE__ */ jsx4(LinkStrokeGradient, { chartId, index })
|
|
972
|
+
] }),
|
|
973
|
+
/* @__PURE__ */ jsx4(
|
|
974
|
+
"path",
|
|
975
|
+
{
|
|
976
|
+
d: linkAreaPath,
|
|
977
|
+
fill: getLinkFill(
|
|
978
|
+
variant,
|
|
979
|
+
chartId,
|
|
980
|
+
index,
|
|
981
|
+
config,
|
|
982
|
+
sourceName,
|
|
983
|
+
targetName
|
|
984
|
+
),
|
|
985
|
+
fillOpacity: isConnected ? 0.4 : 0.1,
|
|
986
|
+
stroke: selectedNode !== null && isConnected ? `url(#${chartId}-link-stroke-${index})` : "none",
|
|
987
|
+
strokeWidth: 1,
|
|
988
|
+
strokeOpacity: 0.3,
|
|
989
|
+
className: "transition-opacity duration-200"
|
|
990
|
+
}
|
|
991
|
+
)
|
|
992
|
+
] });
|
|
993
|
+
};
|
|
994
|
+
var isNodeConnected = (data, selectedNode, nodeName) => {
|
|
995
|
+
if (selectedNode === null || selectedNode === nodeName) return true;
|
|
996
|
+
const selectedIdx = data.nodes.findIndex((node) => node.name === selectedNode);
|
|
997
|
+
const nodeIdx = data.nodes.findIndex((node) => node.name === nodeName);
|
|
998
|
+
return data.links.some(
|
|
999
|
+
(link) => link.source === selectedIdx && link.target === nodeIdx || link.source === nodeIdx && link.target === selectedIdx
|
|
1000
|
+
);
|
|
1001
|
+
};
|
|
1002
|
+
var getLinkFill = (variant, chartId, index, config, sourceName, targetName) => {
|
|
1003
|
+
switch (variant) {
|
|
1004
|
+
case "gradient":
|
|
1005
|
+
return `url(#${chartId}-link-gradient-${index})`;
|
|
1006
|
+
case "source":
|
|
1007
|
+
return sourceName in config ? `url(#${chartId}-sankey-colors-${sourceName})` : "currentColor";
|
|
1008
|
+
case "target":
|
|
1009
|
+
return targetName in config ? `url(#${chartId}-sankey-colors-${targetName})` : "currentColor";
|
|
1010
|
+
case "solid":
|
|
1011
|
+
default:
|
|
1012
|
+
return "currentColor";
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
var NodeColorGradients = ({
|
|
1016
|
+
config,
|
|
1017
|
+
chartId
|
|
1018
|
+
}) => {
|
|
1019
|
+
return /* @__PURE__ */ jsx4(Fragment2, { children: Object.entries(config).map(([dataKey, nodeConfig]) => {
|
|
1020
|
+
const colorsCount = getColorsCount(nodeConfig);
|
|
1021
|
+
return /* @__PURE__ */ jsx4(
|
|
1022
|
+
"linearGradient",
|
|
1023
|
+
{
|
|
1024
|
+
id: `${chartId}-sankey-colors-${dataKey}`,
|
|
1025
|
+
x1: "0",
|
|
1026
|
+
y1: "0",
|
|
1027
|
+
x2: "0",
|
|
1028
|
+
y2: "1",
|
|
1029
|
+
children: colorsCount === 1 ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1030
|
+
/* @__PURE__ */ jsx4("stop", { offset: "0%", stopColor: `var(--color-${dataKey}-0)` }),
|
|
1031
|
+
/* @__PURE__ */ jsx4("stop", { offset: "100%", stopColor: `var(--color-${dataKey}-0)` })
|
|
1032
|
+
] }) : Array.from({ length: colorsCount }, (_, index) => {
|
|
1033
|
+
const offset = `${index / (colorsCount - 1) * 100}%`;
|
|
1034
|
+
return /* @__PURE__ */ jsx4(
|
|
1035
|
+
"stop",
|
|
1036
|
+
{
|
|
1037
|
+
offset,
|
|
1038
|
+
stopColor: `var(--color-${dataKey}-${index}, var(--color-${dataKey}-0))`
|
|
1039
|
+
},
|
|
1040
|
+
offset
|
|
1041
|
+
);
|
|
1042
|
+
})
|
|
1043
|
+
},
|
|
1044
|
+
`${chartId}-sankey-colors-${dataKey}`
|
|
1045
|
+
);
|
|
1046
|
+
}) });
|
|
1047
|
+
};
|
|
1048
|
+
var LinkGradient = ({
|
|
1049
|
+
chartId,
|
|
1050
|
+
index,
|
|
1051
|
+
config,
|
|
1052
|
+
sourceName,
|
|
1053
|
+
targetName
|
|
1054
|
+
}) => {
|
|
1055
|
+
const sourceColor = sourceName in config ? `var(--color-${sourceName}-0)` : "currentColor";
|
|
1056
|
+
const targetColor = targetName in config ? `var(--color-${targetName}-0)` : "currentColor";
|
|
1057
|
+
return /* @__PURE__ */ jsxs4(
|
|
1058
|
+
"linearGradient",
|
|
1059
|
+
{
|
|
1060
|
+
id: `${chartId}-link-gradient-${index}`,
|
|
1061
|
+
x1: "0%",
|
|
1062
|
+
y1: "0%",
|
|
1063
|
+
x2: "100%",
|
|
1064
|
+
y2: "0%",
|
|
1065
|
+
children: [
|
|
1066
|
+
/* @__PURE__ */ jsx4("stop", { offset: "0%", stopColor: sourceColor, stopOpacity: 0.2 }),
|
|
1067
|
+
/* @__PURE__ */ jsx4("stop", { offset: "50%", stopColor: sourceColor, stopOpacity: 0.5 }),
|
|
1068
|
+
/* @__PURE__ */ jsx4("stop", { offset: "100%", stopColor: targetColor, stopOpacity: 0.2 })
|
|
1069
|
+
]
|
|
1070
|
+
}
|
|
1071
|
+
);
|
|
1072
|
+
};
|
|
1073
|
+
var LinkStrokeGradient = ({
|
|
1074
|
+
chartId,
|
|
1075
|
+
index
|
|
1076
|
+
}) => {
|
|
1077
|
+
return /* @__PURE__ */ jsxs4(
|
|
1078
|
+
"linearGradient",
|
|
1079
|
+
{
|
|
1080
|
+
id: `${chartId}-link-stroke-${index}`,
|
|
1081
|
+
x1: "0%",
|
|
1082
|
+
y1: "0%",
|
|
1083
|
+
x2: "100%",
|
|
1084
|
+
y2: "0%",
|
|
1085
|
+
children: [
|
|
1086
|
+
/* @__PURE__ */ jsx4("stop", { offset: "0%", stopColor: "var(--primary)", stopOpacity: 0 }),
|
|
1087
|
+
/* @__PURE__ */ jsx4("stop", { offset: "15%", stopColor: "var(--primary)", stopOpacity: 0.8 }),
|
|
1088
|
+
/* @__PURE__ */ jsx4("stop", { offset: "50%", stopColor: "var(--primary)", stopOpacity: 1 }),
|
|
1089
|
+
/* @__PURE__ */ jsx4("stop", { offset: "85%", stopColor: "var(--primary)", stopOpacity: 0.8 }),
|
|
1090
|
+
/* @__PURE__ */ jsx4("stop", { offset: "100%", stopColor: "var(--primary)", stopOpacity: 0 })
|
|
1091
|
+
]
|
|
1092
|
+
}
|
|
1093
|
+
);
|
|
1094
|
+
};
|
|
1095
|
+
var LoadingSankey = () => {
|
|
1096
|
+
const nodes = [
|
|
1097
|
+
{ x: 30, y: 25, width: 12, height: 65, delay: 0 },
|
|
1098
|
+
{ x: 30, y: 110, width: 12, height: 50, delay: 0.3 },
|
|
1099
|
+
{ x: 30, y: 180, width: 12, height: 45, delay: 0.15 },
|
|
1100
|
+
{ x: 244, y: 20, width: 12, height: 55, delay: 0.45 },
|
|
1101
|
+
{ x: 244, y: 95, width: 12, height: 75, delay: 0.6 },
|
|
1102
|
+
{ x: 244, y: 190, width: 12, height: 40, delay: 0.25 },
|
|
1103
|
+
{ x: 458, y: 35, width: 12, height: 80, delay: 0.5 },
|
|
1104
|
+
{ x: 458, y: 135, width: 12, height: 90, delay: 0.1 }
|
|
1105
|
+
];
|
|
1106
|
+
const links = [
|
|
1107
|
+
{ from: 0, to: 3, width: 26, delay: 0.2 },
|
|
1108
|
+
{ from: 0, to: 4, width: 18, delay: 0.7 },
|
|
1109
|
+
{ from: 1, to: 4, width: 24, delay: 0.4 },
|
|
1110
|
+
{ from: 1, to: 5, width: 12, delay: 0.9 },
|
|
1111
|
+
{ from: 2, to: 4, width: 16, delay: 0.1 },
|
|
1112
|
+
{ from: 2, to: 5, width: 14, delay: 0.55 },
|
|
1113
|
+
{ from: 3, to: 6, width: 22, delay: 0.35 },
|
|
1114
|
+
{ from: 3, to: 7, width: 18, delay: 0.8 },
|
|
1115
|
+
{ from: 4, to: 6, width: 28, delay: 0.05 },
|
|
1116
|
+
{ from: 4, to: 7, width: 32, delay: 0.65 },
|
|
1117
|
+
{ from: 5, to: 7, width: 16, delay: 0.45 }
|
|
1118
|
+
];
|
|
1119
|
+
const getLinkPath = (fromIdx, toIdx) => {
|
|
1120
|
+
const from = nodes[fromIdx];
|
|
1121
|
+
const to = nodes[toIdx];
|
|
1122
|
+
const startX = from.x + from.width;
|
|
1123
|
+
const startY = from.y + from.height / 2;
|
|
1124
|
+
const endX = to.x;
|
|
1125
|
+
const endY = to.y + to.height / 2;
|
|
1126
|
+
const controlX1 = startX + (endX - startX) * 0.4;
|
|
1127
|
+
const controlX2 = startX + (endX - startX) * 0.6;
|
|
1128
|
+
return `M${startX},${startY} C${controlX1},${startY} ${controlX2},${endY} ${endX},${endY}`;
|
|
1129
|
+
};
|
|
1130
|
+
const baseDuration = LOADING_ANIMATION_DURATION / 1e3;
|
|
1131
|
+
return /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1132
|
+
links.map((link, i) => /* @__PURE__ */ jsx4(
|
|
1133
|
+
motion.path,
|
|
1134
|
+
{
|
|
1135
|
+
d: getLinkPath(link.from, link.to),
|
|
1136
|
+
fill: "none",
|
|
1137
|
+
stroke: "currentColor",
|
|
1138
|
+
strokeWidth: link.width,
|
|
1139
|
+
initial: { opacity: 0.04 },
|
|
1140
|
+
animate: { opacity: [0.04, 0.14, 0.04] },
|
|
1141
|
+
transition: {
|
|
1142
|
+
duration: baseDuration * (0.8 + i % 3 * 0.2),
|
|
1143
|
+
delay: link.delay,
|
|
1144
|
+
repeat: Infinity,
|
|
1145
|
+
ease: "easeInOut"
|
|
1146
|
+
}
|
|
1147
|
+
},
|
|
1148
|
+
`loading-link-${link.from}-${link.to}`
|
|
1149
|
+
)),
|
|
1150
|
+
nodes.map((node, i) => /* @__PURE__ */ jsx4(
|
|
1151
|
+
motion.rect,
|
|
1152
|
+
{
|
|
1153
|
+
x: node.x,
|
|
1154
|
+
y: node.y,
|
|
1155
|
+
width: node.width,
|
|
1156
|
+
height: node.height,
|
|
1157
|
+
rx: 2,
|
|
1158
|
+
fill: "currentColor",
|
|
1159
|
+
initial: { opacity: 0.15 },
|
|
1160
|
+
animate: { opacity: [0.15, 0.4, 0.15] },
|
|
1161
|
+
transition: {
|
|
1162
|
+
duration: baseDuration * (0.9 + i % 4 * 0.1),
|
|
1163
|
+
delay: node.delay,
|
|
1164
|
+
repeat: Infinity,
|
|
1165
|
+
ease: "easeInOut"
|
|
1166
|
+
}
|
|
1167
|
+
},
|
|
1168
|
+
`loading-node-${node.x}-${node.y}`
|
|
1169
|
+
))
|
|
1170
|
+
] });
|
|
1171
|
+
};
|
|
1172
|
+
export {
|
|
1173
|
+
Link,
|
|
1174
|
+
Node,
|
|
1175
|
+
NodeLabel,
|
|
1176
|
+
SankeyChart,
|
|
1177
|
+
Tooltip2 as Tooltip
|
|
1178
|
+
};
|
|
1179
|
+
//# sourceMappingURL=sankey-chart.js.map
|