UNPKG

12.3 kBHTMLView Raw
1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="utf-8"/>
5<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
6<meta name="description" content="TypeScript: Display hierarchical data by nesting, where the area of each node is proportional to some value for the node. Clicking consecutively results in selecting containing Groups."/>
7<link rel="stylesheet" href="../assets/css/style.css"/>
8<!-- Copyright 1998-2021 by Northwoods Software Corporation. -->
9<title>Tree Map</title>
10</head>
11
12<body>
13 <!-- This top nav is not part of the sample code -->
14 <nav id="navTop" class="w-full z-30 top-0 text-white bg-nwoods-primary">
15 <div class="w-full container max-w-screen-lg mx-auto flex flex-wrap sm:flex-nowrap items-center justify-between mt-0 py-2">
16 <div class="md:pl-4">
17 <a class="text-white hover:text-white no-underline hover:no-underline
18 font-bold text-2xl lg:text-4xl rounded-lg hover:bg-nwoods-secondary " href="../">
19 <h1 class="mb-0 p-1 ">GoJS</h1>
20 </a>
21 </div>
22 <button id="topnavButton" class="rounded-lg sm:hidden focus:outline-none focus:ring" aria-label="Navigation">
23 <svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6">
24 <path id="topnavOpen" fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z" clip-rule="evenodd"></path>
25 <path id="topnavClosed" class="hidden" fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
26 </svg>
27 </button>
28 <div id="topnavList" class="hidden sm:block items-center w-auto mt-0 text-white p-0 z-20">
29 <ul class="list-reset list-none font-semibold flex justify-end flex-wrap sm:flex-nowrap items-center px-0 pb-0">
30 <li class="p-1 sm:p-0"><a class="topnav-link" href="../learn/">Learn</a></li>
31 <li class="p-1 sm:p-0"><a class="topnav-link" href="../samples/">Samples</a></li>
32 <li class="p-1 sm:p-0"><a class="topnav-link" href="../intro/">Intro</a></li>
33 <li class="p-1 sm:p-0"><a class="topnav-link" href="../api/">API</a></li>
34 <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/products/register.html">Register</a></li>
35 <li class="p-1 sm:p-0"><a class="topnav-link" href="../download.html">Download</a></li>
36 <li class="p-1 sm:p-0"><a class="topnav-link" href="https://forum.nwoods.com/c/gojs/11">Forum</a></li>
37 <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/contact.html"
38 target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/contact.html', 'contact');">Contact</a></li>
39 <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/sales/index.html"
40 target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/sales/index.html', 'buy');">Buy</a></li>
41 </ul>
42 </div>
43 </div>
44 <hr class="border-b border-gray-600 opacity-50 my-0 py-0" />
45 </nav>
46 <div class="md:flex flex-col md:flex-row md:min-h-screen w-full max-w-screen-xl mx-auto">
47 <div id="navSide" class="flex flex-col w-full md:w-48 text-gray-700 bg-white flex-shrink-0"></div>
48 <!-- * * * * * * * * * * * * * -->
49 <!-- Start of GoJS sample code -->
50
51
52 <div class="p-4 w-full">
53 <div id="sample">
54 <div style="margin-bottom: 5px; padding: 5px; background-color: aliceblue">
55 <span style="display: inline-block; vertical-align: top; padding: 5px">
56 <b>New Tree</b><br />
57 MinNodes: <input type="number" width="2" id="minNodes" value="300" /><br />
58 MaxNodes: <input type="number" width="2" id="maxNodes" value="500" /><br />
59 MinChildren: <input type="number" width="2" id="minChil" value="2" /><br />
60 MaxChildren: <input type="number" width="2" id="maxChil" value="5" /><br />
61 <button type="button" id="rebuildGraph">Generate Tree</button>
62 </span>
63 </div>
64 <div id="myDiagramDiv" style="background-color: white; border: solid 1px black; width: 100%; height: 500px"></div>
65 <p>
66 This sample demonstrates a custom Layout, TreeMapLayout, which assumes that the diagram consists of nested Groups and simple Nodes.
67 Each node is positioned and sized to fill an area of the viewport proportionate to its "size", as determined by its Node.data.size property.
68 Each Group gets a size that is the sum of all of its member Nodes.
69 </p>
70 <p>
71 The layout is defined in its own file, as <a href="TreeMapLayout.ts">TreeMapLayout.ts</a>.
72 </p>
73 <p>
74 Clicking repeatedly at the same point will initially select the Node at that point, and then its containing Group, and so on up the chain of containers.
75 </p>
76 </div>
77
78 <script type="module" id="code">
79 import * as go from "../release/go-module.js";
80 import { TreeMapLayout } from './TreeMapLayout.js';
81
82 if (window.goSamples) goSamples(); // init for the samples -- you don't need to call this
83 const $ = go.GraphObject.make; // for conciseness in defining templates
84
85 const myDiagram =
86 $(go.Diagram, 'myDiagramDiv', // must be the ID or reference to div
87 {
88 initialAutoScale: go.Diagram.Uniform,
89 'animationManager.isEnabled': false,
90 layout: $(TreeMapLayout, { isTopLevelHorizontal: false }),
91 allowMove: false, allowCopy: false, allowDelete: false
92 });
93
94 // change selection behavior to cycle up the chain of containing Groups
95 myDiagram.toolManager.clickSelectingTool.standardMouseSelect = function() {
96 const diagram = this.diagram;
97 if (diagram === null || !diagram.allowSelect) return;
98 const e = diagram.lastInput;
99 if (!(e.control || e.meta) && !e.shift) {
100 const part = diagram.findPartAt(e.documentPoint, false);
101 if (part !== null) {
102 let firstselected = null; // is this or any containing Group selected?
103 let node = part;
104 while (node !== null) {
105 if (node.isSelected) {
106 firstselected = node;
107 break;
108 } else {
109 node = node.containingGroup;
110 }
111 }
112 if (firstselected !== null) { // deselect this and select its containing Group
113 firstselected.isSelected = false;
114 const group = firstselected.containingGroup;
115 if (group !== null)
116 group.isSelected = true;
117 return;
118 }
119 }
120 }
121 go.ClickSelectingTool.prototype.standardMouseSelect.call(this);
122 };
123
124 // Nodes and Groups are the absolute minimum template: no elements at all!
125 myDiagram.nodeTemplate =
126 $(go.Node,
127 { background: 'rgba(99,99,99,0.2)' },
128 new go.Binding('background', 'fill'),
129 {
130 toolTip: $('ToolTip',
131 $(go.TextBlock, new go.Binding('text', '', tooltipString).ofObject()))
132 });
133
134 myDiagram.groupTemplate =
135 $(go.Group, 'Auto',
136 { layout: null },
137 { background: 'rgba(99,99,99,0.2)' },
138 new go.Binding('background', 'fill'),
139 {
140 toolTip: $('ToolTip',
141 $(go.TextBlock, new go.Binding('text', '', tooltipString).ofObject()))
142 });
143
144 function tooltipString(part) {
145 if (part instanceof go.Adornment && part.adornedPart !== null) part = part.adornedPart;
146 let msg = createPath(part);
147 msg += '\nsize: ' + part.data.size;
148 if (part instanceof go.Group) {
149 const group = part;
150 msg += '\n# children: ' + group.memberParts.count;
151 msg += '\nsubtotal size: ' + group.data.total;
152 }
153 return msg;
154 }
155
156 function createPath(part) {
157 const parent = part.containingGroup;
158 return (parent !== null ? createPath(parent) + '/' : '') + part.data.text;
159 }
160
161 // generate a tree with the default values
162 rebuildGraph();
163
164 function rebuildGraph() {
165 let minNodes = document.getElementById('minNodes').value;
166 minNodes = parseInt(minNodes, 10);
167 let maxNodes = document.getElementById('maxNodes').value;
168 maxNodes = parseInt(maxNodes, 10);
169 let minChil = document.getElementById('minChil').value;
170 minChil = parseInt(minChil, 10);
171 let maxChil = document.getElementById('maxChil').value;
172 maxChil = parseInt(maxChil, 10);
173 // create and assign a new model
174 const model = new go.GraphLinksModel();
175 model.nodeGroupKeyProperty = 'parent';
176 model.nodeDataArray = generateNodeData(minNodes, maxNodes, minChil, maxChil);
177 myDiagram.model = model;
178 }
179
180 // Creates a random number (between MIN and MAX) of randomly colored nodes.
181 function generateNodeData(minNodes, maxNodes, minChil, maxChil) {
182 const nodeArray = [];
183 if (minNodes === undefined || isNaN(minNodes) || minNodes < 1)
184 minNodes = 1;
185 if (maxNodes === undefined || isNaN(maxNodes) || maxNodes < minNodes)
186 maxNodes = minNodes;
187 // Create a bunch of node data
188 const numNodes = Math.floor(Math.random() * (maxNodes - minNodes + 1)) + minNodes;
189 for (let i = 0; i < numNodes; i++) {
190 const size = Math.random() * Math.random() * 10000; // non-uniform distribution
191 // nodeArray.push(new nodes(i, false, undefined, i.toString(), go.Brush.randomColor(), size, -1));
192 nodeArray.push({
193 key: i,
194 isGroup: false,
195 parent: undefined,
196 text: i.toString(),
197 fill: go.Brush.randomColor(),
198 size: size,
199 total: -1 // use a negative value to indicate that the total for the group has not been computed
200 });
201 }
202 // Takes the random collection of node data and creates a random tree with them.
203 // Respects the minimum and maximum number of links from each node.
204 // The minimum can be disregarded if we run out of nodes to link to.
205 if (nodeArray.length > 1) {
206 if (minChil === undefined || isNaN(minChil) || minChil < 0)
207 minChil = 0;
208 if (maxChil === undefined || isNaN(maxChil) || maxChil < minChil)
209 maxChil = minChil;
210 // keep the Set of node data that do not yet have a parent
211 const available = new go.Set();
212 available.addAll(nodeArray);
213 for (let i = 0; i < nodeArray.length; i++) {
214 const parent = nodeArray[i];
215 available.remove(parent);
216 // assign some number of node data as children of this parent node data
217 const children = Math.floor(Math.random() * (maxChil - minChil + 1)) + minChil;
218 for (let j = 0; j < children; j++) {
219 const child = available.first();
220 if (child === null)
221 break; // oops, ran out already
222 available.remove(child);
223 // have the child node data refer to the parent node data by its key
224 child.parent = parent.key;
225 if (!parent.isGroup) { // make sure PARENT is a group
226 parent.isGroup = true;
227 }
228 let par = parent;
229 while (par !== null) {
230 par.total += child.total; // sum up sizes of all children
231 if (par.parent !== undefined) {
232 par = nodeArray[par.parent];
233 }
234 else {
235 break;
236 }
237 }
238 }
239 }
240 }
241 return nodeArray;
242 }
243 document.getElementById("rebuildGraph").onclick = rebuildGraph;
244
245 window.myDiagram = myDiagram; // Attach to the window for console debugging
246 </script>
247 </div>
248 <!-- * * * * * * * * * * * * * -->
249 <!-- End of GoJS sample code -->
250 </div>
251</body>
252<!-- This script is part of the gojs.net website, and is not needed to run the sample -->
253<script src="../assets/js/goSamples.js"></script>
254</html>