Skip to content

Commit 74ccc0c

Browse files
okay this actually resolves the re-renders
1 parent b7c18de commit 74ccc0c

File tree

1 file changed

+186
-175
lines changed

1 file changed

+186
-175
lines changed

src/ui/SunburstChart/SunburstChart.jsx

+186-175
Original file line numberDiff line numberDiff line change
@@ -67,25 +67,29 @@ function SunburstChart({
6767
const radius = width / 6
6868

6969
// 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))
7981
)
82+
}
8083
// A color function you can pass a number from 0-100 to and get a color back from the specified color range
8184
// 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 }, () =>
8487
scaleSequential()
8588
.domain([colorDomainMin, colorDomainMax])
8689
.interpolator(colorRange)
8790
.clamp(true)
8891
)
92+
}
8993

9094
// Tracks previous location for rendering .. in the breadcrumb.
9195
let previous
@@ -98,185 +102,192 @@ function SunburstChart({
98102
.append('g')
99103
.attr('transform', `translate(${width / 2},${width / 2})`)
100104

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+
})
191146

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+
})
199158

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
204163
.ancestors()
205164
.map((d) => d.data.name)
206-
.slice(0, -1)
207165
.reverse()
208166
.join('/')
209167

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`
218169
}
219-
}
220170

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)
239198
}
240-
}
241199

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+
}
262207

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+
}
270228

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+
})
275247
}
276248
}
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+
}
280291

281292
renderSunburst()
282293

0 commit comments

Comments
 (0)