Skip to content
Draft
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
43 changes: 42 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
},
"dependencies": {
"d3": "^7.9.0",
"react-d3-tree": "^3.6.2"
"react-d3-tree": "^3.6.2",
"webcola": "^3.4.0"
}
}
34 changes: 32 additions & 2 deletions resources/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,37 @@
@tailwind components;
@tailwind utilities;

#treeWrapper {
width: '100%';
.treeWrapper {
height: '100vh';
flex: 1;
max-width: 70%;
}

.treeWrapper.sm {
max-width: 30%;
}

.row {
display: flex;
flex-wrap:wrap;
}

.node {
stroke: #fff;
stroke-width: 1.5px;
cursor: move;
}

.link {
stroke: #999;
stroke-width: 3px;
stroke-opacity: 1;
}

.label {
fill: white;
font-family: Verdana;
font-size: 25px;
text-anchor: middle;
cursor: move;
}
56 changes: 56 additions & 0 deletions resources/js/Components/Trees/DAG.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from "react";
import Tree from "react-d3-tree";
import { scaleOrdinal } from 'd3';

const containerStyles = {
width: '100%',
height: '100vh',
}

const colorScale = scaleOrdinal()
.domain(['Inquiry', 'Reflection', 'Response'])
.range(['yellow', 'blue', 'green']); // Adjust colors as needed

interface DAGProps {}

export default class DAG extends React.Component<DAGProps> {
state = {}

// TODO: Kenji - this breaks clicking on nodes to expand/collapse, and also renders improperly.
// renderNode = (nodeData) => {
// console.log(nodeData);
// const color = colorScale(nodeData.nodeDatum.type);
// return (
// <g>
// <circle r="20" fill={color} />
// <text x="25" y="25" fill="black">{nodeData.nodeDatum.attributes.content}</text>
// </g>
// );
// };


componentDidMount() {
const dimensions = this.treeContainer.getBoundingClientRect();
this.setState({
translate: {
x: dimensions.width / 3,
y: dimensions.height / 4 // TODO: Kenji, why does this need to be /4 for it to be centered?
}
});
}

render() {
return (
<div style={containerStyles} ref={tc => (this.treeContainer = tc)}>
<Tree
data={this.props.data}
translate={this.state.translate}
//renderCustomNodeElement={this.renderNode}
orientation={'vertical'}
separation={{ nonSiblings: 3, siblings: 3 }}
nodeSize={{ x: 160, y: 160 }}
/>
</div>
);
}
}
75 changes: 75 additions & 0 deletions resources/js/Components/Trees/WebCola.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React, { useRef, useEffect, useState } from 'react';
import * as cola from 'webcola';
import * as d3 from 'd3';

// Based on: https://github.com/tgdwyer/WebCola/blob/master/website/examples/smallnonoverlappinggraph.html
const Tree = ({ nodes, links }) => {
const svgRef = useRef(null);
const width = "100%";
const height = "100%";
const color = d3.scaleOrdinal(d3.schemeCategory10);
const colad3 = cola.d3adaptor(d3)
.linkDistance(120)
.avoidOverlaps(true);


useEffect(() => {
const svg = d3.select(svgRef.current); // Select SVG element
const containerWidth = svgRef.current.clientWidth;
const containerHeight = svgRef.current.clientHeight;

// Render nodes and links using colad3
colad3
.nodes(nodes)
.links(links)
.size([containerWidth, containerHeight])
.start();

var link = svg.selectAll(".link")
.data(links)
.enter().append("line")
.attr("class", "link");

var node = svg.selectAll(".node")
.data(nodes)
.enter().append("rect")
.attr("class", "node")
.attr("width", (d) => d.width)
.attr("height", (d) => d.height)
.attr("rx", 5).attr("ry", 5)
.style("fill", (d) => color(1))
.call(colad3.drag);

var label = svg.selectAll(".label")
.data(nodes)
.enter().append("text")
.attr("class", "label")
.text((d) => d.name)
.call(colad3.drag);

colad3.on('tick', () => {
link
.attr('x1', (d) => d.source.x)
.attr('y1', (d) => d.source.y)
.attr('x2', (d) => d.target.x)
.attr('y2', (d) => d.target.y);

node
.attr('x', (d) => d.x - d.width / 2)
.attr('y', (d) => d.y - d.height / 2);

label
.attr('x', (d) => d.x)
.attr('y', function(d) {
const h = this.getBBox().height;
return d.y + h / 4;
});
});
}, [nodes, links]);

return (
<svg ref={svgRef} height={height} width={width}></svg>
)
}

export default Tree;
47 changes: 39 additions & 8 deletions resources/js/Pages/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import { Head } from '@inertiajs/react';
import { PageProps } from '@/types';
import CenteredTree from '../Components/CenteredTree';
import DAG from '../Components/Trees/DAG';
import Tree from '../Components/Trees/WebCola';

// This is a simplified example of an org chart with a depth of 2.
// Note how deeper levels are defined recursively via the `children` property.
const sampleInquiryChart = {
const sampleInquiryChart = [{
name: 'Inquiry',
attributes: {
content: 'What is the colour of the sky?',
Expand All @@ -28,11 +29,36 @@ const sampleInquiryChart = {
{
name: 'Response',
attributes: {
content: 'The sky is clear.'
},
content: 'The sky is clear.',
}
},
],
};
]
}]

// const nodes = [
// { id: 0, name: 'Root', parent: null },
// { id: 1, name: 'Child 1', parent: 0 },
// { id: 2, name: 'Child 2', parent: 0 },
// // ... additional nodes
// ];
// const links = nodes.filter(d => d.parent !== null).map(d => ({ source: d.parent, target: d.id }));

const example = {
"nodes":[
{"name":"a","width":60,"height":40},
{"name":"b","width":60,"height":40},
{"name":"c","width":60,"height":40},
{"name":"d","width":60,"height":40},
{"name":"e","width":60,"height":40}
],
"links":[
{"source":0,"target":1},
{"source":1,"target":2},
{"source":2,"target":0},
{"source":2,"target":3}
]
}


export default function Dashboard({ auth }: PageProps) {
return (
Expand All @@ -47,8 +73,13 @@ export default function Dashboard({ auth }: PageProps) {
<div className="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
<div className="p-6 text-gray-900 dark:text-gray-100">You're logged in!</div>
</div>
<div id="treeWrapper">
<CenteredTree data={sampleInquiryChart} />
<div className="row">
<div className="treeWrapper">
<DAG data={sampleInquiryChart} />
</div>
<div className="treeWrapper sm">
<Tree nodes={example.nodes} links={example.links} />
</div>
</div>
</div>
</div>
Expand Down