UNPKG

force-graph

Version:

2D force-directed graph rendered on HTML5 canvas

83 lines (69 loc) 2.44 kB
<head> <style> body { margin: 0; } </style> <script src="//cdn.jsdelivr.net/npm/force-graph"></script> <!--<script src="../../dist/force-graph.js"></script>--> <style> .clickable { cursor: unset !important } </style> </head> <body> <div id="graph"></div> <script type="module"> import { forceCollide } from 'https://esm.sh/d3-force'; import { csvParse } from 'https://esm.sh/d3-dsv'; fetch('../datasets/d3-dependencies.csv').then(r => r.text()).then(csvParse).then(data => { const nodes = [], links = []; data.forEach(({ size, path }) => { const levels = path.split('/'), level = levels.length - 1, module = level > 0 ? levels[1] : null, leaf = levels.pop(), parent = levels.join('/'); const node = { id: path, leaf, module, collapsed: level > 0, childLinks: [] }; nodes.push(node); if (parent) { links.push({ source: parent, target: path }); } }); const rootId = 'd3'; // link parent/children const nodesById = Object.fromEntries(nodes.map(node => [node.id, node])); links.forEach(link => { nodesById[link.source].childLinks.push(link); }); const getPrunedTree = () => { const visibleNodes = []; const visibleLinks = []; (function traverseTree(node = nodesById[rootId]) { visibleNodes.push(node); if (node.collapsed) return; visibleLinks.push(...node.childLinks); node.childLinks .map(link => ((typeof link.target) === 'object') ? link.target : nodesById[link.target]) // get child node .forEach(traverseTree); })(); // IIFE return { nodes: visibleNodes, links: visibleLinks }; }; const elem = document.getElementById('graph'); const Graph = new ForceGraph(elem) .graphData(getPrunedTree()) .nodeLabel('id') .nodeColor(node => !node.childLinks.length ? 'green' : node.collapsed ? 'red' : 'yellow') .onNodeHover(node => elem.style.cursor = node && node.childLinks.length ? 'pointer' : null) .onNodeClick(node => { node.collapsed = !node.collapsed; // toggle collapse state Graph.graphData(getPrunedTree()); }) .dagMode('td') .dagLevelDistance(90) .d3Force('collision', forceCollide(node => Graph.nodeRelSize())) .warmupTicks(250); }); </script> </body>