Skip to content

Instantly share code, notes, and snippets.

@johangithub
Created January 22, 2018 14:35
Show Gist options
  • Save johangithub/0f0f8311d7d515a443ce7c97aac56e59 to your computer and use it in GitHub Desktop.
Save johangithub/0f0f8311d7d515a443ce7c97aac56e59 to your computer and use it in GitHub Desktop.
Collapsible Tree
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.node text {
font: 15px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 4px;
}
</style>
<script src="http://d3js.org/d3.v4.min.js"></script>
<body>
<button id="collapse">Collapse</button>
<button id="uncollapse">UnCollapse</button>
<svg></svg>
</body>
<script>
var margin = {top: 20, right: 120, bottom: 20, left: 120},
width = 1900 - margin.right - margin.left,
height = 1000 - margin.top - margin.bottom
var svg = d3.select("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`)
var textMargin = 200
var tree = d3.tree()
.size([height, width - textMargin])
var stratify = d3.stratify()
.parentId(function(d) { return d.id.substring(0, d.id.lastIndexOf(".")) })
d3.csv("flare.csv", function(error, data) {
if (error) throw error
root = stratify(data)
.sort(function(a, b) { return (a.height - b.height) || a.id.localeCompare(b.id) })
root.x0 = height / 2
root.y0 = 0
root.children.forEach(collapse)
// sumData(root)
update(root)
})
function sumData(d){
var temp = 0
if (d.data.value) return d.data.value
if (d.children){
temp += d3.sum(d.children, d=>sumData(d))
}
if (d._children){
temp += d3.sum(d._children, d=>sumData(d))
}
return temp
}
function collapse(d){
if (d.children){
d._children = d.children
d._children.forEach(collapse)
d.children = null
}
}
function uncollapse(d){
if (!d.children){
d.children = d._children
d.children ? d.children.forEach(uncollapse) : null
} else {
d.children.forEach(uncollapse)
}
d._children = null
}
function update(source){
var treeData = tree(root)
var nodes = treeData.descendants()
var links = treeData.descendants().slice(1)
var t = d3.transition().duration(750)
// distance between each depth
nodes.forEach(d=>{ d.y = d.depth * 300 })
var node = svg.selectAll("g.node")
.data(nodes, function(d){return d.id || (d.id = ++i)})
var nodeEnter = node.enter().append('g')
.attr("class", "node")
.attr("transform", `translate(${source.y0}, ${source.x0})`)
.on("click", click)
nodeEnter.append('circle')
.attr('class', 'node')
.attr('r', 1e-6)
.style("fill", d => d._children ? "lightsteelblue" : "#fff")
nodeEnter.append("text")
.attr("dy", "-.5em")
.attr("x", d => d.children || d._children ? -8 : 8)
.attr("text-anchor", d => d.children || d._children ? "end" : "start")
.text(d => {return d.id.substring(d.id.lastIndexOf(".") + 1) + ' ' + sumData(d).toString()})
var nodeUpdate = nodeEnter.merge(node)
nodeUpdate
.transition(t)
.attr("transform", d => `translate(${d.y},${d.x})`)
nodeUpdate.select("circle.node")
.attr("r", 15)
.style("fill", d => d._children ? "lightsteelblue" : "#fff")
.attr("cursor", "pointer")
var nodeExit = node.exit().transition(t)
.attr("transform", `translate(${source.y}, ${source.x})`)
.remove()
nodeExit.select("circle")
.attr("r", 1e-6)
nodeExit.select("text")
.attr("fill-opacity", 1e-6)
var link = svg.selectAll("path.link")
.data(links, function(d){ return d.id})
var linkEnter = link
.enter()
.insert("path","g")
.attr("class", "link")
.attr("d", diagonal({x: source.x0, y: source.y0}, {x: source.x0, y: source.y0}))
var linkUpdate = linkEnter.merge(link)
linkUpdate.transition(t)
.attr("d", function(d){
return diagonal(d, d.parent)
})
var linkExit = link.exit().transition(t)
.attr("d", diagonal({x: source.x, y: source.y}, {x: source.x, y: source.y}))
.remove()
nodes.forEach(d => {d.x0 = d.x; d.y0 = d.y})
function click(d) {
if (d.children) {
d._children = d.children
d.children = null
} else {
d.children = d._children
d._children = null
}
update(d)
}
d3.select("#collapse").on("click",()=>{
collapse(root)
update(root)
})
d3.select("#uncollapse").on("click",()=>{
uncollapse(root)
update(root)
})
function diagonal(s, d) {
path = `M ${s.y} ${s.x}
C ${(s.y + d.y) / 2} ${s.x},
${(s.y + d.y) / 2} ${d.x},
${d.y} ${d.x}`
return path
}
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment