Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 13 additions & 87 deletions src/webview/Flow.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,44 @@
import React, { useCallback, useEffect, useRef } from "react";
import React, { useEffect, useRef } from "react";
import ReactFlow, {
addEdge,
MiniMap,
Panel,
Controls,
Background,
useNodesState,
useEdgesState,
ReactFlowInstance,
Node,
Edge
} from "reactflow";
import "reactflow/dist/style.css";
import { ConnectionLineType } from "../types/connection";
import FlowBuilder from './flowBuilder';
import * as d3 from 'd3';
import FlowBuilder from "./flowBuilder";
import { Tree } from "../types/tree";
import { hierarchyData } from "../types/hierarchyData";
import { getNonce } from "../getNonce";
import { FlowNode } from "typescript";

const onInit = (reactFlowInstance: ReactFlowInstance) =>
console.log("flow loaded:", reactFlowInstance);



const OverviewFlow = () => {
const reactFlowWrapper = useRef<HTMLDivElement>(null);

const initialNodes : Node[] = [];
const initialEdges : Edge[] = [];

const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);

const onConnect = useCallback(
(params) => setEdges((eds) => addEdge({ ...params, type: ConnectionLineType.Bezier, animated: true }, eds)),
[]
);


useEffect(() => {
window.addEventListener('message', (e: MessageEvent) => {
// object containing type prop and value prop
const msg : MessageEvent = e;
console.log(e)

// Object containing type prop and value prop
const msg : MessageEvent = e;
const flowBuilder = new FlowBuilder

switch (msg.data.type) {
case 'parsed-data': {
let data : Tree | undefined = msg.data.value;
mappedData(data)

// Creates our Tree structure
flowBuilder.mappedData(data, initialNodes, initialEdges)

setEdges(initialEdges);
setNodes(initialNodes)
break;
Expand All @@ -56,70 +48,6 @@ const OverviewFlow = () => {
}, []);


// Function that creates Tree Structure
function mappedData (data: Tree) : void {

// Create a holder for the heirarchical data (msg.value), data comes in an object of all the Trees
const root : d3.HierarchyNode<Tree> = d3.hierarchy(data)

// Dynamically adjust height and width of display depending on the amount of nodes
const totalNodes : number = root.descendants().length;
const width : number = Math.max(totalNodes * 100, 800);
const height = Math.max(totalNodes * 20, 500)


//create tree layout and give nodes their positions and
const treeLayout : d3.TreeLayout<unknown> = d3.tree()
.size([ width, height ])
.separation((a: d3.HierarchyPointNode<Node>, b: d3.HierarchyPointNode<Node>) => (a.parent == b.parent ? 2 : 2.5))


treeLayout(root);
// Iterate through each Tree and create a node
root.each((node: any) : void => {
console.log('Node', node)
// Create a Node from the current Root and add it to our nodes array
initialNodes.push({
id: node.data.id,
position: { x: node.x ? node.x : 0, y: node.y ? node.y : 0 },
type: 'default',
data: { label: node.data.name },
style: {
borderRadius: '6px',
borderWidth: '2px',
borderColor: '#6b7280',
display: 'flex',
justifyContent: 'center',
placeItems: 'center',
backgroundColor: `${(node.data.isClientComponent) ? '#fdba74' : '#93C5FD'}`,
}
});

// If the current node has a parent, create an edge to show relationship
if (node.data.parent) {
const newEdge : Edge = {
id: `${getNonce()}`,
source: node.data.parent,
target: node.data.id,
type: ConnectionLineType.Bezier,
animated: true,
};


// Check if the edge already exists before adding
const edgeExists : boolean = initialEdges.some(
edge => edge.source === newEdge.source && edge.target === newEdge.target
);

// If edge does not exist, add to our edges array
if (!edgeExists) {
initialEdges.push(newEdge)
}
}
}
)

}

return (
<div style={{ height: '600px', width: '100%' }}>
Expand All @@ -128,8 +56,6 @@ const OverviewFlow = () => {
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
onInit={onInit}
fitView
attributionPosition="top-right"
style={{ width: '100%', height: '100%' }}
Expand Down
180 changes: 50 additions & 130 deletions src/webview/flowBuilder.tsx
Original file line number Diff line number Diff line change
@@ -1,154 +1,74 @@
import React from 'react';
// will create a build func and then call the helper funcs to return an object
// make a new instance of this class in flow, call the build method, and pass this as state
import { ConnectionLineType, Edge, Node } from 'reactflow';
import { Tree } from '../types/tree';
import { getNonce } from '../getNonce';
import * as d3 from 'd3'

interface Node {
id: string;
data: {
label: React.ReactNode;
};
type: string;
position: { x: number, y: number };
style: {
borderRadius: string;
borderWidth: string;
borderColor: string;
display: string;
justifyContent: string;
placeItems: string;
backgroundColor: string;
};
}
// Contructs our family tree for React application root file that was selected

interface Edge {
id: string;
source: string;
target: string;
type: string;
animated: boolean;
}
class FlowBuilder {

interface ParsedDataItem {
fileName: string;
isClientComponent: boolean;
children?: ParsedDataItem[];
thirdParty?: boolean;
reactRouter?: boolean;
}
public mappedData (data: Tree, nodes: Node[], edges: Edge[]) : void {

interface Settings {
thirdParty: boolean;
reactRouter: boolean;
}
// Create a holder for the heirarchical data (msg.value), data comes in an object of all the Trees
const root : d3.HierarchyNode<Tree> = d3.hierarchy(data)

class FlowBuilder {
private parsedData: ParsedDataItem[];
private viewData: ParsedDataItem[];
private id: number;
private x: number;
private y: number;
private edgeId: number;
public initialEdges: Edge[];
public initialNodes: Node[];
// Dynamically adjust height and width of display depending on the amount of nodes
const totalNodes : number = root.descendants().length;
const width : number = Math.max(totalNodes * 100, 800);
const height = Math.max(totalNodes * 20, 500)


constructor(data: ParsedDataItem) {
this.parsedData = [data];
this.id = 0;
this.x = 0;
this.y = 0;
this.initialNodes = [];
this.initialEdges = [];
this.viewData = [];
this.edgeId = 0;
}
//create tree layout and give nodes their positions and
const treeLayout : d3.TreeLayout<unknown> = d3.tree()
.size([ width, height ])
.separation((a: d3.HierarchyPointNode<Node>, b: d3.HierarchyPointNode<Node>) => (a.parent == b.parent ? 2 : 2.5))

private buildNodesArray(parsedData: ParsedDataItem[] | undefined, x: number = this.x, y: number = this.y): void {
if (!parsedData) return;

parsedData.forEach((item) => {
const node: Node = {
id: (++this.id).toString(),
data: {
label: (
<div className="text-sm font-medium text-ellipsis overflow-hidden ..." key={this.id}>{item.fileName}</div>
)
},
// type: item.depth === 0 ? 'input' : '',
treeLayout(root);
// Iterate through each Tree and create a node
root.each((node: any) : void => {

// Create a Node from the current Root and add it to our nodes array
nodes.push({
id: node.data.id,
position: { x: node.x ? node.x : 0, y: node.y ? node.y : 0 },
type: 'default',
position: { x: (x += 40), y: (y += 30) },
data: { label: node.data.name },
style: {
borderRadius: '6px',
borderWidth: '2px',
borderColor: '#6b7280',
display: 'flex',
justifyContent: 'center',
placeItems: 'center',
backgroundColor: `${(item.isClientComponent) ? '#fdba74' : '#93C5FD'}`,
},
};
this.initialNodes.push(node);
if (item.children) {
this.buildNodesArray(item.children, (this.x += 40), (this.y += 30));
}
});
};

private buildEdgesArray(parsedData: ParsedDataItem[] | undefined, parentID?: number): void {
if (!parsedData) return;

parsedData.forEach((item) => {
const nodeID = ++this.edgeId;
if (parentID) {
const edge: Edge = {
id: `e${parentID}-${nodeID}`,
source: parentID.toString(),
target: nodeID.toString(),
type: 'bezier',
animated: false,
backgroundColor: `${(node.data.isClientComponent) ? '#fdba74' : '#93C5FD'}`,
}
});

// If the current node has a parent, create an edge to show relationship
if (node.data.parent) {
const newEdge : Edge = {
id: `${getNonce()}`,
source: node.data.parent,
target: node.data.id,
type: ConnectionLineType.Bezier,
animated: true,
};
this.initialEdges.push(edge);
}
if (item.children) {
this.buildEdgesArray(item.children, nodeID);
}
});
}

public build(settings: Settings): void {
const treeParsed = JSON.parse(JSON.stringify(this.parsedData[0]));
// console.log('settings: ', settings);
const traverse = (node: ParsedDataItem): void => {
let validChildren: ParsedDataItem[] = [];

for (let i = 0; i < node.children?.length; i++) {
if (
node.children[i].thirdParty &&
settings.thirdParty &&
!node.children[i].reactRouter
) {
validChildren.push(node.children[i]);
} else if (node.children[i].reactRouter && settings.reactRouter) {
validChildren.push(node.children[i]);
} else if (
!node.children[i].thirdParty &&
!node.children[i].reactRouter
) {
validChildren.push(node.children[i]);

// Check if the edge already exists before adding
const edgeExists : boolean = edges.some(
edge => edge.source === newEdge.source && edge.target === newEdge.target
);

// If edge does not exist, add to our edges array
if (!edgeExists) {
edges.push(newEdge)
}
}

// Update children with only valid nodes, and recurse through each node
node.children = validChildren;
node.children.forEach((child) => {
traverse(child);
});
}
traverse(treeParsed);
// Update the viewData state
this.viewData = ([treeParsed]);
console.log('viewData:', this.viewData);
this.buildNodesArray(this.viewData);
this.buildEdgesArray(this.viewData);
)

}
}

Expand Down