@@ -67,25 +67,29 @@ function SunburstChart({
67
67
const radius = width / 6
68
68
69
69
// Creates a function for creating arcs representing files and folders.
70
- const createDrawArcFunction = ( parentSpan ) =>
71
- Sentry . startSpan ( { name : 'SunburstChart.drawArc' , parentSpan } , ( ) =>
72
- arc ( )
73
- . startAngle ( ( d ) => d . x0 )
74
- . endAngle ( ( d ) => d . x1 )
75
- . padAngle ( ( d ) => Math . min ( ( d . x1 - d . x0 ) / 2 , 0.005 ) )
76
- . padRadius ( radius * 1.5 )
77
- . innerRadius ( ( d ) => d . y0 * radius )
78
- . outerRadius ( ( d ) => Math . max ( d . y0 * radius , d . y1 * radius - 1 ) )
70
+ function createDrawArcFunction ( parentSpan ) {
71
+ return Sentry . startSpan (
72
+ { name : 'SunburstChart.drawArc' , parentSpan } ,
73
+ ( ) =>
74
+ arc ( )
75
+ . startAngle ( ( d ) => d . x0 )
76
+ . endAngle ( ( d ) => d . x1 )
77
+ . padAngle ( ( d ) => Math . min ( ( d . x1 - d . x0 ) / 2 , 0.005 ) )
78
+ . padRadius ( radius * 1.5 )
79
+ . innerRadius ( ( d ) => d . y0 * radius )
80
+ . outerRadius ( ( d ) => Math . max ( d . y0 * radius , d . y1 * radius - 1 ) )
79
81
)
82
+ }
80
83
// A color function you can pass a number from 0-100 to and get a color back from the specified color range
81
84
// Ex color(10.4)
82
- const createColorFunction = ( parentSpan ) =>
83
- Sentry . startSpan ( { name : 'SunburstChart.color' , parentSpan } , ( ) =>
85
+ function createColorFunction ( parentSpan ) {
86
+ return Sentry . startSpan ( { name : 'SunburstChart.color' , parentSpan } , ( ) =>
84
87
scaleSequential ( )
85
88
. domain ( [ colorDomainMin , colorDomainMax ] )
86
89
. interpolator ( colorRange )
87
90
. clamp ( true )
88
91
)
92
+ }
89
93
90
94
// Tracks previous location for rendering .. in the breadcrumb.
91
95
let previous
@@ -98,185 +102,192 @@ function SunburstChart({
98
102
. append ( 'g' )
99
103
. attr ( 'transform' , `translate(${ width / 2 } ,${ width / 2 } )` )
100
104
101
- const renderSunburst = Sentry . startSpan (
102
- { name : 'SunburstChart.renderSunburst' } ,
103
- ( renderSunburstSpan ) => {
104
- const drawArc = createDrawArcFunction ( renderSunburstSpan )
105
- const color = createColorFunction ( renderSunburstSpan )
106
- const nodesToRender = selectedNode
107
- . descendants ( )
108
- . slice ( 1 )
109
- . filter ( ( d ) => d . depth <= selectedNode . depth + 2 )
110
-
111
- // Renders an arc per data point in the correct location. (Pieces of the circle that add up to a circular graph)
112
- const path = Sentry . startSpan (
113
- { name : 'SunburstChart.renderArcs' , parentSpan : renderSunburstSpan } ,
114
- ( ) =>
115
- g
116
- . append ( 'g' )
117
- . selectAll ( 'path' )
118
- . data ( nodesToRender )
119
- . join ( 'path' )
120
- . attr ( 'fill' , ( d ) => color ( d ?. data ?. value || 0 ) )
121
- // If data point is a file fade the background color a bit.
122
- . attr ( 'fill-opacity' , ( d ) => ( d . children ? 1 : 0.6 ) )
123
- . attr ( 'pointer-events' , ( ) => 'auto' )
124
- . attr ( 'd' , ( d ) => drawArc ( d . current ) )
125
- )
126
- // Events for folders
127
- path
128
- . filter ( ( d ) => d . children )
129
- . style ( 'cursor' , 'pointer' )
130
- . on ( 'click' , clickedFolder )
131
- . on ( 'mouseover' , function ( _event , p ) {
132
- select ( this ) . attr ( 'fill-opacity' , 0.6 )
133
- reactHoverCallback ( { target : p , type : 'folder' } )
134
- } )
135
- . on ( 'mouseout' , function ( _event , _node ) {
136
- select ( this ) . attr ( 'fill-opacity' , 1 )
137
- } )
138
-
139
- // Events for file
140
- path
141
- . filter ( ( d ) => ! d . children )
142
- . style ( 'cursor' , 'pointer' )
143
- . on ( 'click' , function ( _event , node ) {
144
- reactClickCallback ( { target : node , type : 'file' } )
145
- } )
146
- . on ( 'mouseover' , function ( _event , node ) {
147
- select ( this ) . attr ( 'fill-opacity' , 0.6 )
148
- reactHoverCallback ( { target : node , type : 'file' } )
149
- } )
150
-
151
- // Create a11y label / mouse hover tooltip
152
- const formatTitle = ( d ) => {
153
- const coverage = formatData ( d . data . value )
154
- const filePath = d
155
- . ancestors ( )
156
- . map ( ( d ) => d . data . name )
157
- . reverse ( )
158
- . join ( '/' )
159
-
160
- return `${ filePath } \n${ coverage } % coverage`
161
- }
162
-
163
- path . append ( 'title' ) . text ( ( d ) => formatTitle ( d ) )
164
-
165
- // White circle in the middle. Act's as a "back"
166
- g . append ( 'circle' )
167
- . datum ( selectedNode . parent )
168
- . attr ( 'r' , radius )
169
- . attr ( 'class' , 'fill-none' )
170
- . attr ( 'fill' , 'none' )
171
- . attr ( 'pointer-events' , 'all' )
172
- . attr ( 'cursor' , ( d ) => ( d ? 'pointer' : 'default' ) )
173
- . on ( 'click' , clickedFolder )
174
- . on ( 'mouseover' , hoveredRoot )
175
-
176
- g . append ( 'text' )
177
- . datum ( selectedNode . parent )
178
- . text ( '..' )
179
- // if the parent exists (i.e. not root), show the text
180
- . attr ( 'fill-opacity' , ( d ) => ( d ? 1 : 0 ) )
181
- . attr ( 'text-anchor' , 'middle' )
182
- . attr ( 'class' , 'text-7xl fill-ds-gray-quinary select-none' )
183
- . attr ( 'cursor' , 'pointer' )
184
- . on ( 'click' , clickedFolder )
185
- . on ( 'mouseover' , hoveredRoot )
186
-
187
- function clickedFolder ( _event , node ) {
188
- reactClickCallback ( { target : node , type : 'folder' } )
189
- changeLocation ( node )
190
- }
105
+ function renderSunburst ( ) {
106
+ Sentry . startSpan (
107
+ { name : 'SunburstChart.renderSunburst' } ,
108
+ ( renderSunburstSpan ) => {
109
+ const drawArc = createDrawArcFunction ( renderSunburstSpan )
110
+ const color = createColorFunction ( renderSunburstSpan )
111
+ const nodesToRender = selectedNode
112
+ . descendants ( )
113
+ . slice ( 1 )
114
+ . filter ( ( d ) => d . depth <= selectedNode . depth + 2 )
115
+
116
+ // Renders an arc per data point in the correct location. (Pieces of the circle that add up to a circular graph)
117
+ const path = Sentry . startSpan (
118
+ {
119
+ name : 'SunburstChart.renderArcs' ,
120
+ parentSpan : renderSunburstSpan ,
121
+ } ,
122
+ ( ) =>
123
+ g
124
+ . append ( 'g' )
125
+ . selectAll ( 'path' )
126
+ . data ( nodesToRender )
127
+ . join ( 'path' )
128
+ . attr ( 'fill' , ( d ) => color ( d ?. data ?. value || 0 ) )
129
+ // If data point is a file fade the background color a bit.
130
+ . attr ( 'fill-opacity' , ( d ) => ( d . children ? 1 : 0.6 ) )
131
+ . attr ( 'pointer-events' , ( ) => 'auto' )
132
+ . attr ( 'd' , ( d ) => drawArc ( d . current ) )
133
+ )
134
+ // Events for folders
135
+ path
136
+ . filter ( ( d ) => d . children )
137
+ . style ( 'cursor' , 'pointer' )
138
+ . on ( 'click' , clickedFolder )
139
+ . on ( 'mouseover' , function ( _event , p ) {
140
+ select ( this ) . attr ( 'fill-opacity' , 0.6 )
141
+ reactHoverCallback ( { target : p , type : 'folder' } )
142
+ } )
143
+ . on ( 'mouseout' , function ( _event , _node ) {
144
+ select ( this ) . attr ( 'fill-opacity' , 1 )
145
+ } )
191
146
192
- function hoveredRoot ( _event , node ) {
193
- if ( previous ) {
194
- reactHoverCallback ( { target : previous , type : 'folder' } )
195
- return
196
- }
197
- reactHoverCallback ( { target : node , type : 'folder' } )
198
- }
147
+ // Events for file
148
+ path
149
+ . filter ( ( d ) => ! d . children )
150
+ . style ( 'cursor' , 'pointer' )
151
+ . on ( 'click' , function ( _event , node ) {
152
+ reactClickCallback ( { target : node , type : 'file' } )
153
+ } )
154
+ . on ( 'mouseover' , function ( _event , node ) {
155
+ select ( this ) . attr ( 'fill-opacity' , 0.6 )
156
+ reactHoverCallback ( { target : node , type : 'file' } )
157
+ } )
199
158
200
- function reactClickCallback ( { target , type } ) {
201
- if ( target ?. ancestors ) {
202
- // Create a string from the root data down to the current item
203
- const filePath = target
159
+ // Create a11y label / mouse hover tooltip
160
+ const formatTitle = ( d ) => {
161
+ const coverage = formatData ( d . data . value )
162
+ const filePath = d
204
163
. ancestors ( )
205
164
. map ( ( d ) => d . data . name )
206
- . slice ( 0 , - 1 )
207
165
. reverse ( )
208
166
. join ( '/' )
209
167
210
- // callback to parent component with a path, the data node, and raw d3 data
211
- // (just in case we need it for the second iteration to listen to location changes and direct to the correct folder.)
212
- clickHandler . current ( {
213
- path : filePath ,
214
- data : target . data ,
215
- target,
216
- type,
217
- } )
168
+ return `${ filePath } \n${ coverage } % coverage`
218
169
}
219
- }
220
170
221
- function reactHoverCallback ( { target, type } ) {
222
- if ( target ?. ancestors ) {
223
- // Create a string from the root data down to the current item
224
- const filePath = target
225
- . ancestors ( )
226
- . map ( ( d ) => d . data . name )
227
- . slice ( 0 , - 1 )
228
- . reverse ( )
229
- . join ( '/' )
230
-
231
- // callback to parent component with a path, the data node, and raw d3 data
232
- // (just in case we need it for the second iteration to listen to location changes and direct to the correct folder.)
233
- hoverHandler . current ( {
234
- path : filePath ,
235
- data : target . data ,
236
- target,
237
- type,
238
- } )
171
+ path . append ( 'title' ) . text ( ( d ) => formatTitle ( d ) )
172
+
173
+ // White circle in the middle. Act's as a "back"
174
+ g . append ( 'circle' )
175
+ . datum ( selectedNode . parent )
176
+ . attr ( 'r' , radius )
177
+ . attr ( 'class' , 'fill-none' )
178
+ . attr ( 'fill' , 'none' )
179
+ . attr ( 'pointer-events' , 'all' )
180
+ . attr ( 'cursor' , ( d ) => ( d ? 'pointer' : 'default' ) )
181
+ . on ( 'click' , clickedFolder )
182
+ . on ( 'mouseover' , hoveredRoot )
183
+
184
+ g . append ( 'text' )
185
+ . datum ( selectedNode . parent )
186
+ . text ( '..' )
187
+ // if the parent exists (i.e. not root), show the text
188
+ . attr ( 'fill-opacity' , ( d ) => ( d ? 1 : 0 ) )
189
+ . attr ( 'text-anchor' , 'middle' )
190
+ . attr ( 'class' , 'text-7xl fill-ds-gray-quinary select-none' )
191
+ . attr ( 'cursor' , 'pointer' )
192
+ . on ( 'click' , clickedFolder )
193
+ . on ( 'mouseover' , hoveredRoot )
194
+
195
+ function clickedFolder ( _event , node ) {
196
+ reactClickCallback ( { target : node , type : 'folder' } )
197
+ changeLocation ( node )
239
198
}
240
- }
241
199
242
- const changeLocation = Sentry . startSpan (
243
- {
244
- name : 'SunburstChart.handleArcsUpdate' ,
245
- parentSpan : renderSunburstSpan ,
246
- } ,
247
- ( node ) => {
248
- // Because you can move two layers at a time previous !== parent
249
- previous = node
250
-
251
- if ( node ) {
252
- // Update the selected node
253
- setSelectedNode (
254
- node . each ( ( d ) => {
255
- // determine x0 and y0
256
- const x0Min = Math . min (
257
- 1 ,
258
- ( d . x0 - node . x0 ) / ( node . x1 - node . x0 )
259
- )
260
- const x0 = Math . max ( 0 , x0Min ) * 2 * Math . PI
261
- const y0 = Math . max ( 0 , d . y0 - node . depth )
200
+ function hoveredRoot ( _event , node ) {
201
+ if ( previous ) {
202
+ reactHoverCallback ( { target : previous , type : 'folder' } )
203
+ return
204
+ }
205
+ reactHoverCallback ( { target : node , type : 'folder' } )
206
+ }
262
207
263
- // determine x1 and y1
264
- const x1Min = Math . min (
265
- 1 ,
266
- ( d . x1 - node . x0 ) / ( node . x1 - node . x0 )
267
- )
268
- const x1 = Math . max ( 0 , x1Min ) * 2 * Math . PI
269
- const y1 = Math . max ( 0 , d . y1 - node . depth )
208
+ function reactClickCallback ( { target, type } ) {
209
+ if ( target ?. ancestors ) {
210
+ // Create a string from the root data down to the current item
211
+ const filePath = target
212
+ . ancestors ( )
213
+ . map ( ( d ) => d . data . name )
214
+ . slice ( 0 , - 1 )
215
+ . reverse ( )
216
+ . join ( '/' )
217
+
218
+ // callback to parent component with a path, the data node, and raw d3 data
219
+ // (just in case we need it for the second iteration to listen to location changes and direct to the correct folder.)
220
+ clickHandler . current ( {
221
+ path : filePath ,
222
+ data : target . data ,
223
+ target,
224
+ type,
225
+ } )
226
+ }
227
+ }
270
228
271
- // update the cords for the node
272
- d . current = { x0, y0, x1, y1 }
273
- } )
274
- )
229
+ function reactHoverCallback ( { target, type } ) {
230
+ if ( target ?. ancestors ) {
231
+ // Create a string from the root data down to the current item
232
+ const filePath = target
233
+ . ancestors ( )
234
+ . map ( ( d ) => d . data . name )
235
+ . slice ( 0 , - 1 )
236
+ . reverse ( )
237
+ . join ( '/' )
238
+
239
+ // callback to parent component with a path, the data node, and raw d3 data
240
+ // (just in case we need it for the second iteration to listen to location changes and direct to the correct folder.)
241
+ hoverHandler . current ( {
242
+ path : filePath ,
243
+ data : target . data ,
244
+ target,
245
+ type,
246
+ } )
275
247
}
276
248
}
277
- )
278
- }
279
- )
249
+
250
+ function changeLocation ( node ) {
251
+ Sentry . startSpan (
252
+ {
253
+ name : 'SunburstChart.handleArcsUpdate' ,
254
+ parentSpan : renderSunburstSpan ,
255
+ } ,
256
+ ( ) => {
257
+ // Because you can move two layers at a time previous !== parent
258
+ previous = node
259
+
260
+ if ( node ) {
261
+ // Update the selected node
262
+ setSelectedNode (
263
+ node . each ( ( d ) => {
264
+ // determine x0 and y0
265
+ const x0Min = Math . min (
266
+ 1 ,
267
+ ( d . x0 - node . x0 ) / ( node . x1 - node . x0 )
268
+ )
269
+ const x0 = Math . max ( 0 , x0Min ) * 2 * Math . PI
270
+ const y0 = Math . max ( 0 , d . y0 - node . depth )
271
+
272
+ // determine x1 and y1
273
+ const x1Min = Math . min (
274
+ 1 ,
275
+ ( d . x1 - node . x0 ) / ( node . x1 - node . x0 )
276
+ )
277
+ const x1 = Math . max ( 0 , x1Min ) * 2 * Math . PI
278
+ const y1 = Math . max ( 0 , d . y1 - node . depth )
279
+
280
+ // update the cords for the node
281
+ d . current = { x0, y0, x1, y1 }
282
+ } )
283
+ )
284
+ }
285
+ }
286
+ )
287
+ }
288
+ }
289
+ )
290
+ }
280
291
281
292
renderSunburst ( )
282
293
0 commit comments