@@ -9,32 +9,37 @@ import * as d3 from 'd3';
9
9
10
10
11
11
const Map = ( props ) => {
12
- const { snapshot, snapshots } = props ;
13
- const lastSnap = snapshots . length - 1 ;
14
-
15
- const width = 2000 ;
16
- const height = 600 ;
17
- const margin = { top : 10 , right : 120 , bottom : 10 , left : 120 } ;
18
- const dy = 100 ;
19
- const dx = 76 ;
20
- const data = snapshots [ lastSnap ] ;
21
- const tree = d3 . tree ( ) . nodeSize ( [ dx , dy ] ) ;
22
- const diagonal = d3
23
- . linkHorizontal ( )
24
- . x ( ( d ) => d . y )
25
- . y ( ( d ) => d . x ) ;
26
-
27
-
12
+ const { viewIndex, snapshots } = props ;
13
+
14
+ let lastSnap : number | null = null ;
15
+ if ( viewIndex < 0 ) lastSnap = snapshots . length - 1 ;
16
+ else lastSnap = viewIndex ;
17
+
18
+
19
+ const width : number = 900 ;
20
+ const height : number = 600 ;
21
+ let data = snapshots [ lastSnap ] ;
22
+
28
23
29
24
useEffect ( ( ) => {
30
- document . getElementById ( 'canvas' ) . innerHTML = '_' ;
31
- return makeChart ( ) ;
25
+ document . getElementById ( 'canvas' ) . innerHTML = '_' ;
26
+ return makeChart ( data ) ;
32
27
} ) ;
33
28
34
- const makeChart = ( ) => {
35
-
29
+ const makeChart = React . useCallback ( ( data ) => {
30
+
31
+ // Establish Constants
32
+ const margin = { top : 10 , right : 120 , bottom : 10 , left : 120 } ;
33
+ const dy = 100 ;
34
+ const dx = 100 ;
35
+ const tree = d3 . tree ( ) . nodeSize ( [ dx , dy ] ) ;
36
+ const diagonal = d3
37
+ . linkHorizontal ( )
38
+ . x ( ( d ) => d . y )
39
+ . y ( ( d ) => d . x ) ;
36
40
const root = d3 . hierarchy ( data ) ;
37
41
42
+ // Determine descendants of root node use d.depth conditional to how many levels deep to display on first render
38
43
root . x0 = dy / 2 ;
39
44
root . y0 = 0 ;
40
45
root . descendants ( ) . forEach ( ( d , i ) => {
@@ -43,46 +48,50 @@ const Map = (props) => {
43
48
if ( d . depth === 9 ) d . children = null ;
44
49
} ) ;
45
50
46
- console . log ( "root" , root )
47
-
48
- const svg = d3
51
+
52
+ // Create Container for D3 Visualizations
53
+ const svgContainer = d3
49
54
. select ( '#canvas' )
50
55
. attr ( 'width' , width )
51
56
. attr ( 'height' , height )
52
- // .attr('viewBox', [-margin.left, -margin.top, width, dx])
53
- // .style('font', '10px sans-serif')
54
- // .style('user-select', 'none');
55
57
58
+ // create inner container to help with drag and zoom
59
+ const svg : any = svgContainer
60
+ . append ( 'g' )
61
+
62
+ // create links
56
63
const gLink = svg
57
64
. append ( 'g' )
58
65
. attr ( 'fill' , 'none' )
59
66
. attr ( 'stroke' , '#555' )
60
67
. attr ( 'stroke-opacity' , 0.9 )
61
68
. attr ( 'stroke-width' , 1.5 ) ;
62
69
70
+ // create nodes
63
71
const gNode = svg
64
72
. append ( 'g' )
65
73
. attr ( 'cursor' , 'pointer' )
66
74
. attr ( 'pointer-events' , 'all' ) ;
67
75
76
+ // declare re render funciton to handle collapse and expansion of nodes
68
77
function update ( source ) {
69
78
const duration = d3 . event && d3 . event . altKey ? 2500 : 250 ;
70
79
const nodes = root . descendants ( ) . reverse ( ) ;
71
80
const links = root . links ( ) ;
72
81
73
82
// Compute the new tree layout.
74
83
tree ( root ) ;
75
-
76
- console . log ( "tree" , tree ( root ) )
77
84
let left = root ;
78
85
let right = root ;
79
86
root . eachBefore ( ( node ) => {
80
87
if ( node . x < left . x ) left = node ;
81
88
if ( node . x > right . x ) right = node ;
82
89
} ) ;
83
90
84
- const height = right . x - left . x + margin . top + margin . bottom ;
91
+ //use nodes to detrmine height
92
+ const height = right . x - left . x + margin . top + margin . bottom ;
85
93
94
+ // transition between past and present
86
95
const transition = svg
87
96
. transition ( )
88
97
. duration ( duration )
@@ -98,37 +107,59 @@ const Map = (props) => {
98
107
. append ( 'g' )
99
108
. attr ( 'transform' , ( d ) => `translate(${ source . y0 } ,${ source . x0 } )` )
100
109
. attr ( 'fill-opacity' , 0 )
101
- // .attr('stroke-linejoin', 'round')
102
110
. attr ( 'stroke-opacity' , 1 )
103
111
. on ( 'click' , ( d ) => {
104
112
d . children = d . children ? null : d . _children ;
105
113
update ( d ) ;
106
114
} ) ;
107
-
115
+
116
+ // paint circles, color based on children
108
117
nodeEnter
109
118
. append ( 'circle' )
110
119
. attr ( 'r' , 10 )
111
120
. attr ( 'fill' , ( d ) => ( d . _children ? '#46edf2' : '#95B6B7' ) )
112
- //.attr('stroke-linejoin', 'round')
113
121
. attr ( 'stroke-width' , 10 )
114
122
. attr ( 'stroke-opacity' , 1 ) ;
115
-
116
-
123
+
124
+ // append node names
117
125
nodeEnter
118
126
. append ( 'text' )
119
127
. attr ( 'dy' , '.31em' )
120
- . attr ( 'x' , ( d : any ) => ( d . children ? - 50 : 50 ) )
121
- . attr ( 'text-anchor' , ( d : any ) => ( d . children ? 'end' : 'start' ) )
122
- . text ( ( d : any ) => d . data . name )
123
- . style ( 'font-size' , `.8rem` )
128
+ . attr ( 'x' , '-10' )
129
+ . attr ( 'y' , '-5' )
130
+ . attr ( 'text-anchor' , 'end' )
131
+ . text ( ( d : any ) => d . data . name . slice ( 0 , 14 ) )
132
+ . style ( 'font-size' , `.6rem` )
124
133
. style ( 'fill' , 'white' )
125
134
. clone ( true )
126
135
. lower ( )
127
136
. attr ( "stroke-linejoin" , "round" )
128
137
. attr ( 'stroke' , '#646464' )
129
- . attr ( 'stroke-width' , 2 ) ;
138
+ . attr ( 'stroke-width' , 1 ) ;
139
+
140
+ // display the data in the node on hover
141
+
142
+ nodeEnter . on ( 'mouseover' , function ( d : any , i : number ) : any {
143
+ if ( ! d . children ) {
144
+ d3 . select ( this )
145
+ . append ( 'text' )
146
+ . text ( ( ) => {
147
+ console . log ( d )
148
+ return JSON . stringify ( d . data ) } )
149
+ . style ( 'fill' , 'white' )
150
+ . attr ( 'x' , 0 )
151
+ . attr ( 'y' , 0 )
152
+ . style ( 'font-size' , '.6rem' )
153
+ . style ( 'text-align' , 'center' )
154
+ . attr ( 'stroke' , '#646464' )
155
+ . attr ( 'id' , `popup${ i } ` ) ;
156
+ }
157
+ } ) ;
158
+
159
+ nodeEnter . on ( 'mouseout' , function ( d : any , i : number ) : any {
160
+ d3 . select ( `#popup${ i } ` ) . remove ( ) ;
161
+ } ) ;
130
162
131
-
132
163
// Transition nodes to their new position.
133
164
const nodeUpdate = node
134
165
. merge ( nodeEnter )
@@ -153,7 +184,6 @@ const Map = (props) => {
153
184
const linkEnter = link
154
185
. enter ( )
155
186
. append ( 'path' )
156
- //.selectAll('path')
157
187
. attr ( 'd' , ( d ) => {
158
188
const o = { x : source . x0 , y : source . y0 } ;
159
189
return diagonal ( { source : o , target : o } ) ;
@@ -182,17 +212,16 @@ const Map = (props) => {
182
212
183
213
//______________ZOOM______________\\
184
214
185
- // Sets starting zoom but breaks keeping currents zoom on state change
186
-
187
- // let zoom = d3.zoom().on('zoom', zoomed);
188
- // svgContainer.call(
189
- // zoom.transform,
190
- // // Changes the initial view, (left, top)
191
- // d3.zoomIdentity.translate(150, 250).scale(0.2)
192
- // );
215
+ // Sets starting zoom
216
+ let zoom = d3 . zoom ( ) . on ( 'zoom' , zoomed ) ;
217
+ svgContainer . call (
218
+ zoom . transform ,
219
+ // Changes the initial view, (left, top)
220
+ d3 . zoomIdentity . translate ( 150 , 250 ) . scale ( 0.6 )
221
+ ) ;
193
222
194
- // allows the canvas to be zoom-able
195
- svg . call (
223
+ // allows the canvas to be zoom-able
224
+ svgContainer . call (
196
225
d3
197
226
. zoom ( )
198
227
. scaleExtent ( [ 0.15 , 1.5 ] ) // [zoomOut, zoomIn]
@@ -210,7 +239,6 @@ const Map = (props) => {
210
239
. on ( 'drag' , dragged )
211
240
. on ( 'end' , dragEnded )
212
241
) ;
213
-
214
242
function dragStarted ( ) : any {
215
243
d3 . select ( this ) . raise ( ) ;
216
244
svg . attr ( 'cursor' , 'grabbing' ) ;
@@ -223,11 +251,10 @@ const Map = (props) => {
223
251
function dragEnded ( ) : any {
224
252
svg . attr ( 'cursor' , 'grab' ) ;
225
253
}
226
-
227
-
228
-
254
+
255
+ // call update on node click
229
256
update ( root ) ;
230
- } ;
257
+ } , [ data ] ) ;
231
258
// // set the heights and width of the tree to be passed into treeMap function
232
259
// const width: number = 900;
233
260
// const height: number = 600;
0 commit comments