UNPKG

14.6 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: When reshaping an orthogonal link, make sure the points are moved onto a grid."/>
7<link rel="stylesheet" href="../assets/css/style.css"/>
8<!-- Copyright 1998-2021 by Northwoods Software Corporation. -->
9<title>Snap Link Reshaping</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="width: 100%; display: flex; justify-content: space-between">
55 <div id="myPaletteDiv" style="width: 105px; height: 620px; margin-right: 2px; background-color: whitesmoke; border: solid 1px black"></div>
56 <div id="myDiagramDiv" style="flex-grow: 1; height: 620px; border: solid 1px black"></div>
57 </div>
58 <label><input type="checkbox" id="AvoidsNodesCheckBox" checked="checked"/>avoidsNodes</label>
59 <p>
60 This sample is a simplified version of the <a href="../samples/draggableLink.html">Draggable Link</a> sample.
61 Links are not draggable, there are no custom <a>Adornment</a>s, nodes are not rotatable, and links
62 do not have text labels.
63 </p>
64 <p>
65 Its purpose is to demonstrate the <a href="SnapLinkReshapingTool.ts">SnapLinkReshapingTool</a>,
66 an extension of <a>LinkReshapingTool</a> that snaps each dragged reshape handle of selected Links to
67 the nearest grid point. If the <a>SnapLinkReshapingTool.avoidsNodes</a> option is true,
68 as it is by default, then the reshaping is limited to points where the adjacent segments would not
69 be crossing over any avoidable nodes.
70 </p>
71 <p>
72 Note how the "LinkReshaped" DiagramEvent listener changes the <a>Link.routing</a> of the reshaped Link,
73 so that it is no longer AvoidsNodes routing but simple Orthogonal routing.
74 This combined with <a>Link.adjusting</a> being End permits the middle points of the link route to be
75 retained even after the user moves or resizes nodes.
76 Furthermore there is a TwoWay <a>Binding</a> on <a>Link.routing</a>, so that the model remembers
77 whether the link route had ever been reshaped manually.
78 </p>
79 <button id="SaveButton">Save</button>
80 <button id="LoadButton">Load</button>
81 <textarea id="mySavedModel" style="width:100%;height:300px">
82 { "class": "go.GraphLinksModel",
83 "linkFromPortIdProperty": "fromPort",
84 "linkToPortIdProperty": "toPort",
85 "modelData": {"position":"0 0"},
86 "nodeDataArray": [
87 {"text":"DB", "figure":"Database", "fill":"lightgray", "key":-3, "loc":"184 176"},
88 {"text":"DB", "figure":"Database", "fill":"lightgray", "key":-2, "loc":"248 248"},
89 {"text":"DB", "figure":"Database", "fill":"lightgray", "key":-4, "loc":"424 192"},
90 {"text":"DB", "figure":"Database", "fill":"lightgray", "key":-5, "loc":"320 152"},
91 {"text":"DB", "figure":"Database", "fill":"lightgray", "key":-6, "loc":"424 320"},
92 {"text":"DB", "figure":"Database", "fill":"lightgray", "key":-7, "loc":"352 256"},
93 {"text":"DB", "figure":"Database", "fill":"lightgray", "key":-8, "loc":"176 296"},
94 {"text":"DB", "figure":"Database", "fill":"lightgray", "key":-9, "loc":"288 344"},
95 {"text":"Step", "key":-10, "loc":"96 240"},
96 {"text":"Step", "key":-11, "loc":"536 280"}
97 ],
98 "linkDataArray": [
99 {"from":-10, "to":-11, "fromPort":"R", "toPort":"L", "points":[121,240,131,240,132,240,132,240,216,240,216,176,264,176,264,104,392,104,392,240,480,240,480,280,501,280,511,280], "routing":"Orthogonal"}
100 ]}
101 </textarea>
102 </div>
103
104 <script type="module" id="code">
105 import * as go from "../release/go-module.js";
106 import './Figures.js';
107 import { SnapLinkReshapingTool } from './SnapLinkReshapingTool.js';
108
109 if (window.goSamples) goSamples(); // init for the samples -- you don't need to call this
110 const $ = go.GraphObject.make; // for conciseness in defining templates
111
112 const myDiagram =
113 $(go.Diagram, 'myDiagramDiv', // must name or refer to the DIV HTML element
114 {
115 // supply a simple narrow grid that manually reshaped link routes will follow
116 grid: $(go.Panel, 'Grid', { gridCellSize: new go.Size(8, 8) }, $(go.Shape, 'LineH', { stroke: 'lightgray', strokeWidth: 0.5 }), $(go.Shape, 'LineV', { stroke: 'lightgray', strokeWidth: 0.5 })),
117 'draggingTool.isGridSnapEnabled': true,
118 linkReshapingTool: $(SnapLinkReshapingTool),
119 // when the user reshapes a Link, change its Link.routing from AvoidsNodes to Orthogonal,
120 // so that combined with Link.adjusting == End the link will retain its reshaped mid points
121 // even after nodes are moved
122 'LinkReshaped': (e) => { e.subject.routing = go.Link.Orthogonal; },
123 'animationManager.isEnabled': false,
124 'undoManager.isEnabled': true
125 });
126 // when the document is modified, add a "*" to the title and enable the "Save" button
127 myDiagram.addDiagramListener('Modified', (e) => {
128 const button = document.getElementById('SaveButton');
129 if (button)
130 button.disabled = !myDiagram.isModified;
131 const idx = document.title.indexOf('*');
132 if (myDiagram.isModified) {
133 if (idx < 0)
134 document.title += '*';
135 }
136 else {
137 if (idx >= 0)
138 document.title = document.title.substr(0, idx);
139 }
140 });
141 // Define a function for creating a "port" that is normally transparent.
142 // The "name" is used as the GraphObject.portId, the "spot" is used to control how links connect
143 // and where the port is positioned on the node, and the boolean "output" and "input" arguments
144 // control whether the user can draw links from or to the port.
145 function makePort(name, spot, output, input) {
146 // the port is basically just a small transparent square
147 return $(go.Shape, 'Circle', {
148 fill: null,
149 stroke: null,
150 desiredSize: new go.Size(7, 7),
151 alignment: spot,
152 alignmentFocus: spot,
153 portId: name,
154 fromSpot: spot, toSpot: spot,
155 fromLinkable: output, toLinkable: input,
156 cursor: 'pointer' // show a different cursor to indicate potential link point
157 });
158 }
159 myDiagram.nodeTemplate =
160 $(go.Node, 'Spot',
161 { locationSpot: go.Spot.Center },
162 new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify),
163 { selectable: true },
164 { resizable: true, resizeObjectName: 'PANEL' },
165 // the main object is a Panel that surrounds a TextBlock with a Shape
166 $(go.Panel, 'Auto',
167 { name: 'PANEL' },
168 new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify),
169 $(go.Shape, 'Rectangle', // default figure
170 {
171 portId: '',
172 fromLinkable: true, toLinkable: true, cursor: 'pointer',
173 fill: 'white' // default color
174 },
175 new go.Binding('figure'),
176 new go.Binding('fill')),
177 $(go.TextBlock,
178 {
179 font: 'bold 11pt Helvetica, Arial, sans-serif',
180 margin: 8,
181 maxSize: new go.Size(160, NaN),
182 wrap: go.TextBlock.WrapFit,
183 editable: true
184 },
185 new go.Binding('text').makeTwoWay())
186 ),
187 // four small named ports, one on each side:
188 makePort('T', go.Spot.Top, false, true),
189 makePort('L', go.Spot.Left, true, true),
190 makePort('R', go.Spot.Right, true, true),
191 makePort('B', go.Spot.Bottom, true, false),
192 {
193 mouseEnter: (e, node) => {
194 if (node instanceof go.Node)
195 showSmallPorts(node, true);
196 },
197 mouseLeave: (e, node) => {
198 if (node instanceof go.Node)
199 showSmallPorts(node, false);
200 }
201 });
202
203 function showSmallPorts(node, show) {
204 node.ports.each((port) => {
205 if (port.portId !== '') { // don't change the default port, which is the big shape
206 port.fill = show ? 'rgba(0,0,0,.3)' : null;
207 }
208 });
209 }
210
211 myDiagram.linkTemplate =
212 $(go.Link, // the whole link panel
213 { relinkableFrom: true, relinkableTo: true, reshapable: true, resegmentable: true },
214 {
215 routing: go.Link.AvoidsNodes,
216 adjusting: go.Link.End,
217 curve: go.Link.JumpOver,
218 corner: 5,
219 toShortLength: 4
220 },
221 new go.Binding('points').makeTwoWay(),
222 // remember the Link.routing too
223 new go.Binding('routing', 'routing', go.Binding.parseEnum(go.Link, go.Link.AvoidsNodes))
224 .makeTwoWay(go.Binding.toString),
225 $(go.Shape, // the link path shape
226 { isPanelMain: true, strokeWidth: 2 }),
227 $(go.Shape, // the arrowhead
228 { toArrow: 'Standard', stroke: null }));
229
230 load(); // load an initial diagram from some JSON text
231
232 const link = myDiagram.links.first();
233 if (link) link.isSelected = true;
234
235 // initialize the Palette that is on the left side of the page
236 const myPalette =
237 $(go.Palette, 'myPaletteDiv', // must name or refer to the DIV HTML element
238 {
239 maxSelectionCount: 1,
240 nodeTemplateMap: myDiagram.nodeTemplateMap,
241 model: new go.GraphLinksModel([
242 { text: 'Start', figure: 'Circle', fill: 'green' },
243 { text: 'Step' },
244 { text: 'DB', figure: 'Database', fill: 'lightgray' },
245 { text: '???', figure: 'Diamond', fill: 'lightskyblue' },
246 { text: 'End', figure: 'Circle', fill: 'red' },
247 { text: 'Comment', figure: 'RoundedRectangle', fill: 'lightyellow' }
248 ])
249 });
250
251 // Show the diagram's model in JSON format that the user may edit
252 function save() {
253 saveDiagramProperties(); // do this first, before writing to JSON
254 document.getElementById('mySavedModel').value = myDiagram.model.toJson();
255 myDiagram.isModified = false;
256 }
257
258 function load() {
259 myDiagram.model = go.Model.fromJson(document.getElementById('mySavedModel').value);
260 loadDiagramProperties();
261 }
262
263 function saveDiagramProperties() {
264 myDiagram.model.modelData.position = go.Point.stringify(myDiagram.position);
265 }
266
267 // Called by "InitialLayoutCompleted" DiagramEvent listener, NOT directly by load()!
268 function loadDiagramProperties() {
269 // set Diagram.initialPosition, not Diagram.position, to handle initialization side-effects
270 const pos = myDiagram.model.modelData.position;
271 if (pos) myDiagram.initialPosition = go.Point.parse(pos);
272 }
273
274 document.getElementById("SaveButton").onclick = save;
275 document.getElementById("LoadButton").onclick = load;
276 document.getElementById("AvoidsNodesCheckBox").onclick = function(e) {
277 myDiagram.toolManager.linkReshapingTool.avoidsNodes = e.target.checked;
278 }
279
280 window.myDiagram = myDiagram; // Attach to the window for console debugging
281 </script>
282 </div>
283 <!-- * * * * * * * * * * * * * -->
284 <!-- End of GoJS sample code -->
285 </div>
286</body>
287<!-- This script is part of the gojs.net website, and is not needed to run the sample -->
288<script src="../assets/js/goSamples.js"></script>
289</html>