import React , { useState , useRef } from 'react' ; import { scaleLinear } from 'd3-scale' ; import { interpolate } from 'd3-interpolate' ; import { format as d3format } from 'd3-format' ; import { hierarchy } from 'd3-hierarchy' ; import { Group } from '@vx/group' ; import { Partition } from '@vx/hierarchy' ; import { useSpring , animated } from 'react-spring' ; const format = d3format ( '.2f' ) ; const FlameGraph = ( { cleanedComponentAtomTree , width , height } ) => { const root = hierarchy ( cleanedComponentAtomTree ) . sum ( d => { return d . actualDuration ; } ) . sort ( ( a , b ) => b . value - a . value ) ; let totalNodes = 0 ; root . each ( ( ) => { totalNodes = totalNodes + 1 ; return ; } ) ; const averageDiration = root . value / totalNodes ; const margin = { top : 0 , left : 0 , right : 0 , bottom : 0 } ; const color = scaleLinear ( ) . domain ( [ averageDiration / 2 , averageDiration * 3 , averageDiration * 6 , averageDiration * 8 , ] ) . range ( [ '#ffffff' , '#e9c7ff' , '#ee9f30' , '#ff0000' ] ) ; const [ area , setArea ] = useState ( { xDomain : [ 0 , width ] , xRange : [ 0 , width ] , yDomain : [ 0 , height ] , yRange : [ 0 , height ] , } ) ; const xScale = useRef ( scaleLinear ( ) . domain ( area . xDomain ) . range ( area . xRange ) ) ; const yScale = useRef ( scaleLinear ( ) . domain ( area . yDomain ) . range ( area . yRange ) ) ; const xd = interpolate ( xScale . current . domain ( ) , area . xDomain ) ; const yd = interpolate ( yScale . current . domain ( ) , area . yDomain ) ; const yr = interpolate ( yScale . current . range ( ) , area . yRange ) ; const { t } = useSpring ( { native : true , reset : true , from : { t : 0 } , to : { t : 1 } , config : { mass : 5 , tension : 500 , friction : 100 , precision : 0.00001 , } , onFrame : Param => { xScale . current . domain ( xd ( Param . t ) ) ; yScale . current . domain ( yd ( Param . t ) ) . range ( yr ( Param . t ) ) ; } , } ) ; return ( < svg width = { width } height = { height } > < Partition top = { margin . top } left = { margin . left } root = { root } size = { [ height , width ] } padding = { 1 } round = { true } > { data => ( < Group > { data . descendants ( ) . map ( ( node , i ) => ( < animated . g transform = { t . interpolate ( ( ) => `translate(${ xScale . current ( node . y0 ) }, ${ yScale . current ( node . x0 ) })` ) } key = { `node-${ i }` } onClick = { ( ) => { if ( node . y0 === area . xDomain [ 0 ] && node . x0 === area . yDomain [ 0 ] && node . parent ) { setArea ( { ... area , xDomain : [ node . parent . y0 , width ] , yDomain : [ node . parent . x0 , node . parent . x1 ] , yRange : [ 0 , height ] , } ) ; } else { setArea ( { ... area , xDomain : [ node . y0 , width ] , yDomain : [ node . x0 , node . x1 ] , yRange : [ 0 , height ] , } ) ; } } } > < animated . rect id = { `rect-${ i }` } width = { t . interpolate ( ( ) => xScale . current ( node . y1 ) - xScale . current ( node . y0 ) ) } height = { t . interpolate ( ( ) => yScale . current ( node . x1 ) - yScale . current ( node . x0 ) ) } fill = { color ( node . data . actualDuration ) } fillOpacity = { 1 } / > < clipPath id = { `clip-${ i }` } > < use xlinkHref = { `#rect-${ i }` } /> < /clipPath> < text x = { 4 } y = { 13 } clipPath = { `url(#clip-${ i })` } style = { { font : '10px sans-serif' , fontWeight : 'bold' , } } > { node . data . name } < tspan style = { { fontSize : 9 , fillOpacity : 0.8 , } } > { ' ' } { format ( node . data . actualDuration ) } < /tspan> < /text> < /animated.g>))} < /Group>)} < /Partition> < /svg>); } ; export default FlameGraph ;