Skip to content

Commit 73cc432

Browse files
committed
merged files
2 parents 07f4f35 + 7561c47 commit 73cc432

File tree

13 files changed

+558
-165
lines changed

13 files changed

+558
-165
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"scripts": {
1616
"build": "webpack --mode production",
1717
"dev": "webpack --mode development --watch",
18-
"test": "jest --verbose --coverage --watchAll --forceExit",
18+
"test": "jest --verbose --coverage --watchAll --runInBand --detectOpenHandles --forceExit",
1919
"docker-test-lint": "eslint --ext .js --ext .jsx src",
2020
"docs": "typedoc --json docs --inputFiles src/app --inputFiles src/backend --readme docs/readme.md"
2121
},

src/app/components/Map.tsx

Lines changed: 223 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,68 +4,239 @@
44
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
55
/* eslint-disable @typescript-eslint/ban-types */
66

7-
import React, { useState, useEffect } from 'react';
7+
import React, { useEffect, useCallback, useState } from 'react';
88
import * as d3 from 'd3';
99

1010
const Map = (props) => {
11-
const { snapshots } = props;
12-
const lastSnap = snapshots.length - 1;
11+
//import props
12+
const { viewIndex, snapshots ,x ,y, k, setZoomState} = props;
13+
let lastSnap: number | null = null;
14+
if (viewIndex < 0) lastSnap = snapshots.length - 1;
15+
else lastSnap = viewIndex;
1316

14-
// set the heights and width of the tree to be passed into treeMap function
17+
//external constants
1518
const width: number = 900;
1619
const height: number = 600;
20+
let data = snapshots[lastSnap];
1721

18-
// this state allows the canvas to stay at the zoom level on multiple re-renders
19-
const [{ x, y, k }, setZoomState]: any = useState({ x: 0, y: 0, k: 0 });
2022
useEffect(() => {
23+
document.getElementById('canvas').innerHTML = '_';
2124
setZoomState(d3.zoomTransform(d3.select('#canvas').node()));
22-
}, [snapshots[lastSnap]]);
25+
return makeChart(data);
26+
}, [data]);
2327

24-
// Create D3 Tree Diagram
25-
useEffect(() => {
26-
document.getElementById('canvas').innerHTML = '';
28+
const makeChart = useCallback(
29+
(data) => {
30+
// Establish Constants
31+
const margin = { top: 10, right: 120, bottom: 10, left: 120 };
32+
const dy = 120;
33+
const dx = 100;
34+
const tree = d3.tree().nodeSize([dx, dy]);
35+
const diagonal = d3
36+
.linkHorizontal()
37+
.x((d) => d.y)
38+
.y((d) => d.x);
39+
const root = d3.hierarchy(data);
2740

28-
// creating the main svg container for d3 elements
29-
const svgContainer: any = d3
30-
.select('#canvas')
31-
.attr('width', width)
32-
.attr('height', height);
41+
// Determine descendants of root node use d.depth conditional to how many levels deep to display on first render
42+
root.x0 = dy / 2;
43+
root.y0 = 0;
44+
root.descendants().forEach((d, i) => {
45+
d.id = i;
46+
d._children = d.children;
47+
// use to limit depth of children rendered
48+
//if (d.depth === 5) d.children = null;
49+
});
3350

34-
// creating a pseudo-class for reusability
35-
const g: any = svgContainer
36-
.append('g')
37-
.attr('transform', `translate(${x}, ${y}), scale(${k})`); // sets the canvas to the saved zoomState
51+
// Create Container for D3 Visualizations
52+
const svgContainer = d3
53+
.select('#canvas')
54+
.attr('width', width)
55+
.attr('height', height);
3856

39-
// creating the tree map
40-
const treeMap: any = d3.tree().nodeSize([width, height]);
57+
// create inner container to help with drag and zoom
58+
const svg: any = svgContainer
59+
.append('g')
60+
.attr('transform', `translate(${x}, ${y}), scale(${k})`); // sets the canvas to the saved zoomState
4161

42-
// creating the nodes of the tree
43-
const hierarchyNodes: any = d3.hierarchy(snapshots[lastSnap]);
62+
// create links
63+
const gLink = svg
64+
.append('g')
65+
.attr('fill', 'none')
66+
.attr('stroke', '#555')
67+
.attr('stroke-opacity', 0.9)
68+
.attr('stroke-width', 1.5);
4469

45-
// calling the tree function with nodes created from data
46-
const finalMap: any = treeMap(hierarchyNodes);
70+
// create nodes
71+
const gNode = svg
72+
.append('g')
73+
.attr('cursor', 'pointer')
74+
.attr('pointer-events', 'all');
4775

48-
// renders a flat array of objects containing all parent-child links
49-
// renders the paths onto the component
50-
let paths: any = finalMap.links();
76+
// declare re render funciton to handle collapse and expansion of nodes
77+
const update = (source) => {
78+
const duration = 0;
79+
const nodes = root.descendants().reverse();
80+
const links = root.links();
5181

52-
// this creates the paths to each node and its contents in the tree
53-
g.append('g')
54-
.attr('fill', 'none')
55-
.attr('stroke', '#646464')
56-
.attr('stroke-width', 5)
57-
.selectAll('path')
58-
.data(paths)
59-
.enter()
60-
.append('path')
61-
.attr(
62-
'd',
82+
// Compute the new tree layout.
83+
tree(root);
84+
let left = root;
85+
let right = root;
86+
root.eachBefore((node) => {
87+
if (node.x < left.x) left = node;
88+
if (node.x > right.x) right = node;
89+
});
90+
91+
//use nodes to detrmine height
92+
const height = right.x - left.x + margin.top + margin.bottom;
93+
94+
const transition = svg
95+
.transition()
96+
.duration(duration)
97+
.attr('viewBox', [-margin.left, left.x - margin.top, width, height]);
98+
// Update the nodes…
99+
const node = gNode.selectAll('g').data(nodes, (d) => d.id);
100+
101+
// Enter any new nodes at the parent's previous position.
102+
const nodeEnter = node
103+
.enter()
104+
.append('g')
105+
.attr('transform', (d) => `translate(${source.y0},${source.x0})`)
106+
.attr('fill-opacity', 0)
107+
.attr('stroke-opacity', 1)
108+
.on('click', (d) => {
109+
d.children = d.children ? null : d._children;
110+
update(d);
111+
});
112+
113+
// paint circles, color based on children
114+
nodeEnter
115+
.append('circle')
116+
.attr('r', 10)
117+
.attr('fill', (d) => (d._children ? '#46edf2' : '#95B6B7'))
118+
.attr('stroke-width', 10)
119+
.attr('stroke-opacity', 1);
120+
121+
// append node names
122+
nodeEnter
123+
.append('text')
124+
.attr('dy', '.31em')
125+
.attr('x', '-10')
126+
.attr('y', '-5')
127+
.attr('text-anchor', 'end')
128+
.text((d: any) => d.data.name.slice(0, 14)) // Limits Characters in Display
129+
.style('font-size', `.6rem`)
130+
.style('fill', 'white')
131+
.clone(true)
132+
.lower()
133+
.attr('stroke-linejoin', 'round')
134+
.attr('stroke', '#646464')
135+
.attr('stroke-width', 1);
136+
137+
//TODO -> Alter incoming snapshots so there is useful data to show on hover.
138+
// nodeEnter.on('mouseover', function (d: any, i: number): any {
139+
// if (!d.children) {
140+
// d3.select(this)
141+
// .append('text')
142+
// .text(()=>{
143+
// return JSON.stringify(d.data)})
144+
// .style('fill', 'white')
145+
// .attr('x',0)
146+
// .attr('y', 0)
147+
// .style('font-size', '.6rem')
148+
// .style('text-align', 'center')
149+
// .attr('stroke', '#646464')
150+
// .attr('id', `popup${i}`);
151+
// }
152+
// });
153+
// nodeEnter.on('mouseout', function (d: any, i: number): any {
154+
// d3.select(`#popup${i}`).remove();
155+
// });
156+
157+
// Transition nodes to their new position.
158+
const nodeUpdate = node
159+
.merge(nodeEnter)
160+
.transition(transition)
161+
.attr('transform', (d) => `translate(${d.y},${d.x})`)
162+
.attr('fill-opacity', 1)
163+
.attr('stroke-opacity', 1);
164+
165+
// Transition exiting nodes to the parent's new position.
166+
const nodeExit = node
167+
.exit()
168+
.transition(transition)
169+
.remove()
170+
.attr('transform', (d) => `translate(${source.y},${source.x})`)
171+
.attr('fill-opacity', 0)
172+
.attr('stroke-opacity', 0);
173+
174+
// Update the links…
175+
const link = gLink.selectAll('path').data(links, (d) => d.target.id);
176+
177+
// Enter any new links at the parent's previous position.
178+
const linkEnter = link
179+
.enter()
180+
.append('path')
181+
.attr('d', (d) => {
182+
const o = { x: source.x0, y: source.y0 };
183+
return diagonal({ source: o, target: o });
184+
});
185+
186+
// Transition links to their new position.
187+
link.merge(linkEnter).transition(transition).attr('d', diagonal);
188+
189+
// Transition exiting nodes to the parent's new position.
190+
link
191+
.exit()
192+
.transition(transition)
193+
.remove()
194+
.attr('d', (d) => {
195+
const o = { x: source.x, y: source.y };
196+
return diagonal({ source: o, target: o });
197+
});
198+
199+
// Stash the old positions for transition.
200+
root.eachBefore((d) => {
201+
d.x0 = d.x;
202+
d.y0 = d.y;
203+
});
204+
};
205+
206+
//______________ZOOM______________\\
207+
208+
// Sets starting zoom
209+
let zoom = d3.zoom().on('zoom', zoomed);
210+
211+
svgContainer.call(
212+
zoom.transform,
213+
// Changes the initial view, (left, top)
214+
d3.zoomIdentity.translate(x, y).scale(k)
215+
);
216+
217+
// allows the canvas to be zoom-able
218+
svgContainer.call(
63219
d3
64-
.linkHorizontal()
65-
.x((d: any) => d.y)
66-
.y((d: any) => d.x)
220+
.zoom()
221+
.scaleExtent([0.15, 1.5]) // [zoomOut, zoomIn]
222+
.on('zoom', zoomed)
223+
67224
);
225+
function zoomed(d: any) {
226+
svg.attr('transform', d3.event.transform)
227+
.on('mouseup', setZoomState(d3.zoomTransform(d3.select('#canvas').node()))
228+
);
229+
}
68230

231+
// allows the canvas to be draggable
232+
svg.call(
233+
d3
234+
.drag()
235+
236+
);
237+
238+
239+
<<<<<<< HEAD
69240
// returns a flat array of objects containing all the nodes and their information
70241
let nodes: any = hierarchyNodes.descendants();
71242

@@ -170,12 +341,19 @@ const Map = (props) => {
170341

171342

172343
});
344+
=======
345+
// call update on node click
346+
update(root);
347+
},
348+
[data]
349+
);
350+
>>>>>>> master
173351

174352
return (
175353
<div data-testid="canvas">
176-
<div className="Visualizer">
177-
<svg id="canvas"></svg>
178-
</div>
354+
<svg
355+
id="canvas"
356+
></svg>
179357
</div>
180358
);
181359
};

src/app/components/PerfView.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ const PerfView = (props: PerfViewProps) => {
4545
if (viewIndex < 0) indexToDisplay = snapshots.length - 1;
4646
else indexToDisplay = viewIndex;
4747

48-
console.log('SNAPSHOTS IN PERF VIEW', snapshots);
49-
console.log('VIEW INDEX', indexToDisplay);
48+
//console.log('SNAPSHOTS IN PERF VIEW', snapshots);
49+
//console.log('VIEW INDEX', indexToDisplay);
5050

5151
// Set up color scaling function
5252
const colorScale = d3

src/app/components/StateRoute.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,15 @@ interface StateRouteProps {
3939
const StateRoute = (props: StateRouteProps) => {
4040
const { snapshot, hierarchy, snapshots, viewIndex } = props;
4141
const [noRenderData, setNoRenderData] = useState(false);
42-
43-
//Test Map
42+
const [{ x, y, k }, setZoomState]: any = useState({
43+
x: 150,
44+
y: 250,
45+
k: 0.75,
46+
});
47+
//Map
4448
const renderMap = () => {
4549
if (hierarchy) {
46-
return <Map snapshots={snapshots} />;
50+
return <Map viewIndex={viewIndex} snapshots={snapshots} x={x} y={y} k={k} setZoomState={setZoomState} />;
4751
}
4852
return <div className="noState">{NO_STATE_MSG}</div>;
4953
};
@@ -67,12 +71,10 @@ const StateRoute = (props: StateRouteProps) => {
6771
}
6872
return <div className="noState">{NO_STATE_MSG}</div>;
6973
};
70-
74+
7175
let perfChart;
7276
if (true) {
73-
console.log('ViewINDex', viewIndex);
74-
console.log('snapshots', snapshots);
75-
console.log('setnorenderdata', setNoRenderData);
77+
7678
perfChart = (
7779
<PerfView
7880
viewIndex={viewIndex}

0 commit comments

Comments
 (0)