force-graph 1.42.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +228 -0
  3. package/dist/force-graph.common.js +1754 -0
  4. package/dist/force-graph.d.ts +195 -0
  5. package/dist/force-graph.js +12168 -0
  6. package/dist/force-graph.js.map +1 -0
  7. package/dist/force-graph.min.js +5 -0
  8. package/dist/force-graph.module.js +1743 -0
  9. package/example/auto-colored/index.html +34 -0
  10. package/example/basic/index.html +29 -0
  11. package/example/build-a-graph/index.html +108 -0
  12. package/example/click-to-focus/index.html +28 -0
  13. package/example/collision-detection/index.html +50 -0
  14. package/example/curved-links/index.html +37 -0
  15. package/example/curved-links-computed-curvature/index.html +76 -0
  16. package/example/custom-node-shape/index.html +44 -0
  17. package/example/dag-yarn/index.html +96 -0
  18. package/example/dagre/index.html +119 -0
  19. package/example/dash-odd-links/index.html +47 -0
  20. package/example/datasets/blocks.json +1 -0
  21. package/example/datasets/d3-dependencies.csv +464 -0
  22. package/example/datasets/miserables.json +337 -0
  23. package/example/datasets/mplate.mtx +74090 -0
  24. package/example/directional-links-arrows/index.html +29 -0
  25. package/example/directional-links-particles/index.html +22 -0
  26. package/example/dynamic/index.html +42 -0
  27. package/example/emit-particles/index.html +50 -0
  28. package/example/expandable-nodes/index.html +66 -0
  29. package/example/expandable-tree/index.html +85 -0
  30. package/example/fit-to-canvas/index.html +34 -0
  31. package/example/fix-dragged-nodes/index.html +24 -0
  32. package/example/highlight/index.html +84 -0
  33. package/example/huge-1M/index.html +37 -0
  34. package/example/img-nodes/imgs/cat.jpg +0 -0
  35. package/example/img-nodes/imgs/dog.jpg +0 -0
  36. package/example/img-nodes/imgs/eagle.jpg +0 -0
  37. package/example/img-nodes/imgs/elephant.jpg +0 -0
  38. package/example/img-nodes/imgs/grasshopper.jpg +0 -0
  39. package/example/img-nodes/imgs/octopus.jpg +0 -0
  40. package/example/img-nodes/imgs/owl.jpg +0 -0
  41. package/example/img-nodes/imgs/panda.jpg +0 -0
  42. package/example/img-nodes/imgs/squirrel.jpg +0 -0
  43. package/example/img-nodes/imgs/tiger.jpg +0 -0
  44. package/example/img-nodes/imgs/whale.jpg +0 -0
  45. package/example/img-nodes/index.html +43 -0
  46. package/example/large-graph/index.html +41 -0
  47. package/example/load-json/index.html +24 -0
  48. package/example/medium-graph/index.html +26 -0
  49. package/example/medium-graph/preview.png +0 -0
  50. package/example/move-viewport/index.html +42 -0
  51. package/example/multi-selection/index.html +57 -0
  52. package/example/responsive/index.html +37 -0
  53. package/example/text-links/index.html +69 -0
  54. package/example/text-nodes/index.html +42 -0
  55. package/example/tree/index.html +71 -0
  56. package/package.json +72 -0
  57. package/src/canvas-force-graph.js +544 -0
  58. package/src/color-utils.js +17 -0
  59. package/src/dagDepths.js +51 -0
  60. package/src/force-graph.css +35 -0
  61. package/src/force-graph.js +644 -0
  62. package/src/index.d.ts +195 -0
  63. package/src/index.js +3 -0
  64. package/src/kapsule-link.js +34 -0
@@ -0,0 +1,29 @@
1
+ <head>
2
+ <style> body { margin: 0; } </style>
3
+
4
+ <script src="//unpkg.com/force-graph"></script>
5
+ <!--<script src="../../dist/force-graph.js"></script>-->
6
+ </head>
7
+
8
+ <body>
9
+ <div id="graph"></div>
10
+
11
+ <script>
12
+ // Random tree
13
+ const N = 40;
14
+ const gData = {
15
+ nodes: [...Array(N).keys()].map(i => ({ id: i })),
16
+ links: [...Array(N).keys()]
17
+ .filter(id => id)
18
+ .map(id => ({
19
+ source: id,
20
+ target: Math.round(Math.random() * (id-1))
21
+ }))
22
+ };
23
+
24
+ const Graph = ForceGraph()
25
+ (document.getElementById('graph'))
26
+ .graphData(gData)
27
+ .linkDirectionalArrowLength(6);
28
+ </script>
29
+ </body>
@@ -0,0 +1,22 @@
1
+ <head>
2
+ <style> body { margin: 0; } </style>
3
+
4
+ <script src="//unpkg.com/force-graph"></script>
5
+ <!--<script src="../../dist/force-graph.js"></script>-->
6
+ </head>
7
+
8
+ <body>
9
+ <div id="graph"></div>
10
+
11
+ <script>
12
+ fetch('../datasets/miserables.json').then(res => res.json()).then(data => {
13
+ const Graph = ForceGraph()
14
+ (document.getElementById('graph'))
15
+ .graphData(data)
16
+ .nodeLabel('id')
17
+ .nodeAutoColorBy('group')
18
+ .linkDirectionalParticles("value")
19
+ .linkDirectionalParticleSpeed(d => d.value * 0.001);
20
+ });
21
+ </script>
22
+ </body>
@@ -0,0 +1,42 @@
1
+ <head>
2
+ <style> body { margin: 0; } </style>
3
+
4
+ <script src="//unpkg.com/force-graph"></script>
5
+ <!--<script src="../../dist/force-graph.js"></script>-->
6
+ </head>
7
+
8
+ <body>
9
+ <div id="graph"></div>
10
+
11
+ <script>
12
+ const initData = {
13
+ nodes: [ {id: 0 } ],
14
+ links: []
15
+ };
16
+
17
+ const elem = document.getElementById("graph");
18
+
19
+ const Graph = ForceGraph()(elem)
20
+ .onNodeClick(removeNode)
21
+ .graphData(initData);
22
+
23
+ setInterval(() => {
24
+ const { nodes, links } = Graph.graphData();
25
+ const id = nodes.length;
26
+ Graph.graphData({
27
+ nodes: [...nodes, { id }],
28
+ links: [...links, { source: id, target: Math.round(Math.random() * (id-1)) }]
29
+ });
30
+ }, 1000);
31
+
32
+ //
33
+
34
+ function removeNode(node) {
35
+ let { nodes, links } = Graph.graphData();
36
+ links = links.filter(l => l.source !== node && l.target !== node); // Remove links attached to node
37
+ nodes.splice(node.id, 1); // Remove node
38
+ nodes.forEach((n, idx) => { n.id = idx; }); // Reset node ids to array index
39
+ Graph.graphData({ nodes, links });
40
+ }
41
+ </script>
42
+ </body>
@@ -0,0 +1,50 @@
1
+ <head>
2
+ <style>
3
+ body { margin: 0; }
4
+
5
+ #emit-particles-btn {
6
+ position: absolute;
7
+ top: 10px;
8
+ right: 10px;
9
+ font-size: 13px;
10
+ }
11
+ </style>
12
+
13
+ <script src="//unpkg.com/force-graph"></script>
14
+ <!--<script src="../../dist/force-graph.js"></script>-->
15
+ </head>
16
+
17
+ <body>
18
+ <div id="graph"></div>
19
+ <button id="emit-particles-btn">Emit 10 Random Particles</button>
20
+
21
+ <script>
22
+ // Random tree
23
+ const N = 50;
24
+ const links = [...Array(N).keys()]
25
+ .filter(id => id)
26
+ .map(id => ({
27
+ source: id,
28
+ target: Math.round(Math.random() * (id-1))
29
+ }));
30
+ const gData = {
31
+ nodes: [...Array(N).keys()].map(i => ({ id: i })),
32
+ links
33
+ };
34
+
35
+ const Graph = ForceGraph()
36
+ (document.getElementById('graph'))
37
+ .linkDirectionalParticleColor(() => 'red')
38
+ .linkHoverPrecision(10)
39
+ .graphData(gData);
40
+
41
+ Graph.onLinkClick(Graph.emitParticle); // emit particles on link click
42
+
43
+ document.getElementById('emit-particles-btn').addEventListener('click', () => {
44
+ [...Array(10).keys()].forEach(() => {
45
+ const link = links[Math.floor(Math.random() * links.length)];
46
+ Graph.emitParticle(link);
47
+ });
48
+ });
49
+ </script>
50
+ </body>
@@ -0,0 +1,66 @@
1
+ <head>
2
+ <style> body { margin: 0; } </style>
3
+
4
+ <script src="//unpkg.com/force-graph"></script>
5
+ <!-- <script src="../../dist/force-graph.js"></script>-->
6
+
7
+ <style>
8
+ .clickable { cursor: unset !important }
9
+ </style>
10
+ </head>
11
+
12
+ <body>
13
+ <div id="graph"></div>
14
+
15
+ <script>
16
+ const rootId = 0;
17
+
18
+ // Random tree
19
+ const N = 300;
20
+ const gData = {
21
+ nodes: [...Array(N).keys()].map(i => ({ id: i, collapsed: i !== rootId, childLinks: [] })),
22
+ links: [...Array(N).keys()]
23
+ .filter(id => id)
24
+ .map(id => ({
25
+ source: Math.round(Math.random() * (id - 1)),
26
+ target: id
27
+ }))
28
+ };
29
+
30
+ // link parent/children
31
+ const nodesById = Object.fromEntries(gData.nodes.map(node => [node.id, node]));
32
+ gData.links.forEach(link => {
33
+ nodesById[link.source].childLinks.push(link);
34
+ });
35
+
36
+ const getPrunedTree = () => {
37
+ const visibleNodes = [];
38
+ const visibleLinks = [];
39
+
40
+ (function traverseTree(node = nodesById[rootId]) {
41
+ visibleNodes.push(node);
42
+ if (node.collapsed) return;
43
+ visibleLinks.push(...node.childLinks);
44
+ node.childLinks
45
+ .map(link => ((typeof link.target) === 'object') ? link.target : nodesById[link.target]) // get child node
46
+ .forEach(traverseTree);
47
+ })(); // IIFE
48
+
49
+ return { nodes: visibleNodes, links: visibleLinks };
50
+ };
51
+
52
+ const elem = document.getElementById('graph');
53
+ const Graph = ForceGraph()(elem)
54
+ .graphData(getPrunedTree())
55
+ .onNodeHover(node => elem.style.cursor = node && node.childLinks.length ? 'pointer' : null)
56
+ .onNodeClick(node => {
57
+ if (node.childLinks.length) {
58
+ node.collapsed = !node.collapsed; // toggle collapse state
59
+ Graph.graphData(getPrunedTree());
60
+ }
61
+ })
62
+ .linkDirectionalParticles(1)
63
+ .linkDirectionalParticleWidth(2.5)
64
+ .nodeColor(node => !node.childLinks.length ? 'green' : node.collapsed ? 'red' : 'yellow');
65
+ </script>
66
+ </body>
@@ -0,0 +1,85 @@
1
+ <head>
2
+ <style> body { margin: 0; } </style>
3
+
4
+ <script src="//unpkg.com/d3-dsv"></script>
5
+ <script src="//unpkg.com/d3-quadtree"></script>
6
+ <script src="//unpkg.com/d3-force"></script>
7
+
8
+ <script src="//unpkg.com/force-graph"></script>
9
+ <!--<script src="../../dist/force-graph.js"></script>-->
10
+
11
+ <style>
12
+ .clickable { cursor: unset !important }
13
+ </style>
14
+ </head>
15
+
16
+ <body>
17
+ <div id="graph"></div>
18
+
19
+ <script>
20
+ fetch('../datasets/d3-dependencies.csv').then(r => r.text()).then(d3.csvParse).then(data => {
21
+ const nodes = [], links = [];
22
+ data.forEach(({ size, path }) => {
23
+ const levels = path.split('/'),
24
+ level = levels.length - 1,
25
+ module = level > 0 ? levels[1] : null,
26
+ leaf = levels.pop(),
27
+ parent = levels.join('/');
28
+
29
+ const node = {
30
+ id: path,
31
+ leaf,
32
+ module,
33
+ collapsed: level > 0,
34
+ childLinks: []
35
+ };
36
+
37
+ nodes.push(node);
38
+
39
+ if (parent) {
40
+ links.push({ source: parent, target: path });
41
+ }
42
+ });
43
+
44
+ const rootId = 'd3';
45
+
46
+ // link parent/children
47
+ const nodesById = Object.fromEntries(nodes.map(node => [node.id, node]));
48
+ links.forEach(link => {
49
+ nodesById[link.source].childLinks.push(link);
50
+ });
51
+
52
+ const getPrunedTree = () => {
53
+ const visibleNodes = [];
54
+ const visibleLinks = [];
55
+
56
+ (function traverseTree(node = nodesById[rootId]) {
57
+ visibleNodes.push(node);
58
+ if (node.collapsed) return;
59
+ visibleLinks.push(...node.childLinks);
60
+ node.childLinks
61
+ .map(link => ((typeof link.target) === 'object') ? link.target : nodesById[link.target]) // get child node
62
+ .forEach(traverseTree);
63
+ })(); // IIFE
64
+
65
+ return { nodes: visibleNodes, links: visibleLinks };
66
+ };
67
+
68
+ const elem = document.getElementById('graph');
69
+ const Graph = ForceGraph()
70
+ (elem)
71
+ .graphData(getPrunedTree())
72
+ .nodeLabel('id')
73
+ .nodeColor(node => !node.childLinks.length ? 'green' : node.collapsed ? 'red' : 'yellow')
74
+ .onNodeHover(node => elem.style.cursor = node && node.childLinks.length ? 'pointer' : null)
75
+ .onNodeClick(node => {
76
+ node.collapsed = !node.collapsed; // toggle collapse state
77
+ Graph.graphData(getPrunedTree());
78
+ })
79
+ .dagMode('td')
80
+ .dagLevelDistance(90)
81
+ .d3Force('collision', d3.forceCollide(node => Graph.nodeRelSize()))
82
+ .warmupTicks(250);
83
+ });
84
+ </script>
85
+ </body>
@@ -0,0 +1,34 @@
1
+ <head>
2
+ <style> body { margin: 0; } </style>
3
+
4
+ <script src="//unpkg.com/force-graph"></script>
5
+ <!-- <script src="../../dist/force-graph.js"></script>-->
6
+ </head>
7
+
8
+ <body>
9
+ <div id="graph"></div>
10
+
11
+ <script>
12
+ // Random tree
13
+ const N = 100;
14
+ const gData = {
15
+ nodes: [...Array(N).keys()].map(i => ({ id: i })),
16
+ links: [...Array(N).keys()]
17
+ .filter(id => id)
18
+ .map(id => ({
19
+ source: id,
20
+ target: Math.round(Math.random() * (id-1))
21
+ }))
22
+ };
23
+
24
+ const Graph = ForceGraph()
25
+ (document.getElementById('graph'))
26
+ .cooldownTicks(100)
27
+ .graphData(gData);
28
+
29
+ Graph.d3Force('center', null);
30
+
31
+ // fit to canvas when engine stops
32
+ Graph.onEngineStop(() => Graph.zoomToFit(400));
33
+ </script>
34
+ </body>
@@ -0,0 +1,24 @@
1
+ <head>
2
+ <style> body { margin: 0; } </style>
3
+
4
+ <script src="//unpkg.com/force-graph"></script>
5
+ <!--<script src="../../dist/force-graph.js"></script>-->
6
+ </head>
7
+
8
+ <body>
9
+ <div id="graph"></div>
10
+
11
+ <script>
12
+ fetch('../datasets/miserables.json').then(res => res.json()).then(data => {
13
+ const Graph = ForceGraph()
14
+ (document.getElementById('graph'))
15
+ .graphData(data)
16
+ .nodeLabel('id')
17
+ .nodeAutoColorBy('group')
18
+ .onNodeDragEnd(node => {
19
+ node.fx = node.x;
20
+ node.fy = node.y;
21
+ });
22
+ });
23
+ </script>
24
+ </body>
@@ -0,0 +1,84 @@
1
+ <head>
2
+ <style> body { margin: 0; } </style>
3
+
4
+ <script src="//unpkg.com/force-graph"></script>
5
+ <!--<script src="../../dist/force-graph.js"></script>-->
6
+ </head>
7
+
8
+ <body>
9
+ <div id="graph"></div>
10
+
11
+ <script>
12
+ // Random tree
13
+ const N = 80;
14
+ const gData = {
15
+ nodes: [...Array(N).keys()].map(i => ({ id: i })),
16
+ links: [...Array(N).keys()]
17
+ .filter(id => id)
18
+ .map(id => ({
19
+ source: id,
20
+ target: Math.round(Math.random() * (id-1))
21
+ }))
22
+ };
23
+
24
+ // cross-link node objects
25
+ gData.links.forEach(link => {
26
+ const a = gData.nodes[link.source];
27
+ const b = gData.nodes[link.target];
28
+ !a.neighbors && (a.neighbors = []);
29
+ !b.neighbors && (b.neighbors = []);
30
+ a.neighbors.push(b);
31
+ b.neighbors.push(a);
32
+
33
+ !a.links && (a.links = []);
34
+ !b.links && (b.links = []);
35
+ a.links.push(link);
36
+ b.links.push(link);
37
+ });
38
+
39
+ const NODE_R = 8;
40
+
41
+ const highlightNodes = new Set();
42
+ const highlightLinks = new Set();
43
+ let hoverNode = null;
44
+
45
+ const elem = document.getElementById('graph');
46
+
47
+ ForceGraph()(elem)
48
+ .graphData(gData)
49
+ .nodeRelSize(NODE_R)
50
+ .onNodeHover(node => {
51
+ highlightNodes.clear();
52
+ highlightLinks.clear();
53
+ if (node) {
54
+ highlightNodes.add(node);
55
+ node.neighbors.forEach(neighbor => highlightNodes.add(neighbor));
56
+ node.links.forEach(link => highlightLinks.add(link));
57
+ }
58
+
59
+ hoverNode = node || null;
60
+ })
61
+ .onLinkHover(link => {
62
+ highlightNodes.clear();
63
+ highlightLinks.clear();
64
+
65
+ if (link) {
66
+ highlightLinks.add(link);
67
+ highlightNodes.add(link.source);
68
+ highlightNodes.add(link.target);
69
+ }
70
+ })
71
+ .autoPauseRedraw(false) // keep redrawing after engine has stopped
72
+ .linkWidth(link => highlightLinks.has(link) ? 5 : 1)
73
+ .linkDirectionalParticles(4)
74
+ .linkDirectionalParticleWidth(link => highlightLinks.has(link) ? 4 : 0)
75
+ .nodeCanvasObjectMode(node => highlightNodes.has(node) ? 'before' : undefined)
76
+ .nodeCanvasObject((node, ctx) => {
77
+ // add ring just for highlighted nodes
78
+ ctx.beginPath();
79
+ ctx.arc(node.x, node.y, NODE_R * 1.4, 0, 2 * Math.PI, false);
80
+ ctx.fillStyle = node === hoverNode ? 'red' : 'orange';
81
+ ctx.fill();
82
+ });
83
+ </script>
84
+ </body>
@@ -0,0 +1,37 @@
1
+ <head>
2
+ <style> body { margin: 0; } </style>
3
+
4
+ <script src="//unpkg.com/force-graph"></script>
5
+ <!--<script src="../../dist/force-graph.js"></script>-->
6
+ </head>
7
+
8
+ <body>
9
+ <div id="graph"></div>
10
+
11
+ <script>
12
+ window.devicePixelRatio = 1; // use standard resolution in retina displays
13
+
14
+ // Random tree
15
+ const N = 500000;
16
+ const gData = {
17
+ nodes: [...Array(N).keys()].map(i => ({ id: i })),
18
+ links: [...Array(N).keys()]
19
+ .filter(id => id)
20
+ .map(id => ({
21
+ source: id,
22
+ target: Math.round(Math.random() * (id-1))
23
+ }))
24
+ };
25
+
26
+ const Graph = ForceGraph()
27
+ (document.getElementById('graph'))
28
+ .linkColor(() => 'rgba(0,0,0,0.04)')
29
+ .enablePointerInteraction(false)
30
+ .d3AlphaDecay(0)
31
+ .d3VelocityDecay(0.08)
32
+ .warmupTicks(20)
33
+ .cooldownTicks(0)
34
+ .zoom(0.01)
35
+ .graphData(gData);
36
+ </script>
37
+ </body>
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,43 @@
1
+ <head>
2
+ <style> body { margin: 0; } </style>
3
+
4
+ <script src="//unpkg.com/force-graph"></script>
5
+ <!--<script src="../../dist/force-graph.js"></script>-->
6
+ </head>
7
+
8
+ <body>
9
+ <div id="graph"></div>
10
+
11
+ <script>
12
+ const imgs = ['cat.jpg', 'dog.jpg', 'eagle.jpg', 'elephant.jpg', 'grasshopper.jpg', 'octopus.jpg', 'owl.jpg', 'panda.jpg', 'squirrel.jpg', 'tiger.jpg', 'whale.jpg']
13
+ .map(src => {
14
+ const img = new Image();
15
+ img.src = `./imgs/${src}`;
16
+ return img;
17
+ });
18
+
19
+ // Random connected graph
20
+ const gData = {
21
+ nodes: imgs.map((img, id) => ({ id, img })),
22
+ links: [...Array(imgs.length).keys()]
23
+ .filter(id => id)
24
+ .map(id => ({
25
+ source: id,
26
+ target: Math.round(Math.random() * (id-1))
27
+ }))
28
+ };
29
+
30
+ const Graph = ForceGraph()
31
+ (document.getElementById('graph'))
32
+ .nodeCanvasObject(({ img, x, y }, ctx) => {
33
+ const size = 12;
34
+ ctx.drawImage(img, x - size / 2, y - size / 2, size, size);
35
+ })
36
+ .nodePointerAreaPaint((node, color, ctx) => {
37
+ const size = 12;
38
+ ctx.fillStyle = color;
39
+ ctx.fillRect(node.x - size / 2, node.y - size / 2, size, size); // draw square as pointer trap
40
+ })
41
+ .graphData(gData);
42
+ </script>
43
+ </body>
@@ -0,0 +1,41 @@
1
+ <head>
2
+ <style> body { margin: 0; } </style>
3
+
4
+ <script src="//unpkg.com/force-graph"></script>
5
+ <!--<script src="../../dist/force-graph.js"></script>-->
6
+ </head>
7
+
8
+ <body>
9
+ <div id="graph"></div>
10
+
11
+ <script>
12
+ window.devicePixelRatio = 1; // use standard resolution in retina displays
13
+
14
+ fetch('../datasets/mplate.mtx').then(res => res.text()).then(mtxData => {
15
+ let nodeSet = new Set();
16
+
17
+ const pairs = mtxData.split('\n')
18
+ .slice(14, -1)
19
+ .map(d => d.split(' '));
20
+
21
+ pairs.forEach(d => {
22
+ nodeSet.add(d[0]);
23
+ nodeSet.add(d[1]);
24
+ });
25
+
26
+ const nodes = Array.from(nodeSet).map(d => ({ id: d }));
27
+ const links = pairs.filter(d => d[0] !== d[1])
28
+ .map(d => ({ source: d[0], target: d[1] }));
29
+
30
+ const Graph = ForceGraph()
31
+ (document.getElementById('graph'))
32
+ .graphData({ nodes, links })
33
+ .d3AlphaDecay(0)
34
+ .d3VelocityDecay(0.08)
35
+ .cooldownTime(60000)
36
+ .linkColor(() => 'rgba(0,0,0,0.05)')
37
+ .zoom(0.05)
38
+ .enablePointerInteraction(false);
39
+ });
40
+ </script>
41
+ </body>
@@ -0,0 +1,24 @@
1
+ <head>
2
+ <style> body { margin: 0; } </style>
3
+
4
+ <script src="//unpkg.com/force-graph"></script>
5
+ <!--<script src="../../dist/force-graph.js"></script>-->
6
+ </head>
7
+
8
+ <body>
9
+ <div id="graph"></div>
10
+
11
+ <script>
12
+ fetch('../datasets/miserables.json').then(res => res.json()).then(data => {
13
+ const Graph = ForceGraph()
14
+ (document.getElementById('graph'))
15
+ .graphData(data)
16
+ .nodeId('id')
17
+ .nodeVal('val')
18
+ .nodeLabel('id')
19
+ .nodeAutoColorBy('group')
20
+ .linkSource('source')
21
+ .linkTarget('target')
22
+ });
23
+ </script>
24
+ </body>
@@ -0,0 +1,26 @@
1
+ <head>
2
+ <style> body { margin: 0; } </style>
3
+
4
+ <script src="//unpkg.com/force-graph"></script>
5
+ <!--<script src="../../dist/force-graph.js"></script>-->
6
+ </head>
7
+
8
+ <body>
9
+ <div id="graph"></div>
10
+
11
+ <script>
12
+ fetch('../datasets/blocks.json').then(res => res.json()).then(data => {
13
+ const elem = document.getElementById('graph');
14
+
15
+ const Graph = ForceGraph()(elem)
16
+ .backgroundColor('#101020')
17
+ .nodeRelSize(6)
18
+ .nodeAutoColorBy('user')
19
+ .nodeLabel(node => `${node.user}: ${node.description}`)
20
+ .linkColor(() => 'rgba(255,255,255,0.2)')
21
+ .linkDirectionalParticles(1)
22
+ .onNodeClick(node => window.open(`https://bl.ocks.org/${node.user}/${node.id}`, '_blank'))
23
+ .graphData(data);
24
+ });
25
+ </script>
26
+ </body>
Binary file