Skip to content

Instantly share code, notes, and snippets.

@tonyfast
Forked from mbostock/.block
Last active August 29, 2015 14:14
Show Gist options
  • Save tonyfast/2ef05eda3932a0a65149 to your computer and use it in GitHub Desktop.
Save tonyfast/2ef05eda3932a0a65149 to your computer and use it in GitHub Desktop.
Collapsible tree with dict-like object

An interactive version of a Reingold–Tilford tree. Click on the nodes to expand or collapse.

Modifications

Very minor tweaks were made to create a collapsible tree of a dict-like object.

  • The dict is converted to the flare.json tree structure with a default size.
  • I added a subset of a gist API response because that is what I am working on.
  • I added lodash because the tree converter was already written.
[
{
"url": "https://api.github.com/gists/f7c69bda4418117bae72",
"forks_url": "https://api.github.com/gists/f7c69bda4418117bae72/forks",
"commits_url": "https://api.github.com/gists/f7c69bda4418117bae72/commits",
"id": "f7c69bda4418117bae72",
"git_pull_url": "https://gist.github.com/f7c69bda4418117bae72.git",
"git_push_url": "https://gist.github.com/f7c69bda4418117bae72.git",
"html_url": "https://gist.github.com/f7c69bda4418117bae72",
"files": {
"query.js": {
"filename": "query.js",
"type": "application/javascript",
"language": "JavaScript",
"raw_url": "https://gist.githubusercontent.com/tonyfast/f7c69bda4418117bae72/raw/5a759e6b982974963c9f6aa8dad3bf818e053113/query.js",
"size": 265
}
},
"public": true,
"created_at": "2014-12-16T20:14:27Z",
"updated_at": "2014-12-16T20:14:28Z",
"description": "Get Query Parameters from url",
"comments": 0,
"user": null,
"comments_url": "https://api.github.com/gists/f7c69bda4418117bae72/comments",
"owner": {
"login": "tonyfast",
"id": 4236275,
"avatar_url": "https://avatars.githubusercontent.com/u/4236275?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/tonyfast",
"html_url": "https://github.com/tonyfast",
"followers_url": "https://api.github.com/users/tonyfast/followers",
"following_url": "https://api.github.com/users/tonyfast/following{/other_user}",
"gists_url": "https://api.github.com/users/tonyfast/gists{/gist_id}",
"starred_url": "https://api.github.com/users/tonyfast/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/tonyfast/subscriptions",
"organizations_url": "https://api.github.com/users/tonyfast/orgs",
"repos_url": "https://api.github.com/users/tonyfast/repos",
"events_url": "https://api.github.com/users/tonyfast/events{/privacy}",
"received_events_url": "https://api.github.com/users/tonyfast/received_events",
"type": "User",
"site_admin": false
}
},
{
"url": "https://api.github.com/gists/f2d83a03551d26bc57f2",
"forks_url": "https://api.github.com/gists/f2d83a03551d26bc57f2/forks",
"commits_url": "https://api.github.com/gists/f2d83a03551d26bc57f2/commits",
"id": "f2d83a03551d26bc57f2",
"git_pull_url": "https://gist.github.com/f2d83a03551d26bc57f2.git",
"git_push_url": "https://gist.github.com/f2d83a03551d26bc57f2.git",
"html_url": "https://gist.github.com/f2d83a03551d26bc57f2",
"files": {
"README.md": {
"filename": "README.md",
"type": "text/plain",
"language": "Markdown",
"raw_url": "https://gist.githubusercontent.com/tonyfast/f2d83a03551d26bc57f2/raw/86092a26be3e9c11a0a7091cc42c1ddaae77ff67/README.md",
"size": 66
},
"handlebars.js": {
"filename": "handlebars.js",
"type": "application/javascript",
"language": "JavaScript",
"raw_url": "https://gist.githubusercontent.com/tonyfast/f2d83a03551d26bc57f2/raw/f826bbfd3879d006111ddaf0def6a05a2c329078/handlebars.js",
"size": 100900
},
"index.html": {
"filename": "index.html",
"type": "text/html",
"language": "HTML",
"raw_url": "https://gist.githubusercontent.com/tonyfast/f2d83a03551d26bc57f2/raw/beeb556b545ea42be2fa1ded9b5bd2fcd98b31f4/index.html",
"size": 318
},
"jquery.js": {
"filename": "jquery.js",
"type": "application/javascript",
"language": "JavaScript",
"raw_url": "https://gist.githubusercontent.com/tonyfast/f2d83a03551d26bc57f2/raw/9f7b3d38958726758eddc89880d50db98e11cfac/jquery.js",
"size": 247351
},
"jsyaml.js": {
"filename": "jsyaml.js",
"type": "application/javascript",
"language": "JavaScript",
"raw_url": "https://gist.githubusercontent.com/tonyfast/f2d83a03551d26bc57f2/raw/f518f133dd07d9f6e2873ac34c7ccac26b6ff98c/jsyaml.js",
"size": 92370
},
"main.js": {
"filename": "main.js",
"type": "application/javascript",
"language": "JavaScript",
"raw_url": "https://gist.githubusercontent.com/tonyfast/f2d83a03551d26bc57f2/raw/868332bd4330189dcdb19a088481573dc2c47edc/main.js",
"size": 2258
},
"markdown.js": {
"filename": "markdown.js",
"type": "application/javascript",
"language": "JavaScript",
"raw_url": "https://gist.githubusercontent.com/tonyfast/f2d83a03551d26bc57f2/raw/7412794ae3df971e2abb66fc99015bd62a826f0a/markdown.js",
"size": 57341
},
"readme.md": {
"filename": "readme.md",
"type": "text/plain",
"language": "Markdown",
"raw_url": "https://gist.githubusercontent.com/tonyfast/f2d83a03551d26bc57f2/raw/3a0c1566bdb2e299cd4b4025c01e4404602898be/readme.md",
"size": 144
},
"require.js": {
"filename": "require.js",
"type": "application/javascript",
"language": "JavaScript",
"raw_url": "https://gist.githubusercontent.com/tonyfast/f2d83a03551d26bc57f2/raw/77a5bb1d3be75c2f6e0d7f8484d00b890319da0f/require.js",
"size": 83083
},
"underscore.js": {
"filename": "underscore.js",
"type": "application/javascript",
"language": "JavaScript",
"raw_url": "https://gist.githubusercontent.com/tonyfast/f2d83a03551d26bc57f2/raw/b4f49a0204cae8f267d9ae0f3009d9a485c1ba46/underscore.js",
"size": 47559
}
},
"public": true,
"created_at": "2014-12-15T03:49:02Z",
"updated_at": "2014-12-20T19:06:02Z",
"description": "Load Structured Data in Gist and Parse the Markdownified text with Moustache notation",
"comments": 0,
"user": null,
"comments_url": "https://api.github.com/gists/f2d83a03551d26bc57f2/comments",
"owner": {
"login": "tonyfast",
"id": 4236275,
"avatar_url": "https://avatars.githubusercontent.com/u/4236275?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/tonyfast",
"html_url": "https://github.com/tonyfast",
"followers_url": "https://api.github.com/users/tonyfast/followers",
"following_url": "https://api.github.com/users/tonyfast/following{/other_user}",
"gists_url": "https://api.github.com/users/tonyfast/gists{/gist_id}",
"starred_url": "https://api.github.com/users/tonyfast/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/tonyfast/subscriptions",
"organizations_url": "https://api.github.com/users/tonyfast/orgs",
"repos_url": "https://api.github.com/users/tonyfast/repos",
"events_url": "https://api.github.com/users/tonyfast/events{/privacy}",
"received_events_url": "https://api.github.com/users/tonyfast/received_events",
"type": "User",
"site_admin": false
}
}
]
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node text {
font: 10px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
<script>
var margin = {top: 20, right: 120, bottom: 20, left: 120},
width = 960 - margin.right - margin.left,
height = 800 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("data.json", function(error, flare) {
function make_tree( d ){
return {
'name' : 'root',
'children' : _.map( d, make_node )
}
}
function make_node( d, k ){
// numerical name are arrays
if ( _.isObject( d ) ){
return {
'name' : k,
'children' : _.map( d, make_node )
}
} else {
return {'name' : k,
'size' : 10000
}
}
}
root = make_tree( flare );
root.x0 = height / 2;
root.y0 = 0;
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
update(root);
});
d3.select(self.frameElement).style("height", "800px");
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 180; });
// Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click", click);
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeEnter.append("text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("circle")
.attr("r", 4.5)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment