Skip to content

Commit 5ddb32b

Browse files
authored
Merge pull request #8 from kevinfey/chart
Improved Zoom in Map.tsx and Chart.tsx
2 parents 29b29b7 + 249584c commit 5ddb32b

File tree

2 files changed

+176
-129
lines changed

2 files changed

+176
-129
lines changed

src/app/components/Chart.tsx

Lines changed: 142 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,37 @@
1-
/* eslint-disable @typescript-eslint/no-explicit-any */
2-
/* eslint-disable max-len */
3-
/* eslint-disable @typescript-eslint/ban-types */
4-
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
5-
/* eslint-disable eqeqeq */
6-
/* eslint-disable react/prop-types */
7-
/* eslint-disable no-mixed-operators */
8-
/* eslint-disable prefer-template */
9-
/* eslint-disable no-return-assign */
10-
/* eslint-disable prefer-arrow-callback */
11-
/* eslint-disable func-names */
12-
/* eslint-disable no-underscore-dangle */
13-
/* eslint-disable no-param-reassign */
14-
/* eslint-disable no-use-before-define */
15-
/* eslint-disable react/no-string-refs */
16-
171
import React, { Component } from 'react';
182
import * as d3 from 'd3';
193

204
/**
215
* @var colors: Colors array for the diffrerent node branches, each color is for a different branch
226
*/
23-
const colors = ['#95B6B7', '#475485', '#519331', '#AA5039', '#8B2F5F', '#C5B738', '#858DFF', '#FF8D02', '#FFCD51', '#ACDAE6', '#FC997E', '#CF93AD', '#AA3939', '#AA6C39', '#226666', '#2C4870'];
7+
const colors = [
8+
'#95B6B7',
9+
'#475485',
10+
'#519331',
11+
'#AA5039',
12+
'#8B2F5F',
13+
'#C5B738',
14+
'#858DFF',
15+
'#FF8D02',
16+
'#FFCD51',
17+
'#ACDAE6',
18+
'#FC997E',
19+
'#CF93AD',
20+
'#AA3939',
21+
'#AA6C39',
22+
'#226666',
23+
'#2C4870',
24+
];
2425

25-
const filter = (data:any[]) => {
26+
const filterHooks = (data: any[]) => {
2627
if (data[0].children && data[0].state === 'stateless') {
27-
return filter(data[0].children);
28+
return filterHooks(data[0].children);
2829
}
2930
return JSON.stringify(data[0].state);
3031
};
3132

33+
//const filterRecoil = (data: any[]) => {};
34+
3235
interface ChartProps {
3336
hierarchy: Record<string, unknown>;
3437
}
@@ -38,15 +41,18 @@ class Chart extends Component {
3841
/**
3942
* @method maked3Tree :Creates a new D3 Tree
4043
*/
41-
constructor(props:ChartProps) {
44+
constructor(props: ChartProps) {
4245
super(props);
46+
// what React.createRef() is doing.
4347
this.chartRef = React.createRef();
4448
this.maked3Tree = this.maked3Tree.bind(this);
4549
this.removed3Tree = this.removed3Tree.bind(this);
50+
this.isRecoil = false;
4651
}
4752

4853
componentDidMount() {
4954
const { hierarchy } = this.props;
55+
console.log('HIERARCHYYYYY', hierarchy);
5056
root = JSON.parse(JSON.stringify(hierarchy));
5157
this.maked3Tree();
5258
}
@@ -78,12 +84,14 @@ class Chart extends Component {
7884
// };
7985
const width = 600; // - margin.right - margin.left;
8086
const height = 600; // 700 - margin.top - margin.bottom;
81-
const chartContainer = d3.select(this.chartRef.current)
82-
.append('svg') // chartContainer is now pointing to svg
87+
const svgContainer = d3
88+
.select(this.chartRef.current)
89+
.append('svg') // svgContainer is now pointing to svg
8390
.attr('width', width)
8491
.attr('height', height);
8592

86-
const g = chartContainer.append('g')
93+
const g = svgContainer
94+
.append('g')
8795
// this is changing where the graph is located physically
8896
.attr('transform', `translate(${width / 2 + 4}, ${height / 2 + 2})`);
8997

@@ -96,12 +104,15 @@ class Chart extends Component {
96104
// (our object titled dataset), which must be an object representing the root node
97105
const hierarchy = d3.hierarchy(root);
98106

99-
const tree = d3.tree()
107+
const tree = d3
108+
.tree()
100109
// this assigns width of tree to be 2pi
101110
// .size([2 * Math.PI, radius / 1.3])
102111
.nodeSize([width / 10, height / 10])
103112
// .separation(function (a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; });
104-
.separation(function (a:{parent:object}, b:{parent:object}) { return (a.parent == b.parent ? 2 : 2); });
113+
.separation(function (a: { parent: object }, b: { parent: object }) {
114+
return a.parent == b.parent ? 2 : 2;
115+
});
105116

106117
const d3root = tree(hierarchy);
107118

@@ -114,16 +125,30 @@ class Chart extends Component {
114125
.enter()
115126
.append('path')
116127
.attr('class', 'link')
117-
.attr('d', d3.linkRadial()
118-
.angle((d:{x:number}) => d.x)
119-
.radius((d:{y:number}) => d.y));
128+
.attr(
129+
'd',
130+
d3
131+
.linkRadial()
132+
.angle((d: { x: number }) => d.x)
133+
.radius((d: { y: number }) => d.y)
134+
);
120135

121-
const node = g.selectAll('.node')
136+
const node = g
137+
.selectAll('.node')
122138
// root.descendants gets an array of of all nodes
123139
.data(d3root.descendants())
124140
.enter()
125141
.append('g')
126-
.style('fill', function (d:{data:{branch:number}}) {
142+
.style('fill', function (d) {
143+
let loadTime =
144+
d.data.stateSnapshot.children[0].componentData.actualDuration;
145+
146+
if (loadTime !== undefined) {
147+
if (loadTime > 16) {
148+
return '#ff0000';
149+
}
150+
}
151+
127152
if (d.data.branch < colors.length) {
128153
return colors[d.data.branch];
129154
}
@@ -134,96 +159,134 @@ class Chart extends Component {
134159
return colors[indexColors];
135160
})
136161
.attr('class', 'node--internal')
137-
// })
138-
// assigning class to the node based on whether node has children or not
139-
// .attr('class', function (d) {
140-
// return 'node' + (d.children ? ' node--internal' : ' node--leaf');
141-
// })
142-
.attr('transform', function (d:{x:number, y:number}) {
162+
.attr('transform', function (d: { x: number; y: number }) {
143163
return 'translate(' + reinfeldTidierAlgo(d.x, d.y) + ')';
144164
});
145165

146-
node.append('circle')
166+
node
167+
.append('circle')
147168
.attr('r', 15)
148-
.on('mouseover', function (d:any) {
149-
d3.select(this)
150-
.transition(100)
151-
.duration(20)
152-
.attr('r', 25);
153-
154-
tooltipDiv.transition()
155-
.duration(50)
156-
.style('opacity', 0.9);
157-
158-
tooltipDiv.html(filter(d.data.stateSnapshot.children), this)
159-
.style('left', (d3.event.pageX - 90) + 'px')
160-
.style('top', (d3.event.pageY - 65) + 'px');
169+
.on('mouseover', function (d: any) {
170+
d3.select(this).transition(100).duration(20).attr('r', 25);
171+
172+
tooltipDiv.transition().duration(50).style('opacity', 0.9);
173+
174+
if (d.data.stateSnapshot.children[0].name === 'RecoilRoot') {
175+
console.log('enter');
176+
this.isRecoil = true;
177+
}
178+
console.log('isRecoil', this.isRecoil);
179+
180+
console.log('d.data', d.data);
181+
console.log('d.data.stateSnapshot', d.data.stateSnapshot);
182+
console.log(
183+
'd.data.stateSnapshot.children',
184+
d.data.stateSnapshot.children
185+
);
186+
if (!this.isRecoil) {
187+
tooltipDiv
188+
.html(filterHooks(d.data.stateSnapshot.children), this)
189+
.style('left', d3.event.pageX - 90 + 'px')
190+
.style('top', d3.event.pageY - 65 + 'px');
191+
} else {
192+
tooltipDiv
193+
.html(
194+
'Load Time : ' +
195+
JSON.stringify(
196+
d.data.stateSnapshot.children[0].componentData.actualDuration
197+
).substring(0, 5) +
198+
' ms',
199+
this
200+
)
201+
.style('left', d3.event.pageX - 90 + 'px')
202+
.style('top', d3.event.pageY - 65 + 'px');
203+
}
161204
})
162205
// eslint-disable-next-line no-unused-vars
163206
// eslint-disable-next-line @typescript-eslint/no-unused-vars
164-
.on('mouseout', function (d:any) {
165-
d3.select(this)
166-
.transition()
167-
.duration(300)
168-
.attr('r', 15);
169-
170-
tooltipDiv.transition()
171-
.duration(400)
172-
.style('opacity', 0);
207+
.on('mouseout', function (d: any) {
208+
d3.select(this).transition().duration(300).attr('r', 15);
209+
210+
tooltipDiv.transition().duration(400).style('opacity', 0);
173211
});
174212
node
175213
.append('text')
176214
// adjusts the y coordinates for the node text
177215
.attr('dy', '0.5em')
178-
.attr('x', function (d:{x:number; children?:[]}) {
216+
.attr('x', function (d: { x: number; children?: [] }) {
179217
// this positions how far the text is from leaf nodes (ones without children)
180218
// negative number before the colon moves the text of rightside nodes,
181219
// positive number moves the text for the leftside nodes
182220
return d.x < Math.PI === !d.children ? 0 : 0;
183221
})
184222
.attr('text-anchor', 'middle')
185223
// this arranges the angle of the text
186-
.attr('transform', function (d:{x:number, y:number}) { return 'rotate(' + (d.x < Math.PI ? d.x - Math.PI / 2 : d.x + Math.PI / 2) * 1 / Math.PI + ')'; })
187-
.text(function (d:{data:{name:number, branch:number}}) {
224+
.attr('transform', function (d: { x: number; y: number }) {
225+
return (
226+
'rotate(' +
227+
((d.x < Math.PI ? d.x - Math.PI / 2 : d.x + Math.PI / 2) * 1) /
228+
Math.PI +
229+
')'
230+
);
231+
})
232+
.text(function (d: { data: { name: number; branch: number } }) {
188233
// display the name of the specific patch
189234
return `${d.data.name}.${d.data.branch}`;
190235
});
191236

192237
// allows svg to be dragged around
193-
node.call(d3.drag()
194-
.on('start', dragstarted)
195-
.on('drag', dragged)
196-
.on('end', dragended));
238+
node.call(
239+
d3
240+
.drag()
241+
.on('start', dragstarted)
242+
.on('drag', dragged)
243+
.on('end', dragended)
244+
);
197245

198-
chartContainer.call(d3.zoom()
199-
.extent([[0, 0], [width, height]])
200-
.scaleExtent([0, 8]) // scaleExtent([minimum scale factor, maximum scale factor])
201-
.on('zoom', zoomed));
246+
// d3 zoom functionality
247+
let zoom = d3.zoom().on('zoom', zoomed);
248+
svgContainer.call(
249+
zoom.transform,
250+
// Changes the initial view, (left, top)
251+
d3.zoomIdentity.translate(width/2, height/2).scale(1)
252+
);
253+
// allows the canvas to be zoom-able
254+
svgContainer.call(
255+
d3
256+
.zoom()
257+
.scaleExtent([0, 0.9]) // [zoomOut, zoomIn]
258+
.on('zoom', zoomed)
259+
);
260+
// helper function that allows for zooming
261+
function zoomed(d: any) {
262+
g.attr('transform', d3.event.transform);
263+
}
202264

265+
// Drag
203266
function dragstarted() {
204267
d3.select(this).raise();
205268
g.attr('cursor', 'grabbing');
206269
}
207270

208-
function dragged(d:{x:number, y:number}) {
209-
d3.select(this).attr('dx', d.x = d3.event.x).attr('dy', d.y = d3.event.y);
271+
function dragged(d: { x: number; y: number }) {
272+
d3.select(this)
273+
.attr('dx', (d.x = d3.event.x))
274+
.attr('dy', (d.y = d3.event.y));
210275
}
211276

212277
function dragended() {
213278
g.attr('cursor', 'grab');
214279
}
215280

216-
function zoomed() {
217-
g.attr('transform', d3.event.transform);
218-
}
219-
220281
// define the div for the tooltip
221-
const tooltipDiv = d3.select('body').append('div')
282+
const tooltipDiv = d3
283+
.select('body')
284+
.append('div')
222285
.attr('class', 'tooltip')
223286
.style('opacity', 0);
224287

225-
function reinfeldTidierAlgo(x:number, y:number) {
226-
return [(y = +y) * Math.cos(x -= Math.PI / 2), y * Math.sin(x)];
288+
function reinfeldTidierAlgo(x: number, y: number) {
289+
return [(y = +y) * Math.cos((x -= Math.PI / 2)), y * Math.sin(x)];
227290
}
228291
}
229292

0 commit comments

Comments
 (0)