1+ import React , { useEffect , useRef } from 'react' ;
2+ import * as d3 from 'd3' ;
3+
4+ const D3Graph = ( { graphData, layout } ) => {
5+ const containerRef = useRef ( null ) ;
6+
7+ useEffect ( ( ) => {
8+ if ( ! graphData ) return ;
9+
10+ // Set up SVG dimensions
11+ const width = window . innerWidth ;
12+ const height = window . innerHeight - 50 ;
13+
14+ // Clear previous graph
15+ d3 . select ( containerRef . current ) . select ( 'svg' ) . remove ( ) ;
16+
17+ // Create SVG element
18+ const svg = d3 . select ( containerRef . current )
19+ . append ( 'svg' )
20+ . attr ( 'width' , width )
21+ . attr ( 'height' , height )
22+ . call ( d3 . zoom ( ) . on ( 'zoom' , ( event ) => {
23+ svg . attr ( 'transform' , event . transform ) ;
24+ } ) )
25+ . append ( 'g' ) ;
26+
27+ // Set up the simulation
28+ const simulation = d3 . forceSimulation ( graphData . nodes )
29+ . force ( 'link' , d3 . forceLink ( graphData . edges ) . id ( d => d . id ) . distance ( 100 ) )
30+ . force ( 'charge' , d3 . forceManyBody ( ) . strength ( - 300 ) )
31+ . force ( 'center' , d3 . forceCenter ( width / 2 , height / 2 ) ) ;
32+
33+ // Apply different layout algorithms
34+ if ( layout === 'hierarchical' ) {
35+ simulation . force ( 'y' , d3 . forceY ( ) . strength ( 0.1 ) ) ;
36+ simulation . force ( 'x' , d3 . forceX ( ) . strength ( 0.1 ) ) ;
37+ }
38+
39+ // Create links
40+ const link = svg . append ( 'g' )
41+ . attr ( 'class' , 'links' )
42+ . selectAll ( 'line' )
43+ . data ( graphData . edges )
44+ . enter ( )
45+ . append ( 'line' )
46+ . attr ( 'stroke-width' , 2 )
47+ . attr ( 'stroke' , '#fff' ) ;
48+
49+ // Create link labels
50+ const linkLabels = svg . append ( 'g' )
51+ . attr ( 'class' , 'link-labels' )
52+ . selectAll ( 'text' )
53+ . data ( graphData . edges )
54+ . enter ( )
55+ . append ( 'text' )
56+ . attr ( 'class' , 'link-label' )
57+ . attr ( 'dx' , 15 )
58+ . attr ( 'dy' , '.35em' )
59+ . text ( d => d . label ) // Correctly reading the label property for edges
60+ . attr ( 'fill' , '#fff' ) ;
61+
62+ // Create nodes
63+ const node = svg . append ( 'g' )
64+ . attr ( 'class' , 'nodes' )
65+ . selectAll ( 'circle' )
66+ . data ( graphData . nodes )
67+ . enter ( )
68+ . append ( 'circle' )
69+ . attr ( 'r' , 25 )
70+ . attr ( 'fill' , '#69b3a2' )
71+ . attr ( 'stroke' , '#508e7f' )
72+ . call ( d3 . drag ( )
73+ . on ( 'start' , dragstarted )
74+ . on ( 'drag' , dragged )
75+ . on ( 'end' , dragended ) ) ;
76+
77+ // Node labels
78+ const nodeLabels = svg . append ( 'g' )
79+ . attr ( 'class' , 'node-labels' )
80+ . selectAll ( 'text' )
81+ . data ( graphData . nodes )
82+ . enter ( )
83+ . append ( 'text' )
84+ . attr ( 'class' , 'node-label' )
85+ . attr ( 'dx' , 15 )
86+ . attr ( 'dy' , '.35em' )
87+ . text ( d => d . label ) // Correctly reading the label property for nodes
88+ . attr ( 'fill' , '#fff' ) ;
89+
90+ // Update simulation
91+ simulation
92+ . nodes ( graphData . nodes )
93+ . on ( 'tick' , ticked ) ;
94+
95+ simulation . force ( 'link' )
96+ . links ( graphData . edges ) ;
97+
98+ function ticked ( ) {
99+ link
100+ . attr ( 'x1' , d => d . source . x )
101+ . attr ( 'y1' , d => d . source . y )
102+ . attr ( 'x2' , d => d . target . x )
103+ . attr ( 'y2' , d => d . target . y ) ;
104+
105+ node
106+ . attr ( 'cx' , d => d . x )
107+ . attr ( 'cy' , d => d . y ) ;
108+
109+ nodeLabels
110+ . attr ( 'x' , d => d . x )
111+ . attr ( 'y' , d => d . y ) ;
112+
113+ linkLabels
114+ . attr ( 'x' , d => ( d . source . x + d . target . x ) / 2 )
115+ . attr ( 'y' , d => ( d . source . y + d . target . y ) / 2 ) ;
116+ }
117+
118+ function dragstarted ( event , d ) {
119+ if ( ! event . active ) simulation . alphaTarget ( 0.3 ) . restart ( ) ;
120+ d . fx = d . x ;
121+ d . fy = d . y ;
122+ }
123+
124+ function dragged ( event , d ) {
125+ d . fx = event . x ;
126+ d . fy = event . y ;
127+ }
128+
129+ function dragended ( event , d ) {
130+ if ( ! event . active ) simulation . alphaTarget ( 0 ) ;
131+ d . fx = null ;
132+ d . fy = null ;
133+ }
134+
135+ // Stabilize nodes after a certain time
136+ setTimeout ( ( ) => {
137+ simulation . alphaTarget ( 0 ) . restart ( ) ;
138+ } , 5000 ) ; // 5 seconds stabilization time
139+
140+ } , [ graphData , layout ] ) ;
141+
142+ return < div ref = { containerRef } style = { { width : '100vw' , height : 'calc(100vh - 50px)' , background : '#1A2130' } } /> ;
143+ } ;
144+
145+ export default D3Graph ;
0 commit comments