@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.
- package/README.md +240 -0
- package/dist/charts/ForceDirectedGraph.d.ts +40 -0
- package/dist/charts/ForceDirectedGraph.js +294 -0
- package/dist/charts/ForceDirectedGraph.js.map +1 -0
- package/dist/components/badge.d.ts +13 -0
- package/dist/components/badge.js +32 -0
- package/dist/components/badge.js.map +1 -0
- package/dist/components/button.d.ts +14 -0
- package/dist/components/button.js +52 -0
- package/dist/components/button.js.map +1 -0
- package/dist/components/card.d.ts +10 -0
- package/dist/components/card.js +66 -0
- package/dist/components/card.js.map +1 -0
- package/dist/components/checkbox.d.ts +8 -0
- package/dist/components/checkbox.js +42 -0
- package/dist/components/checkbox.js.map +1 -0
- package/dist/components/container.d.ts +8 -0
- package/dist/components/container.js +36 -0
- package/dist/components/container.js.map +1 -0
- package/dist/components/grid.d.ts +9 -0
- package/dist/components/grid.js +44 -0
- package/dist/components/grid.js.map +1 -0
- package/dist/components/input.d.ts +7 -0
- package/dist/components/input.js +30 -0
- package/dist/components/input.js.map +1 -0
- package/dist/components/label.d.ts +10 -0
- package/dist/components/label.js +28 -0
- package/dist/components/label.js.map +1 -0
- package/dist/components/radio-group.d.ts +17 -0
- package/dist/components/radio-group.js +64 -0
- package/dist/components/radio-group.js.map +1 -0
- package/dist/components/select.d.ts +15 -0
- package/dist/components/select.js +45 -0
- package/dist/components/select.js.map +1 -0
- package/dist/components/separator.d.ts +9 -0
- package/dist/components/separator.js +30 -0
- package/dist/components/separator.js.map +1 -0
- package/dist/components/stack.d.ts +11 -0
- package/dist/components/stack.js +60 -0
- package/dist/components/stack.js.map +1 -0
- package/dist/components/switch.d.ts +9 -0
- package/dist/components/switch.js +49 -0
- package/dist/components/switch.js.map +1 -0
- package/dist/components/textarea.d.ts +7 -0
- package/dist/components/textarea.js +29 -0
- package/dist/components/textarea.js.map +1 -0
- package/dist/hooks/useD3.d.ts +6 -0
- package/dist/hooks/useD3.js +35 -0
- package/dist/hooks/useD3.js.map +1 -0
- package/dist/hooks/useDebounce.d.ts +3 -0
- package/dist/hooks/useDebounce.js +19 -0
- package/dist/hooks/useDebounce.js.map +1 -0
- package/dist/hooks/useForceSimulation.d.ts +39 -0
- package/dist/hooks/useForceSimulation.js +107 -0
- package/dist/hooks/useForceSimulation.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +927 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/cn.d.ts +5 -0
- package/dist/utils/cn.js +11 -0
- package/dist/utils/cn.js.map +1 -0
- package/dist/utils/colors.d.ts +19 -0
- package/dist/utils/colors.js +52 -0
- package/dist/utils/colors.js.map +1 -0
- package/dist/utils/formatters.d.ts +13 -0
- package/dist/utils/formatters.js +100 -0
- package/dist/utils/formatters.js.map +1 -0
- package/package.json +83 -0
- package/src/charts/ForceDirectedGraph.tsx +356 -0
- package/src/components/badge.tsx +35 -0
- package/src/components/button.tsx +53 -0
- package/src/components/card.tsx +78 -0
- package/src/components/checkbox.tsx +39 -0
- package/src/components/container.tsx +31 -0
- package/src/components/grid.tsx +40 -0
- package/src/components/input.tsx +24 -0
- package/src/components/label.tsx +24 -0
- package/src/components/radio-group.tsx +71 -0
- package/src/components/select.tsx +53 -0
- package/src/components/separator.tsx +29 -0
- package/src/components/stack.tsx +61 -0
- package/src/components/switch.tsx +49 -0
- package/src/components/textarea.tsx +23 -0
- package/src/hooks/useD3.ts +125 -0
- package/src/hooks/useDebounce.ts +44 -0
- package/src/hooks/useForceSimulation.ts +328 -0
- package/src/index.ts +51 -0
- package/src/utils/cn.ts +11 -0
- package/src/utils/colors.ts +58 -0
- package/src/utils/formatters.ts +161 -0
- 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 };"]}
|