1
+ /* eslint-disable no-use-before-define */
1
2
/* eslint-disable react/no-this-in-sfc */
2
3
/* eslint-disable no-unused-vars */
3
4
/* eslint-disable react/prop-types */
17
18
18
19
import React , { useEffect , useState , useRef , useCallback } from 'react' ;
19
20
import * as d3 from 'd3' ;
20
- // import { addNewSnapshots } from '../actions/actions ';
21
+ import { schemeSet1 as colorScheme } from 'd3 ' ;
21
22
22
- // const windowRef = useRef(null);
23
- // const winWidth = null;
24
- // const winHeight = null;
23
+ // import { addNewSnapshots } from '../actions/actions';
25
24
26
- // useEffect(() => {
27
- // if (windowRef.current) {
28
- // winWidth = windowRef.current.offsetHeight;
29
- // winHeight = windowRef.current.offsetWidth;
30
- // console.log('** SETTING WINDOW SIZES: ', winWidth, winHeight);
31
- // }
32
- // }, [windowRef]);
33
25
34
- const PerfView = ( { snapshots, viewIndex } ) => {
35
- const [ chartData , updateChartData ] = useState ( snapshots [ snapshots . length - 1 ] ) ;
26
+ const PerfView = ( { snapshots, viewIndex, width = 600 , height = 600 } ) => {
27
+ // console.log('***** constructor *****' );
36
28
const svgRef = useRef ( null ) ;
37
29
38
- // Todo: implement update functions...
39
- const [ curZoom , setZoom ] = useState ( null ) ;
40
- const [ width , setWidth ] = useState ( 600 ) ;
41
- const [ height , setHeight ] = useState ( 600 ) ;
30
+ // Figure out which snapshot index to use
31
+ let indexToDisplay = null ;
32
+ if ( viewIndex < 0 ) indexToDisplay = snapshots . length - 1 ;
33
+ else indexToDisplay = viewIndex ;
42
34
43
- // set up color scaling function
44
- const color = d3 . scaleLinear ( )
35
+ // Set up color scaling function
36
+ const colorScale = d3 . scaleLinear ( )
45
37
. domain ( [ 0 , 7 ] )
46
- . range ( [ 'hsl(152,80%,80 %)' , 'hsl(228 ,30%,40 %)' ] )
38
+ . range ( [ 'hsl(200,60%,60 %)' , 'hsl(255 ,30%,30 %)' ] )
47
39
. interpolate ( d3 . interpolateHcl ) ;
48
40
49
- // set up circle-packing layout function
41
+ // Set up circle-packing layout function
50
42
const packFunc = useCallback ( data => {
51
43
return d3 . pack ( )
52
44
. size ( [ width , height ] )
53
45
. padding ( 3 ) ( d3 . hierarchy ( data )
54
- . sum ( d => {
55
- // console.log('in pack func. d=', d);
56
- return d . componentData . actualDuration ;
57
- } )
58
- . sort ( ( a , b ) => {
59
- // console.log('in sort func. a&b=', a, b);
60
- return b . value - a . value ;
61
- } ) ) ;
46
+ . sum ( d => { return d . componentData . actualDuration || 0 ; } )
47
+ . sort ( ( a , b ) => { return b . value - a . value ; } ) ) ;
62
48
} , [ width , height ] ) ;
63
49
64
- // first run, or user has made changes in their app; clear old tree and get current chartData
50
+ // If indexToDisplay changes, clear old tree nodes
65
51
useEffect ( ( ) => {
66
- console . log ( 'PerfView -> snapshots' , snapshots ) ;
67
- console . log ( 'Current viewIndex: ' , viewIndex ) ;
68
- for ( let i = 0 ; i < snapshots . length ; i ++ ) {
69
- console . log ( `SNAPSHOT[${ i } ] App actualDuration:` , snapshots [ i ] . children [ 0 ] . componentData . actualDuration ) ;
70
- }
71
-
72
- // clear old tree
52
+ // console.log('***** useEffect - CLEARING');
73
53
while ( svgRef . current . hasChildNodes ( ) ) {
74
54
svgRef . current . removeChild ( svgRef . current . lastChild ) ;
75
55
}
76
-
77
- let indexToDisplay = null ;
78
- if ( viewIndex < 0 ) indexToDisplay = snapshots . length - 1 ;
79
- else indexToDisplay = viewIndex ;
80
-
81
- updateChartData ( snapshots [ indexToDisplay ] ) ;
82
- console . log ( `Using snapshots[${ indexToDisplay } ]` ) ;
83
- } , [ svgRef , viewIndex , snapshots , chartData ] ) ;
56
+ } , [ indexToDisplay , svgRef ] ) ;
84
57
85
58
useEffect ( ( ) => {
86
- console . log ( 'PerfView -> chartData' , chartData ) ;
59
+ // console.log(`***** useEffect - MAIN -> snapshots[${indexToDisplay}]`, snapshots[indexToDisplay]);
60
+
61
+ // Error, no App-level component present
62
+ if ( snapshots [ indexToDisplay ] . children . length < 1 ) return ;
87
63
88
- // generate tree with our data
89
- const packedRoot = packFunc ( chartData ) ;
90
- // console.log('PerfView -> packedRoot', packedRoot);
64
+ // Generate tree with our data
65
+ const packedRoot = packFunc ( snapshots [ indexToDisplay ] ) ;
91
66
92
- // initial focus points at root of tree
93
- let focus = packedRoot ;
67
+ // Set initial focus to root node
68
+ let curFocus = packedRoot ;
69
+
70
+ // View [x, y, r]
94
71
let view ;
95
72
96
- // set up viewBox dimensions and onClick for parent svg
73
+ // Set up viewBox dimensions and onClick for parent svg
97
74
const svg = d3 . select ( svgRef . current )
98
75
. attr ( 'viewBox' , `-${ width / 2 } -${ height / 2 } ${ width } ${ height } ` )
99
- . on ( 'click' , ( ) => zoom ( packedRoot ) ) ;
76
+ . on ( 'click' , ( ) => zoomToNode ( packedRoot ) ) ;
100
77
101
- // connect circles below root to data
78
+ // Connect circles below root to data
102
79
const node = svg . append ( 'g' )
103
80
. selectAll ( 'circle' )
104
81
. data ( packedRoot . descendants ( ) . slice ( 1 ) )
105
82
. enter ( ) . append ( 'circle' )
106
- . attr ( 'fill' , d => ( d . children ? color ( d . depth ) : 'white' ) )
107
- . attr ( 'pointer-events' , d => ( ! d . children ? 'none' : null ) )
108
- . on ( 'mouseover' , ( ) => d3 . select ( this ) . attr ( 'stroke' , '#000' ) )
109
- . on ( 'mouseout' , ( ) => d3 . select ( this ) . attr ( 'stroke' , null ) )
110
- . on ( 'click' , d => focus !== d && ( zoom ( d ) , d3 . event . stopPropagation ( ) ) ) ;
111
-
112
- // console.log('PerfView -> node', node);
113
- // console.log('packedRoot.descendants()', packedRoot.descendants());
83
+ . attr ( 'fill' , d => ( d . children ? colorScale ( d . depth ) : 'white' ) )
84
+ . attr ( 'pointer-events' , d => ( ! d . children ? 'none' : null ) )
85
+ . on ( 'mouseover' , ( ) => d3 . select ( this ) . attr ( 'stroke' , '#000' ) )
86
+ . on ( 'mouseout' , ( ) => d3 . select ( this ) . attr ( 'stroke' , null ) )
87
+ . on ( 'click' , d => curFocus !== d && ( zoomToNode ( d ) , d3 . event . stopPropagation ( ) ) ) ;
114
88
115
- // generate text labels
89
+ // Generate text labels. Set (only) root to visible initially
116
90
const label = svg . append ( 'g' )
117
- . attr ( 'class' , 'perf-chart-labels' )
91
+ // .style('fill', 'rgb(231, 231, 231)')
92
+ . attr ( 'class' , 'perf-chart-labels' )
118
93
. selectAll ( 'text' )
119
94
. data ( packedRoot . descendants ( ) )
120
95
. enter ( ) . append ( 'text' )
121
- . style ( 'fill-opacity' , d => ( d . parent === packedRoot ? 1 : 0 ) )
122
- . style ( 'display' , d => ( d . parent === packedRoot ? 'inline' : 'none' ) )
123
- . text ( d => {
124
- // console.log('generating text label for d: ', d);
125
- return `${ d . data . name } : ${ Number . parseFloat ( d . data . componentData . actualDuration ) . toFixed ( 2 ) } ms` ;
126
- } ) ;
96
+ . style ( 'fill-opacity' , d => ( d . parent === packedRoot ? 1 : 0 ) )
97
+ . style ( 'display' , d => ( d . parent === packedRoot ? 'inline' : 'none' ) )
98
+ . text ( d => `${ d . data . name } : \
99
+ ${ Number . parseFloat ( d . data . componentData . actualDuration || 0 ) . toFixed ( 2 ) } ms` ) ;
127
100
101
+ // Remove any unused nodes
128
102
label . exit ( ) . remove ( ) ;
129
103
node . exit ( ) . remove ( ) ;
130
104
131
- // jump to default zoom state
132
- zoomTo ( [ packedRoot . x , packedRoot . y , packedRoot . r * 2 ] ) ;
105
+ // Zoom size of nodes and labels to focus view on root node
106
+ zoomViewArea ( [ packedRoot . x , packedRoot . y , packedRoot . r * 2 ] ) ;
133
107
134
- function zoomTo ( v ) {
135
- // console.log("zoomTo -> v", v);
136
- const k = width / v [ 2 ] ;
137
- view = v ;
138
- label . attr ( 'transform' , d => `translate(${ ( d . x - v [ 0 ] ) * k } ,${ ( d . y - v [ 1 ] ) * k } )` ) ;
139
- node . attr ( 'transform' , d => `translate(${ ( d . x - v [ 0 ] ) * k } ,${ ( d . y - v [ 1 ] ) * k } )` ) ;
108
+ // Zoom/relocated nodes and labels based on dimensions given [x, y, r]
109
+ function zoomViewArea ( newXYR ) {
110
+ console . log ( "zoomTo -> newXYR" , newXYR ) ;
111
+ const k = width / newXYR [ 2 ] ;
112
+ view = newXYR ;
113
+ label . attr ( 'transform' , d => `translate(${ ( d . x - newXYR [ 0 ] ) * k } ,${ ( d . y - newXYR [ 1 ] ) * k } )` ) ;
114
+ node . attr ( 'transform' , d => `translate(${ ( d . x - newXYR [ 0 ] ) * k } ,${ ( d . y - newXYR [ 1 ] ) * k } )` ) ;
140
115
node . attr ( 'r' , d => d . r * k ) ;
141
116
}
142
117
143
- function zoom ( d ) {
118
+ // Transition visibility of labels
119
+ function zoomToNode ( newFocus ) {
144
120
// console.log("zoom -> d", d);
145
- const focus0 = focus ;
146
- focus = d ;
147
-
148
121
const transition = svg . transition ( )
149
- . duration ( d3 . event . altKey ? 7500 : 750 )
150
- . tween ( 'zoom' , d => {
151
- const i = d3 . interpolateZoom ( view , [ focus . x , focus . y , focus . r * 2 ] ) ;
152
- return t => zoomTo ( i ( t ) ) ;
153
- } ) ;
154
-
155
- label
156
- . filter ( function ( d ) { return d . parent === focus || this . style . display === 'inline' ; } )
122
+ . duration ( d3 . event . altKey ? 7500 : 750 )
123
+ . tween ( 'zoom' , d => {
124
+ const i = d3 . interpolateZoom ( view , [ newFocus . x , newFocus . y , newFocus . r * 2 ] ) ;
125
+ return t => zoomViewArea ( i ( t ) ) ;
126
+ } ) ;
127
+
128
+ // Grab all nodes that were previously displayed, or who's parent is the new target newFocus
129
+ // Transition their labels to visible or not
130
+ label . filter ( function ( d ) { console . log ( 'label filtering. d=' , d ) ; return d . parent === newFocus || this . style . display === 'inline' ; } )
157
131
. transition ( transition )
158
- . style ( 'fill-opacity' , d => ( d . parent === focus ? 1 : 0 ) )
159
- . on ( 'start' , function ( d ) { if ( d . parent === focus ) this . style . display = 'inline' ; } )
160
- . on ( 'end' , function ( d ) { if ( d . parent !== focus ) this . style . display = 'none' ; } ) ;
132
+ . style ( 'fill-opacity' , d => ( d . parent === newFocus ? 1 : 0 ) )
133
+ . on ( 'start' , function ( d ) { if ( d . parent === newFocus ) this . style . display = 'inline' ; } )
134
+ . on ( 'end' , function ( d ) { if ( d . parent !== newFocus ) this . style . display = 'none' ; } ) ;
135
+
136
+ curFocus = newFocus ;
161
137
}
162
- } , [ chartData , color , packFunc , width , height ] ) ;
138
+ } , [ colorScale , packFunc , width , height , indexToDisplay , snapshots ] ) ;
163
139
164
140
return < svg className = "perfContainer" ref = { svgRef } /> ;
165
141
} ;
166
142
167
143
export default PerfView ;
144
+
145
+
146
+ // d3.quantize(d3.interpolateHcl('#60c96e', '#4d4193'), 10);
147
+ // const colorScale = d3.scaleOrdinal(colorScheme);
0 commit comments