Skip to content

Instantly share code, notes, and snippets.

@eesur
Last active January 29, 2018 13:01
Show Gist options
  • Save eesur/6d47408e1e7378fbb811836f6fe230c1 to your computer and use it in GitHub Desktop.
Save eesur/6d47408e1e7378fbb811836f6fe230c1 to your computer and use it in GitHub Desktop.
d3js | enclosing diagram | circle pack
license: mit
height: 500
border: no
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

enclosing diagram (circle pack), green circles represent items/institutions and the containing grey circles their clusters. Data is randomly generated and has no meaning, just for testing appearance of varying quantities

*{box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,Consolas,monaco,monospace;width:960px;margin:0 auto;background:#2f292b;font-size:14px;color:#a5a6a9;letter-spacing:3px}header{left:15px;width:250px}circle.item{fill:#2f292b;cursor:pointer;transition:fill .2s ease}circle.item:hover{fill:#e6c700}circle.cluster{fill:#f45844}circle.root{display:none}span{color:#f45844;font-weight:700}input[type=range]{-webkit-appearance:none;width:100%;height:2px;background:#a5a6a9;background-position:center;background-repeat:no-repeat;margin:auto}input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:30px;height:30px;border-radius:100%;background:#a5a6a9;position:relative;border:3px solid #a5a6a9;z-index:2;cursor:pointer}input:focus{outline:0}.d3-tip{font-weight:400;line-height:1.5;padding:12px;background:rgba(0,0,0,.8);color:#f5f5f5;border-radius:2px;pointer-events:none}.d3-tip:after{box-sizing:border-box;display:inline;font-size:10px;width:100%;line-height:1;color:#231f20;position:absolute;pointer-events:none}.d3-tip.n:after{content:"\25BC";margin:-1px 0 0 0;top:100%;left:0;text-align:center}
!function(n){function t(a){if(e[a])return e[a].exports;var g=e[a]={i:a,l:!1,exports:{}};return n[a].call(g.exports,g,g.exports,t),g.l=!0,g.exports}var e={};t.m=n,t.c=e,t.i=function(n){return n},t.d=function(n,e,a){t.o(n,e)||Object.defineProperty(n,e,{configurable:!1,enumerable:!0,get:a})},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},t.p="",t(t.s=1)}([function(module,exports,__webpack_require__){"use strict";eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nfunction _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }\n\nvar d3 = window.d3;\n\nexports.default = function () {\n var amount = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 4;\n\n // list of clusters\n var clusters = createClusters(amount);\n // have a root node to start with\n var data = [{ name: 'root', parent: null }];\n\n clusters.forEach(function (d) {\n var r = Math.floor(d3.randomUniform(2, 40)());\n var children = createChildren(r, d);\n // pass in the parent items\n data.push({ name: d, parent: 'root' });\n // pass in some children\n data = data.concat(children);\n });\n\n return data;\n};\n\n// create clusters\n\n\nfunction createClusters() {\n var amount = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 4;\n var name = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'cluster_';\n\n var list = [].concat(_toConsumableArray(Array(amount).keys())).map(function (d) {\n return name + d;\n });\n return list;\n}\n\n// add some dummy data to each cluster\nfunction createChildren() {\n var amount = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 10;\n var cluster = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'c_1';\n\n var o = [].concat(_toConsumableArray(Array(amount).keys())).map(function (d, i) {\n return {\n name: cluster + ' | item_' + i,\n parent: cluster\n };\n });\n return o;\n}//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMC5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy9kdW1teURhdGEuanM/NWI2MSJdLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCBkMyA9IHdpbmRvdy5kM1xuXG5leHBvcnQgZGVmYXVsdCAoYW1vdW50ID0gNCkgPT4ge1xuICAvLyBsaXN0IG9mIGNsdXN0ZXJzXG4gIGNvbnN0IGNsdXN0ZXJzID0gY3JlYXRlQ2x1c3RlcnMoYW1vdW50KVxuICAvLyBoYXZlIGEgcm9vdCBub2RlIHRvIHN0YXJ0IHdpdGhcbiAgbGV0IGRhdGEgPSBbXG4gICAge25hbWU6ICdyb290JywgcGFyZW50OiBudWxsfVxuICBdXG5cbiAgY2x1c3RlcnMuZm9yRWFjaChkID0+IHtcbiAgICBsZXQgciA9IE1hdGguZmxvb3IoZDMucmFuZG9tVW5pZm9ybSgyLCA0MCkoKSlcbiAgICBsZXQgY2hpbGRyZW4gPSBjcmVhdGVDaGlsZHJlbihyLCBkKVxuICAgIC8vIHBhc3MgaW4gdGhlIHBhcmVudCBpdGVtc1xuICAgIGRhdGEucHVzaCh7bmFtZTogZCwgcGFyZW50OiAncm9vdCd9KVxuICAgIC8vIHBhc3MgaW4gc29tZSBjaGlsZHJlblxuICAgIGRhdGEgPSBkYXRhLmNvbmNhdChjaGlsZHJlbilcbiAgfSlcblxuICByZXR1cm4gZGF0YVxufVxuXG4vLyBjcmVhdGUgY2x1c3RlcnNcbmZ1bmN0aW9uIGNyZWF0ZUNsdXN0ZXJzIChhbW91bnQgPSA0LCBuYW1lID0gJ2NsdXN0ZXJfJykge1xuICBjb25zdCBsaXN0ID0gWy4uLkFycmF5KGFtb3VudCkua2V5cygpXS5tYXAoZCA9PiBuYW1lICsgZClcbiAgcmV0dXJuIGxpc3Rcbn1cblxuLy8gYWRkIHNvbWUgZHVtbXkgIGRhdGEgdG8gZWFjaCBjbHVzdGVyXG5mdW5jdGlvbiBjcmVhdGVDaGlsZHJlbiAoYW1vdW50ID0gMTAsIGNsdXN0ZXIgPSAnY18xJykge1xuICBjb25zdCBvID0gWy4uLkFycmF5KGFtb3VudCkua2V5cygpXS5tYXAoKGQsIGkpID0+IHtcbiAgICByZXR1cm4ge1xuICAgICAgbmFtZTogYCR7Y2x1c3Rlcn0gfCBpdGVtXyR7aX1gLFxuICAgICAgcGFyZW50OiBjbHVzdGVyXG4gICAgfVxuICB9KVxuICByZXR1cm4gb1xufVxuXG5cblxuLy8gV0VCUEFDSyBGT09URVIgLy9cbi8vIGR1bW15RGF0YS5qcyJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7QUFBQTtBQUNBO0FBQ0E7QUFBQTtBQUNBO0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQURBO0FBQUE7QUFBQTtBQUNBO0FBQUE7QUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUFBO0FBQUE7QUFDQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBRkE7QUFJQTtBQUNBO0FBQ0EiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///0\n")},function(module,exports,__webpack_require__){"use strict";eval("\n\nvar _dummyData = __webpack_require__(0);\n\nvar _dummyData2 = _interopRequireDefault(_dummyData);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar d3 = window.d3;\n\nfunction enclosureDiagram(bind, flatData, config) {\n config = {\n width: 960,\n height: 800,\n radius: 5,\n padding: 5\n };\n\n var _config = config,\n width = _config.width,\n height = _config.height;\n\n var selection = d3.select(bind);\n\n // initialize tooltip after a destroy\n d3.selectAll('.d3-tip').remove();\n var tip = d3.tip().attr('class', 'd3-tip').html(function (d) {\n return d.data.id;\n }).offset([-10, 1]);\n // invoke the tip in the context of your visualization\n selection.call(tip);\n\n // convert the flat data into a hierarchy\n var data = d3.stratify().id(function (d) {\n return d.name;\n }).parentId(function (d) {\n return d.parent;\n })(flatData);\n\n var packLayout = d3.pack().size([width, height]).radius(function () {\n return config.radius;\n }).padding(config.padding);\n\n var rootNode = d3.hierarchy(data);\n\n // run .sum() on the hierarchy; return 1 for size of leaf nodes\n rootNode.sum(function () {\n return 1;\n });\n\n packLayout(rootNode);\n\n renderCircles(rootNode.descendants());\n\n function renderCircles(nodes) {\n var circles = selection.selectAll('circle').data(nodes, function (d) {\n return d.data.id;\n });\n circles.exit().remove();\n circles.enter().append('circle').attr('class', function (d) {\n if (d.depth === 0) {\n return 'root';\n } else if (d.depth === 1) {\n return 'cluster';\n } else {\n return 'item';\n }\n }).merge(circles).attr('cx', function (d) {\n return d.x;\n }).attr('cy', function (d) {\n return d.y;\n }).attr('r', function (d) {\n return d.r;\n })\n // .on('mouseover', tip.show)\n .on('mouseover', function (d) {\n // use a timeout/delay to stop crazy flickering\n var context = this;\n var args = [].slice.call(arguments);\n var timeout = null;\n args.push(this);\n clearTimeout(timeout);\n timeout = setTimeout(function () {\n tip.show.apply(context, args);\n }, 100);\n }).on('mouseout', tip.hide);\n }\n}\n\n// run the visualisation\nvar chart = enclosureDiagram;\nchart('g#vis', (0, _dummyData2.default)(27));\n\n// when the input range changes re-render (set in index.html)\nd3.select('#cluster-amount').on('input', function () {\n d3.select('#slider-amount').text(+this.value);\n chart('g#vis', (0, _dummyData2.default)(+this.value));\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMS5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy9zY3JpcHQuanM/OWE5NSJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgZHVtbXlEYXRhIGZyb20gJy4vZHVtbXlEYXRhJ1xuY29uc3QgZDMgPSB3aW5kb3cuZDNcblxuZnVuY3Rpb24gZW5jbG9zdXJlRGlhZ3JhbSAoYmluZCwgZmxhdERhdGEsIGNvbmZpZykge1xuICBjb25maWcgPSB7XG4gICAgd2lkdGg6IDk2MCxcbiAgICBoZWlnaHQ6IDgwMCxcbiAgICByYWRpdXM6IDUsXG4gICAgcGFkZGluZzogNVxuICB9XG5cbiAgY29uc3QgeyB3aWR0aCwgaGVpZ2h0IH0gPSBjb25maWdcbiAgY29uc3Qgc2VsZWN0aW9uID0gZDMuc2VsZWN0KGJpbmQpXG5cbiAgLy8gaW5pdGlhbGl6ZSB0b29sdGlwIGFmdGVyIGEgZGVzdHJveVxuICBkMy5zZWxlY3RBbGwoJy5kMy10aXAnKS5yZW1vdmUoKVxuICBjb25zdCB0aXAgPSBkMy50aXAoKS5hdHRyKCdjbGFzcycsICdkMy10aXAnKS5odG1sKGQgPT4gZC5kYXRhLmlkKS5vZmZzZXQoWy0xMCwgMV0pXG4gIC8vIGludm9rZSB0aGUgdGlwIGluIHRoZSBjb250ZXh0IG9mIHlvdXIgdmlzdWFsaXphdGlvblxuICBzZWxlY3Rpb24uY2FsbCh0aXApXG5cbiAgLy8gY29udmVydCB0aGUgZmxhdCBkYXRhIGludG8gYSBoaWVyYXJjaHlcbiAgY29uc3QgZGF0YSA9IGQzLnN0cmF0aWZ5KClcbiAgICAuaWQoZCA9PiBkLm5hbWUpXG4gICAgLnBhcmVudElkKGQgPT4gZC5wYXJlbnQpKGZsYXREYXRhKVxuXG4gIGNvbnN0IHBhY2tMYXlvdXQgPSBkMy5wYWNrKClcbiAgICAuc2l6ZShbd2lkdGgsIGhlaWdodF0pXG4gICAgLnJhZGl1cygoKSA9PiBjb25maWcucmFkaXVzKVxuICAgIC5wYWRkaW5nKGNvbmZpZy5wYWRkaW5nKVxuXG4gIGNvbnN0IHJvb3ROb2RlID0gZDMuaGllcmFyY2h5KGRhdGEpXG5cbiAgLy8gcnVuIC5zdW0oKSBvbiB0aGUgaGllcmFyY2h5OyByZXR1cm4gMSBmb3Igc2l6ZSBvZiBsZWFmIG5vZGVzXG4gIHJvb3ROb2RlLnN1bSgoKSA9PiAxKVxuXG4gIHBhY2tMYXlvdXQocm9vdE5vZGUpXG5cbiAgcmVuZGVyQ2lyY2xlcyhyb290Tm9kZS5kZXNjZW5kYW50cygpKVxuXG4gIGZ1bmN0aW9uIHJlbmRlckNpcmNsZXMgKG5vZGVzKSB7XG4gICAgY29uc3QgY2lyY2xlcyA9IHNlbGVjdGlvblxuICAgICAgLnNlbGVjdEFsbCgnY2lyY2xlJylcbiAgICAgIC5kYXRhKG5vZGVzLCBkID0+IGQuZGF0YS5pZClcbiAgICBjaXJjbGVzLmV4aXQoKS5yZW1vdmUoKVxuICAgIGNpcmNsZXMuZW50ZXIoKS5hcHBlbmQoJ2NpcmNsZScpXG4gICAgICAuYXR0cignY2xhc3MnLCBkID0+IHtcbiAgICAgICAgaWYgKGQuZGVwdGggPT09IDApIHtcbiAgICAgICAgICByZXR1cm4gJ3Jvb3QnXG4gICAgICAgIH0gZWxzZSBpZiAoZC5kZXB0aCA9PT0gMSkge1xuICAgICAgICAgIHJldHVybiAnY2x1c3RlcidcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXR1cm4gJ2l0ZW0nXG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgICAubWVyZ2UoY2lyY2xlcylcbiAgICAgIC5hdHRyKCdjeCcsIGQgPT4gZC54KVxuICAgICAgLmF0dHIoJ2N5JywgZCA9PiBkLnkpXG4gICAgICAuYXR0cigncicsIGQgPT4gZC5yKVxuICAgICAgLy8gLm9uKCdtb3VzZW92ZXInLCB0aXAuc2hvdylcbiAgICAgIC5vbignbW91c2VvdmVyJywgZnVuY3Rpb24gKGQpIHtcbiAgICAgICAgLy8gdXNlIGEgdGltZW91dC9kZWxheSB0byBzdG9wIGNyYXp5IGZsaWNrZXJpbmdcbiAgICAgICAgY29uc3QgY29udGV4dCA9IHRoaXNcbiAgICAgICAgbGV0IGFyZ3MgPSBbXS5zbGljZS5jYWxsKGFyZ3VtZW50cylcbiAgICAgICAgbGV0IHRpbWVvdXQgPSBudWxsXG4gICAgICAgIGFyZ3MucHVzaCh0aGlzKVxuICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dClcbiAgICAgICAgdGltZW91dCA9IHNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICAgIHRpcC5zaG93LmFwcGx5KGNvbnRleHQsIGFyZ3MpXG4gICAgICAgIH0sIDEwMClcbiAgICAgIH0pXG4gICAgICAub24oJ21vdXNlb3V0JywgdGlwLmhpZGUpXG4gIH1cbn1cblxuLy8gcnVuIHRoZSB2aXN1YWxpc2F0aW9uXG5jb25zdCBjaGFydCA9IGVuY2xvc3VyZURpYWdyYW1cbmNoYXJ0KCdnI3ZpcycsIGR1bW15RGF0YSgyNykpXG5cbi8vIHdoZW4gdGhlIGlucHV0IHJhbmdlIGNoYW5nZXMgcmUtcmVuZGVyIChzZXQgaW4gaW5kZXguaHRtbClcbmQzLnNlbGVjdCgnI2NsdXN0ZXItYW1vdW50Jykub24oJ2lucHV0JywgZnVuY3Rpb24gKCkge1xuICBkMy5zZWxlY3QoJyNzbGlkZXItYW1vdW50JykudGV4dCgrdGhpcy52YWx1ZSlcbiAgY2hhcnQoJ2cjdmlzJywgZHVtbXlEYXRhKCt0aGlzLnZhbHVlKSlcbn0pXG5cblxuXG4vLyBXRUJQQUNLIEZPT1RFUiAvL1xuLy8gc2NyaXB0LmpzIl0sIm1hcHBpbmdzIjoiOztBQUFBO0FBQ0E7Ozs7O0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUpBO0FBQ0E7QUFGQTtBQUFBO0FBQUE7QUFDQTtBQVFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQUE7QUFDQTtBQUFBO0FBQ0E7QUFDQTtBQUVBO0FBQUE7QUFDQTtBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQUE7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBRUE7QUFBQTtBQUNBO0FBQ0E7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBRUE7QUFBQTtBQUNBO0FBQUE7QUFDQTtBQUFBO0FBQ0E7QUFkQTtBQWdCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///1\n")}]);
const d3 = window.d3
export default (amount = 4) => {
// list of clusters
const clusters = createClusters(amount)
// have a root node to start with
let data = [
{name: 'root', parent: null}
]
clusters.forEach(d => {
let r = Math.floor(d3.randomUniform(2, 40)())
let children = createChildren(r, d)
// pass in the parent items
data.push({name: d, parent: 'root'})
// pass in some children
data = data.concat(children)
})
return data
}
// create clusters
function createClusters (amount = 4, name = 'cluster_') {
const list = [...Array(amount).keys()].map(d => name + d)
return list
}
// add some dummy data to each cluster
function createChildren (amount = 10, cluster = 'c_1') {
const o = [...Array(amount).keys()].map((d, i) => {
return {
name: `${cluster} | item_${i}`,
parent: cluster
}
})
return o
}
<!DOCTYPE html>
<title>d3js | enclosing diagram | circle pack</title>
<link href="//npmcdn.com/basscss@8.0.2/css/basscss.min.css" rel="stylesheet">
<link href='dist.css' rel='stylesheet' />
<body>
<header class="fixed top-0">
<p class="">Number of clusters: <span id="slider-amount">27</span></p>
<input type="range" id="cluster-amount" value="27">
</header>
<svg width="960" height="800">
<g id="vis"></g>
</svg>
<script src='https://d3js.org/d3.v4.min.js'></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.7.1/d3-tip.min.js"></script>
<script src='dist.js'></script>
<script>
// set a min and max to to the range slider
document.getElementById('cluster-amount').max = '50'
document.getElementById('cluster-amount').min = '1'
// change frame height for bl.ocks
d3.select(self.frameElement).style('height', '800px')
</script>
</body>
import dummyData from './dummyData'
const d3 = window.d3
function enclosureDiagram (bind, flatData, config) {
config = {
width: 960,
height: 800,
radius: 5,
padding: 5,
...config
}
const { width, height } = config
const selection = d3.select(bind)
// initialize tooltip after a destroy
d3.selectAll('.d3-tip').remove()
const tip = d3.tip().attr('class', 'd3-tip').html(d => d.data.id).offset([-10, 1])
// invoke the tip in the context of your visualization
selection.call(tip)
// convert the flat data into a hierarchy
const data = d3.stratify()
.id(d => d.name)
.parentId(d => d.parent)(flatData)
const packLayout = d3.pack()
.size([width, height])
.radius(() => config.radius)
.padding(config.padding)
const rootNode = d3.hierarchy(data)
// run .sum() on the hierarchy; return 1 for size of leaf nodes
rootNode.sum(() => 1)
packLayout(rootNode)
renderCircles(rootNode.descendants())
function renderCircles (nodes) {
const circles = selection
.selectAll('circle')
.data(nodes, d => d.data.id)
circles.exit().remove()
circles.enter().append('circle')
.attr('class', d => {
if (d.depth === 0) {
return 'root'
} else if (d.depth === 1) {
return 'cluster'
} else {
return 'item'
}
})
.merge(circles)
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', d => d.r)
// .on('mouseover', tip.show)
.on('mouseover', function (d) {
// use a timeout/delay to stop crazy flickering
const context = this
let args = [].slice.call(arguments)
let timeout = null
args.push(this)
clearTimeout(timeout)
timeout = setTimeout(function () {
tip.show.apply(context, args)
}, 100)
})
.on('mouseout', tip.hide)
}
}
// run the visualisation
const chart = enclosureDiagram
chart('g#vis', dummyData(27))
// when the input range changes re-render (set in index.html)
d3.select('#cluster-amount').on('input', function () {
d3.select('#slider-amount').text(+this.value)
chart('g#vis', dummyData(+this.value))
})
*
box-sizing border-box
body
font-family:-apple-system,BlinkMacSystemFont,Consolas,monaco,monospace
width: 960px
margin: 0 auto
background: #2f292b
font-size: 14px
color: #a5a6a9
letter-spacing: 3px
header
left: 15px
width: 250px
circle.item
fill: #2f292b
cursor: pointer
transition: fill 0.2s ease
circle.item:hover
fill: #e6c700
circle.cluster
fill: #f45844
circle.root
display: none
span
color: #f45844
font-weight: 700
input[type="range"]
-webkit-appearance:none
width: 100%
height:2px
background: #a5a6a9
background-position:center
background-repeat:no-repeat
margin: auto
input[type="range"]::-webkit-slider-thumb
-webkit-appearance:none
width: 30px
height: 30px
border-radius: 100%
background: #a5a6a9
position:relative
border: 3px solid #a5a6a9
z-index: 2
cursor: pointer
input:focus
outline: none
.d3-tip
font-weight: normal
line-height: 1.5
padding: 12px
background: rgba(0, 0, 0, 0.8)
color: #f5f5f5
border-radius: 2px
pointer-events: none
.d3-tip:after
box-sizing: border-box
display: inline
font-size: 10px
width: 100%
line-height: 1
color: #231f20
position: absolute
pointer-events: none
.d3-tip.n:after
content: "\25BC"
margin: -1px 0 0 0
top: 100%
left: 0
text-align: center
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment