@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/README.md ADDED
@@ -0,0 +1,240 @@
1
+ # @aiready/components
2
+
3
+ Unified shared components library (UI, charts, hooks, utilities) for AIReady.
4
+
5
+ ## Features
6
+
7
+ - 🎨 **UI Components**: Button, Card, Input, Label, Badge (shadcn/ui based)
8
+ - 📊 **D3 Charts**: Coming in Phase 2 (LineChart, BarChart, ForceGraph)
9
+ - 🪝 **React Hooks**: Coming in Phase 3 (useDebounce, useTheme, useD3)
10
+ - 🛠️ **Utilities**: className merging, formatters, color schemes
11
+ - 🌙 **Dark Mode**: Built-in support via Tailwind CSS
12
+ - 🎯 **Tree-shakeable**: Granular exports for optimal bundle size
13
+ - 📦 **TypeScript**: Full type safety
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pnpm add @aiready/components
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### Import Everything
24
+
25
+ ```tsx
26
+ import { Button, Card, Input, Label, Badge } from '@aiready/components';
27
+
28
+ function App() {
29
+ return (
30
+ <Card>
31
+ <CardHeader>
32
+ <CardTitle>Welcome</CardTitle>
33
+ </CardHeader>
34
+ <CardContent>
35
+ <Label htmlFor="email">Email</Label>
36
+ <Input id="email" type="email" placeholder="you@example.com" />
37
+ </CardContent>
38
+ <CardFooter>
39
+ <Button>Submit</Button>
40
+ <Badge variant="secondary">New</Badge>
41
+ </CardFooter>
42
+ </Card>
43
+ );
44
+ }
45
+ ```
46
+
47
+ ### Import Specific Components (Tree-shaking)
48
+
49
+ ```tsx
50
+ import { Button } from '@aiready/components/button';
51
+ import { Card } from '@aiready/components/card';
52
+ ```
53
+
54
+ ## Components
55
+
56
+ ### Button
57
+
58
+ ```tsx
59
+ import { Button } from '@aiready/components/button';
60
+
61
+ <Button variant="default">Click me</Button>
62
+ <Button variant="destructive" size="sm">Delete</Button>
63
+ <Button variant="outline" size="lg">Outline</Button>
64
+ <Button variant="ghost">Ghost</Button>
65
+ <Button variant="link">Link</Button>
66
+ ```
67
+
68
+ **Variants**: `default`, `destructive`, `outline`, `secondary`, `ghost`, `link`
69
+ **Sizes**: `default`, `sm`, `lg`, `icon`
70
+
71
+ ### Card
72
+
73
+ ```tsx
74
+ import {
75
+ Card,
76
+ CardHeader,
77
+ CardTitle,
78
+ CardDescription,
79
+ CardContent,
80
+ CardFooter
81
+ } from '@aiready/components/card';
82
+
83
+ <Card>
84
+ <CardHeader>
85
+ <CardTitle>Card Title</CardTitle>
86
+ <CardDescription>Card description here</CardDescription>
87
+ </CardHeader>
88
+ <CardContent>
89
+ <p>Card content</p>
90
+ </CardContent>
91
+ <CardFooter>
92
+ <Button>Action</Button>
93
+ </CardFooter>
94
+ </Card>
95
+ ```
96
+
97
+ ### Input
98
+
99
+ ```tsx
100
+ import { Input } from '@aiready/components/input';
101
+
102
+ <Input type="text" placeholder="Enter text..." />
103
+ <Input type="email" placeholder="Email..." />
104
+ <Input type="password" placeholder="Password..." />
105
+ ```
106
+
107
+ ### Label
108
+
109
+ ```tsx
110
+ import { Label } from '@aiready/components/label';
111
+
112
+ <Label htmlFor="username">Username</Label>
113
+ <Input id="username" />
114
+ ```
115
+
116
+ ### Badge
117
+
118
+ ```tsx
119
+ import { Badge } from '@aiready/components/badge';
120
+
121
+ <Badge variant="default">Default</Badge>
122
+ <Badge variant="secondary">Secondary</Badge>
123
+ <Badge variant="destructive">Error</Badge>
124
+ <Badge variant="outline">Outline</Badge>
125
+ ```
126
+
127
+ ## Tailwind CSS Setup
128
+
129
+ This package requires Tailwind CSS. Add the package to your Tailwind config:
130
+
131
+ ```js
132
+ // tailwind.config.js
133
+ export default {
134
+ content: [
135
+ './src/**/*.{ts,tsx}',
136
+ './node_modules/@aiready/components/**/*.{js,ts,jsx,tsx}',
137
+ ],
138
+ theme: {
139
+ extend: {
140
+ // Use the shared config
141
+ },
142
+ },
143
+ plugins: [],
144
+ };
145
+ ```
146
+
147
+ ### CSS Variables
148
+
149
+ Add these CSS variables to your global styles:
150
+
151
+ ```css
152
+ @layer base {
153
+ :root {
154
+ --background: 0 0% 100%;
155
+ --foreground: 222.2 84% 4.9%;
156
+ --card: 0 0% 100%;
157
+ --card-foreground: 222.2 84% 4.9%;
158
+ --primary: 222.2 47.4% 11.2%;
159
+ --primary-foreground: 210 40% 98%;
160
+ --secondary: 210 40% 96.1%;
161
+ --secondary-foreground: 222.2 47.4% 11.2%;
162
+ --muted: 210 40% 96.1%;
163
+ --muted-foreground: 215.4 16.3% 46.9%;
164
+ --accent: 210 40% 96.1%;
165
+ --accent-foreground: 222.2 47.4% 11.2%;
166
+ --destructive: 0 84.2% 60.2%;
167
+ --destructive-foreground: 210 40% 98%;
168
+ --border: 214.3 31.8% 91.4%;
169
+ --input: 214.3 31.8% 91.4%;
170
+ --ring: 222.2 84% 4.9%;
171
+ --radius: 0.5rem;
172
+ }
173
+
174
+ .dark {
175
+ --background: 222.2 84% 4.9%;
176
+ --foreground: 210 40% 98%;
177
+ --card: 222.2 84% 4.9%;
178
+ --card-foreground: 210 40% 98%;
179
+ --primary: 210 40% 98%;
180
+ --primary-foreground: 222.2 47.4% 11.2%;
181
+ --secondary: 217.2 32.6% 17.5%;
182
+ --secondary-foreground: 210 40% 98%;
183
+ --muted: 217.2 32.6% 17.5%;
184
+ --muted-foreground: 215 20.2% 65.1%;
185
+ --accent: 217.2 32.6% 17.5%;
186
+ --accent-foreground: 210 40% 98%;
187
+ --destructive: 0 62.8% 30.6%;
188
+ --destructive-foreground: 210 40% 98%;
189
+ --border: 217.2 32.6% 17.5%;
190
+ --input: 217.2 32.6% 17.5%;
191
+ --ring: 212.7 26.8% 83.9%;
192
+ }
193
+ }
194
+ ```
195
+
196
+ ## Development
197
+
198
+ ```bash
199
+ # Install dependencies
200
+ pnpm install
201
+
202
+ # Build package
203
+ pnpm build
204
+
205
+ # Watch mode
206
+ pnpm dev
207
+
208
+ # Type check
209
+ pnpm typecheck
210
+
211
+ # Run tests
212
+ pnpm test
213
+ ```
214
+
215
+ ## Roadmap
216
+
217
+ ### Phase 1: Foundation ✅ COMPLETE
218
+ - [x] Button, Card, Input, Label, Badge components
219
+ - [x] Tailwind configuration
220
+ - [x] TypeScript setup
221
+ - [x] Build system (tsup)
222
+
223
+ ### Phase 2: Extended UI + Charts (Week 2)
224
+ - [ ] Layout components (Container, Grid, Stack)
225
+ - [ ] Interactive components (Modal, Dropdown, Tabs, Tooltip)
226
+ - [ ] Form components (Select, Checkbox, Radio, Switch)
227
+ - [ ] D3 Charts (LineChart, BarChart, ScatterPlot)
228
+
229
+ ### Phase 3: Advanced Charts + Utilities (Week 3)
230
+ - [ ] Advanced charts (ForceGraph, HeatMap, TreeMap)
231
+ - [ ] React hooks (useD3, useDebounce, useTheme)
232
+ - [ ] Utilities (formatters, color schemes)
233
+
234
+ ## License
235
+
236
+ MIT
237
+
238
+ ## Maintainer
239
+
240
+ Peng Cao (@caopengau)
@@ -0,0 +1,40 @@
1
+ import React__default from 'react';
2
+ import { SimulationNode, SimulationLink, ForceSimulationOptions } from '../hooks/useForceSimulation.js';
3
+ import 'd3';
4
+
5
+ interface GraphNode extends SimulationNode {
6
+ id: string;
7
+ label?: string;
8
+ color?: string;
9
+ size?: number;
10
+ group?: string;
11
+ }
12
+ interface GraphLink extends SimulationLink {
13
+ color?: string;
14
+ width?: number;
15
+ label?: string;
16
+ }
17
+ interface ForceDirectedGraphProps {
18
+ nodes: GraphNode[];
19
+ links: GraphLink[];
20
+ width: number;
21
+ height: number;
22
+ simulationOptions?: Partial<ForceSimulationOptions>;
23
+ enableZoom?: boolean;
24
+ enableDrag?: boolean;
25
+ onNodeClick?: (node: GraphNode) => void;
26
+ onNodeHover?: (node: GraphNode | null) => void;
27
+ onLinkClick?: (link: GraphLink) => void;
28
+ selectedNodeId?: string;
29
+ hoveredNodeId?: string;
30
+ defaultNodeColor?: string;
31
+ defaultNodeSize?: number;
32
+ defaultLinkColor?: string;
33
+ defaultLinkWidth?: number;
34
+ showNodeLabels?: boolean;
35
+ showLinkLabels?: boolean;
36
+ className?: string;
37
+ }
38
+ declare const ForceDirectedGraph: React__default.FC<ForceDirectedGraphProps>;
39
+
40
+ export { ForceDirectedGraph, type ForceDirectedGraphProps, type GraphLink, type GraphNode };
@@ -0,0 +1,294 @@
1
+ import { useRef, useState, useEffect, useCallback } from 'react';
2
+ import * as d3 from 'd3';
3
+ import { clsx } from 'clsx';
4
+ import { twMerge } from 'tailwind-merge';
5
+ import { jsxs, jsx } from 'react/jsx-runtime';
6
+
7
+ // src/charts/ForceDirectedGraph.tsx
8
+ function useForceSimulation(initialNodes, initialLinks, options) {
9
+ const {
10
+ chargeStrength = -300,
11
+ linkDistance = 100,
12
+ linkStrength = 1,
13
+ collisionStrength = 1,
14
+ collisionRadius = 10,
15
+ centerStrength = 0.1,
16
+ width,
17
+ height,
18
+ alphaDecay = 0.0228,
19
+ velocityDecay = 0.4
20
+ } = options;
21
+ const [nodes, setNodes] = useState(initialNodes);
22
+ const [links, setLinks] = useState(initialLinks);
23
+ const [isRunning, setIsRunning] = useState(false);
24
+ const [alpha, setAlpha] = useState(1);
25
+ const simulationRef = useRef(null);
26
+ useEffect(() => {
27
+ const nodesCopy = initialNodes.map((node) => ({ ...node }));
28
+ const linksCopy = initialLinks.map((link) => ({ ...link }));
29
+ const simulation = d3.forceSimulation(nodesCopy).force(
30
+ "link",
31
+ d3.forceLink(linksCopy).id((d) => d.id).distance(linkDistance).strength(linkStrength)
32
+ ).force("charge", d3.forceManyBody().strength(chargeStrength)).force("center", d3.forceCenter(width / 2, height / 2).strength(centerStrength)).force(
33
+ "collision",
34
+ d3.forceCollide().radius(collisionRadius).strength(collisionStrength)
35
+ ).alphaDecay(alphaDecay).velocityDecay(velocityDecay);
36
+ simulationRef.current = simulation;
37
+ simulation.on("tick", () => {
38
+ setNodes([...nodesCopy]);
39
+ setLinks([...linksCopy]);
40
+ setAlpha(simulation.alpha());
41
+ setIsRunning(simulation.alpha() > simulation.alphaMin());
42
+ });
43
+ simulation.on("end", () => {
44
+ setIsRunning(false);
45
+ });
46
+ return () => {
47
+ simulation.stop();
48
+ };
49
+ }, [
50
+ initialNodes,
51
+ initialLinks,
52
+ chargeStrength,
53
+ linkDistance,
54
+ linkStrength,
55
+ collisionStrength,
56
+ collisionRadius,
57
+ centerStrength,
58
+ width,
59
+ height,
60
+ alphaDecay,
61
+ velocityDecay
62
+ ]);
63
+ const restart = () => {
64
+ if (simulationRef.current) {
65
+ simulationRef.current.alpha(1).restart();
66
+ setIsRunning(true);
67
+ }
68
+ };
69
+ const stop = () => {
70
+ if (simulationRef.current) {
71
+ simulationRef.current.stop();
72
+ setIsRunning(false);
73
+ }
74
+ };
75
+ return {
76
+ nodes,
77
+ links,
78
+ restart,
79
+ stop,
80
+ isRunning,
81
+ alpha
82
+ };
83
+ }
84
+ function cn(...inputs) {
85
+ return twMerge(clsx(inputs));
86
+ }
87
+ var ForceDirectedGraph = ({
88
+ nodes: initialNodes,
89
+ links: initialLinks,
90
+ width,
91
+ height,
92
+ simulationOptions,
93
+ enableZoom = true,
94
+ enableDrag = true,
95
+ onNodeClick,
96
+ onNodeHover,
97
+ onLinkClick,
98
+ selectedNodeId,
99
+ hoveredNodeId,
100
+ defaultNodeColor = "#69b3a2",
101
+ defaultNodeSize = 10,
102
+ defaultLinkColor = "#999",
103
+ defaultLinkWidth = 1,
104
+ showNodeLabels = true,
105
+ showLinkLabels = false,
106
+ className
107
+ }) => {
108
+ const svgRef = useRef(null);
109
+ const gRef = useRef(null);
110
+ const [transform, setTransform] = useState({ k: 1, x: 0, y: 0 });
111
+ const { nodes, links, restart } = useForceSimulation(initialNodes, initialLinks, {
112
+ width,
113
+ height,
114
+ ...simulationOptions
115
+ });
116
+ useEffect(() => {
117
+ if (!enableZoom || !svgRef.current || !gRef.current) return;
118
+ const svg = d3.select(svgRef.current);
119
+ const g = d3.select(gRef.current);
120
+ const zoom2 = d3.zoom().scaleExtent([0.1, 10]).on("zoom", (event) => {
121
+ g.attr("transform", event.transform);
122
+ setTransform(event.transform);
123
+ });
124
+ svg.call(zoom2);
125
+ return () => {
126
+ svg.on(".zoom", null);
127
+ };
128
+ }, [enableZoom]);
129
+ const handleDragStart = useCallback(
130
+ (event, node) => {
131
+ if (!enableDrag) return;
132
+ event.stopPropagation();
133
+ node.fx = node.x;
134
+ node.fy = node.y;
135
+ restart();
136
+ },
137
+ [enableDrag, restart]
138
+ );
139
+ const handleDrag = useCallback(
140
+ (event, node) => {
141
+ if (!enableDrag) return;
142
+ const svg = svgRef.current;
143
+ if (!svg) return;
144
+ const rect = svg.getBoundingClientRect();
145
+ const x = (event.clientX - rect.left - transform.x) / transform.k;
146
+ const y = (event.clientY - rect.top - transform.y) / transform.k;
147
+ node.fx = x;
148
+ node.fy = y;
149
+ },
150
+ [enableDrag, transform]
151
+ );
152
+ const handleDragEnd = useCallback(
153
+ (event, node) => {
154
+ if (!enableDrag) return;
155
+ event.stopPropagation();
156
+ node.fx = null;
157
+ node.fy = null;
158
+ },
159
+ [enableDrag]
160
+ );
161
+ const handleNodeClick = useCallback(
162
+ (node) => {
163
+ onNodeClick?.(node);
164
+ },
165
+ [onNodeClick]
166
+ );
167
+ const handleNodeMouseEnter = useCallback(
168
+ (node) => {
169
+ onNodeHover?.(node);
170
+ },
171
+ [onNodeHover]
172
+ );
173
+ const handleNodeMouseLeave = useCallback(() => {
174
+ onNodeHover?.(null);
175
+ }, [onNodeHover]);
176
+ const handleLinkClick = useCallback(
177
+ (link) => {
178
+ onLinkClick?.(link);
179
+ },
180
+ [onLinkClick]
181
+ );
182
+ return /* @__PURE__ */ jsxs(
183
+ "svg",
184
+ {
185
+ ref: svgRef,
186
+ width,
187
+ height,
188
+ className: cn("bg-white dark:bg-gray-900", className),
189
+ children: [
190
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
191
+ "marker",
192
+ {
193
+ id: "arrow",
194
+ viewBox: "0 0 10 10",
195
+ refX: "20",
196
+ refY: "5",
197
+ markerWidth: "6",
198
+ markerHeight: "6",
199
+ orient: "auto",
200
+ children: /* @__PURE__ */ jsx("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: defaultLinkColor })
201
+ }
202
+ ) }),
203
+ /* @__PURE__ */ jsxs("g", { ref: gRef, children: [
204
+ links.map((link, i) => {
205
+ const source = link.source;
206
+ const target = link.target;
207
+ if (!source.x || !source.y || !target.x || !target.y) return null;
208
+ return /* @__PURE__ */ jsxs("g", { children: [
209
+ /* @__PURE__ */ jsx(
210
+ "line",
211
+ {
212
+ x1: source.x,
213
+ y1: source.y,
214
+ x2: target.x,
215
+ y2: target.y,
216
+ stroke: link.color || defaultLinkColor,
217
+ strokeWidth: link.width || defaultLinkWidth,
218
+ opacity: 0.6,
219
+ className: "cursor-pointer transition-opacity hover:opacity-100",
220
+ onClick: () => handleLinkClick(link)
221
+ }
222
+ ),
223
+ showLinkLabels && link.label && /* @__PURE__ */ jsx(
224
+ "text",
225
+ {
226
+ x: (source.x + target.x) / 2,
227
+ y: (source.y + target.y) / 2,
228
+ fill: "#666",
229
+ fontSize: "10",
230
+ textAnchor: "middle",
231
+ dominantBaseline: "middle",
232
+ pointerEvents: "none",
233
+ children: link.label
234
+ }
235
+ )
236
+ ] }, `link-${i}`);
237
+ }),
238
+ nodes.map((node) => {
239
+ if (!node.x || !node.y) return null;
240
+ const isSelected = selectedNodeId === node.id;
241
+ const isHovered = hoveredNodeId === node.id;
242
+ const nodeSize = node.size || defaultNodeSize;
243
+ const nodeColor = node.color || defaultNodeColor;
244
+ return /* @__PURE__ */ jsxs(
245
+ "g",
246
+ {
247
+ transform: `translate(${node.x},${node.y})`,
248
+ className: "cursor-pointer",
249
+ onClick: () => handleNodeClick(node),
250
+ onMouseEnter: () => handleNodeMouseEnter(node),
251
+ onMouseLeave: handleNodeMouseLeave,
252
+ onMouseDown: (e) => handleDragStart(e, node),
253
+ onMouseMove: (e) => handleDrag(e, node),
254
+ onMouseUp: (e) => handleDragEnd(e, node),
255
+ children: [
256
+ /* @__PURE__ */ jsx(
257
+ "circle",
258
+ {
259
+ r: nodeSize,
260
+ fill: nodeColor,
261
+ stroke: isSelected ? "#000" : isHovered ? "#666" : "none",
262
+ strokeWidth: isSelected ? 3 : 2,
263
+ opacity: isHovered || isSelected ? 1 : 0.9,
264
+ className: "transition-all"
265
+ }
266
+ ),
267
+ showNodeLabels && node.label && /* @__PURE__ */ jsx(
268
+ "text",
269
+ {
270
+ y: nodeSize + 15,
271
+ fill: "#333",
272
+ fontSize: "12",
273
+ textAnchor: "middle",
274
+ dominantBaseline: "middle",
275
+ pointerEvents: "none",
276
+ className: "select-none",
277
+ children: node.label
278
+ }
279
+ )
280
+ ]
281
+ },
282
+ node.id
283
+ );
284
+ })
285
+ ] })
286
+ ]
287
+ }
288
+ );
289
+ };
290
+ ForceDirectedGraph.displayName = "ForceDirectedGraph";
291
+
292
+ export { ForceDirectedGraph };
293
+ //# sourceMappingURL=ForceDirectedGraph.js.map
294
+ //# sourceMappingURL=ForceDirectedGraph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/useForceSimulation.ts","../../src/utils/cn.ts","../../src/charts/ForceDirectedGraph.tsx"],"names":["useRef","useState","useEffect","d32","zoom"],"mappings":"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","file":"ForceDirectedGraph.js","sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport * as d3 from 'd3';\n\nexport interface SimulationNode extends d3.SimulationNodeDatum {\n id: string;\n [key: string]: any;\n}\n\nexport interface SimulationLink extends d3.SimulationLinkDatum<SimulationNode> {\n source: string | SimulationNode;\n target: string | SimulationNode;\n [key: string]: any;\n}\n\nexport interface ForceSimulationOptions {\n /**\n * Strength of the charge force (repulsion between nodes)\n * @default -300\n */\n chargeStrength?: number;\n\n /**\n * Distance for links between nodes\n * @default 100\n */\n linkDistance?: number;\n\n /**\n * Strength of the link force\n * @default 1\n */\n linkStrength?: number;\n\n /**\n * Strength of collision detection\n * @default 1\n */\n collisionStrength?: number;\n\n /**\n * Radius for collision detection (node size)\n * @default 10\n */\n collisionRadius?: number;\n\n /**\n * Strength of centering force\n * @default 0.1\n */\n centerStrength?: number;\n\n /**\n * Width of the simulation space\n */\n width: number;\n\n /**\n * Height of the simulation space\n */\n height: number;\n\n /**\n * Alpha decay rate (how quickly the simulation cools down)\n * @default 0.0228\n */\n alphaDecay?: number;\n\n /**\n * Velocity decay (friction)\n * @default 0.4\n */\n velocityDecay?: number;\n}\n\nexport interface UseForceSimulationReturn {\n /**\n * Current nodes with positions\n */\n nodes: SimulationNode[];\n\n /**\n * Current links\n */\n links: SimulationLink[];\n\n /**\n * Restart the simulation\n */\n restart: () => void;\n\n /**\n * Stop the simulation\n */\n stop: () => void;\n\n /**\n * Whether the simulation is currently running\n */\n isRunning: boolean;\n\n /**\n * Current alpha value (simulation heat)\n */\n alpha: number;\n}\n\n/**\n * Hook for managing d3-force simulations\n * Automatically handles simulation lifecycle, tick updates, and cleanup\n *\n * @param initialNodes - Initial nodes for the simulation\n * @param initialLinks - Initial links for the simulation\n * @param options - Configuration options for the force simulation\n * @returns Simulation state and control functions\n *\n * @example\n * ```tsx\n * function NetworkGraph() {\n * const nodes = [\n * { id: 'node1', name: 'Node 1' },\n * { id: 'node2', name: 'Node 2' },\n * { id: 'node3', name: 'Node 3' },\n * ];\n *\n * const links = [\n * { source: 'node1', target: 'node2' },\n * { source: 'node2', target: 'node3' },\n * ];\n *\n * const { nodes: simulatedNodes, links: simulatedLinks, restart } = useForceSimulation(\n * nodes,\n * links,\n * {\n * width: 800,\n * height: 600,\n * chargeStrength: -500,\n * linkDistance: 150,\n * }\n * );\n *\n * return (\n * <svg width={800} height={600}>\n * {simulatedLinks.map((link, i) => (\n * <line\n * key={i}\n * x1={(link.source as SimulationNode).x}\n * y1={(link.source as SimulationNode).y}\n * x2={(link.target as SimulationNode).x}\n * y2={(link.target as SimulationNode).y}\n * stroke=\"#999\"\n * />\n * ))}\n * {simulatedNodes.map((node) => (\n * <circle\n * key={node.id}\n * cx={node.x}\n * cy={node.y}\n * r={10}\n * fill=\"#69b3a2\"\n * />\n * ))}\n * </svg>\n * );\n * }\n * ```\n */\nexport function useForceSimulation(\n initialNodes: SimulationNode[],\n initialLinks: SimulationLink[],\n options: ForceSimulationOptions\n): UseForceSimulationReturn {\n const {\n chargeStrength = -300,\n linkDistance = 100,\n linkStrength = 1,\n collisionStrength = 1,\n collisionRadius = 10,\n centerStrength = 0.1,\n width,\n height,\n alphaDecay = 0.0228,\n velocityDecay = 0.4,\n } = options;\n\n const [nodes, setNodes] = useState<SimulationNode[]>(initialNodes);\n const [links, setLinks] = useState<SimulationLink[]>(initialLinks);\n const [isRunning, setIsRunning] = useState(false);\n const [alpha, setAlpha] = useState(1);\n\n const simulationRef = useRef<d3.Simulation<SimulationNode, SimulationLink> | null>(null);\n\n useEffect(() => {\n // Create a copy of nodes and links to avoid mutating the original data\n const nodesCopy = initialNodes.map((node) => ({ ...node }));\n const linksCopy = initialLinks.map((link) => ({ ...link }));\n\n // Create the simulation\n const simulation = d3\n .forceSimulation<SimulationNode>(nodesCopy)\n .force(\n 'link',\n d3\n .forceLink<SimulationNode, SimulationLink>(linksCopy)\n .id((d) => d.id)\n .distance(linkDistance)\n .strength(linkStrength)\n )\n .force('charge', d3.forceManyBody().strength(chargeStrength))\n .force('center', d3.forceCenter(width / 2, height / 2).strength(centerStrength))\n .force(\n 'collision',\n d3.forceCollide<SimulationNode>().radius(collisionRadius).strength(collisionStrength)\n )\n .alphaDecay(alphaDecay)\n .velocityDecay(velocityDecay);\n\n simulationRef.current = simulation;\n\n // Update state on each tick\n simulation.on('tick', () => {\n setNodes([...nodesCopy]);\n setLinks([...linksCopy]);\n setAlpha(simulation.alpha());\n setIsRunning(simulation.alpha() > simulation.alphaMin());\n });\n\n simulation.on('end', () => {\n setIsRunning(false);\n });\n\n // Cleanup on unmount\n return () => {\n simulation.stop();\n };\n }, [\n initialNodes,\n initialLinks,\n chargeStrength,\n linkDistance,\n linkStrength,\n collisionStrength,\n collisionRadius,\n centerStrength,\n width,\n height,\n alphaDecay,\n velocityDecay,\n ]);\n\n const restart = () => {\n if (simulationRef.current) {\n simulationRef.current.alpha(1).restart();\n setIsRunning(true);\n }\n };\n\n const stop = () => {\n if (simulationRef.current) {\n simulationRef.current.stop();\n setIsRunning(false);\n }\n };\n\n return {\n nodes,\n links,\n restart,\n stop,\n isRunning,\n alpha,\n };\n}\n\n/**\n * Hook for creating a draggable force simulation\n * Provides drag handlers that can be attached to node elements\n *\n * @param simulation - The d3 force simulation instance\n * @returns Drag behavior that can be applied to nodes\n *\n * @example\n * ```tsx\n * function DraggableNetworkGraph() {\n * const simulation = useRef<d3.Simulation<SimulationNode, SimulationLink>>();\n * const drag = useDrag(simulation.current);\n *\n * return (\n * <svg>\n * {nodes.map((node) => (\n * <circle\n * key={node.id}\n * {...drag}\n * cx={node.x}\n * cy={node.y}\n * r={10}\n * />\n * ))}\n * </svg>\n * );\n * }\n * ```\n */\nexport function useDrag(simulation: d3.Simulation<SimulationNode, any> | null | undefined) {\n const dragStarted = (event: any, node: SimulationNode) => {\n if (!simulation) return;\n if (!event.active) simulation.alphaTarget(0.3).restart();\n node.fx = node.x;\n node.fy = node.y;\n };\n\n const dragged = (event: any, node: SimulationNode) => {\n node.fx = event.x;\n node.fy = event.y;\n };\n\n const dragEnded = (event: any, node: SimulationNode) => {\n if (!simulation) return;\n if (!event.active) simulation.alphaTarget(0);\n node.fx = null;\n node.fy = null;\n };\n\n return {\n onDragStart: dragStarted,\n onDrag: dragged,\n onDragEnd: dragEnded,\n };\n}","import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merges class names using clsx and tailwind-merge\n * @param inputs - Class values to merge\n * @returns Merged class names\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}","import React, { useCallback, useEffect, useRef, useState } from 'react';\nimport * as d3 from 'd3';\nimport {\n useForceSimulation,\n type SimulationNode,\n type SimulationLink,\n type ForceSimulationOptions,\n} from '../hooks/useForceSimulation';\nimport { cn } from '../utils/cn';\n\nexport interface GraphNode extends SimulationNode {\n id: string;\n label?: string;\n color?: string;\n size?: number;\n group?: string;\n}\n\nexport interface GraphLink extends SimulationLink {\n color?: string;\n width?: number;\n label?: string;\n}\n\nexport interface ForceDirectedGraphProps {\n /**\n * Array of nodes to display\n */\n nodes: GraphNode[];\n\n /**\n * Array of links between nodes\n */\n links: GraphLink[];\n\n /**\n * Width of the graph container\n */\n width: number;\n\n /**\n * Height of the graph container\n */\n height: number;\n\n /**\n * Force simulation options\n */\n simulationOptions?: Partial<ForceSimulationOptions>;\n\n /**\n * Whether to enable zoom and pan\n * @default true\n */\n enableZoom?: boolean;\n\n /**\n * Whether to enable node dragging\n * @default true\n */\n enableDrag?: boolean;\n\n /**\n * Callback when a node is clicked\n */\n onNodeClick?: (node: GraphNode) => void;\n\n /**\n * Callback when a node is hovered\n */\n onNodeHover?: (node: GraphNode | null) => void;\n\n /**\n * Callback when a link is clicked\n */\n onLinkClick?: (link: GraphLink) => void;\n\n /**\n * Selected node ID\n */\n selectedNodeId?: string;\n\n /**\n * Hovered node ID\n */\n hoveredNodeId?: string;\n\n /**\n * Default node color\n * @default \"#69b3a2\"\n */\n defaultNodeColor?: string;\n\n /**\n * Default node size\n * @default 10\n */\n defaultNodeSize?: number;\n\n /**\n * Default link color\n * @default \"#999\"\n */\n defaultLinkColor?: string;\n\n /**\n * Default link width\n * @default 1\n */\n defaultLinkWidth?: number;\n\n /**\n * Whether to show node labels\n * @default true\n */\n showNodeLabels?: boolean;\n\n /**\n * Whether to show link labels\n * @default false\n */\n showLinkLabels?: boolean;\n\n /**\n * Additional CSS classes\n */\n className?: string;\n}\n\nexport const ForceDirectedGraph: React.FC<ForceDirectedGraphProps> = ({\n nodes: initialNodes,\n links: initialLinks,\n width,\n height,\n simulationOptions,\n enableZoom = true,\n enableDrag = true,\n onNodeClick,\n onNodeHover,\n onLinkClick,\n selectedNodeId,\n hoveredNodeId,\n defaultNodeColor = '#69b3a2',\n defaultNodeSize = 10,\n defaultLinkColor = '#999',\n defaultLinkWidth = 1,\n showNodeLabels = true,\n showLinkLabels = false,\n className,\n}) => {\n const svgRef = useRef<SVGSVGElement>(null);\n const gRef = useRef<SVGGElement>(null);\n const [transform, setTransform] = useState({ k: 1, x: 0, y: 0 });\n\n // Initialize simulation\n const { nodes, links, restart } = useForceSimulation(initialNodes, initialLinks, {\n width,\n height,\n ...simulationOptions,\n });\n\n // Set up zoom behavior\n useEffect(() => {\n if (!enableZoom || !svgRef.current || !gRef.current) return;\n\n const svg = d3.select(svgRef.current);\n const g = d3.select(gRef.current);\n\n const zoom = d3\n .zoom<SVGSVGElement, unknown>()\n .scaleExtent([0.1, 10])\n .on('zoom', (event) => {\n g.attr('transform', event.transform);\n setTransform(event.transform);\n });\n\n svg.call(zoom);\n\n return () => {\n svg.on('.zoom', null);\n };\n }, [enableZoom]);\n\n // Set up drag behavior\n const handleDragStart = useCallback(\n (event: React.MouseEvent, node: GraphNode) => {\n if (!enableDrag) return;\n event.stopPropagation();\n node.fx = node.x;\n node.fy = node.y;\n restart();\n },\n [enableDrag, restart]\n );\n\n const handleDrag = useCallback(\n (event: React.MouseEvent, node: GraphNode) => {\n if (!enableDrag) return;\n const svg = svgRef.current;\n if (!svg) return;\n\n const rect = svg.getBoundingClientRect();\n const x = (event.clientX - rect.left - transform.x) / transform.k;\n const y = (event.clientY - rect.top - transform.y) / transform.k;\n\n node.fx = x;\n node.fy = y;\n },\n [enableDrag, transform]\n );\n\n const handleDragEnd = useCallback(\n (event: React.MouseEvent, node: GraphNode) => {\n if (!enableDrag) return;\n event.stopPropagation();\n node.fx = null;\n node.fy = null;\n },\n [enableDrag]\n );\n\n const handleNodeClick = useCallback(\n (node: GraphNode) => {\n onNodeClick?.(node);\n },\n [onNodeClick]\n );\n\n const handleNodeMouseEnter = useCallback(\n (node: GraphNode) => {\n onNodeHover?.(node);\n },\n [onNodeHover]\n );\n\n const handleNodeMouseLeave = useCallback(() => {\n onNodeHover?.(null);\n }, [onNodeHover]);\n\n const handleLinkClick = useCallback(\n (link: GraphLink) => {\n onLinkClick?.(link);\n },\n [onLinkClick]\n );\n\n return (\n <svg\n ref={svgRef}\n width={width}\n height={height}\n className={cn('bg-white dark:bg-gray-900', className)}\n >\n <defs>\n {/* Arrow marker for directed graphs */}\n <marker\n id=\"arrow\"\n viewBox=\"0 0 10 10\"\n refX=\"20\"\n refY=\"5\"\n markerWidth=\"6\"\n markerHeight=\"6\"\n orient=\"auto\"\n >\n <path d=\"M 0 0 L 10 5 L 0 10 z\" fill={defaultLinkColor} />\n </marker>\n </defs>\n\n <g ref={gRef}>\n {/* Render links */}\n {links.map((link, i) => {\n const source = link.source as GraphNode;\n const target = link.target as GraphNode;\n if (!source.x || !source.y || !target.x || !target.y) return null;\n\n return (\n <g key={`link-${i}`}>\n <line\n x1={source.x}\n y1={source.y}\n x2={target.x}\n y2={target.y}\n stroke={link.color || defaultLinkColor}\n strokeWidth={link.width || defaultLinkWidth}\n opacity={0.6}\n className=\"cursor-pointer transition-opacity hover:opacity-100\"\n onClick={() => handleLinkClick(link)}\n />\n {showLinkLabels && link.label && (\n <text\n x={(source.x + target.x) / 2}\n y={(source.y + target.y) / 2}\n fill=\"#666\"\n fontSize=\"10\"\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n pointerEvents=\"none\"\n >\n {link.label}\n </text>\n )}\n </g>\n );\n })}\n\n {/* Render nodes */}\n {nodes.map((node) => {\n if (!node.x || !node.y) return null;\n\n const isSelected = selectedNodeId === node.id;\n const isHovered = hoveredNodeId === node.id;\n const nodeSize = node.size || defaultNodeSize;\n const nodeColor = node.color || defaultNodeColor;\n\n return (\n <g\n key={node.id}\n transform={`translate(${node.x},${node.y})`}\n className=\"cursor-pointer\"\n onClick={() => handleNodeClick(node)}\n onMouseEnter={() => handleNodeMouseEnter(node)}\n onMouseLeave={handleNodeMouseLeave}\n onMouseDown={(e) => handleDragStart(e, node)}\n onMouseMove={(e) => handleDrag(e, node)}\n onMouseUp={(e) => handleDragEnd(e, node)}\n >\n <circle\n r={nodeSize}\n fill={nodeColor}\n stroke={isSelected ? '#000' : isHovered ? '#666' : 'none'}\n strokeWidth={isSelected ? 3 : 2}\n opacity={isHovered || isSelected ? 1 : 0.9}\n className=\"transition-all\"\n />\n {showNodeLabels && node.label && (\n <text\n y={nodeSize + 15}\n fill=\"#333\"\n fontSize=\"12\"\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n pointerEvents=\"none\"\n className=\"select-none\"\n >\n {node.label}\n </text>\n )}\n </g>\n );\n })}\n </g>\n </svg>\n );\n};\n\nForceDirectedGraph.displayName = 'ForceDirectedGraph';"]}
@@ -0,0 +1,13 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as class_variance_authority_types from 'class-variance-authority/types';
3
+ import * as React from 'react';
4
+ import { VariantProps } from 'class-variance-authority';
5
+
6
+ declare const badgeVariants: (props?: ({
7
+ variant?: "default" | "destructive" | "outline" | "secondary" | null | undefined;
8
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
9
+ interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
10
+ }
11
+ declare function Badge({ className, variant, ...props }: BadgeProps): react_jsx_runtime.JSX.Element;
12
+
13
+ export { Badge, type BadgeProps, badgeVariants };
@@ -0,0 +1,32 @@
1
+ import { cva } from 'class-variance-authority';
2
+ import { clsx } from 'clsx';
3
+ import { twMerge } from 'tailwind-merge';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ // src/components/badge.tsx
7
+ function cn(...inputs) {
8
+ return twMerge(clsx(inputs));
9
+ }
10
+ var badgeVariants = cva(
11
+ "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",
12
+ {
13
+ variants: {
14
+ variant: {
15
+ default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
16
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
17
+ destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
18
+ outline: "text-foreground"
19
+ }
20
+ },
21
+ defaultVariants: {
22
+ variant: "default"
23
+ }
24
+ }
25
+ );
26
+ function Badge({ className, variant, ...props }) {
27
+ return /* @__PURE__ */ jsx("div", { className: cn(badgeVariants({ variant }), className), ...props });
28
+ }
29
+
30
+ export { Badge, badgeVariants };
31
+ //# sourceMappingURL=badge.js.map
32
+ //# sourceMappingURL=badge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/cn.ts","../../src/components/badge.tsx"],"names":[],"mappings":";;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACNA,IAAM,aAAA,GAAgB,GAAA;AAAA,EACpB,wKAAA;AAAA,EACA;AAAA,IACE,QAAA,EAAU;AAAA,MACR,OAAA,EAAS;AAAA,QACP,OAAA,EACE,2EAAA;AAAA,QACF,SAAA,EACE,iFAAA;AAAA,QACF,WAAA,EACE,uFAAA;AAAA,QACF,OAAA,EAAS;AAAA;AACX,KACF;AAAA,IACA,eAAA,EAAiB;AAAA,MACf,OAAA,EAAS;AAAA;AACX;AAEJ;AAMA,SAAS,MAAM,EAAE,SAAA,EAAW,OAAA,EAAS,GAAG,OAAM,EAAe;AAC3D,EAAA,uBACE,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,EAAA,CAAG,aAAA,CAAc,EAAE,OAAA,EAAS,CAAA,EAAG,SAAS,CAAA,EAAI,GAAG,KAAA,EAAO,CAAA;AAE1E","file":"badge.js","sourcesContent":["import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merges class names using clsx and tailwind-merge\n * @param inputs - Class values to merge\n * @returns Merged class names\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}","import * as React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../utils/cn';\n\nconst badgeVariants = cva(\n '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',\n {\n variants: {\n variant: {\n default:\n 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',\n secondary:\n 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',\n destructive:\n 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',\n outline: 'text-foreground',\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n }\n);\n\nexport interface BadgeProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof badgeVariants> {}\n\nfunction Badge({ className, variant, ...props }: BadgeProps) {\n return (\n <div className={cn(badgeVariants({ variant }), className)} {...props} />\n );\n}\n\nexport { Badge, badgeVariants };"]}