Skip to content

Instantly share code, notes, and snippets.

@JasonGoemaat
Created August 1, 2024 13:44
Show Gist options
  • Save JasonGoemaat/02c584094a05d47f3fe9f65cabce4126 to your computer and use it in GitHub Desktop.
Save JasonGoemaat/02c584094a05d47f3fe9f65cabce4126 to your computer and use it in GitHub Desktop.
Claude D3 Graph
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vertical Algorithm Topics Tree with Progress Bars</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f0f0;
}
#tree-container {
width: 95vw;
height: 95vh;
background-color: white;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
.node rect {
fill: #fff;
stroke: steelblue;
stroke-width: 2px;
}
.node text {
font: 12px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
.progress-bar {
fill: #4CAF50;
}
.progress-bar-bg {
fill: #e0e0e0;
}
</style>
</head>
<body>
<div id="tree-container"></div>
<script>
const treeData = {
"Arrays & Hashing": {
"children": [
"Two Pointers",
"Stack"
]
},
"Two Pointers": {
"children": [
"Binary Search",
"Sliding Window",
"Linked List"
]
},
"Stack": {
"children": []
},
"Binary Search": {
"children": [
"Trees"
]
},
"Sliding Window": {
"children": []
},
"Linked List": {
"children": [
"Trees"
]
},
"Trees": {
"children": [
"Tries",
"Backtracking",
"Heap / Priority Queue"
]
},
"Tries": {
"children": []
},
"Backtracking": {
"children": [
"Graphs",
"1-D DP"
]
},
"Heap / Priority Queue": {
"children": [
"Intervals",
"Greedy",
"Advanced Graphs"
]
},
"Graphs": {
"children": [
"Advanced Graphs",
"2-D DP",
"Math & Geometry"
]
},
"1-D DP": {
"children": [
"2-D DP",
"Bit Manipulation"
]
},
"Intervals": {
"children": []
},
"Greedy": {
"children": []
},
"Advanced Graphs": {
"children": []
},
"2-D DP": {
"children": []
},
"Bit Manipulation": {
"children": [
"Math & Geometry"
]
},
"Math & Geometry": {
"children": []
}
};
function createGraph(data) {
const graph = { nodes: {}, links: [] };
function addNode(name) {
if (!graph.nodes[name]) {
graph.nodes[name] = {
name,
children: new Set(),
progress: Math.random() // Random progress for demonstration
};
}
return graph.nodes[name];
}
Object.entries(data).forEach(([parent, info]) => {
const parentNode = addNode(parent);
info.children.forEach(child => {
const childNode = addNode(child);
parentNode.children.add(child);
graph.links.push({ source: parent, target: child });
});
});
return graph;
}
const graph = createGraph(treeData);
const margin = { top: 40, right: 120, bottom: 50, left: 120 };
const width = window.innerWidth - margin.left - margin.right;
const height = window.innerHeight - margin.top - margin.bottom;
const svg = d3.select("#tree-container").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
function createHierarchy(graph, root) {
const visited = new Set();
function dfs(nodeName) {
if (visited.has(nodeName)) return null;
visited.add(nodeName);
const node = {
name: nodeName,
children: [],
progress: graph.nodes[nodeName].progress
};
graph.nodes[nodeName].children.forEach(childName => {
const childNode = dfs(childName);
if (childNode) node.children.push(childNode);
});
return node.children.length ? node : { name: nodeName, progress: node.progress };
}
return dfs(root);
}
const hierarchicalData = createHierarchy(graph, "Arrays & Hashing");
const tree = d3.tree().size([width, height]);
const root = d3.hierarchy(hierarchicalData);
tree(root);
const link = svg.selectAll(".link")
.data(root.descendants().slice(1))
.enter().append("path")
.attr("class", "link")
.attr("d", d => `
M${d.x},${d.y}
C${d.x},${(d.y + d.parent.y) / 2}
${d.parent.x},${(d.y + d.parent.y) / 2}
${d.parent.x},${d.parent.y}
`);
const node = svg.selectAll(".node")
.data(root.descendants())
.enter().append("g")
.attr("class", "node")
.attr("transform", d => `translate(${d.x},${d.y})`);
const boxWidth = 120;
const boxHeight = 60;
node.append("rect")
.attr("width", boxWidth)
.attr("height", boxHeight)
.attr("x", -boxWidth / 2)
.attr("y", -boxHeight / 2)
.attr("rx", 5)
.attr("ry", 5);
node.append("text")
.attr("dy", "-0.5em")
.attr("text-anchor", "middle")
.text(d => d.data.name)
.call(wrap, boxWidth - 10);
const progressBarHeight = 10;
const progressBarY = boxHeight / 4;
node.append("rect")
.attr("class", "progress-bar-bg")
.attr("width", boxWidth - 10)
.attr("height", progressBarHeight)
.attr("x", -boxWidth / 2 + 5)
.attr("y", progressBarY);
node.append("rect")
.attr("class", "progress-bar")
.attr("width", d => (boxWidth - 10) * d.data.progress)
.attr("height", progressBarHeight)
.attr("x", -boxWidth / 2 + 5)
.attr("y", progressBarY);
node.append("text")
.attr("dy", "2.5em")
.attr("text-anchor", "middle")
.text(d => `${Math.round(d.data.progress * 100)}%`);
const additionalLinks = graph.links.filter(link =>
!root.descendants().some(d =>
d.data.name === link.source &&
d.children && d.children.some(child => child.data.name === link.target)
)
);
svg.selectAll(".additional-link")
.data(additionalLinks)
.enter().append("path")
.attr("class", "link")
.attr("d", d => {
const source = root.descendants().find(node => node.data.name === d.source);
const target = root.descendants().find(node => node.data.name === d.target);
return `
M${source.x},${source.y}
C${source.x},${(source.y + target.y) / 2}
${target.x},${(source.y + target.y) / 2}
${target.x},${target.y}
`;
});
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment