1+ /* eslint-disable no-use-before-define */
12/* eslint-disable react/no-this-in-sfc */
23/* eslint-disable no-unused-vars */
34/* eslint-disable react/prop-types */
1718
1819import React , { useEffect , useState , useRef , useCallback } from 'react' ;
1920import * as d3 from 'd3' ;
20- // import { addNewSnapshots } from '../actions/actions ';
21+ import { schemeSet1 as colorScheme } from 'd3 ' ;
2122
22- // const windowRef = useRef(null);
23- // const winWidth = null;
24- // const winHeight = null;
23+ // import { addNewSnapshots } from '../actions/actions';
2524
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]);
3325
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 *****' );
3628 const svgRef = useRef ( null ) ;
3729
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 ;
4234
43- // set up color scaling function
44- const color = d3 . scaleLinear ( )
35+ // Set up color scaling function
36+ const colorScale = d3 . scaleLinear ( )
4537 . domain ( [ 0 , 7 ] )
46- . range ( [ 'hsl(152,80%,80 %)' , 'hsl(228 ,30%,40 %)' ] )
38+ . range ( [ 'hsl(200,60%,60 %)' , 'hsl(255 ,30%,30 %)' ] )
4739 . interpolate ( d3 . interpolateHcl ) ;
4840
49- // set up circle-packing layout function
41+ // Set up circle-packing layout function
5042 const packFunc = useCallback ( data => {
5143 return d3 . pack ( )
5244 . size ( [ width , height ] )
5345 . 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 ; } ) ) ;
6248 } , [ width , height ] ) ;
6349
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
6551 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');
7353 while ( svgRef . current . hasChildNodes ( ) ) {
7454 svgRef . current . removeChild ( svgRef . current . lastChild ) ;
7555 }
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 ] ) ;
8457
8558 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 ;
8763
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 ] ) ;
9166
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]
9471 let view ;
9572
96- // set up viewBox dimensions and onClick for parent svg
73+ // Set up viewBox dimensions and onClick for parent svg
9774 const svg = d3 . select ( svgRef . current )
9875 . attr ( 'viewBox' , `-${ width / 2 } -${ height / 2 } ${ width } ${ height } ` )
99- . on ( 'click' , ( ) => zoom ( packedRoot ) ) ;
76+ . on ( 'click' , ( ) => zoomToNode ( packedRoot ) ) ;
10077
101- // connect circles below root to data
78+ // Connect circles below root to data
10279 const node = svg . append ( 'g' )
10380 . selectAll ( 'circle' )
10481 . data ( packedRoot . descendants ( ) . slice ( 1 ) )
10582 . 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 ( ) ) ) ;
11488
115- // generate text labels
89+ // Generate text labels. Set (only) root to visible initially
11690 const label = svg . append ( 'g' )
117- . attr ( 'class' , 'perf-chart-labels' )
91+ // .style('fill', 'rgb(231, 231, 231)')
92+ . attr ( 'class' , 'perf-chart-labels' )
11893 . selectAll ( 'text' )
11994 . data ( packedRoot . descendants ( ) )
12095 . 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` ) ;
127100
101+ // Remove any unused nodes
128102 label . exit ( ) . remove ( ) ;
129103 node . exit ( ) . remove ( ) ;
130104
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 ] ) ;
133107
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 } )` ) ;
140115 node . attr ( 'r' , d => d . r * k ) ;
141116 }
142117
143- function zoom ( d ) {
118+ // Transition visibility of labels
119+ function zoomToNode ( newFocus ) {
144120 // console.log("zoom -> d", d);
145- const focus0 = focus ;
146- focus = d ;
147-
148121 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' ; } )
157131 . 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 ;
161137 }
162- } , [ chartData , color , packFunc , width , height ] ) ;
138+ } , [ colorScale , packFunc , width , height , indexToDisplay , snapshots ] ) ;
163139
164140 return < svg className = "perfContainer" ref = { svgRef } /> ;
165141} ;
166142
167143export default PerfView ;
144+
145+
146+ // d3.quantize(d3.interpolateHcl('#60c96e', '#4d4193'), 10);
147+ // const colorScale = d3.scaleOrdinal(colorScheme);
0 commit comments