@aiready/components 0.1.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 (91) hide show
  1. package/README.md +240 -0
  2. package/dist/charts/ForceDirectedGraph.d.ts +40 -0
  3. package/dist/charts/ForceDirectedGraph.js +294 -0
  4. package/dist/charts/ForceDirectedGraph.js.map +1 -0
  5. package/dist/components/badge.d.ts +13 -0
  6. package/dist/components/badge.js +32 -0
  7. package/dist/components/badge.js.map +1 -0
  8. package/dist/components/button.d.ts +14 -0
  9. package/dist/components/button.js +52 -0
  10. package/dist/components/button.js.map +1 -0
  11. package/dist/components/card.d.ts +10 -0
  12. package/dist/components/card.js +66 -0
  13. package/dist/components/card.js.map +1 -0
  14. package/dist/components/checkbox.d.ts +8 -0
  15. package/dist/components/checkbox.js +42 -0
  16. package/dist/components/checkbox.js.map +1 -0
  17. package/dist/components/container.d.ts +8 -0
  18. package/dist/components/container.js +36 -0
  19. package/dist/components/container.js.map +1 -0
  20. package/dist/components/grid.d.ts +9 -0
  21. package/dist/components/grid.js +44 -0
  22. package/dist/components/grid.js.map +1 -0
  23. package/dist/components/input.d.ts +7 -0
  24. package/dist/components/input.js +30 -0
  25. package/dist/components/input.js.map +1 -0
  26. package/dist/components/label.d.ts +10 -0
  27. package/dist/components/label.js +28 -0
  28. package/dist/components/label.js.map +1 -0
  29. package/dist/components/radio-group.d.ts +17 -0
  30. package/dist/components/radio-group.js +64 -0
  31. package/dist/components/radio-group.js.map +1 -0
  32. package/dist/components/select.d.ts +15 -0
  33. package/dist/components/select.js +45 -0
  34. package/dist/components/select.js.map +1 -0
  35. package/dist/components/separator.d.ts +9 -0
  36. package/dist/components/separator.js +30 -0
  37. package/dist/components/separator.js.map +1 -0
  38. package/dist/components/stack.d.ts +11 -0
  39. package/dist/components/stack.js +60 -0
  40. package/dist/components/stack.js.map +1 -0
  41. package/dist/components/switch.d.ts +9 -0
  42. package/dist/components/switch.js +49 -0
  43. package/dist/components/switch.js.map +1 -0
  44. package/dist/components/textarea.d.ts +7 -0
  45. package/dist/components/textarea.js +29 -0
  46. package/dist/components/textarea.js.map +1 -0
  47. package/dist/hooks/useD3.d.ts +6 -0
  48. package/dist/hooks/useD3.js +35 -0
  49. package/dist/hooks/useD3.js.map +1 -0
  50. package/dist/hooks/useDebounce.d.ts +3 -0
  51. package/dist/hooks/useDebounce.js +19 -0
  52. package/dist/hooks/useDebounce.js.map +1 -0
  53. package/dist/hooks/useForceSimulation.d.ts +39 -0
  54. package/dist/hooks/useForceSimulation.js +107 -0
  55. package/dist/hooks/useForceSimulation.js.map +1 -0
  56. package/dist/index.d.ts +27 -0
  57. package/dist/index.js +927 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/utils/cn.d.ts +5 -0
  60. package/dist/utils/cn.js +11 -0
  61. package/dist/utils/cn.js.map +1 -0
  62. package/dist/utils/colors.d.ts +19 -0
  63. package/dist/utils/colors.js +52 -0
  64. package/dist/utils/colors.js.map +1 -0
  65. package/dist/utils/formatters.d.ts +13 -0
  66. package/dist/utils/formatters.js +100 -0
  67. package/dist/utils/formatters.js.map +1 -0
  68. package/package.json +83 -0
  69. package/src/charts/ForceDirectedGraph.tsx +356 -0
  70. package/src/components/badge.tsx +35 -0
  71. package/src/components/button.tsx +53 -0
  72. package/src/components/card.tsx +78 -0
  73. package/src/components/checkbox.tsx +39 -0
  74. package/src/components/container.tsx +31 -0
  75. package/src/components/grid.tsx +40 -0
  76. package/src/components/input.tsx +24 -0
  77. package/src/components/label.tsx +24 -0
  78. package/src/components/radio-group.tsx +71 -0
  79. package/src/components/select.tsx +53 -0
  80. package/src/components/separator.tsx +29 -0
  81. package/src/components/stack.tsx +61 -0
  82. package/src/components/switch.tsx +49 -0
  83. package/src/components/textarea.tsx +23 -0
  84. package/src/hooks/useD3.ts +125 -0
  85. package/src/hooks/useDebounce.ts +44 -0
  86. package/src/hooks/useForceSimulation.ts +328 -0
  87. package/src/index.ts +51 -0
  88. package/src/utils/cn.ts +11 -0
  89. package/src/utils/colors.ts +58 -0
  90. package/src/utils/formatters.ts +161 -0
  91. package/tailwind.config.js +46 -0
package/dist/index.js ADDED
@@ -0,0 +1,927 @@
1
+ import * as React2 from 'react';
2
+ import { useState, useEffect, useRef, useCallback } from 'react';
3
+ import { cva } from 'class-variance-authority';
4
+ import { clsx } from 'clsx';
5
+ import { twMerge } from 'tailwind-merge';
6
+ import { jsx, jsxs } from 'react/jsx-runtime';
7
+ import * as d32 from 'd3';
8
+
9
+ // src/components/button.tsx
10
+ function cn(...inputs) {
11
+ return twMerge(clsx(inputs));
12
+ }
13
+ var buttonVariants = cva(
14
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
15
+ {
16
+ variants: {
17
+ variant: {
18
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
19
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
20
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
21
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
22
+ ghost: "hover:bg-accent hover:text-accent-foreground",
23
+ link: "text-primary underline-offset-4 hover:underline"
24
+ },
25
+ size: {
26
+ default: "h-10 px-4 py-2",
27
+ sm: "h-9 rounded-md px-3",
28
+ lg: "h-11 rounded-md px-8",
29
+ icon: "h-10 w-10"
30
+ }
31
+ },
32
+ defaultVariants: {
33
+ variant: "default",
34
+ size: "default"
35
+ }
36
+ }
37
+ );
38
+ var Button = React2.forwardRef(
39
+ ({ className, variant, size, ...props }, ref) => {
40
+ return /* @__PURE__ */ jsx(
41
+ "button",
42
+ {
43
+ className: cn(buttonVariants({ variant, size, className })),
44
+ ref,
45
+ ...props
46
+ }
47
+ );
48
+ }
49
+ );
50
+ Button.displayName = "Button";
51
+ var Card = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
52
+ "div",
53
+ {
54
+ ref,
55
+ className: cn(
56
+ "rounded-lg border bg-card text-card-foreground shadow-sm",
57
+ className
58
+ ),
59
+ ...props
60
+ }
61
+ ));
62
+ Card.displayName = "Card";
63
+ var CardHeader = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
64
+ "div",
65
+ {
66
+ ref,
67
+ className: cn("flex flex-col space-y-1.5 p-6", className),
68
+ ...props
69
+ }
70
+ ));
71
+ CardHeader.displayName = "CardHeader";
72
+ var CardTitle = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
73
+ "h3",
74
+ {
75
+ ref,
76
+ className: cn(
77
+ "text-2xl font-semibold leading-none tracking-tight",
78
+ className
79
+ ),
80
+ ...props
81
+ }
82
+ ));
83
+ CardTitle.displayName = "CardTitle";
84
+ var CardDescription = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
85
+ "p",
86
+ {
87
+ ref,
88
+ className: cn("text-sm text-muted-foreground", className),
89
+ ...props
90
+ }
91
+ ));
92
+ CardDescription.displayName = "CardDescription";
93
+ var CardContent = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
94
+ CardContent.displayName = "CardContent";
95
+ var CardFooter = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
96
+ "div",
97
+ {
98
+ ref,
99
+ className: cn("flex items-center p-6 pt-0", className),
100
+ ...props
101
+ }
102
+ ));
103
+ CardFooter.displayName = "CardFooter";
104
+ var Input = React2.forwardRef(
105
+ ({ className, type, ...props }, ref) => {
106
+ return /* @__PURE__ */ jsx(
107
+ "input",
108
+ {
109
+ type,
110
+ className: cn(
111
+ "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
112
+ className
113
+ ),
114
+ ref,
115
+ ...props
116
+ }
117
+ );
118
+ }
119
+ );
120
+ Input.displayName = "Input";
121
+ var labelVariants = cva(
122
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
123
+ );
124
+ var Label = React2.forwardRef(
125
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
126
+ "label",
127
+ {
128
+ ref,
129
+ className: cn(labelVariants(), className),
130
+ ...props
131
+ }
132
+ )
133
+ );
134
+ Label.displayName = "Label";
135
+ var badgeVariants = cva(
136
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
137
+ {
138
+ variants: {
139
+ variant: {
140
+ default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
141
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
142
+ destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
143
+ outline: "text-foreground"
144
+ }
145
+ },
146
+ defaultVariants: {
147
+ variant: "default"
148
+ }
149
+ }
150
+ );
151
+ function Badge({ className, variant, ...props }) {
152
+ return /* @__PURE__ */ jsx("div", { className: cn(badgeVariants({ variant }), className), ...props });
153
+ }
154
+ var Container = React2.forwardRef(
155
+ ({ className, size = "lg", ...props }, ref) => {
156
+ return /* @__PURE__ */ jsx(
157
+ "div",
158
+ {
159
+ ref,
160
+ className: cn(
161
+ "mx-auto w-full px-4 sm:px-6 lg:px-8",
162
+ {
163
+ "max-w-screen-sm": size === "sm",
164
+ "max-w-screen-md": size === "md",
165
+ "max-w-screen-lg": size === "lg",
166
+ "max-w-screen-xl": size === "xl",
167
+ "max-w-full": size === "full"
168
+ },
169
+ className
170
+ ),
171
+ ...props
172
+ }
173
+ );
174
+ }
175
+ );
176
+ Container.displayName = "Container";
177
+ var Grid = React2.forwardRef(
178
+ ({ className, cols = 3, gap = "md", ...props }, ref) => {
179
+ return /* @__PURE__ */ jsx(
180
+ "div",
181
+ {
182
+ ref,
183
+ className: cn(
184
+ "grid",
185
+ {
186
+ "grid-cols-1": cols === 1,
187
+ "grid-cols-1 sm:grid-cols-2": cols === 2,
188
+ "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3": cols === 3,
189
+ "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4": cols === 4,
190
+ "grid-cols-1 sm:grid-cols-2 lg:grid-cols-5": cols === 5,
191
+ "grid-cols-1 sm:grid-cols-2 lg:grid-cols-6": cols === 6,
192
+ "grid-cols-1 sm:grid-cols-2 lg:grid-cols-12": cols === 12
193
+ },
194
+ {
195
+ "gap-2": gap === "sm",
196
+ "gap-4": gap === "md",
197
+ "gap-6": gap === "lg",
198
+ "gap-8": gap === "xl"
199
+ },
200
+ className
201
+ ),
202
+ ...props
203
+ }
204
+ );
205
+ }
206
+ );
207
+ Grid.displayName = "Grid";
208
+ var Stack = React2.forwardRef(
209
+ ({
210
+ className,
211
+ direction = "vertical",
212
+ spacing = "md",
213
+ align = "stretch",
214
+ justify = "start",
215
+ ...props
216
+ }, ref) => {
217
+ return /* @__PURE__ */ jsx(
218
+ "div",
219
+ {
220
+ ref,
221
+ className: cn(
222
+ "flex",
223
+ {
224
+ "flex-col": direction === "vertical",
225
+ "flex-row": direction === "horizontal"
226
+ },
227
+ {
228
+ "gap-1": spacing === "xs",
229
+ "gap-2": spacing === "sm",
230
+ "gap-4": spacing === "md",
231
+ "gap-6": spacing === "lg",
232
+ "gap-8": spacing === "xl"
233
+ },
234
+ {
235
+ "items-start": align === "start",
236
+ "items-center": align === "center",
237
+ "items-end": align === "end",
238
+ "items-stretch": align === "stretch"
239
+ },
240
+ {
241
+ "justify-start": justify === "start",
242
+ "justify-center": justify === "center",
243
+ "justify-end": justify === "end",
244
+ "justify-between": justify === "between",
245
+ "justify-around": justify === "around"
246
+ },
247
+ className
248
+ ),
249
+ ...props
250
+ }
251
+ );
252
+ }
253
+ );
254
+ Stack.displayName = "Stack";
255
+ var Separator = React2.forwardRef(
256
+ ({ className, orientation = "horizontal", decorative = true, ...props }, ref) => /* @__PURE__ */ jsx(
257
+ "div",
258
+ {
259
+ ref,
260
+ role: decorative ? "none" : "separator",
261
+ "aria-orientation": orientation,
262
+ className: cn(
263
+ "shrink-0 bg-border",
264
+ orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
265
+ className
266
+ ),
267
+ ...props
268
+ }
269
+ )
270
+ );
271
+ Separator.displayName = "Separator";
272
+ var Checkbox = React2.forwardRef(
273
+ ({ className, label, id, ...props }, ref) => {
274
+ const checkboxId = id || React2.useId();
275
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
276
+ /* @__PURE__ */ jsx(
277
+ "input",
278
+ {
279
+ type: "checkbox",
280
+ id: checkboxId,
281
+ ref,
282
+ className: cn(
283
+ "h-4 w-4 rounded border-gray-300 text-primary focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
284
+ className
285
+ ),
286
+ ...props
287
+ }
288
+ ),
289
+ label && /* @__PURE__ */ jsx(
290
+ "label",
291
+ {
292
+ htmlFor: checkboxId,
293
+ className: "ml-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
294
+ children: label
295
+ }
296
+ )
297
+ ] });
298
+ }
299
+ );
300
+ Checkbox.displayName = "Checkbox";
301
+ var RadioGroup = React2.forwardRef(
302
+ ({
303
+ className,
304
+ options,
305
+ value,
306
+ onChange,
307
+ name,
308
+ orientation = "vertical",
309
+ ...props
310
+ }, ref) => {
311
+ return /* @__PURE__ */ jsx(
312
+ "div",
313
+ {
314
+ ref,
315
+ className: cn(
316
+ "flex",
317
+ orientation === "vertical" ? "flex-col gap-2" : "flex-row gap-4",
318
+ className
319
+ ),
320
+ ...props,
321
+ children: options.map((option) => {
322
+ const id = `${name}-${option.value}`;
323
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
324
+ /* @__PURE__ */ jsx(
325
+ "input",
326
+ {
327
+ type: "radio",
328
+ id,
329
+ name,
330
+ value: option.value,
331
+ checked: value === option.value,
332
+ disabled: option.disabled,
333
+ onChange: (e) => onChange?.(e.target.value),
334
+ className: "h-4 w-4 border-gray-300 text-primary focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
335
+ }
336
+ ),
337
+ /* @__PURE__ */ jsx(
338
+ "label",
339
+ {
340
+ htmlFor: id,
341
+ className: "ml-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
342
+ children: option.label
343
+ }
344
+ )
345
+ ] }, option.value);
346
+ })
347
+ }
348
+ );
349
+ }
350
+ );
351
+ RadioGroup.displayName = "RadioGroup";
352
+ var Switch = React2.forwardRef(
353
+ ({ className, label, id, checked, onCheckedChange, onChange, ...props }, ref) => {
354
+ const switchId = id || React2.useId();
355
+ const handleChange = (e) => {
356
+ onChange?.(e);
357
+ onCheckedChange?.(e.target.checked);
358
+ };
359
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
360
+ /* @__PURE__ */ jsxs("label", { htmlFor: switchId, className: "relative inline-flex cursor-pointer items-center", children: [
361
+ /* @__PURE__ */ jsx(
362
+ "input",
363
+ {
364
+ type: "checkbox",
365
+ id: switchId,
366
+ ref,
367
+ checked,
368
+ onChange: handleChange,
369
+ className: "peer sr-only",
370
+ ...props
371
+ }
372
+ ),
373
+ /* @__PURE__ */ jsx(
374
+ "div",
375
+ {
376
+ className: cn(
377
+ 'peer h-6 w-11 rounded-full bg-gray-200 after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:border after:border-gray-300 after:bg-white after:transition-all after:content-[""] peer-checked:bg-primary peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-ring peer-focus:ring-offset-2 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
378
+ className
379
+ )
380
+ }
381
+ )
382
+ ] }),
383
+ label && /* @__PURE__ */ jsx("span", { className: "ml-3 text-sm font-medium text-foreground", children: label })
384
+ ] });
385
+ }
386
+ );
387
+ Switch.displayName = "Switch";
388
+ var Textarea = React2.forwardRef(
389
+ ({ className, ...props }, ref) => {
390
+ return /* @__PURE__ */ jsx(
391
+ "textarea",
392
+ {
393
+ className: cn(
394
+ "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
395
+ className
396
+ ),
397
+ ref,
398
+ ...props
399
+ }
400
+ );
401
+ }
402
+ );
403
+ Textarea.displayName = "Textarea";
404
+ var Select = React2.forwardRef(
405
+ ({ className, options, placeholder, onChange, ...props }, ref) => {
406
+ const handleChange = (e) => {
407
+ onChange?.(e.target.value);
408
+ };
409
+ return /* @__PURE__ */ jsxs(
410
+ "select",
411
+ {
412
+ ref,
413
+ className: cn(
414
+ "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
415
+ className
416
+ ),
417
+ onChange: handleChange,
418
+ ...props,
419
+ children: [
420
+ placeholder && /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: placeholder }),
421
+ options.map((option) => /* @__PURE__ */ jsx(
422
+ "option",
423
+ {
424
+ value: option.value,
425
+ disabled: option.disabled,
426
+ children: option.label
427
+ },
428
+ option.value
429
+ ))
430
+ ]
431
+ }
432
+ );
433
+ }
434
+ );
435
+ Select.displayName = "Select";
436
+
437
+ // src/utils/colors.ts
438
+ var severityColors = {
439
+ critical: "#ef4444",
440
+ // red-500
441
+ major: "#f59e0b",
442
+ // amber-500
443
+ minor: "#eab308",
444
+ // yellow-500
445
+ info: "#60a5fa"
446
+ // blue-400
447
+ };
448
+ var domainColors = [
449
+ "#3b82f6",
450
+ // blue-500
451
+ "#8b5cf6",
452
+ // violet-500
453
+ "#ec4899",
454
+ // pink-500
455
+ "#10b981",
456
+ // green-500
457
+ "#f59e0b",
458
+ // amber-500
459
+ "#06b6d4",
460
+ // cyan-500
461
+ "#f97316",
462
+ // orange-500
463
+ "#a855f7"
464
+ // purple-500
465
+ ];
466
+ var chartColors = {
467
+ primary: "#3b82f6",
468
+ success: "#10b981",
469
+ warning: "#f59e0b",
470
+ danger: "#ef4444",
471
+ info: "#06b6d4"
472
+ };
473
+ function getDomainColor(index) {
474
+ return domainColors[index % domainColors.length];
475
+ }
476
+ function getSeverityColor(severity) {
477
+ return severityColors[severity];
478
+ }
479
+ function hexToRgba(hex, alpha) {
480
+ const r = parseInt(hex.slice(1, 3), 16);
481
+ const g = parseInt(hex.slice(3, 5), 16);
482
+ const b = parseInt(hex.slice(5, 7), 16);
483
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
484
+ }
485
+
486
+ // src/utils/formatters.ts
487
+ function formatNumber(value) {
488
+ return new Intl.NumberFormat("en-US").format(value);
489
+ }
490
+ function formatCompactNumber(value) {
491
+ return new Intl.NumberFormat("en-US", {
492
+ notation: "compact",
493
+ maximumFractionDigits: 1
494
+ }).format(value);
495
+ }
496
+ function formatPercentage(value, decimals = 1) {
497
+ return `${(value * 100).toFixed(decimals)}%`;
498
+ }
499
+ function formatFileSize(bytes) {
500
+ const units = ["B", "KB", "MB", "GB", "TB"];
501
+ let size = bytes;
502
+ let unitIndex = 0;
503
+ while (size >= 1024 && unitIndex < units.length - 1) {
504
+ size /= 1024;
505
+ unitIndex++;
506
+ }
507
+ return `${size.toFixed(1)} ${units[unitIndex]}`;
508
+ }
509
+ function formatRelativeTime(date) {
510
+ const now = /* @__PURE__ */ new Date();
511
+ const diffMs = now.getTime() - date.getTime();
512
+ const diffSec = Math.floor(diffMs / 1e3);
513
+ const diffMin = Math.floor(diffSec / 60);
514
+ const diffHour = Math.floor(diffMin / 60);
515
+ const diffDay = Math.floor(diffHour / 24);
516
+ const diffMonth = Math.floor(diffDay / 30);
517
+ const diffYear = Math.floor(diffDay / 365);
518
+ if (diffYear > 0) {
519
+ return `${diffYear} year${diffYear > 1 ? "s" : ""} ago`;
520
+ }
521
+ if (diffMonth > 0) {
522
+ return `${diffMonth} month${diffMonth > 1 ? "s" : ""} ago`;
523
+ }
524
+ if (diffDay > 0) {
525
+ return `${diffDay} day${diffDay > 1 ? "s" : ""} ago`;
526
+ }
527
+ if (diffHour > 0) {
528
+ return `${diffHour} hour${diffHour > 1 ? "s" : ""} ago`;
529
+ }
530
+ if (diffMin > 0) {
531
+ return `${diffMin} minute${diffMin > 1 ? "s" : ""} ago`;
532
+ }
533
+ return "just now";
534
+ }
535
+ function formatDate(date) {
536
+ return new Intl.DateTimeFormat("en-US", {
537
+ year: "numeric",
538
+ month: "short",
539
+ day: "numeric"
540
+ }).format(date);
541
+ }
542
+ function formatDateTime(date) {
543
+ return new Intl.DateTimeFormat("en-US", {
544
+ year: "numeric",
545
+ month: "short",
546
+ day: "numeric",
547
+ hour: "numeric",
548
+ minute: "2-digit",
549
+ hour12: true
550
+ }).format(date);
551
+ }
552
+ function formatDuration(ms) {
553
+ const seconds = Math.floor(ms / 1e3);
554
+ const minutes = Math.floor(seconds / 60);
555
+ const hours = Math.floor(minutes / 60);
556
+ if (hours > 0) {
557
+ return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
558
+ }
559
+ if (minutes > 0) {
560
+ return `${minutes}m ${seconds % 60}s`;
561
+ }
562
+ return `${seconds}s`;
563
+ }
564
+ function formatMetric(value, unit = "number") {
565
+ switch (unit) {
566
+ case "bytes":
567
+ return formatFileSize(value);
568
+ case "percentage":
569
+ return formatPercentage(value);
570
+ case "duration":
571
+ return formatDuration(value);
572
+ default:
573
+ return formatCompactNumber(value);
574
+ }
575
+ }
576
+ function formatRange(min, max) {
577
+ return `${formatCompactNumber(min)} - ${formatCompactNumber(max)}`;
578
+ }
579
+ function formatDecimal(value, decimals = 2) {
580
+ return value.toFixed(decimals);
581
+ }
582
+ function useDebounce(value, delay = 300) {
583
+ const [debouncedValue, setDebouncedValue] = useState(value);
584
+ useEffect(() => {
585
+ const timer = setTimeout(() => {
586
+ setDebouncedValue(value);
587
+ }, delay);
588
+ return () => {
589
+ clearTimeout(timer);
590
+ };
591
+ }, [value, delay]);
592
+ return debouncedValue;
593
+ }
594
+ function useD3(renderFn, dependencies = []) {
595
+ const ref = useRef(null);
596
+ useEffect(() => {
597
+ if (ref.current) {
598
+ const selection = d32.select(ref.current);
599
+ renderFn(selection);
600
+ }
601
+ }, dependencies);
602
+ return ref;
603
+ }
604
+ function useD3WithResize(renderFn, dependencies = []) {
605
+ const ref = useRef(null);
606
+ useEffect(() => {
607
+ if (!ref.current) return;
608
+ const selection = d32.select(ref.current);
609
+ const render = () => renderFn(selection);
610
+ render();
611
+ const resizeObserver = new ResizeObserver(() => {
612
+ render();
613
+ });
614
+ resizeObserver.observe(ref.current);
615
+ return () => {
616
+ resizeObserver.disconnect();
617
+ };
618
+ }, dependencies);
619
+ return ref;
620
+ }
621
+ function useForceSimulation(initialNodes, initialLinks, options) {
622
+ const {
623
+ chargeStrength = -300,
624
+ linkDistance = 100,
625
+ linkStrength = 1,
626
+ collisionStrength = 1,
627
+ collisionRadius = 10,
628
+ centerStrength = 0.1,
629
+ width,
630
+ height,
631
+ alphaDecay = 0.0228,
632
+ velocityDecay = 0.4
633
+ } = options;
634
+ const [nodes, setNodes] = useState(initialNodes);
635
+ const [links, setLinks] = useState(initialLinks);
636
+ const [isRunning, setIsRunning] = useState(false);
637
+ const [alpha, setAlpha] = useState(1);
638
+ const simulationRef = useRef(null);
639
+ useEffect(() => {
640
+ const nodesCopy = initialNodes.map((node) => ({ ...node }));
641
+ const linksCopy = initialLinks.map((link) => ({ ...link }));
642
+ const simulation = d32.forceSimulation(nodesCopy).force(
643
+ "link",
644
+ d32.forceLink(linksCopy).id((d) => d.id).distance(linkDistance).strength(linkStrength)
645
+ ).force("charge", d32.forceManyBody().strength(chargeStrength)).force("center", d32.forceCenter(width / 2, height / 2).strength(centerStrength)).force(
646
+ "collision",
647
+ d32.forceCollide().radius(collisionRadius).strength(collisionStrength)
648
+ ).alphaDecay(alphaDecay).velocityDecay(velocityDecay);
649
+ simulationRef.current = simulation;
650
+ simulation.on("tick", () => {
651
+ setNodes([...nodesCopy]);
652
+ setLinks([...linksCopy]);
653
+ setAlpha(simulation.alpha());
654
+ setIsRunning(simulation.alpha() > simulation.alphaMin());
655
+ });
656
+ simulation.on("end", () => {
657
+ setIsRunning(false);
658
+ });
659
+ return () => {
660
+ simulation.stop();
661
+ };
662
+ }, [
663
+ initialNodes,
664
+ initialLinks,
665
+ chargeStrength,
666
+ linkDistance,
667
+ linkStrength,
668
+ collisionStrength,
669
+ collisionRadius,
670
+ centerStrength,
671
+ width,
672
+ height,
673
+ alphaDecay,
674
+ velocityDecay
675
+ ]);
676
+ const restart = () => {
677
+ if (simulationRef.current) {
678
+ simulationRef.current.alpha(1).restart();
679
+ setIsRunning(true);
680
+ }
681
+ };
682
+ const stop = () => {
683
+ if (simulationRef.current) {
684
+ simulationRef.current.stop();
685
+ setIsRunning(false);
686
+ }
687
+ };
688
+ return {
689
+ nodes,
690
+ links,
691
+ restart,
692
+ stop,
693
+ isRunning,
694
+ alpha
695
+ };
696
+ }
697
+ function useDrag(simulation) {
698
+ const dragStarted = (event, node) => {
699
+ if (!simulation) return;
700
+ if (!event.active) simulation.alphaTarget(0.3).restart();
701
+ node.fx = node.x;
702
+ node.fy = node.y;
703
+ };
704
+ const dragged = (event, node) => {
705
+ node.fx = event.x;
706
+ node.fy = event.y;
707
+ };
708
+ const dragEnded = (event, node) => {
709
+ if (!simulation) return;
710
+ if (!event.active) simulation.alphaTarget(0);
711
+ node.fx = null;
712
+ node.fy = null;
713
+ };
714
+ return {
715
+ onDragStart: dragStarted,
716
+ onDrag: dragged,
717
+ onDragEnd: dragEnded
718
+ };
719
+ }
720
+ var ForceDirectedGraph = ({
721
+ nodes: initialNodes,
722
+ links: initialLinks,
723
+ width,
724
+ height,
725
+ simulationOptions,
726
+ enableZoom = true,
727
+ enableDrag = true,
728
+ onNodeClick,
729
+ onNodeHover,
730
+ onLinkClick,
731
+ selectedNodeId,
732
+ hoveredNodeId,
733
+ defaultNodeColor = "#69b3a2",
734
+ defaultNodeSize = 10,
735
+ defaultLinkColor = "#999",
736
+ defaultLinkWidth = 1,
737
+ showNodeLabels = true,
738
+ showLinkLabels = false,
739
+ className
740
+ }) => {
741
+ const svgRef = useRef(null);
742
+ const gRef = useRef(null);
743
+ const [transform, setTransform] = useState({ k: 1, x: 0, y: 0 });
744
+ const { nodes, links, restart } = useForceSimulation(initialNodes, initialLinks, {
745
+ width,
746
+ height,
747
+ ...simulationOptions
748
+ });
749
+ useEffect(() => {
750
+ if (!enableZoom || !svgRef.current || !gRef.current) return;
751
+ const svg = d32.select(svgRef.current);
752
+ const g = d32.select(gRef.current);
753
+ const zoom2 = d32.zoom().scaleExtent([0.1, 10]).on("zoom", (event) => {
754
+ g.attr("transform", event.transform);
755
+ setTransform(event.transform);
756
+ });
757
+ svg.call(zoom2);
758
+ return () => {
759
+ svg.on(".zoom", null);
760
+ };
761
+ }, [enableZoom]);
762
+ const handleDragStart = useCallback(
763
+ (event, node) => {
764
+ if (!enableDrag) return;
765
+ event.stopPropagation();
766
+ node.fx = node.x;
767
+ node.fy = node.y;
768
+ restart();
769
+ },
770
+ [enableDrag, restart]
771
+ );
772
+ const handleDrag = useCallback(
773
+ (event, node) => {
774
+ if (!enableDrag) return;
775
+ const svg = svgRef.current;
776
+ if (!svg) return;
777
+ const rect = svg.getBoundingClientRect();
778
+ const x = (event.clientX - rect.left - transform.x) / transform.k;
779
+ const y = (event.clientY - rect.top - transform.y) / transform.k;
780
+ node.fx = x;
781
+ node.fy = y;
782
+ },
783
+ [enableDrag, transform]
784
+ );
785
+ const handleDragEnd = useCallback(
786
+ (event, node) => {
787
+ if (!enableDrag) return;
788
+ event.stopPropagation();
789
+ node.fx = null;
790
+ node.fy = null;
791
+ },
792
+ [enableDrag]
793
+ );
794
+ const handleNodeClick = useCallback(
795
+ (node) => {
796
+ onNodeClick?.(node);
797
+ },
798
+ [onNodeClick]
799
+ );
800
+ const handleNodeMouseEnter = useCallback(
801
+ (node) => {
802
+ onNodeHover?.(node);
803
+ },
804
+ [onNodeHover]
805
+ );
806
+ const handleNodeMouseLeave = useCallback(() => {
807
+ onNodeHover?.(null);
808
+ }, [onNodeHover]);
809
+ const handleLinkClick = useCallback(
810
+ (link) => {
811
+ onLinkClick?.(link);
812
+ },
813
+ [onLinkClick]
814
+ );
815
+ return /* @__PURE__ */ jsxs(
816
+ "svg",
817
+ {
818
+ ref: svgRef,
819
+ width,
820
+ height,
821
+ className: cn("bg-white dark:bg-gray-900", className),
822
+ children: [
823
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
824
+ "marker",
825
+ {
826
+ id: "arrow",
827
+ viewBox: "0 0 10 10",
828
+ refX: "20",
829
+ refY: "5",
830
+ markerWidth: "6",
831
+ markerHeight: "6",
832
+ orient: "auto",
833
+ children: /* @__PURE__ */ jsx("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: defaultLinkColor })
834
+ }
835
+ ) }),
836
+ /* @__PURE__ */ jsxs("g", { ref: gRef, children: [
837
+ links.map((link, i) => {
838
+ const source = link.source;
839
+ const target = link.target;
840
+ if (!source.x || !source.y || !target.x || !target.y) return null;
841
+ return /* @__PURE__ */ jsxs("g", { children: [
842
+ /* @__PURE__ */ jsx(
843
+ "line",
844
+ {
845
+ x1: source.x,
846
+ y1: source.y,
847
+ x2: target.x,
848
+ y2: target.y,
849
+ stroke: link.color || defaultLinkColor,
850
+ strokeWidth: link.width || defaultLinkWidth,
851
+ opacity: 0.6,
852
+ className: "cursor-pointer transition-opacity hover:opacity-100",
853
+ onClick: () => handleLinkClick(link)
854
+ }
855
+ ),
856
+ showLinkLabels && link.label && /* @__PURE__ */ jsx(
857
+ "text",
858
+ {
859
+ x: (source.x + target.x) / 2,
860
+ y: (source.y + target.y) / 2,
861
+ fill: "#666",
862
+ fontSize: "10",
863
+ textAnchor: "middle",
864
+ dominantBaseline: "middle",
865
+ pointerEvents: "none",
866
+ children: link.label
867
+ }
868
+ )
869
+ ] }, `link-${i}`);
870
+ }),
871
+ nodes.map((node) => {
872
+ if (!node.x || !node.y) return null;
873
+ const isSelected = selectedNodeId === node.id;
874
+ const isHovered = hoveredNodeId === node.id;
875
+ const nodeSize = node.size || defaultNodeSize;
876
+ const nodeColor = node.color || defaultNodeColor;
877
+ return /* @__PURE__ */ jsxs(
878
+ "g",
879
+ {
880
+ transform: `translate(${node.x},${node.y})`,
881
+ className: "cursor-pointer",
882
+ onClick: () => handleNodeClick(node),
883
+ onMouseEnter: () => handleNodeMouseEnter(node),
884
+ onMouseLeave: handleNodeMouseLeave,
885
+ onMouseDown: (e) => handleDragStart(e, node),
886
+ onMouseMove: (e) => handleDrag(e, node),
887
+ onMouseUp: (e) => handleDragEnd(e, node),
888
+ children: [
889
+ /* @__PURE__ */ jsx(
890
+ "circle",
891
+ {
892
+ r: nodeSize,
893
+ fill: nodeColor,
894
+ stroke: isSelected ? "#000" : isHovered ? "#666" : "none",
895
+ strokeWidth: isSelected ? 3 : 2,
896
+ opacity: isHovered || isSelected ? 1 : 0.9,
897
+ className: "transition-all"
898
+ }
899
+ ),
900
+ showNodeLabels && node.label && /* @__PURE__ */ jsx(
901
+ "text",
902
+ {
903
+ y: nodeSize + 15,
904
+ fill: "#333",
905
+ fontSize: "12",
906
+ textAnchor: "middle",
907
+ dominantBaseline: "middle",
908
+ pointerEvents: "none",
909
+ className: "select-none",
910
+ children: node.label
911
+ }
912
+ )
913
+ ]
914
+ },
915
+ node.id
916
+ );
917
+ })
918
+ ] })
919
+ ]
920
+ }
921
+ );
922
+ };
923
+ ForceDirectedGraph.displayName = "ForceDirectedGraph";
924
+
925
+ export { Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Container, ForceDirectedGraph, Grid, Input, Label, RadioGroup, Select, Separator, Stack, Switch, Textarea, badgeVariants, buttonVariants, chartColors, cn, domainColors, formatCompactNumber, formatDate, formatDateTime, formatDecimal, formatDuration, formatFileSize, formatMetric, formatNumber, formatPercentage, formatRange, formatRelativeTime, getDomainColor, getSeverityColor, hexToRgba, severityColors, useD3, useD3WithResize, useDebounce, useDrag, useForceSimulation };
926
+ //# sourceMappingURL=index.js.map
927
+ //# sourceMappingURL=index.js.map