1
+ import * as Sentry from '@sentry/react'
1
2
import { interpolate } from 'd3-interpolate'
2
3
import { scaleSequential } from 'd3-scale'
3
4
import { select } from 'd3-selection'
@@ -26,38 +27,40 @@ function SunburstChart({
26
27
const hoverHandler = useRef ( onHover )
27
28
28
29
// this state stores the root node of the sunburst chart
29
- const [ root ] = useState ( ( ) => {
30
- // go through the data and add `value` to each node
31
- const stack = [ data ]
32
- const nodeMap = new Map ( )
33
-
34
- // create a new root node with the value of the root node
35
- const result = { ...data , value : selectorHandler . current ( data ) }
36
- // add the root node to the node map
37
- nodeMap . set ( data , result )
38
-
39
- // while there are nodes to process, pop the last node from the stack
40
- while ( stack . length > 0 ) {
41
- const node = stack . pop ( )
42
- const currentNode = nodeMap . get ( node )
43
-
44
- // if the node has children, process them
45
- if ( Array . isArray ( node . children ) ) {
46
- currentNode . children = node . children . map ( ( child ) => {
47
- // sad ... some browsers still lack support for structuredClone
48
- const newChild = JSON . parse ( JSON . stringify ( child ) )
49
- Object . assign ( newChild , { value : selectorHandler . current ( child ) } )
50
-
51
- nodeMap . set ( child , newChild )
52
- stack . push ( child )
53
- return newChild
54
- } )
30
+ const [ root ] = useState ( ( ) =>
31
+ Sentry . startSpan ( { name : 'SunburstChart.createRoot' } , ( ) => {
32
+ // go through the data and add `value` to each node
33
+ const stack = [ data ]
34
+ const nodeMap = new Map ( )
35
+
36
+ // create a new root node with the value of the root node
37
+ const result = { ...data , value : selectorHandler . current ( data ) }
38
+ // add the root node to the node map
39
+ nodeMap . set ( data , result )
40
+
41
+ // while there are nodes to process, pop the last node from the stack
42
+ while ( stack . length > 0 ) {
43
+ const node = stack . pop ( )
44
+ const currentNode = nodeMap . get ( node )
45
+
46
+ // if the node has children, process them
47
+ if ( Array . isArray ( node . children ) ) {
48
+ currentNode . children = node . children . map ( ( child ) => {
49
+ // sad ... some browsers still lack support for structuredClone
50
+ const newChild = JSON . parse ( JSON . stringify ( child ) )
51
+ Object . assign ( newChild , { value : selectorHandler . current ( child ) } )
52
+
53
+ nodeMap . set ( child , newChild )
54
+ stack . push ( child )
55
+ return newChild
56
+ } )
57
+ }
55
58
}
56
- }
57
59
58
- // partition the data and add the `current` property to each node
59
- return partitionFn ( result ) . each ( ( d ) => ( d . current = d ) )
60
- } )
60
+ // partition the data and add the `current` property to each node
61
+ return partitionFn ( result ) . each ( ( d ) => ( d . current = d ) )
62
+ } )
63
+ )
61
64
62
65
// In this case D3 is handling rendering not React, so useLayoutEffect is used to handle rendering
63
66
// and changes outside of the React lifecycle.
@@ -72,20 +75,24 @@ function SunburstChart({
72
75
const radius = width / 6
73
76
74
77
// Creates a function for creating arcs representing files and folders.
75
- const drawArc = arc ( )
76
- . startAngle ( ( d ) => d . x0 )
77
- . endAngle ( ( d ) => d . x1 )
78
- . padAngle ( ( d ) => Math . min ( ( d . x1 - d . x0 ) / 2 , 0.005 ) )
79
- . padRadius ( radius * 1.5 )
80
- . innerRadius ( ( d ) => d . y0 * radius )
81
- . outerRadius ( ( d ) => Math . max ( d . y0 * radius , d . y1 * radius - 1 ) )
78
+ const drawArc = Sentry . startSpan ( { name : 'SunburstChart.drawArc' } , ( ) => {
79
+ return arc ( )
80
+ . startAngle ( ( d ) => d . x0 )
81
+ . endAngle ( ( d ) => d . x1 )
82
+ . padAngle ( ( d ) => Math . min ( ( d . x1 - d . x0 ) / 2 , 0.005 ) )
83
+ . padRadius ( radius * 1.5 )
84
+ . innerRadius ( ( d ) => d . y0 * radius )
85
+ . outerRadius ( ( d ) => Math . max ( d . y0 * radius , d . y1 * radius - 1 ) )
86
+ } )
82
87
83
88
// A color function you can pass a number from 0-100 to and get a color back from the specified color range
84
89
// Ex color(10.4)
85
- const color = scaleSequential ( )
86
- . domain ( [ colorDomainMin , colorDomainMax ] )
87
- . interpolator ( colorRange )
88
- . clamp ( true )
90
+ const color = Sentry . startSpan ( { name : 'SunburstChart.color' } , ( ) => {
91
+ return scaleSequential ( )
92
+ . domain ( [ colorDomainMin , colorDomainMax ] )
93
+ . interpolator ( colorRange )
94
+ . clamp ( true )
95
+ } )
89
96
90
97
// Tracks previous location for rendering .. in the breadcrumb.
91
98
let previous
@@ -99,18 +106,22 @@ function SunburstChart({
99
106
. attr ( 'transform' , `translate(${ width / 2 } ,${ width / 2 } )` )
100
107
101
108
// Renders an arc per data point in the correct location. (Pieces of the circle that add up to a circular graph)
102
- const path = g
103
- . append ( 'g' )
104
- . selectAll ( 'path' )
105
- . data ( root . descendants ( ) . slice ( 1 ) )
106
- . join ( 'path' )
107
- . attr ( 'fill' , ( d ) => color ( d ?. data ?. value || 0 ) )
108
- // If data point is a file fade the background color a bit.
109
- . attr ( 'fill-opacity' , ( d ) =>
110
- arcVisible ( d . current ) ? ( d . children ? 1 : 0.6 ) : 0
111
- )
112
- . attr ( 'pointer-events' , ( d ) => ( arcVisible ( d . current ) ? 'auto' : 'none' ) )
113
- . attr ( 'd' , ( d ) => drawArc ( d . current ) )
109
+ const path = Sentry . startSpan ( { name : 'SunburstChart.renderArcs' } , ( ) =>
110
+ g
111
+ . append ( 'g' )
112
+ . selectAll ( 'path' )
113
+ . data ( root . descendants ( ) . slice ( 1 ) )
114
+ . join ( 'path' )
115
+ . attr ( 'fill' , ( d ) => color ( d ?. data ?. value || 0 ) )
116
+ // If data point is a file fade the background color a bit.
117
+ . attr ( 'fill-opacity' , ( d ) =>
118
+ arcVisible ( d . current ) ? ( d . children ? 1 : 0.6 ) : 0
119
+ )
120
+ . attr ( 'pointer-events' , ( d ) =>
121
+ arcVisible ( d . current ) ? 'auto' : 'none'
122
+ )
123
+ . attr ( 'd' , ( d ) => drawArc ( d . current ) )
124
+ )
114
125
115
126
// Events for folders
116
127
path
@@ -232,49 +243,55 @@ function SunburstChart({
232
243
handleTextUpdate ( { current : p , selected, transition : t } )
233
244
}
234
245
235
- function handleArcsUpdate ( { current, selected, transition } ) {
236
- parent . datum ( selected )
237
-
238
- // Handle animating in/out of a folder
239
- root . each ( ( d ) => {
240
- // determine x0 and y0
241
- const x0Min = Math . min (
242
- 1 ,
243
- ( d . x0 - current . x0 ) / ( current . x1 - current . x0 )
244
- )
245
- const x0 = Math . max ( 0 , x0Min ) * 2 * Math . PI
246
- const y0 = Math . max ( 0 , d . y0 - current . depth )
247
-
248
- // determine x1 and y1
249
- const x1Min = Math . min (
250
- 1 ,
251
- ( d . x1 - current . x0 ) / ( current . x1 - current . x0 )
252
- )
253
- const x1 = Math . max ( 0 , x1Min ) * 2 * Math . PI
254
- const y1 = Math . max ( 0 , d . y1 - current . depth )
255
-
256
- d . target = { x0, y0, x1, y1 }
257
- } )
258
-
259
- // Transition the data on all arcs, even the ones that aren’t visible,
260
- // so that if this transition is interrupted, entering arcs will start
261
- // the next transition from the desired position.
262
- path
263
- . transition ( transition )
264
- . tween ( 'data' , ( d ) => {
265
- const i = interpolate ( d . current , d . target )
266
- return ( t ) => ( d . current = i ( t ) )
267
- } )
268
- . filter ( function ( d ) {
269
- return + this . getAttribute ( 'fill-opacity' ) || arcVisible ( d . target )
246
+ const handleArcsUpdate = ( { current, selected, transition } ) =>
247
+ Sentry . startSpan ( { name : 'SunburstChart.handleArcsUpdate' } , ( ) => {
248
+ parent . datum ( selected )
249
+
250
+ // Handle animating in/out of a folder
251
+ Sentry . startSpan ( { name : 'SunburstChart.calculateCoordinates' } , ( ) => {
252
+ root . each ( ( d ) => {
253
+ // determine x0 and y0
254
+ const x0Min = Math . min (
255
+ 1 ,
256
+ ( d . x0 - current . x0 ) / ( current . x1 - current . x0 )
257
+ )
258
+ const x0 = Math . max ( 0 , x0Min ) * 2 * Math . PI
259
+ const y0 = Math . max ( 0 , d . y0 - current . depth )
260
+
261
+ // determine x1 and y1
262
+ const x1Min = Math . min (
263
+ 1 ,
264
+ ( d . x1 - current . x0 ) / ( current . x1 - current . x0 )
265
+ )
266
+ const x1 = Math . max ( 0 , x1Min ) * 2 * Math . PI
267
+ const y1 = Math . max ( 0 , d . y1 - current . depth )
268
+
269
+ d . target = { x0, y0, x1, y1 }
270
+ } )
270
271
} )
271
- . attr ( 'fill-opacity' , ( d ) =>
272
- arcVisible ( d . target ) ? ( d . children ? 1 : 0.6 ) : 0
273
- )
274
- . attr ( 'pointer-events' , ( d ) => ( arcVisible ( d . target ) ? 'auto' : 'none' ) )
275
272
276
- . attrTween ( 'd' , ( d ) => ( ) => drawArc ( d . current ) )
277
- }
273
+ // Transition the data on all arcs, even the ones that aren’t visible,
274
+ // so that if this transition is interrupted, entering arcs will start
275
+ // the next transition from the desired position.
276
+ Sentry . startSpan ( { name : 'SunburstChart.transitionArcs' } , ( ) => {
277
+ path
278
+ . transition ( transition )
279
+ . tween ( 'data' , ( d ) => {
280
+ const i = interpolate ( d . current , d . target )
281
+ return ( t ) => ( d . current = i ( t ) )
282
+ } )
283
+ . filter ( function ( d ) {
284
+ return + this . getAttribute ( 'fill-opacity' ) || arcVisible ( d . target )
285
+ } )
286
+ . attr ( 'fill-opacity' , ( d ) =>
287
+ arcVisible ( d . target ) ? ( d . children ? 1 : 0.6 ) : 0
288
+ )
289
+ . attr ( 'pointer-events' , ( d ) =>
290
+ arcVisible ( d . target ) ? 'auto' : 'none'
291
+ )
292
+ . attrTween ( 'd' , ( d ) => ( ) => drawArc ( d . current ) )
293
+ } )
294
+ } )
278
295
279
296
function handleTextUpdate ( { current, selected, transition } ) {
280
297
backText . datum ( selected )
@@ -328,4 +345,6 @@ SunburstChart.propTypes = {
328
345
colorDomainMax : PropTypes . number ,
329
346
}
330
347
331
- export default SunburstChart
348
+ export default Sentry . withProfiler ( SunburstChart , {
349
+ name : 'SunburstChart' ,
350
+ } )
0 commit comments