@joint/core 4.0.4 → 4.1.0
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.
- package/README.md +0 -8
- package/dist/geometry.js +4962 -6132
- package/dist/geometry.min.js +2 -2
- package/dist/joint.d.ts +328 -50
- package/dist/joint.js +34067 -37565
- package/dist/joint.min.js +2 -2
- package/dist/joint.nowrap.js +34067 -37565
- package/dist/joint.nowrap.min.js +2 -2
- package/dist/vectorizer.js +7288 -8907
- package/dist/vectorizer.min.js +2 -2
- package/dist/version.mjs +1 -1
- package/package.json +10 -15
- package/src/{linkTools → cellTools}/Button.mjs +8 -6
- package/src/{elementTools → cellTools}/Control.mjs +3 -3
- package/src/{linkTools → cellTools}/HoverConnect.mjs +1 -1
- package/src/dia/Cell.mjs +60 -33
- package/src/dia/CellView.mjs +75 -8
- package/src/dia/ElementView.mjs +13 -8
- package/src/dia/Graph.mjs +148 -40
- package/src/dia/HighlighterView.mjs +8 -4
- package/src/dia/LinkView.mjs +42 -3
- package/src/dia/Paper.mjs +84 -0
- package/src/dia/ToolView.mjs +29 -4
- package/src/dia/ToolsView.mjs +25 -10
- package/src/dia/attributes/connection.mjs +5 -0
- package/src/dia/attributes/defs.mjs +3 -0
- package/src/dia/attributes/eval.mjs +3 -3
- package/src/dia/attributes/index.mjs +3 -0
- package/src/dia/attributes/shape.mjs +4 -0
- package/src/dia/attributes/text.mjs +15 -5
- package/src/dia/ports.mjs +4 -0
- package/src/elementTools/HoverConnect.mjs +5 -5
- package/src/elementTools/index.mjs +5 -4
- package/src/g/rect.mjs +13 -5
- package/src/layout/ports/port.mjs +4 -5
- package/src/linkTools/Anchor.mjs +1 -1
- package/src/linkTools/Arrowhead.mjs +2 -1
- package/src/linkTools/RotateLabel.mjs +110 -0
- package/src/linkTools/Segments.mjs +1 -1
- package/src/linkTools/Vertices.mjs +41 -4
- package/src/linkTools/index.mjs +7 -4
- package/src/mvc/View.mjs +0 -1
- package/src/mvc/ViewBase.mjs +2 -1
- package/src/routers/rightAngle.mjs +538 -140
- package/src/shapes/standard.mjs +8 -1
- package/src/{dia/attributes → util}/calc.mjs +24 -12
- package/src/util/index.mjs +1 -0
- package/src/util/util.mjs +39 -0
- package/src/util/utilHelpers.mjs +2 -1
- package/types/geometry.d.ts +6 -1
- package/types/joint.d.ts +321 -48
- /package/src/{linkTools → cellTools}/Boundary.mjs +0 -0
- /package/src/{linkTools → cellTools}/Connect.mjs +0 -0
- /package/src/{linkTools → cellTools}/helpers.mjs +0 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { evalCalcAttribute, isCalcAttribute } from '../dia/attributes/calc.mjs';
|
|
2
1
|
import { ToolView } from '../dia/ToolView.mjs';
|
|
3
2
|
import { getViewBBox } from './helpers.mjs';
|
|
4
3
|
import * as util from '../util/index.mjs';
|
|
@@ -41,13 +40,13 @@ export const Button = ToolView.extend({
|
|
|
41
40
|
const { x: offsetX = 0, y: offsetY = 0 } = offset;
|
|
42
41
|
if (util.isPercentage(x)) {
|
|
43
42
|
x = parseFloat(x) / 100 * bbox.width;
|
|
44
|
-
} else if (
|
|
45
|
-
x = Number(
|
|
43
|
+
} else if (util.isCalcExpression(x)) {
|
|
44
|
+
x = Number(util.evalCalcExpression(x, bbox));
|
|
46
45
|
}
|
|
47
46
|
if (util.isPercentage(y)) {
|
|
48
47
|
y = parseFloat(y) / 100 * bbox.height;
|
|
49
|
-
} else if (
|
|
50
|
-
y = Number(
|
|
48
|
+
} else if (util.isCalcExpression(y)) {
|
|
49
|
+
y = Number(util.evalCalcExpression(y, bbox));
|
|
51
50
|
}
|
|
52
51
|
let matrix = V.createSVGMatrix().translate(bbox.x + bbox.width / 2, bbox.y + bbox.height / 2);
|
|
53
52
|
if (rotate) matrix = matrix.rotate(angle);
|
|
@@ -57,7 +56,10 @@ export const Button = ToolView.extend({
|
|
|
57
56
|
},
|
|
58
57
|
getLinkMatrix() {
|
|
59
58
|
const { relatedView: view, options } = this;
|
|
60
|
-
const { offset = 0, distance = 0, rotate, scale } = options;
|
|
59
|
+
const { offset = 0, distance: distanceOpt = 0, rotate, scale } = options;
|
|
60
|
+
const distance = (typeof distanceOpt === 'function')
|
|
61
|
+
? distanceOpt.call(this, view, this)
|
|
62
|
+
: distanceOpt;
|
|
61
63
|
let tangent, position, angle;
|
|
62
64
|
if (util.isPercentage(distance)) {
|
|
63
65
|
tangent = view.getTangentAtRatio(parseFloat(distance) / 100);
|
|
@@ -133,7 +133,7 @@ export const Control = ToolView.extend({
|
|
|
133
133
|
const { clientX, clientY } = util.normalizeEvent(evt);
|
|
134
134
|
const coords = paper.clientToLocalPoint(clientX, clientY);
|
|
135
135
|
const relativeCoords = model.getRelativePointFromAbsolute(coords);
|
|
136
|
-
this.setPosition(relatedView, relativeCoords,
|
|
136
|
+
this.setPosition(relatedView, relativeCoords, evt);
|
|
137
137
|
this.update();
|
|
138
138
|
},
|
|
139
139
|
onPointerUp: function(_evt) {
|
|
@@ -144,9 +144,9 @@ export const Control = ToolView.extend({
|
|
|
144
144
|
this.toggleExtras(false);
|
|
145
145
|
relatedView.model.stopBatch('control-move', { ui: true, tool: this.cid });
|
|
146
146
|
},
|
|
147
|
-
onPointerDblClick: function() {
|
|
147
|
+
onPointerDblClick: function(evt) {
|
|
148
148
|
const { relatedView } = this;
|
|
149
|
-
this.resetPosition(relatedView,
|
|
149
|
+
this.resetPosition(relatedView, evt);
|
|
150
150
|
this.update();
|
|
151
151
|
}
|
|
152
152
|
|
package/src/dia/Cell.mjs
CHANGED
|
@@ -25,7 +25,8 @@ import {
|
|
|
25
25
|
defaultsDeep,
|
|
26
26
|
has,
|
|
27
27
|
sortBy,
|
|
28
|
-
defaults
|
|
28
|
+
defaults,
|
|
29
|
+
objectDifference
|
|
29
30
|
} from '../util/util.mjs';
|
|
30
31
|
import { Model } from '../mvc/Model.mjs';
|
|
31
32
|
import { cloneCells } from '../util/cloneCells.mjs';
|
|
@@ -42,6 +43,22 @@ const attributesMerger = function(a, b) {
|
|
|
42
43
|
}
|
|
43
44
|
};
|
|
44
45
|
|
|
46
|
+
function removeEmptyAttributes(obj) {
|
|
47
|
+
|
|
48
|
+
// Remove toplevel empty attributes
|
|
49
|
+
for (const key in obj) {
|
|
50
|
+
|
|
51
|
+
const objValue = obj[key];
|
|
52
|
+
const isRealObject = isObject(objValue) && !Array.isArray(objValue);
|
|
53
|
+
|
|
54
|
+
if (!isRealObject) continue;
|
|
55
|
+
|
|
56
|
+
if (isEmpty(objValue)) {
|
|
57
|
+
delete obj[key];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
45
62
|
export const Cell = Model.extend({
|
|
46
63
|
|
|
47
64
|
// This is the same as mvc.Model with the only difference that is uses util.merge
|
|
@@ -75,48 +92,45 @@ export const Cell = Model.extend({
|
|
|
75
92
|
throw new Error('Must define a translate() method.');
|
|
76
93
|
},
|
|
77
94
|
|
|
78
|
-
toJSON: function() {
|
|
95
|
+
toJSON: function(opt) {
|
|
79
96
|
|
|
97
|
+
const { ignoreDefaults, ignoreEmptyAttributes = false } = opt || {};
|
|
80
98
|
const defaults = result(this.constructor.prototype, 'defaults');
|
|
81
|
-
const defaultAttrs = defaults.attrs || {};
|
|
82
|
-
const attrs = this.attributes.attrs;
|
|
83
|
-
const finalAttrs = {};
|
|
84
|
-
|
|
85
|
-
// Loop through all the attributes and
|
|
86
|
-
// omit the default attributes as they are implicitly reconstructible by the cell 'type'.
|
|
87
|
-
forIn(attrs, function(attr, selector) {
|
|
88
99
|
|
|
89
|
-
|
|
100
|
+
if (ignoreDefaults === false) {
|
|
101
|
+
// Return all attributes without omitting the defaults
|
|
102
|
+
const finalAttributes = cloneDeep(this.attributes);
|
|
90
103
|
|
|
91
|
-
|
|
104
|
+
if (!ignoreEmptyAttributes) return finalAttributes;
|
|
92
105
|
|
|
93
|
-
|
|
94
|
-
// Check if the `value` is object and if yes, go one level deep.
|
|
95
|
-
if (isObject(value) && !Array.isArray(value)) {
|
|
106
|
+
removeEmptyAttributes(finalAttributes);
|
|
96
107
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (!defaultAttr || !defaultAttr[name] || !isEqual(defaultAttr[name][name2], value2)) {
|
|
108
|
+
return finalAttributes;
|
|
109
|
+
}
|
|
100
110
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
});
|
|
111
|
+
let defaultAttributes = {};
|
|
112
|
+
let attributes = cloneDeep(this.attributes);
|
|
105
113
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
114
|
+
if (ignoreDefaults === true) {
|
|
115
|
+
// Compare all attributes with the defaults
|
|
116
|
+
defaultAttributes = defaults;
|
|
117
|
+
} else {
|
|
118
|
+
// Compare only the specified attributes with the defaults, use `attrs` as a default if not specified
|
|
119
|
+
const differentiateKeys = Array.isArray(ignoreDefaults) ? ignoreDefaults : ['attrs'];
|
|
109
120
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
121
|
+
differentiateKeys.forEach((key) => {
|
|
122
|
+
defaultAttributes[key] = defaults[key] || {};
|
|
113
123
|
});
|
|
114
|
-
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Omit `id` and `type` attribute from the defaults since it should be always present
|
|
127
|
+
const finalAttributes = objectDifference(attributes, omit(defaultAttributes, 'id', 'type'), { maxDepth: 4 });
|
|
115
128
|
|
|
116
|
-
|
|
117
|
-
|
|
129
|
+
if (ignoreEmptyAttributes) {
|
|
130
|
+
removeEmptyAttributes(finalAttributes);
|
|
131
|
+
}
|
|
118
132
|
|
|
119
|
-
return
|
|
133
|
+
return finalAttributes;
|
|
120
134
|
},
|
|
121
135
|
|
|
122
136
|
initialize: function(options) {
|
|
@@ -325,12 +339,25 @@ export const Cell = Model.extend({
|
|
|
325
339
|
return this.set('parent', parent, opt);
|
|
326
340
|
},
|
|
327
341
|
|
|
328
|
-
embed: function(cell, opt) {
|
|
342
|
+
embed: function(cell, opt = {}) {
|
|
329
343
|
const cells = Array.isArray(cell) ? cell : [cell];
|
|
330
344
|
if (!this.canEmbed(cells)) {
|
|
331
345
|
throw new Error('Recursive embedding not allowed.');
|
|
332
346
|
}
|
|
333
|
-
if (
|
|
347
|
+
if (opt.reparent) {
|
|
348
|
+
const parents = uniq(cells.map(c => c.getParentCell()));
|
|
349
|
+
|
|
350
|
+
// Unembed cells from their current parents.
|
|
351
|
+
parents.forEach((parent) => {
|
|
352
|
+
// Cell doesn't have to be embedded.
|
|
353
|
+
if (!parent) return;
|
|
354
|
+
|
|
355
|
+
// Pass all the `cells` since the `dia.Cell._unembedCells` method can handle cases
|
|
356
|
+
// where not all elements of `cells` are embedded in the same parent.
|
|
357
|
+
parent._unembedCells(cells, opt);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
} else if (cells.some(c => c.isEmbedded() && this.id !== c.parent())) {
|
|
334
361
|
throw new Error('Embedding of already embedded cells is not allowed.');
|
|
335
362
|
}
|
|
336
363
|
this._embedCells(cells, opt);
|
package/src/dia/CellView.mjs
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
merge,
|
|
17
17
|
uniq
|
|
18
18
|
} from '../util/index.mjs';
|
|
19
|
-
import { Point, Rect } from '../g/index.mjs';
|
|
19
|
+
import { Point, Rect, intersection } from '../g/index.mjs';
|
|
20
20
|
import V from '../V/index.mjs';
|
|
21
21
|
import $ from '../mvc/Dom/index.mjs';
|
|
22
22
|
import { HighlighterView } from './HighlighterView.mjs';
|
|
@@ -559,13 +559,54 @@ export const CellView = View.extend({
|
|
|
559
559
|
if (!rawAttrs.hasOwnProperty(attrName)) continue;
|
|
560
560
|
attrVal = rawAttrs[attrName];
|
|
561
561
|
def = this.getAttributeDefinition(attrName);
|
|
562
|
-
if (def
|
|
563
|
-
if (
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
562
|
+
if (def) {
|
|
563
|
+
if (attrVal === null) {
|
|
564
|
+
// Assign the unset attribute name.
|
|
565
|
+
let unsetAttrName;
|
|
566
|
+
if (isFunction(def.unset)) {
|
|
567
|
+
unsetAttrName = def.unset.call(this, node, rawAttrs, this);
|
|
568
|
+
} else {
|
|
569
|
+
unsetAttrName = def.unset;
|
|
570
|
+
}
|
|
571
|
+
if (!unsetAttrName && isString(def.set)) {
|
|
572
|
+
// We unset an alias attribute.
|
|
573
|
+
unsetAttrName = def.set;
|
|
574
|
+
}
|
|
575
|
+
if (!unsetAttrName) {
|
|
576
|
+
// There is no alias for the attribute. We unset the attribute itself.
|
|
577
|
+
unsetAttrName = attrName;
|
|
578
|
+
}
|
|
579
|
+
// Unset the attribute.
|
|
580
|
+
if (isString(unsetAttrName) && unsetAttrName) {
|
|
581
|
+
// Unset a single attribute.
|
|
582
|
+
normalAttrs || (normalAttrs = {});
|
|
583
|
+
// values takes precedence over unset values
|
|
584
|
+
if (unsetAttrName in normalAttrs) continue;
|
|
585
|
+
normalAttrs[unsetAttrName] = attrVal;
|
|
586
|
+
} else if (Array.isArray(unsetAttrName) && unsetAttrName.length > 0) {
|
|
587
|
+
// Unset multiple attributes.
|
|
588
|
+
normalAttrs || (normalAttrs = {});
|
|
589
|
+
for (i = 0, n = unsetAttrName.length; i < n; i++) {
|
|
590
|
+
const attrName = unsetAttrName[i];
|
|
591
|
+
// values takes precedence over unset values
|
|
592
|
+
if (attrName in normalAttrs) continue;
|
|
593
|
+
normalAttrs[attrName] = attrVal;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
// The unset value is neither a string nor an array.
|
|
597
|
+
// The attribute is not unset.
|
|
598
|
+
} else {
|
|
599
|
+
if (!isFunction(def.qualify) || def.qualify.call(this, attrVal, node, rawAttrs, this)) {
|
|
600
|
+
if (isString(def.set)) {
|
|
601
|
+
// An alias e.g 'xlink:href' -> 'href'
|
|
602
|
+
normalAttrs || (normalAttrs = {});
|
|
603
|
+
normalAttrs[def.set] = attrVal;
|
|
604
|
+
}
|
|
605
|
+
relatives.push(attrName, def);
|
|
606
|
+
} else {
|
|
607
|
+
normalAttrs || (normalAttrs = {});
|
|
608
|
+
normalAttrs[attrName] = attrVal;
|
|
609
|
+
}
|
|
569
610
|
}
|
|
570
611
|
} else {
|
|
571
612
|
normalAttrs || (normalAttrs = {});
|
|
@@ -727,6 +768,12 @@ export const CellView = View.extend({
|
|
|
727
768
|
this.metrics = {};
|
|
728
769
|
},
|
|
729
770
|
|
|
771
|
+
cleanNodeCache: function(node) {
|
|
772
|
+
const id = node.id;
|
|
773
|
+
if (!id) return;
|
|
774
|
+
delete this.metrics[id];
|
|
775
|
+
},
|
|
776
|
+
|
|
730
777
|
nodeCache: function(magnet) {
|
|
731
778
|
|
|
732
779
|
var metrics = this.metrics;
|
|
@@ -1279,7 +1326,27 @@ export const CellView = View.extend({
|
|
|
1279
1326
|
setInteractivity: function(value) {
|
|
1280
1327
|
|
|
1281
1328
|
this.options.interactive = value;
|
|
1329
|
+
},
|
|
1330
|
+
|
|
1331
|
+
isIntersecting: function(geometryShape, geometryData) {
|
|
1332
|
+
return intersection.exists(geometryShape, this.getNodeBBox(this.el), geometryData);
|
|
1333
|
+
},
|
|
1334
|
+
|
|
1335
|
+
isEnclosedIn: function(geometryRect) {
|
|
1336
|
+
return geometryRect.containsRect(this.getNodeBBox(this.el));
|
|
1337
|
+
},
|
|
1338
|
+
|
|
1339
|
+
isInArea: function(geometryRect, options = {}) {
|
|
1340
|
+
if (options.strict) {
|
|
1341
|
+
return this.isEnclosedIn(geometryRect);
|
|
1342
|
+
}
|
|
1343
|
+
return this.isIntersecting(geometryRect);
|
|
1344
|
+
},
|
|
1345
|
+
|
|
1346
|
+
isAtPoint: function(point, options) {
|
|
1347
|
+
return this.getNodeBBox(this.el).containsPoint(point, options);
|
|
1282
1348
|
}
|
|
1349
|
+
|
|
1283
1350
|
}, {
|
|
1284
1351
|
|
|
1285
1352
|
Flags,
|
package/src/dia/ElementView.mjs
CHANGED
|
@@ -265,16 +265,16 @@ export const ElementView = CellView.extend({
|
|
|
265
265
|
|
|
266
266
|
getTranslateString: function() {
|
|
267
267
|
|
|
268
|
-
|
|
269
|
-
return
|
|
268
|
+
const { x, y } = this.model.position();
|
|
269
|
+
return `translate(${x},${y})`;
|
|
270
270
|
},
|
|
271
271
|
|
|
272
272
|
getRotateString: function() {
|
|
273
|
-
|
|
274
|
-
|
|
273
|
+
|
|
274
|
+
const angle = this.model.angle();
|
|
275
275
|
if (!angle) return null;
|
|
276
|
-
|
|
277
|
-
return
|
|
276
|
+
const { width, height } = this.model.size();
|
|
277
|
+
return `rotate(${angle},${width / 2},${height / 2})`;
|
|
278
278
|
},
|
|
279
279
|
|
|
280
280
|
// Rotatable & Scalable Group
|
|
@@ -404,9 +404,9 @@ export const ElementView = CellView.extend({
|
|
|
404
404
|
if (isFunction(findParentBy)) {
|
|
405
405
|
candidates = toArray(findParentBy.call(graph, this, evt, x, y));
|
|
406
406
|
} else if (findParentBy === 'pointer') {
|
|
407
|
-
candidates =
|
|
407
|
+
candidates = graph.findElementsAtPoint({ x, y });
|
|
408
408
|
} else {
|
|
409
|
-
candidates = graph.
|
|
409
|
+
candidates = graph.findElementsUnderElement(model, { searchBy: findParentBy });
|
|
410
410
|
}
|
|
411
411
|
|
|
412
412
|
candidates = candidates.filter((el) => {
|
|
@@ -540,6 +540,11 @@ export const ElementView = CellView.extend({
|
|
|
540
540
|
}
|
|
541
541
|
},
|
|
542
542
|
|
|
543
|
+
getTargetParentView: function(evt) {
|
|
544
|
+
const { candidateEmbedView = null } = this.eventData(evt);
|
|
545
|
+
return candidateEmbedView;
|
|
546
|
+
},
|
|
547
|
+
|
|
543
548
|
getDelegatedView: function() {
|
|
544
549
|
|
|
545
550
|
var view = this;
|
package/src/dia/Graph.mjs
CHANGED
|
@@ -35,14 +35,25 @@ const GraphCells = Collection.extend({
|
|
|
35
35
|
throw new Error(`dia.Graph: Could not find cell constructor for type: '${type}'. Make sure to add the constructor to 'cellNamespace'.`);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
return new ModelClass(attrs, opt);
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
_addReference: function(model, options) {
|
|
42
|
+
Collection.prototype._addReference.apply(this, arguments);
|
|
43
|
+
// If not in `dry` mode and the model does not have a graph reference yet,
|
|
44
|
+
// set the reference.
|
|
45
|
+
if (!options.dry && !model.graph) {
|
|
46
|
+
model.graph = this.graph;
|
|
43
47
|
}
|
|
48
|
+
},
|
|
44
49
|
|
|
45
|
-
|
|
50
|
+
_removeReference: function(model, options) {
|
|
51
|
+
Collection.prototype._removeReference.apply(this, arguments);
|
|
52
|
+
// If not in `dry` mode and the model has a reference to this exact graph,
|
|
53
|
+
// remove the reference.
|
|
54
|
+
if (!options.dry && model.graph === this.graph) {
|
|
55
|
+
model.graph = null;
|
|
56
|
+
}
|
|
46
57
|
},
|
|
47
58
|
|
|
48
59
|
// `comparator` makes it easy to sort cells based on their `z` index.
|
|
@@ -197,12 +208,12 @@ export const Graph = Model.extend({
|
|
|
197
208
|
return (this._in && this._in[node]) || {};
|
|
198
209
|
},
|
|
199
210
|
|
|
200
|
-
toJSON: function() {
|
|
211
|
+
toJSON: function(opt = {}) {
|
|
201
212
|
|
|
202
213
|
// JointJS does not recursively call `toJSON()` on attributes that are themselves models/collections.
|
|
203
214
|
// It just clones the attributes. Therefore, we must call `toJSON()` on the cells collection explicitly.
|
|
204
215
|
var json = Model.prototype.toJSON.apply(this, arguments);
|
|
205
|
-
json.cells = this.get('cells').toJSON();
|
|
216
|
+
json.cells = this.get('cells').toJSON(opt.cellAttributes);
|
|
206
217
|
return json;
|
|
207
218
|
},
|
|
208
219
|
|
|
@@ -268,20 +279,12 @@ export const Graph = Model.extend({
|
|
|
268
279
|
return this;
|
|
269
280
|
},
|
|
270
281
|
|
|
271
|
-
_prepareCell: function(cell
|
|
282
|
+
_prepareCell: function(cell) {
|
|
272
283
|
|
|
273
|
-
|
|
284
|
+
let attrs;
|
|
274
285
|
if (cell instanceof Model) {
|
|
275
286
|
attrs = cell.attributes;
|
|
276
|
-
if (!cell.graph && (!opt || !opt.dry)) {
|
|
277
|
-
// An element can not be member of more than one graph.
|
|
278
|
-
// A cell stops being the member of the graph after it's explicitly removed.
|
|
279
|
-
cell.graph = this;
|
|
280
|
-
}
|
|
281
287
|
} else {
|
|
282
|
-
// In case we're dealing with a plain JS object, we have to set the reference
|
|
283
|
-
// to the `graph` right after the actual model is created. This happens in the `model()` function
|
|
284
|
-
// of `joint.dia.GraphCells`.
|
|
285
288
|
attrs = cell;
|
|
286
289
|
}
|
|
287
290
|
|
|
@@ -391,11 +394,39 @@ export const Graph = Model.extend({
|
|
|
391
394
|
// then propagated to the graph model. If we didn't remove the cell silently, two `remove` events
|
|
392
395
|
// would be triggered on the graph model.
|
|
393
396
|
this.get('cells').remove(cell, { silent: true });
|
|
397
|
+
},
|
|
394
398
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
+
transferCellEmbeds: function(sourceCell, targetCell, opt = {}) {
|
|
400
|
+
|
|
401
|
+
const batchName = 'transfer-embeds';
|
|
402
|
+
this.startBatch(batchName);
|
|
403
|
+
|
|
404
|
+
// Embed children of the source cell in the target cell.
|
|
405
|
+
const children = sourceCell.getEmbeddedCells();
|
|
406
|
+
targetCell.embed(children, { ...opt, reparent: true });
|
|
407
|
+
|
|
408
|
+
this.stopBatch(batchName);
|
|
409
|
+
},
|
|
410
|
+
|
|
411
|
+
transferCellConnectedLinks: function(sourceCell, targetCell, opt = {}) {
|
|
412
|
+
|
|
413
|
+
const batchName = 'transfer-connected-links';
|
|
414
|
+
this.startBatch(batchName);
|
|
415
|
+
|
|
416
|
+
// Reconnect all the links connected to the old cell to the new cell.
|
|
417
|
+
const connectedLinks = this.getConnectedLinks(sourceCell, opt);
|
|
418
|
+
connectedLinks.forEach((link) => {
|
|
419
|
+
|
|
420
|
+
if (link.getSourceCell() === sourceCell) {
|
|
421
|
+
link.prop(['source', 'id'], targetCell.id, opt);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (link.getTargetCell() === sourceCell) {
|
|
425
|
+
link.prop(['target', 'id'], targetCell.id, opt);
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
this.stopBatch(batchName);
|
|
399
430
|
},
|
|
400
431
|
|
|
401
432
|
// Get a cell by `id`.
|
|
@@ -961,28 +992,105 @@ export const Graph = Model.extend({
|
|
|
961
992
|
util.invoke(this.getConnectedLinks(model), 'remove', opt);
|
|
962
993
|
},
|
|
963
994
|
|
|
964
|
-
// Find all
|
|
965
|
-
|
|
966
|
-
|
|
995
|
+
// Find all cells at given point
|
|
996
|
+
|
|
997
|
+
findElementsAtPoint: function(point, opt) {
|
|
998
|
+
return this._filterAtPoint(this.getElements(), point, opt);
|
|
999
|
+
},
|
|
1000
|
+
|
|
1001
|
+
findLinksAtPoint: function(point, opt) {
|
|
1002
|
+
return this._filterAtPoint(this.getLinks(), point, opt);
|
|
1003
|
+
},
|
|
1004
|
+
|
|
1005
|
+
findCellsAtPoint: function(point, opt) {
|
|
1006
|
+
return this._filterAtPoint(this.getCells(), point, opt);
|
|
1007
|
+
},
|
|
1008
|
+
|
|
1009
|
+
_filterAtPoint: function(cells, point, opt = {}) {
|
|
1010
|
+
return cells.filter(el => el.getBBox({ rotate: true }).containsPoint(point, opt));
|
|
1011
|
+
},
|
|
1012
|
+
|
|
1013
|
+
// Find all cells in given area
|
|
1014
|
+
|
|
1015
|
+
findElementsInArea: function(area, opt = {}) {
|
|
1016
|
+
return this._filterInArea(this.getElements(), area, opt);
|
|
1017
|
+
},
|
|
1018
|
+
|
|
1019
|
+
findLinksInArea: function(area, opt = {}) {
|
|
1020
|
+
return this._filterInArea(this.getLinks(), area, opt);
|
|
967
1021
|
},
|
|
968
1022
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1023
|
+
findCellsInArea: function(area, opt = {}) {
|
|
1024
|
+
return this._filterInArea(this.getCells(), area, opt);
|
|
1025
|
+
},
|
|
1026
|
+
|
|
1027
|
+
_filterInArea: function(cells, area, opt = {}) {
|
|
1028
|
+
const r = new g.Rect(area);
|
|
972
1029
|
const { strict = false } = opt;
|
|
973
1030
|
const method = strict ? 'containsRect' : 'intersect';
|
|
974
|
-
return
|
|
975
|
-
},
|
|
976
|
-
|
|
977
|
-
// Find all
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
1031
|
+
return cells.filter(el => r[method](el.getBBox({ rotate: true })));
|
|
1032
|
+
},
|
|
1033
|
+
|
|
1034
|
+
// Find all cells under the given element.
|
|
1035
|
+
|
|
1036
|
+
findElementsUnderElement: function(element, opt) {
|
|
1037
|
+
return this._filterCellsUnderElement(this.getElements(), element, opt);
|
|
1038
|
+
},
|
|
1039
|
+
|
|
1040
|
+
findLinksUnderElement: function(element, opt) {
|
|
1041
|
+
return this._filterCellsUnderElement(this.getLinks(), element, opt);
|
|
1042
|
+
},
|
|
1043
|
+
|
|
1044
|
+
findCellsUnderElement: function(element, opt) {
|
|
1045
|
+
return this._filterCellsUnderElement(this.getCells(), element, opt);
|
|
1046
|
+
},
|
|
1047
|
+
|
|
1048
|
+
_isValidElementUnderElement: function(el1, el2) {
|
|
1049
|
+
return el1.id !== el2.id && !el1.isEmbeddedIn(el2);
|
|
1050
|
+
},
|
|
1051
|
+
|
|
1052
|
+
_isValidLinkUnderElement: function(link, el) {
|
|
1053
|
+
return (
|
|
1054
|
+
link.source().id !== el.id &&
|
|
1055
|
+
link.target().id !== el.id &&
|
|
1056
|
+
!link.isEmbeddedIn(el)
|
|
1057
|
+
);
|
|
1058
|
+
},
|
|
1059
|
+
|
|
1060
|
+
_validateCellsUnderElement: function(cells, element) {
|
|
1061
|
+
return cells.filter(cell => {
|
|
1062
|
+
return cell.isLink()
|
|
1063
|
+
? this._isValidLinkUnderElement(cell, element)
|
|
1064
|
+
: this._isValidElementUnderElement(cell, element);
|
|
1065
|
+
});
|
|
1066
|
+
},
|
|
1067
|
+
|
|
1068
|
+
_getFindUnderElementGeometry: function(element, searchBy = 'bbox') {
|
|
1069
|
+
const bbox = element.getBBox({ rotate: true });
|
|
1070
|
+
return (searchBy !== 'bbox') ? util.getRectPoint(bbox, searchBy) : bbox;
|
|
1071
|
+
},
|
|
1072
|
+
|
|
1073
|
+
_filterCellsUnderElement: function(cells, element, opt = {}) {
|
|
1074
|
+
const geometry = this._getFindUnderElementGeometry(element, opt.searchBy);
|
|
1075
|
+
const filteredCells = (geometry.type === g.types.Point)
|
|
1076
|
+
? this._filterAtPoint(cells, geometry)
|
|
1077
|
+
: this._filterInArea(cells, geometry, opt);
|
|
1078
|
+
return this._validateCellsUnderElement(filteredCells, element);
|
|
1079
|
+
},
|
|
1080
|
+
|
|
1081
|
+
// @deprecated use `findElementsInArea` instead
|
|
1082
|
+
findModelsInArea: function(area, opt) {
|
|
1083
|
+
return this.findElementsInArea(area, opt);
|
|
1084
|
+
},
|
|
1085
|
+
|
|
1086
|
+
// @deprecated use `findElementsAtPoint` instead
|
|
1087
|
+
findModelsFromPoint: function(point) {
|
|
1088
|
+
return this.findElementsAtPoint(point);
|
|
1089
|
+
},
|
|
1090
|
+
|
|
1091
|
+
// @deprecated use `findModelsUnderElement` instead
|
|
1092
|
+
findModelsUnderElement: function(element, opt) {
|
|
1093
|
+
return this.findElementsUnderElement(element, opt);
|
|
986
1094
|
},
|
|
987
1095
|
|
|
988
1096
|
// Return bounding box of all elements.
|
|
@@ -278,18 +278,22 @@ export const HighlighterView = mvc.View.extend({
|
|
|
278
278
|
});
|
|
279
279
|
},
|
|
280
280
|
|
|
281
|
-
|
|
281
|
+
getAll(paper, id = null) {
|
|
282
|
+
const views = [];
|
|
282
283
|
const { _views } = this;
|
|
283
|
-
|
|
284
284
|
for (let cid in _views) {
|
|
285
285
|
for (let hid in _views[cid]) {
|
|
286
286
|
const view = _views[cid][hid];
|
|
287
|
-
|
|
288
287
|
if (view.cellView.paper === paper && view instanceof this && (id === null || hid === id)) {
|
|
289
|
-
|
|
288
|
+
views.push(view);
|
|
290
289
|
}
|
|
291
290
|
}
|
|
292
291
|
}
|
|
292
|
+
return views;
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
removeAll(paper, id = null) {
|
|
296
|
+
this.getAll(paper, id).forEach(view => view.remove());
|
|
293
297
|
},
|
|
294
298
|
|
|
295
299
|
update(cellView, id = null, dirty = false) {
|