Published
Edited
Mar 14, 2020
Insert cell
Insert cell
parsedDocs = ({
name: '__root',
description: '',
children: Array.from(docsMD.matchAll(/^## \[[\w -]+ \((d3-.+)\)\].+(((\n?\n(?!^## )).+)+)/gm))
.map(m => {
const libName = m[1]
const haystack = m[2].trim()
const libDescriptionMatch = haystack.match(/([^*](.+\n)+)/m)
const libDescription = libDescriptionMatch.index === 0 ? libDescriptionMatch[1].trim() : ''
const subcategories = Array.from(haystack.matchAll(/^### \[?([\w ()-]+)\]?.{0,}(((\n?\n(?!^### )).+)+)/gm))
.map(m => {
const subcategoryName = m[1];
const haystack = m[2].trim()
const subDescriptionMatch = haystack.match(/^(?!\*)((.+\n)+)/m)
const subDescription = subDescriptionMatch !== null ? subDescriptionMatch[1].trim() : ''
return { subcategoryName, subDescription, haystack }
})
const subsubcategories = (
subcategories.length !== 0
? subcategories
: [{ subcategoryName: "__general", subDescription: "", haystack }]
).map(cat => {
const { haystack } = cat
const functions = Array.from(haystack.matchAll(/^\* \[(d3\.[\w]+)\]\(.+\) -( (.+)){0,}((\n(?!\* \[d3).+){0,})/gm))
.map(m => {
const haystack = m[4].trim()
const children = Array.from(haystack.matchAll(/\* \[\*([\w]+)\*(\.[\w]+)?\]\(.+\) -( (.+))?/gm))
.map(m => ({ name: m[1]+(m[2] || ''), description: m[4] || '', children: Array() }))
return { name: m[1], description: m[3] || '', children, }
})
return { children: functions, name: cat.subcategoryName, description: cat.subDescription, m: haystack }
})
return { name: libName, description: libDescription, children: subsubcategories }
})
})
Insert cell
Insert cell
{
const height = 700;
const margin = { top: 20, left: 20, right: 20, bottom: 20 }
const innerWidth = width - margin.right - margin.left
const innerHeight = height - margin.top - margin.bottom
const svg = d3.select(DOM.svg(width, height))
.attr('overflow', 'hidden')
const tree = d3.tree()
.size([innerHeight, innerWidth])
const root = d3.hierarchy(parsedDocs)
const links = tree(root).links()
const descendants = tree(root).descendants()
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`)
const xValue = d => d.y
const yValue = d => d.x
const titleValue = d => d.data.name;
g.selectAll('path').data(links).enter()
.append('path')
.attr('class', 'link')
.attr('fill', 'none')
.attr('stroke', 'black')
.attr('d', d3.linkHorizontal().x(xValue).y(yValue))
g.selectAll('circle').data(descendants).enter()
.append('circle')
.attr('class', 'point')
.attr('r', 5)
.attr('cx', xValue)
.attr('cy', yValue)
.append('title')
.text(titleValue)
const zoom = d3.zoom()
.on('zoom', handleZoomTree)
g.call(zoom)
.on('wheel.zoom', null)
return svg.node()
}
Insert cell
handleZoomTree = function() {
d3.select(this).attr('transform', d3.event.transform)
}
Insert cell
Insert cell
{
const height = 800;
const margin = { top: 20, left: 20, right: 20, bottom: 20 }
const innerWidth = width - margin.right - margin.left
const innerHeight = height - margin.top - margin.bottom
const svg = d3.select(DOM.svg(width, height))
.attr('overflow', 'hidden')
const sizeValue = d => d.children.length + 1
const colorValue = d => d.height
const nameValue = d => d.data.name !== '__general' ? d.data.name : d.parent.data.name
const titleValue = d => nameValue(d) +
( d.data.description !== '' ? '\n' + d.data.description : '' ) +
'\n' + d.ancestors().reverse().map(d => d.data.name).filter(s => s.indexOf('__') !== 0).join('/')
const filterLabel = d => d.height === 3
const colorScale = d3.scaleOrdinal()
.domain(d3.hierarchy(parsedDocs).descendants().map(colorValue))
.range(d3.schemeBlues[5])
const root = d3.hierarchy(parsedDocs)
.sum(sizeValue)
.sort((a, b) => b.value - a.value)
const pack = d3.pack()
.size([innerWidth, innerHeight])
.padding(3)
const packRoot = pack(root)
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`)
const nodes = g.selectAll('g')
.data(d3.nest().key(d => d.height).entries(packRoot.descendants()))
.join('g')
.selectAll('g').data(d => d.values, d => (d.nodeUid = DOM.uid('node')).id)
.join('g')
.attr('id', d => d.nodeUid.id)
.attr('transform', d => `translate(${d.x},${d.y})`)
nodes.append('circle')
.attr('fill', d => colorScale(colorValue(d)))
.attr('r', d => d.r)
.append('title')
.text(titleValue)
const pathGenerator = d3.arc()
.startAngle(- Math.PI / 2)
.endAngle(Math.PI / 2)
const labels = g.selectAll('.pathLabel').data(packRoot.descendants().filter(filterLabel)).enter().append('g')
.attr('class', 'pathLabel')
.attr('transform', d => `translate(${d.x},${d.y})`)
labels.append('path')
.attr('d', d => pathGenerator({ outerRadius: d.r }))
.attr('id', d => (d.pathUid = DOM.uid('path')).id)
.attr('fill', 'none')
labels.append('text')
.attr('font-size', d => d.r / 4)
.append('textPath')
.attr('href', d => "#" + d.pathUid.id)
.style("text-anchor","middle")
.attr("startOffset", "50%")
.text(nameValue)

const zoom = d3.zoom()
.on('zoom', handleZoomPack)
g.call(zoom)
.on('wheel.zoom', null)
return svg.node()
}
Insert cell
handleZoomPack = function() {
d3.select(this).attr('transform', d3.event.transform)
}
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more