UNPKG

26.8 kBJavaScriptView Raw
1/*
2* Copyright (C) 1998-2021 by Northwoods Software Corporation. All Rights Reserved.
3*/
4var __extends = (this && this.__extends) || (function () {
5 var extendStatics = function (d, b) {
6 extendStatics = Object.setPrototypeOf ||
7 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
8 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
9 return extendStatics(d, b);
10 };
11 return function (d, b) {
12 extendStatics(d, b);
13 function __() { this.constructor = d; }
14 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15 };
16})();
17(function (factory) {
18 if (typeof module === "object" && typeof module.exports === "object") {
19 var v = factory(require, exports);
20 if (v !== undefined) module.exports = v;
21 }
22 else if (typeof define === "function" && define.amd) {
23 define(["require", "exports", "../release/go.js"], factory);
24 }
25})(function (require, exports) {
26 "use strict";
27 Object.defineProperty(exports, "__esModule", { value: true });
28 exports.GuidedDraggingTool = void 0;
29 /*
30 * This is an extension and not part of the main GoJS library.
31 * Note that the API for this class may change with any version, even point releases.
32 * If you intend to use an extension in production, you should copy the code to your own source directory.
33 * Extensions can be found in the GoJS kit under the extensions or extensionsTS folders.
34 * See the Extensions intro page (https://gojs.net/latest/intro/extensions.html) for more information.
35 */
36 var go = require("../release/go.js");
37 /**
38 * The GuidedDraggingTool class makes guidelines visible as the parts are dragged around a diagram
39 * when the selected part is nearly aligned with another part.
40 *
41 * If you want to experiment with this extension, try the <a href="../../extensionsJSM/GuidedDragging.html">Guided Dragging</a> sample.
42 * @category Tool Extension
43 */
44 var GuidedDraggingTool = /** @class */ (function (_super) {
45 __extends(GuidedDraggingTool, _super);
46 /**
47 * Constructs a GuidedDraggingTool and sets up the temporary guideline parts.
48 */
49 function GuidedDraggingTool() {
50 var _this = _super.call(this) || this;
51 // properties that the programmer can modify
52 _this._guidelineSnapDistance = 6;
53 _this._isGuidelineEnabled = true;
54 _this._horizontalGuidelineColor = 'gray';
55 _this._verticalGuidelineColor = 'gray';
56 _this._centerGuidelineColor = 'gray';
57 _this._guidelineWidth = 1;
58 _this._searchDistance = 1000;
59 _this._isGuidelineSnapEnabled = true;
60 var partProperties = { layerName: 'Tool', isInDocumentBounds: false };
61 var shapeProperties = { stroke: 'gray', isGeometryPositioned: true };
62 var $ = go.GraphObject.make;
63 // temporary parts for horizonal guidelines
64 _this.guidelineHtop =
65 $(go.Part, partProperties, $(go.Shape, shapeProperties, { geometryString: 'M0 0 100 0' }));
66 _this.guidelineHbottom =
67 $(go.Part, partProperties, $(go.Shape, shapeProperties, { geometryString: 'M0 0 100 0' }));
68 _this.guidelineHcenter =
69 $(go.Part, partProperties, $(go.Shape, shapeProperties, { geometryString: 'M0 0 100 0' }));
70 // temporary parts for vertical guidelines
71 _this.guidelineVleft =
72 $(go.Part, partProperties, $(go.Shape, shapeProperties, { geometryString: 'M0 0 0 100' }));
73 _this.guidelineVright =
74 $(go.Part, partProperties, $(go.Shape, shapeProperties, { geometryString: 'M0 0 0 100' }));
75 _this.guidelineVcenter =
76 $(go.Part, partProperties, $(go.Shape, shapeProperties, { geometryString: 'M0 0 0 100' }));
77 return _this;
78 }
79 Object.defineProperty(GuidedDraggingTool.prototype, "guidelineSnapDistance", {
80 /**
81 * Gets or sets the margin of error for which guidelines show up.
82 *
83 * The default value is 6.
84 * Guidelines will show up when the aligned nods are ± 6px away from perfect alignment.
85 */
86 get: function () { return this._guidelineSnapDistance; },
87 set: function (val) {
88 if (typeof val !== 'number' || isNaN(val) || val < 0)
89 throw new Error('new value for GuideddraggingTool.guidelineSnapDistance must be a non-negative number');
90 if (this._guidelineSnapDistance !== val) {
91 this._guidelineSnapDistance = val;
92 }
93 },
94 enumerable: false,
95 configurable: true
96 });
97 Object.defineProperty(GuidedDraggingTool.prototype, "isGuidelineEnabled", {
98 /**
99 * Gets or sets whether the guidelines are enabled or disable.
100 *
101 * The default value is true.
102 */
103 get: function () { return this._isGuidelineEnabled; },
104 set: function (val) {
105 if (typeof val !== 'boolean')
106 throw new Error('new value for GuidedDraggingTool.isGuidelineEnabled must be a boolean value.');
107 if (this._isGuidelineEnabled !== val) {
108 this._isGuidelineEnabled = val;
109 }
110 },
111 enumerable: false,
112 configurable: true
113 });
114 Object.defineProperty(GuidedDraggingTool.prototype, "horizontalGuidelineColor", {
115 /**
116 * Gets or sets the color of horizontal guidelines.
117 *
118 * The default value is "gray".
119 */
120 get: function () { return this._horizontalGuidelineColor; },
121 set: function (val) {
122 if (this._horizontalGuidelineColor !== val) {
123 this._horizontalGuidelineColor = val;
124 this.guidelineHbottom.elements.first().stroke = this._horizontalGuidelineColor;
125 this.guidelineHtop.elements.first().stroke = this._horizontalGuidelineColor;
126 }
127 },
128 enumerable: false,
129 configurable: true
130 });
131 Object.defineProperty(GuidedDraggingTool.prototype, "verticalGuidelineColor", {
132 /**
133 * Gets or sets the color of vertical guidelines.
134 *
135 * The default value is "gray".
136 */
137 get: function () { return this._verticalGuidelineColor; },
138 set: function (val) {
139 if (this._verticalGuidelineColor !== val) {
140 this._verticalGuidelineColor = val;
141 this.guidelineVleft.elements.first().stroke = this._verticalGuidelineColor;
142 this.guidelineVright.elements.first().stroke = this._verticalGuidelineColor;
143 }
144 },
145 enumerable: false,
146 configurable: true
147 });
148 Object.defineProperty(GuidedDraggingTool.prototype, "centerGuidelineColor", {
149 /**
150 * Gets or sets the color of center guidelines.
151 *
152 * The default value is "gray".
153 */
154 get: function () { return this._centerGuidelineColor; },
155 set: function (val) {
156 if (this._centerGuidelineColor !== val) {
157 this._centerGuidelineColor = val;
158 this.guidelineVcenter.elements.first().stroke = this._centerGuidelineColor;
159 this.guidelineHcenter.elements.first().stroke = this._centerGuidelineColor;
160 }
161 },
162 enumerable: false,
163 configurable: true
164 });
165 Object.defineProperty(GuidedDraggingTool.prototype, "guidelineWidth", {
166 /**
167 * Gets or sets the width guidelines.
168 *
169 * The default value is 1.
170 */
171 get: function () { return this._guidelineWidth; },
172 set: function (val) {
173 if (typeof val !== 'number' || isNaN(val) || val < 0)
174 throw new Error('New value for GuidedDraggingTool.guidelineWidth must be a non-negative number.');
175 if (this._guidelineWidth !== val) {
176 this._guidelineWidth = val;
177 this.guidelineVcenter.elements.first().strokeWidth = val;
178 this.guidelineHcenter.elements.first().strokeWidth = val;
179 this.guidelineVleft.elements.first().strokeWidth = val;
180 this.guidelineVright.elements.first().strokeWidth = val;
181 this.guidelineHbottom.elements.first().strokeWidth = val;
182 this.guidelineHtop.elements.first().strokeWidth = val;
183 }
184 },
185 enumerable: false,
186 configurable: true
187 });
188 Object.defineProperty(GuidedDraggingTool.prototype, "searchDistance", {
189 /**
190 * Gets or sets the distance around the selected part to search for aligned parts.
191 *
192 * The default value is 1000.
193 * Set this to Infinity if you want to search the entire diagram no matter how far away.
194 */
195 get: function () { return this._searchDistance; },
196 set: function (val) {
197 if (typeof val !== 'number' || isNaN(val) || val <= 0)
198 throw new Error('new value for GuidedDraggingTool.searchDistance must be a positive number.');
199 if (this._searchDistance !== val) {
200 this._searchDistance = val;
201 }
202 },
203 enumerable: false,
204 configurable: true
205 });
206 Object.defineProperty(GuidedDraggingTool.prototype, "isGuidelineSnapEnabled", {
207 /**
208 * Gets or sets whether snapping to guidelines is enabled.
209 *
210 * The default value is true.
211 */
212 get: function () { return this._isGuidelineSnapEnabled; },
213 set: function (val) {
214 if (typeof val !== 'boolean')
215 throw new Error('new value for GuidedDraggingTool.isGuidelineSnapEnabled must be a boolean.');
216 if (this._isGuidelineSnapEnabled !== val) {
217 this._isGuidelineSnapEnabled = val;
218 }
219 },
220 enumerable: false,
221 configurable: true
222 });
223 /**
224 * Removes all of the guidelines from the grid.
225 */
226 GuidedDraggingTool.prototype.clearGuidelines = function () {
227 this.diagram.remove(this.guidelineHbottom);
228 this.diagram.remove(this.guidelineHcenter);
229 this.diagram.remove(this.guidelineHtop);
230 this.diagram.remove(this.guidelineVleft);
231 this.diagram.remove(this.guidelineVright);
232 this.diagram.remove(this.guidelineVcenter);
233 };
234 /**
235 * Calls the base method and removes the guidelines from the graph.
236 */
237 GuidedDraggingTool.prototype.doDeactivate = function () {
238 _super.prototype.doDeactivate.call(this);
239 // clear any guidelines when dragging is done
240 this.clearGuidelines();
241 };
242 /**
243 * Shows vertical and horizontal guidelines for the dragged part.
244 */
245 GuidedDraggingTool.prototype.doDragOver = function (pt, obj) {
246 // clear all existing guidelines in case either show... method decides to show a guideline
247 this.clearGuidelines();
248 // gets the selected part
249 var draggingParts = this.copiedParts || this.draggedParts;
250 if (draggingParts === null)
251 return;
252 var partItr = draggingParts.iterator;
253 if (partItr.next()) {
254 var part = partItr.key;
255 this.showHorizontalMatches(part, this.isGuidelineEnabled, false);
256 this.showVerticalMatches(part, this.isGuidelineEnabled, false);
257 }
258 };
259 /**
260 * On a mouse-up, snaps the selected part to the nearest guideline.
261 * If not snapping, the part remains at its position.
262 */
263 GuidedDraggingTool.prototype.doDropOnto = function (pt, obj) {
264 this.clearGuidelines();
265 // gets the selected (perhaps copied) Part
266 var draggingParts = this.copiedParts || this.draggedParts;
267 if (draggingParts === null)
268 return;
269 var partItr = draggingParts.iterator;
270 if (partItr.next()) {
271 var part = partItr.key;
272 // snaps only when the mouse is released without shift modifier
273 var e = this.diagram.lastInput;
274 var snap = this.isGuidelineSnapEnabled && !e.shift;
275 this.showHorizontalMatches(part, false, snap); // false means don't show guidelines
276 this.showVerticalMatches(part, false, snap);
277 }
278 };
279 /**
280 * When nodes are shifted due to being guided upon a drop, make sure all connected link routes are invalidated,
281 * since the node is likely to have moved a different amount than all its connected links in the regular
282 * operation of the DraggingTool.
283 */
284 GuidedDraggingTool.prototype.invalidateLinks = function (node) {
285 if (node instanceof go.Node)
286 node.invalidateConnectedLinks();
287 };
288 /**
289 * This predicate decides whether or not the given Part should guide the dragged part.
290 * @param {Part} part a stationary Part to which the dragged part might be aligned
291 * @param {Part} guidedpart the Part being dragged
292 */
293 GuidedDraggingTool.prototype.isGuiding = function (part, guidedpart) {
294 return part instanceof go.Part &&
295 !part.isSelected &&
296 !(part instanceof go.Link) &&
297 guidedpart instanceof go.Part &&
298 part.containingGroup === guidedpart.containingGroup &&
299 part.layer !== null && !part.layer.isTemporary;
300 };
301 /**
302 * This finds parts that are aligned near the selected part along horizontal lines. It compares the selected
303 * part to all parts within a rectangle approximately twice the {@link #searchDistance} wide.
304 * The guidelines appear when a part is aligned within a margin-of-error equal to {@link #guidelineSnapDistance}.
305 * @param {Node} part
306 * @param {boolean} guideline if true, show guideline
307 * @param {boolean} snap if true, snap the part to where the guideline would be
308 */
309 GuidedDraggingTool.prototype.showHorizontalMatches = function (part, guideline, snap) {
310 var _this = this;
311 var objBounds = part.locationObject.getDocumentBounds();
312 var p0 = objBounds.y;
313 var p1 = objBounds.y + objBounds.height / 2;
314 var p2 = objBounds.y + objBounds.height;
315 var marginOfError = this.guidelineSnapDistance;
316 var distance = this.searchDistance;
317 // compares with parts within narrow vertical area
318 var area = objBounds.copy();
319 area.inflate(distance, marginOfError + 1);
320 var otherObjs = this.diagram.findObjectsIn(area, function (obj) { return obj.part; }, function (p) { return _this.isGuiding(p, part); }, true);
321 var bestDiff = marginOfError;
322 var bestObj = null; // TS 2.6 won't let this be go.Part | null
323 var bestSpot = go.Spot.Default;
324 var bestOtherSpot = go.Spot.Default;
325 // horizontal line -- comparing y-values
326 otherObjs.each(function (other) {
327 if (other === part)
328 return; // ignore itself
329 var otherBounds = other.locationObject.getDocumentBounds();
330 var q0 = otherBounds.y;
331 var q1 = otherBounds.y + otherBounds.height / 2;
332 var q2 = otherBounds.y + otherBounds.height;
333 // compare center with center of OTHER part
334 if (Math.abs(p1 - q1) < bestDiff) {
335 bestDiff = Math.abs(p1 - q1);
336 bestObj = other;
337 bestSpot = go.Spot.Center;
338 bestOtherSpot = go.Spot.Center;
339 }
340 // compare top side with top and bottom sides of OTHER part
341 if (Math.abs(p0 - q0) < bestDiff) {
342 bestDiff = Math.abs(p0 - q0);
343 bestObj = other;
344 bestSpot = go.Spot.Top;
345 bestOtherSpot = go.Spot.Top;
346 }
347 else if (Math.abs(p0 - q2) < bestDiff) {
348 bestDiff = Math.abs(p0 - q2);
349 bestObj = other;
350 bestSpot = go.Spot.Top;
351 bestOtherSpot = go.Spot.Bottom;
352 }
353 // compare bottom side with top and bottom sides of OTHER part
354 if (Math.abs(p2 - q0) < bestDiff) {
355 bestDiff = Math.abs(p2 - q0);
356 bestObj = other;
357 bestSpot = go.Spot.Bottom;
358 bestOtherSpot = go.Spot.Top;
359 }
360 else if (Math.abs(p2 - q2) < bestDiff) {
361 bestDiff = Math.abs(p2 - q2);
362 bestObj = other;
363 bestSpot = go.Spot.Bottom;
364 bestOtherSpot = go.Spot.Bottom;
365 }
366 });
367 if (bestObj !== null) {
368 var offsetX = objBounds.x - part.actualBounds.x;
369 var offsetY = objBounds.y - part.actualBounds.y;
370 var bestBounds = bestObj.locationObject.getDocumentBounds();
371 // line extends from x0 to x2
372 var x0 = Math.min(objBounds.x, bestBounds.x) - 10;
373 var x2 = Math.max(objBounds.x + objBounds.width, bestBounds.x + bestBounds.width) + 10;
374 // find bestObj's desired Y
375 var bestPoint = new go.Point().setRectSpot(bestBounds, bestOtherSpot);
376 if (bestSpot === go.Spot.Center) {
377 if (snap) {
378 // call Part.move in order to automatically move member Parts of Groups
379 part.move(new go.Point(objBounds.x - offsetX, bestPoint.y - objBounds.height / 2 - offsetY));
380 this.invalidateLinks(part);
381 }
382 if (guideline) {
383 this.guidelineHcenter.position = new go.Point(x0, bestPoint.y);
384 this.guidelineHcenter.elt(0).width = x2 - x0;
385 this.diagram.add(this.guidelineHcenter);
386 }
387 }
388 else if (bestSpot === go.Spot.Top) {
389 if (snap) {
390 part.move(new go.Point(objBounds.x - offsetX, bestPoint.y - offsetY));
391 this.invalidateLinks(part);
392 }
393 if (guideline) {
394 this.guidelineHtop.position = new go.Point(x0, bestPoint.y);
395 this.guidelineHtop.elt(0).width = x2 - x0;
396 this.diagram.add(this.guidelineHtop);
397 }
398 }
399 else if (bestSpot === go.Spot.Bottom) {
400 if (snap) {
401 part.move(new go.Point(objBounds.x - offsetX, bestPoint.y - objBounds.height - offsetY));
402 this.invalidateLinks(part);
403 }
404 if (guideline) {
405 this.guidelineHbottom.position = new go.Point(x0, bestPoint.y);
406 this.guidelineHbottom.elt(0).width = x2 - x0;
407 this.diagram.add(this.guidelineHbottom);
408 }
409 }
410 }
411 };
412 /**
413 * This finds parts that are aligned near the selected part along vertical lines. It compares the selected
414 * part to all parts within a rectangle approximately twice the {@link #searchDistance} tall.
415 * The guidelines appear when a part is aligned within a margin-of-error equal to {@link #guidelineSnapDistance}.
416 * @param {Part} part
417 * @param {boolean} guideline if true, show guideline
418 * @param {boolean} snap if true, don't show guidelines but just snap the part to where the guideline would be
419 */
420 GuidedDraggingTool.prototype.showVerticalMatches = function (part, guideline, snap) {
421 var _this = this;
422 var objBounds = part.locationObject.getDocumentBounds();
423 var p0 = objBounds.x;
424 var p1 = objBounds.x + objBounds.width / 2;
425 var p2 = objBounds.x + objBounds.width;
426 var marginOfError = this.guidelineSnapDistance;
427 var distance = this.searchDistance;
428 // compares with parts within narrow vertical area
429 var area = objBounds.copy();
430 area.inflate(marginOfError + 1, distance);
431 var otherObjs = this.diagram.findObjectsIn(area, function (obj) { return obj.part; }, function (p) { return _this.isGuiding(p, part); }, true);
432 var bestDiff = marginOfError;
433 var bestObj = null; // TS 2.6 won't let this be go.Part | null
434 var bestSpot = go.Spot.Default;
435 var bestOtherSpot = go.Spot.Default;
436 // vertical line -- comparing x-values
437 otherObjs.each(function (other) {
438 if (other === part)
439 return; // ignore itself
440 var otherBounds = other.locationObject.getDocumentBounds();
441 var q0 = otherBounds.x;
442 var q1 = otherBounds.x + otherBounds.width / 2;
443 var q2 = otherBounds.x + otherBounds.width;
444 // compare center with center of OTHER part
445 if (Math.abs(p1 - q1) < bestDiff) {
446 bestDiff = Math.abs(p1 - q1);
447 bestObj = other;
448 bestSpot = go.Spot.Center;
449 bestOtherSpot = go.Spot.Center;
450 }
451 // compare left side with left and right sides of OTHER part
452 if (Math.abs(p0 - q0) < bestDiff) {
453 bestDiff = Math.abs(p0 - q0);
454 bestObj = other;
455 bestSpot = go.Spot.Left;
456 bestOtherSpot = go.Spot.Left;
457 }
458 else if (Math.abs(p0 - q2) < bestDiff) {
459 bestDiff = Math.abs(p0 - q2);
460 bestObj = other;
461 bestSpot = go.Spot.Left;
462 bestOtherSpot = go.Spot.Right;
463 }
464 // compare right side with left and right sides of OTHER part
465 if (Math.abs(p2 - q0) < bestDiff) {
466 bestDiff = Math.abs(p2 - q0);
467 bestObj = other;
468 bestSpot = go.Spot.Right;
469 bestOtherSpot = go.Spot.Left;
470 }
471 else if (Math.abs(p2 - q2) < bestDiff) {
472 bestDiff = Math.abs(p2 - q2);
473 bestObj = other;
474 bestSpot = go.Spot.Right;
475 bestOtherSpot = go.Spot.Right;
476 }
477 });
478 if (bestObj !== null) {
479 var offsetX = objBounds.x - part.actualBounds.x;
480 var offsetY = objBounds.y - part.actualBounds.y;
481 var bestBounds = bestObj.locationObject.getDocumentBounds();
482 // line extends from y0 to y2
483 var y0 = Math.min(objBounds.y, bestBounds.y) - 10;
484 var y2 = Math.max(objBounds.y + objBounds.height, bestBounds.y + bestBounds.height) + 10;
485 // find bestObj's desired X
486 var bestPoint = new go.Point().setRectSpot(bestBounds, bestOtherSpot);
487 if (bestSpot === go.Spot.Center) {
488 if (snap) {
489 // call Part.move in order to automatically move member Parts of Groups
490 part.move(new go.Point(bestPoint.x - objBounds.width / 2 - offsetX, objBounds.y - offsetY));
491 this.invalidateLinks(part);
492 }
493 if (guideline) {
494 this.guidelineVcenter.position = new go.Point(bestPoint.x, y0);
495 this.guidelineVcenter.elt(0).height = y2 - y0;
496 this.diagram.add(this.guidelineVcenter);
497 }
498 }
499 else if (bestSpot === go.Spot.Left) {
500 if (snap) {
501 part.move(new go.Point(bestPoint.x - offsetX, objBounds.y - offsetY));
502 this.invalidateLinks(part);
503 }
504 if (guideline) {
505 this.guidelineVleft.position = new go.Point(bestPoint.x, y0);
506 this.guidelineVleft.elt(0).height = y2 - y0;
507 this.diagram.add(this.guidelineVleft);
508 }
509 }
510 else if (bestSpot === go.Spot.Right) {
511 if (snap) {
512 part.move(new go.Point(bestPoint.x - objBounds.width - offsetX, objBounds.y - offsetY));
513 this.invalidateLinks(part);
514 }
515 if (guideline) {
516 this.guidelineVright.position = new go.Point(bestPoint.x, y0);
517 this.guidelineVright.elt(0).height = y2 - y0;
518 this.diagram.add(this.guidelineVright);
519 }
520 }
521 }
522 };
523 return GuidedDraggingTool;
524 }(go.DraggingTool));
525 exports.GuidedDraggingTool = GuidedDraggingTool;
526});