@joint/core 4.1.3 → 4.2.0-alpha.1
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 +4 -2
- package/dist/geometry.js +129 -124
- package/dist/geometry.min.js +4 -3
- package/dist/joint.d.ts +352 -160
- package/dist/joint.js +3654 -2191
- package/dist/joint.min.js +4 -3
- package/dist/joint.nowrap.js +3653 -2188
- package/dist/joint.nowrap.min.js +4 -3
- package/dist/vectorizer.js +489 -279
- package/dist/vectorizer.min.js +4 -3
- package/dist/version.mjs +1 -1
- package/package.json +33 -27
- package/src/V/create.mjs +51 -0
- package/src/V/index.mjs +89 -159
- package/src/V/namespace.mjs +9 -0
- package/src/V/transform.mjs +183 -0
- package/src/V/traverse.mjs +16 -0
- package/src/alg/Deque.mjs +126 -0
- package/src/anchors/index.mjs +140 -33
- package/src/cellTools/Boundary.mjs +15 -13
- package/src/cellTools/Button.mjs +7 -5
- package/src/cellTools/Control.mjs +38 -15
- package/src/cellTools/HoverConnect.mjs +5 -1
- package/src/cellTools/helpers.mjs +44 -3
- package/src/config/index.mjs +8 -0
- package/src/connectionPoints/index.mjs +24 -9
- package/src/connectionStrategies/index.mjs +1 -1
- package/src/connectors/jumpover.mjs +1 -1
- package/src/dia/Cell.mjs +32 -12
- package/src/dia/CellView.mjs +53 -38
- package/src/dia/Element.mjs +81 -35
- package/src/dia/ElementView.mjs +2 -1
- package/src/dia/HighlighterView.mjs +54 -11
- package/src/dia/LinkView.mjs +118 -98
- package/src/dia/Paper.mjs +831 -231
- package/src/dia/PaperLayer.mjs +9 -2
- package/src/dia/ToolView.mjs +4 -0
- package/src/dia/ToolsView.mjs +12 -3
- package/src/dia/attributes/text.mjs +16 -5
- package/src/dia/layers/GridLayer.mjs +5 -0
- package/src/dia/ports.mjs +344 -111
- package/src/elementTools/HoverConnect.mjs +14 -8
- package/src/env/index.mjs +7 -4
- package/src/g/rect.mjs +7 -0
- package/src/highlighters/stroke.mjs +1 -1
- package/src/layout/ports/port.mjs +30 -15
- package/src/layout/ports/portLabel.mjs +1 -1
- package/src/linkAnchors/index.mjs +2 -2
- package/src/linkTools/Anchor.mjs +2 -2
- package/src/linkTools/Vertices.mjs +4 -6
- package/src/mvc/View.mjs +4 -0
- package/src/mvc/ViewBase.mjs +1 -1
- package/src/util/util.mjs +1 -1
- package/src/util/utilHelpers.mjs +2 -0
- package/types/geometry.d.ts +65 -59
- package/types/joint.d.ts +278 -102
- package/types/vectorizer.d.ts +11 -1
- package/src/V/annotation.mjs +0 -0
|
@@ -21,7 +21,7 @@ function pinnedElementEnd(relative, end, view, magnet, coords) {
|
|
|
21
21
|
|
|
22
22
|
var angle = view.model.angle();
|
|
23
23
|
var bbox = view.getNodeUnrotatedBBox(magnet);
|
|
24
|
-
var origin = view.model.
|
|
24
|
+
var origin = view.model.getCenter();
|
|
25
25
|
coords.rotate(origin, angle);
|
|
26
26
|
var dx = coords.x - bbox.x;
|
|
27
27
|
var dy = coords.y - bbox.y;
|
|
@@ -337,7 +337,7 @@ function buildRoundedSegment(offset, path, curr, prev, next) {
|
|
|
337
337
|
* @property {number} size optional size of a jump arc
|
|
338
338
|
* @return {string} created `D` attribute of SVG path
|
|
339
339
|
*/
|
|
340
|
-
export const jumpover = function(sourcePoint, targetPoint, route, opt) {
|
|
340
|
+
export const jumpover = function(sourcePoint, targetPoint, route, opt) {
|
|
341
341
|
|
|
342
342
|
setupUpdating(this);
|
|
343
343
|
|
package/src/dia/Cell.mjs
CHANGED
|
@@ -32,14 +32,14 @@ import { Model } from '../mvc/Model.mjs';
|
|
|
32
32
|
import { cloneCells } from '../util/cloneCells.mjs';
|
|
33
33
|
import { attributes } from './attributes/index.mjs';
|
|
34
34
|
import * as g from '../g/index.mjs';
|
|
35
|
-
|
|
35
|
+
import { config } from '../config/index.mjs';
|
|
36
36
|
|
|
37
37
|
// Cell base model.
|
|
38
38
|
// --------------------------
|
|
39
39
|
|
|
40
40
|
const attributesMerger = function(a, b) {
|
|
41
41
|
if (Array.isArray(a)) {
|
|
42
|
-
return b;
|
|
42
|
+
return cloneDeep(b);
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
45
|
|
|
@@ -78,12 +78,20 @@ export const Cell = Model.extend({
|
|
|
78
78
|
if ((defaults = result(this, 'defaults'))) {
|
|
79
79
|
//<custom code>
|
|
80
80
|
// Replaced the call to _.defaults with util.merge.
|
|
81
|
-
const customizer = (options && options.mergeArrays === true)
|
|
81
|
+
const customizer = (options && options.mergeArrays === true)
|
|
82
|
+
? false
|
|
83
|
+
: config.cellDefaultsMergeStrategy || attributesMerger;
|
|
82
84
|
attrs = merge({}, defaults, attrs, customizer);
|
|
83
85
|
//</custom code>
|
|
84
86
|
}
|
|
85
87
|
this.set(attrs, options);
|
|
86
88
|
this.changed = {};
|
|
89
|
+
if (options && options.portLayoutNamespace) {
|
|
90
|
+
this.portLayoutNamespace = options.portLayoutNamespace;
|
|
91
|
+
}
|
|
92
|
+
if (options && options.portLabelLayoutNamespace) {
|
|
93
|
+
this.portLabelLayoutNamespace = options.portLabelLayoutNamespace;
|
|
94
|
+
}
|
|
87
95
|
this.initialize.apply(this, arguments);
|
|
88
96
|
},
|
|
89
97
|
|
|
@@ -543,7 +551,11 @@ export const Cell = Model.extend({
|
|
|
543
551
|
if (!opt.deep) {
|
|
544
552
|
// Shallow cloning.
|
|
545
553
|
|
|
546
|
-
|
|
554
|
+
// Preserve the original's `portLayoutNamespace` and `portLabelLayoutNamespace`.
|
|
555
|
+
const clone = new this.constructor(this.attributes, {
|
|
556
|
+
portLayoutNamespace: this.portLayoutNamespace,
|
|
557
|
+
portLabelLayoutNamespace: this.portLabelLayoutNamespace
|
|
558
|
+
});
|
|
547
559
|
// We don't want the clone to have the same ID as the original.
|
|
548
560
|
clone.set(this.getIdAttribute(), this.generateId());
|
|
549
561
|
// A shallow cloned element does not carry over the original embeds.
|
|
@@ -557,7 +569,7 @@ export const Cell = Model.extend({
|
|
|
557
569
|
} else {
|
|
558
570
|
// Deep cloning.
|
|
559
571
|
|
|
560
|
-
// For a deep clone, simply call `
|
|
572
|
+
// For a deep clone, simply call `util.cloneCells()` with the cell and all its embedded cells.
|
|
561
573
|
return toArray(cloneCells([this].concat(this.getEmbeddedCells({ deep: true }))));
|
|
562
574
|
}
|
|
563
575
|
},
|
|
@@ -630,7 +642,7 @@ export const Cell = Model.extend({
|
|
|
630
642
|
options.rewrite && unsetByPath(baseAttributes, path, '/');
|
|
631
643
|
|
|
632
644
|
// Merge update with the model attributes.
|
|
633
|
-
var attributes = merge(baseAttributes, update);
|
|
645
|
+
var attributes = merge(baseAttributes, update, config.cellMergeStrategy);
|
|
634
646
|
// Finally, set the property to the updated attributes.
|
|
635
647
|
return this.set(property, attributes[property], options);
|
|
636
648
|
|
|
@@ -653,7 +665,11 @@ export const Cell = Model.extend({
|
|
|
653
665
|
const changedAttributes = {};
|
|
654
666
|
for (const key in props) {
|
|
655
667
|
// Merging the values of changed attributes with the current ones.
|
|
656
|
-
const { changedValue } = merge(
|
|
668
|
+
const { changedValue } = merge(
|
|
669
|
+
merge({}, { changedValue: this.attributes[key] }),
|
|
670
|
+
{ changedValue: props[key] },
|
|
671
|
+
config.cellMergeStrategy
|
|
672
|
+
);
|
|
657
673
|
changedAttributes[key] = changedValue;
|
|
658
674
|
}
|
|
659
675
|
|
|
@@ -921,9 +937,13 @@ export const Cell = Model.extend({
|
|
|
921
937
|
return new g.Rect(0, 0, 0, 0);
|
|
922
938
|
},
|
|
923
939
|
|
|
940
|
+
getCenter: function() {
|
|
941
|
+
return this.getBBox().center();
|
|
942
|
+
},
|
|
943
|
+
|
|
924
944
|
getPointRotatedAroundCenter(angle, x, y) {
|
|
925
945
|
const point = new g.Point(x, y);
|
|
926
|
-
if (angle) point.rotate(this.
|
|
946
|
+
if (angle) point.rotate(this.getCenter(), angle);
|
|
927
947
|
return point;
|
|
928
948
|
},
|
|
929
949
|
|
|
@@ -948,9 +968,10 @@ export const Cell = Model.extend({
|
|
|
948
968
|
|
|
949
969
|
getAttributeDefinition: function(attrName) {
|
|
950
970
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
971
|
+
const defNS = this.attributes;
|
|
972
|
+
const globalDefNS = attributes;
|
|
973
|
+
const definition = (defNS && defNS[attrName]) || globalDefNS[attrName];
|
|
974
|
+
return definition !== undefined ? definition : null;
|
|
954
975
|
},
|
|
955
976
|
|
|
956
977
|
define: function(type, defaults, protoProps, staticProps) {
|
|
@@ -969,4 +990,3 @@ export const Cell = Model.extend({
|
|
|
969
990
|
return Cell;
|
|
970
991
|
}
|
|
971
992
|
});
|
|
972
|
-
|
package/src/dia/CellView.mjs
CHANGED
|
@@ -795,7 +795,15 @@ export const CellView = View.extend({
|
|
|
795
795
|
getNodeBoundingRect: function(magnet) {
|
|
796
796
|
|
|
797
797
|
var metrics = this.nodeCache(magnet);
|
|
798
|
-
if (metrics.boundingRect === undefined)
|
|
798
|
+
if (metrics.boundingRect === undefined) {
|
|
799
|
+
const { measureNode } = this.paper.options;
|
|
800
|
+
if (typeof measureNode === 'function') {
|
|
801
|
+
// Measure the node bounding box using the paper's measureNode method.
|
|
802
|
+
metrics.boundingRect = measureNode(magnet, this);
|
|
803
|
+
} else {
|
|
804
|
+
metrics.boundingRect = V(magnet).getBBox();
|
|
805
|
+
}
|
|
806
|
+
}
|
|
799
807
|
return new Rect(metrics.boundingRect);
|
|
800
808
|
},
|
|
801
809
|
|
|
@@ -810,7 +818,12 @@ export const CellView = View.extend({
|
|
|
810
818
|
} else {
|
|
811
819
|
target = el;
|
|
812
820
|
}
|
|
813
|
-
metrics.magnetMatrix = V(magnet).getTransformToElement(target
|
|
821
|
+
metrics.magnetMatrix = V(magnet).getTransformToElement(target, {
|
|
822
|
+
// We use `safe` mode if the magnet is not visible (not in the DOM render tree).
|
|
823
|
+
// The browser would not be able to calculate the transformation matrix
|
|
824
|
+
// using `getScreenCTM()` method.
|
|
825
|
+
safe: !magnet.checkVisibility()
|
|
826
|
+
});
|
|
814
827
|
}
|
|
815
828
|
return V.createSVGMatrix(metrics.magnetMatrix);
|
|
816
829
|
},
|
|
@@ -994,33 +1007,38 @@ export const CellView = View.extend({
|
|
|
994
1007
|
const refNodeId = refNode ? V.ensureId(refNode) : '';
|
|
995
1008
|
let refBBox = bboxCache[refNodeId];
|
|
996
1009
|
if (!refBBox) {
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1010
|
+
if (refNode) {
|
|
1011
|
+
// Get the bounding box of the reference element using to the common ancestor
|
|
1012
|
+
// transformation space.
|
|
1013
|
+
//
|
|
1014
|
+
// @example 1
|
|
1015
|
+
// <g transform="translate(11, 13)">
|
|
1016
|
+
// <rect @selector="b" x="1" y="2" width="3" height="4"/>
|
|
1017
|
+
// <rect @selector="a"/>
|
|
1018
|
+
// </g>
|
|
1019
|
+
//
|
|
1020
|
+
// In this case, the reference bounding box can not be affected
|
|
1021
|
+
// by the `transform` attribute of the `<g>` element,
|
|
1022
|
+
// because the exact transformation will be applied to the `a` element
|
|
1023
|
+
// as well as to the `b` element.
|
|
1024
|
+
//
|
|
1025
|
+
// @example 2
|
|
1026
|
+
// <g transform="translate(11, 13)">
|
|
1027
|
+
// <rect @selector="b" x="1" y="2" width="3" height="4"/>
|
|
1028
|
+
// </g>
|
|
1029
|
+
// <rect @selector="a"/>
|
|
1030
|
+
//
|
|
1031
|
+
// In this case, the reference bounding box have to be affected by the
|
|
1032
|
+
// `transform` attribute of the `<g>` element, because the `a` element
|
|
1033
|
+
// is not descendant of the `<g>` element and will not be affected
|
|
1034
|
+
// by the transformation.
|
|
1035
|
+
const refRect = this.getNodeBoundingRect(refNode);
|
|
1036
|
+
const refTMatrix = V(refNode).getTransformToElement(V.getCommonAncestor(node, refNode));
|
|
1037
|
+
refBBox = V.transformRect(refRect, refTMatrix);
|
|
1038
|
+
} else {
|
|
1039
|
+
refBBox = opt.rootBBox;
|
|
1040
|
+
}
|
|
1041
|
+
bboxCache[refNodeId] = refBBox;
|
|
1024
1042
|
}
|
|
1025
1043
|
|
|
1026
1044
|
if (roAttrs) {
|
|
@@ -1375,14 +1393,11 @@ Object.defineProperty(CellView.prototype, 'useCSSSelectors', {
|
|
|
1375
1393
|
}
|
|
1376
1394
|
});
|
|
1377
1395
|
|
|
1378
|
-
//
|
|
1379
|
-
|
|
1380
|
-
let parent = node1;
|
|
1381
|
-
do {
|
|
1382
|
-
if (parent.contains(node2)) return parent;
|
|
1383
|
-
parent = parent.parentNode;
|
|
1384
|
-
} while (parent);
|
|
1385
|
-
return null;
|
|
1386
|
-
}
|
|
1396
|
+
// Internal tag to identify this object as a cell view instance.
|
|
1397
|
+
// Used instead of `instanceof` for performance and cross-frame safety.
|
|
1387
1398
|
|
|
1399
|
+
export const CELL_VIEW_MARKER = Symbol('joint.cellViewMarker');
|
|
1388
1400
|
|
|
1401
|
+
Object.defineProperty(CellView.prototype, CELL_VIEW_MARKER, {
|
|
1402
|
+
value: true,
|
|
1403
|
+
});
|
package/src/dia/Element.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Cell } from './Cell.mjs';
|
|
2
2
|
import { Point, toRad, normalizeAngle, Rect } from '../g/index.mjs';
|
|
3
|
-
import { isNumber, isObject, interpolate, assign, invoke, normalizeSides } from '../util/index.mjs';
|
|
3
|
+
import { isNumber, isObject, interpolate, assign, invoke, normalizeSides, omit } from '../util/index.mjs';
|
|
4
4
|
import { elementPortPrototype } from './ports.mjs';
|
|
5
5
|
|
|
6
6
|
// Element base model.
|
|
@@ -344,22 +344,24 @@ export const Element = Cell.extend({
|
|
|
344
344
|
const { graph } = this;
|
|
345
345
|
if (!graph) throw new Error('Element must be part of a graph.');
|
|
346
346
|
|
|
347
|
-
|
|
348
|
-
|
|
347
|
+
// Get element children, optionally filtered according to `opt.filter`.
|
|
348
|
+
const filteredChildElements = this._getFilteredChildElements(opt.filter);
|
|
349
349
|
|
|
350
350
|
this.startBatch('fit-embeds', opt);
|
|
351
351
|
|
|
352
352
|
if (opt.deep) {
|
|
353
353
|
// `opt.deep = true` means "fit to all descendants".
|
|
354
354
|
// As the first action of the fitting algorithm, recursively apply `fitToChildren()` on all descendants.
|
|
355
|
-
// - i.e. the algorithm is applied in reverse-depth order - start from deepest descendant, then go up (= this element)
|
|
356
|
-
|
|
355
|
+
// - i.e. the algorithm is applied in reverse-depth order - start from deepest descendant, then go up (= this element)
|
|
356
|
+
// - omit `opt.minRect` - it only makes sense for the first level of recursion if there are no filtered children, but in this case we do have filtered children
|
|
357
|
+
invoke(filteredChildElements, 'fitToChildren', omit(opt, 'minRect'));
|
|
357
358
|
}
|
|
358
359
|
|
|
359
360
|
// Set new size and position of this element, based on:
|
|
360
|
-
// - union of bboxes of
|
|
361
|
+
// - union of bboxes of filtered element children
|
|
361
362
|
// - inflated by given `opt.padding`
|
|
362
|
-
|
|
363
|
+
// - containing at least `opt.minRect` (if this is the first level of recursion and there are no filtered children)
|
|
364
|
+
this._fitToElements(Object.assign({ elements: filteredChildElements }, opt));
|
|
363
365
|
|
|
364
366
|
this.stopBatch('fit-embeds');
|
|
365
367
|
|
|
@@ -375,25 +377,27 @@ export const Element = Cell.extend({
|
|
|
375
377
|
// If the current element is `opt.terminator`, it means that this element has already been processed as parent so we can exit now.
|
|
376
378
|
if (opt.deep && opt.terminator && ((opt.terminator === this) || (opt.terminator === this.id))) return this;
|
|
377
379
|
|
|
380
|
+
// If this element has no parent, there is nothing for us to do.
|
|
378
381
|
const parentElement = this.getParentCell();
|
|
379
382
|
if (!parentElement || !parentElement.isElement()) return this;
|
|
380
383
|
|
|
381
|
-
// Get
|
|
382
|
-
const
|
|
383
|
-
if (siblingElements.length === 0) return this;
|
|
384
|
+
// Get element children of parent element (i.e. this element + any sibling elements), optionally filtered according to `opt.filter`.
|
|
385
|
+
const filteredSiblingElements = parentElement._getFilteredChildElements(opt.filter);
|
|
384
386
|
|
|
385
387
|
this.startBatch('fit-parent', opt);
|
|
386
388
|
|
|
387
389
|
// Set new size and position of parent element, based on:
|
|
388
|
-
// - union of bboxes of
|
|
390
|
+
// - union of bboxes of filtered element children of parent element (i.e. this element + any sibling elements)
|
|
389
391
|
// - inflated by given `opt.padding`
|
|
390
|
-
|
|
392
|
+
// - containing at least `opt.minRect` (if this is the first level of recursion and there are no filtered siblings)
|
|
393
|
+
parentElement._fitToElements(Object.assign({ elements: filteredSiblingElements }, opt));
|
|
391
394
|
|
|
392
395
|
if (opt.deep) {
|
|
393
396
|
// `opt.deep = true` means "fit all ancestors to their respective children".
|
|
394
397
|
// As the last action of the fitting algorithm, recursively apply `fitParent()` on all ancestors.
|
|
395
|
-
// - i.e. the algorithm is applied in reverse-depth order - start from deepest descendant (= this element), then go up
|
|
396
|
-
|
|
398
|
+
// - i.e. the algorithm is applied in reverse-depth order - start from deepest descendant (= this element), then go up
|
|
399
|
+
// - omit `opt.minRect` - `minRect` is not relevant for the parent of parent element (and upwards)
|
|
400
|
+
parentElement.fitParent(omit(opt, 'minRect'));
|
|
397
401
|
}
|
|
398
402
|
|
|
399
403
|
this.stopBatch('fit-parent');
|
|
@@ -401,45 +405,87 @@ export const Element = Cell.extend({
|
|
|
401
405
|
return this;
|
|
402
406
|
},
|
|
403
407
|
|
|
408
|
+
_getFilteredChildElements: function(filter) {
|
|
409
|
+
|
|
410
|
+
let filterFn;
|
|
411
|
+
if (typeof filter === 'function') {
|
|
412
|
+
filterFn = (cell) => (cell.isElement() && filter(cell));
|
|
413
|
+
} else {
|
|
414
|
+
filterFn = (cell) => (cell.isElement());
|
|
415
|
+
}
|
|
416
|
+
return this.getEmbeddedCells().filter(filterFn);
|
|
417
|
+
},
|
|
418
|
+
|
|
404
419
|
// Assumption: This element is part of a graph.
|
|
405
420
|
_fitToElements: function(opt = {}) {
|
|
406
421
|
|
|
422
|
+
let minBBox = null;
|
|
423
|
+
if (opt.minRect) {
|
|
424
|
+
// Coerce `opt.minRect` to g.Rect
|
|
425
|
+
// (missing properties are taken from this element's current bbox).
|
|
426
|
+
const minRect = assign(this.getBBox(), opt.minRect);
|
|
427
|
+
minBBox = new Rect(minRect);
|
|
428
|
+
}
|
|
429
|
+
|
|
407
430
|
const elementsBBox = this.graph.getCellsBBox(opt.elements);
|
|
408
|
-
// If no `opt.elements` were provided, do nothing.
|
|
409
|
-
if (!elementsBBox)
|
|
431
|
+
// If no `opt.elements` were provided, do nothing (but if `opt.minRect` was provided, set that as this element's bbox instead).
|
|
432
|
+
if (!elementsBBox) {
|
|
433
|
+
this._setBBox(minBBox, opt);
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
410
436
|
|
|
411
437
|
const { expandOnly, shrinkOnly } = opt;
|
|
412
|
-
// This combination is meaningless, do nothing.
|
|
413
|
-
if (expandOnly && shrinkOnly)
|
|
438
|
+
// This combination is meaningless, do nothing (but if `opt.minRect` was provided, set that as this element's bbox instead).
|
|
439
|
+
if (expandOnly && shrinkOnly) {
|
|
440
|
+
this._setBBox(minBBox, opt);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
414
443
|
|
|
415
444
|
// Calculate new size and position of this element based on:
|
|
416
445
|
// - union of bboxes of `opt.elements`
|
|
417
|
-
// - inflated by `opt.padding` (
|
|
446
|
+
// - inflated by normalized `opt.padding` (missing sides = 0)
|
|
418
447
|
let { x, y, width, height } = elementsBBox;
|
|
419
448
|
const { left, right, top, bottom } = normalizeSides(opt.padding);
|
|
420
449
|
x -= left;
|
|
421
450
|
y -= top;
|
|
422
451
|
width += left + right;
|
|
423
452
|
height += bottom + top;
|
|
424
|
-
let
|
|
453
|
+
let contentBBox = new Rect(x, y, width, height);
|
|
425
454
|
|
|
426
455
|
if (expandOnly) {
|
|
427
456
|
// Non-shrinking is enforced by taking union of this element's current bbox with bbox calculated from `opt.elements`.
|
|
428
|
-
|
|
457
|
+
contentBBox = this.getBBox().union(contentBBox);
|
|
429
458
|
|
|
430
459
|
} else if (shrinkOnly) {
|
|
431
460
|
// Non-expansion is enforced by taking intersection of this element's current bbox with bbox calculated from `opt.elements`.
|
|
432
|
-
const intersectionBBox = this.getBBox().intersect(
|
|
433
|
-
// If all children are outside this element's current bbox, then `intersectionBBox` is `null
|
|
434
|
-
if
|
|
461
|
+
const intersectionBBox = this.getBBox().intersect(contentBBox);
|
|
462
|
+
// If all children are outside this element's current bbox, then `intersectionBBox` is `null`.
|
|
463
|
+
// That does not make sense, do nothing (but if `opt.minRect` was provided, set that as this element's bbox instead).
|
|
464
|
+
if (!intersectionBBox) {
|
|
465
|
+
this._setBBox(minBBox, opt);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
435
468
|
|
|
436
|
-
|
|
469
|
+
contentBBox = intersectionBBox;
|
|
437
470
|
}
|
|
438
471
|
|
|
439
472
|
// Set the new size and position of this element.
|
|
473
|
+
// - if `opt.minRect` was provided, add it via union to calculated bbox
|
|
474
|
+
let resultBBox = contentBBox;
|
|
475
|
+
if (minBBox) {
|
|
476
|
+
resultBBox = resultBBox.union(minBBox);
|
|
477
|
+
}
|
|
478
|
+
this._setBBox(resultBBox, opt);
|
|
479
|
+
},
|
|
480
|
+
|
|
481
|
+
_setBBox: function(bbox, opt) {
|
|
482
|
+
|
|
483
|
+
if (!bbox) return;
|
|
484
|
+
|
|
485
|
+
const { x, y, width, height } = bbox;
|
|
440
486
|
this.set({
|
|
441
|
-
position: { x
|
|
442
|
-
size: { width
|
|
487
|
+
position: { x, y },
|
|
488
|
+
size: { width, height }
|
|
443
489
|
}, opt);
|
|
444
490
|
},
|
|
445
491
|
|
|
@@ -451,7 +497,7 @@ export const Element = Cell.extend({
|
|
|
451
497
|
|
|
452
498
|
if (origin) {
|
|
453
499
|
|
|
454
|
-
var center = this.
|
|
500
|
+
var center = this.getCenter();
|
|
455
501
|
var size = this.get('size');
|
|
456
502
|
var position = this.get('position');
|
|
457
503
|
center.rotate(origin, this.get('angle') - angle);
|
|
@@ -497,6 +543,11 @@ export const Element = Cell.extend({
|
|
|
497
543
|
return bbox;
|
|
498
544
|
},
|
|
499
545
|
|
|
546
|
+
getCenter: function() {
|
|
547
|
+
const { position: { x, y }, size: { width, height }} = this.attributes;
|
|
548
|
+
return new Point(x + width / 2, y + height / 2);
|
|
549
|
+
},
|
|
550
|
+
|
|
500
551
|
getPointFromConnectedLink: function(link, endType) {
|
|
501
552
|
// Center of the model
|
|
502
553
|
var bbox = this.getBBox();
|
|
@@ -506,14 +557,9 @@ export const Element = Cell.extend({
|
|
|
506
557
|
if (!endDef) return center;
|
|
507
558
|
var portId = endDef.port;
|
|
508
559
|
if (!portId || !this.hasPort(portId)) return center;
|
|
509
|
-
|
|
510
|
-
var portsPositions = this.getPortsPositions(portGroup);
|
|
511
|
-
var portCenter = new Point(portsPositions[portId]).offset(bbox.origin());
|
|
512
|
-
var angle = this.angle();
|
|
513
|
-
if (angle) portCenter.rotate(center, -angle);
|
|
514
|
-
return portCenter;
|
|
560
|
+
return this.getPortCenter(portId);
|
|
515
561
|
}
|
|
562
|
+
|
|
516
563
|
});
|
|
517
564
|
|
|
518
565
|
assign(Element.prototype, elementPortPrototype);
|
|
519
|
-
|
package/src/dia/ElementView.mjs
CHANGED
|
@@ -389,7 +389,8 @@ export const ElementView = CellView.extend({
|
|
|
389
389
|
parent.unembed(element, { ui: true });
|
|
390
390
|
data.initialParentId = parentId;
|
|
391
391
|
} else {
|
|
392
|
-
data.initialParentId
|
|
392
|
+
// `data.initialParentId` can be explicitly set to a dummy value to enable validation of unembedding.
|
|
393
|
+
data.initialParentId = data.initialParentId || null;
|
|
393
394
|
}
|
|
394
395
|
},
|
|
395
396
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as mvc from '../mvc/index.mjs';
|
|
2
2
|
import V from '../V/index.mjs';
|
|
3
|
-
import { isPlainObject, result } from '../util/util.mjs';
|
|
3
|
+
import { isNumber, isPlainObject, result } from '../util/util.mjs';
|
|
4
4
|
|
|
5
5
|
function toArray(obj) {
|
|
6
6
|
if (!obj) return [];
|
|
@@ -71,7 +71,7 @@ export const HighlighterView = mvc.View.extend({
|
|
|
71
71
|
}
|
|
72
72
|
} else if (nodeSelector) {
|
|
73
73
|
el = V.toNode(nodeSelector);
|
|
74
|
-
if (!(el instanceof SVGElement)) el = null;
|
|
74
|
+
if (!(el instanceof SVGElement) || !cellView.el.contains(el)) el = null;
|
|
75
75
|
}
|
|
76
76
|
return el ? el : null;
|
|
77
77
|
},
|
|
@@ -106,8 +106,8 @@ export const HighlighterView = mvc.View.extend({
|
|
|
106
106
|
this.transform();
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
109
|
-
const {
|
|
110
|
-
const { layer: layerName } = options;
|
|
109
|
+
const { paper } = cellView;
|
|
110
|
+
const { layer: layerName, z } = options;
|
|
111
111
|
if (layerName) {
|
|
112
112
|
let vGroup;
|
|
113
113
|
if (detachedTransformGroup) {
|
|
@@ -117,20 +117,41 @@ export const HighlighterView = mvc.View.extend({
|
|
|
117
117
|
vGroup = V('g').addClass('highlight-transform').append(el);
|
|
118
118
|
}
|
|
119
119
|
this.transformGroup = vGroup;
|
|
120
|
-
paper.getLayerView(layerName).insertSortedNode(vGroup.node,
|
|
120
|
+
paper.getLayerView(layerName).insertSortedNode(vGroup.node, z);
|
|
121
121
|
} else {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
const children = cellView.el.children;
|
|
123
|
+
|
|
124
|
+
const index = Math.max(z, 0);
|
|
125
|
+
const beforeChild = children[index];
|
|
126
|
+
|
|
127
|
+
// If the provided `z` is a number and there is an element on the index,
|
|
128
|
+
// we need to insert the highlighter before the element on the index.
|
|
129
|
+
// Otherwise, the highlighter will be appended as the last child.
|
|
130
|
+
const toBeInserted = isNumber(z) && beforeChild;
|
|
131
|
+
const isElementAtTargetPosition = toBeInserted
|
|
132
|
+
// If the element is being inserted, check if it is not already at the correct position.
|
|
133
|
+
? el === beforeChild
|
|
134
|
+
// If the element is being appended, check if it is not already last child.
|
|
135
|
+
: !el.nextElementSibling;
|
|
136
|
+
|
|
137
|
+
// If the element is already mounted and does not require repositioning, do nothing.
|
|
138
|
+
if (el.parentNode && isElementAtTargetPosition) return;
|
|
139
|
+
|
|
140
|
+
if (toBeInserted) {
|
|
141
|
+
cellView.el.insertBefore(el, beforeChild);
|
|
142
|
+
} else {
|
|
143
|
+
cellView.el.appendChild(el);
|
|
126
144
|
}
|
|
127
145
|
}
|
|
128
146
|
},
|
|
129
147
|
|
|
130
148
|
unmount() {
|
|
131
|
-
const { MOUNTABLE, transformGroup, vel } = this;
|
|
149
|
+
const { MOUNTABLE, transformGroup, vel, options } = this;
|
|
132
150
|
if (!MOUNTABLE) return;
|
|
133
|
-
if (
|
|
151
|
+
if (options.layer) {
|
|
152
|
+
if (!transformGroup) return;
|
|
153
|
+
// else: if `transformGroup` is not null, it means the highlighter
|
|
154
|
+
// has not been mounted yet
|
|
134
155
|
this.transformGroup = null;
|
|
135
156
|
this.detachedTransformGroup = transformGroup;
|
|
136
157
|
transformGroup.remove();
|
|
@@ -241,6 +262,28 @@ export const HighlighterView = mvc.View.extend({
|
|
|
241
262
|
}
|
|
242
263
|
},
|
|
243
264
|
|
|
265
|
+
// Check if the cellView has a highlighter with the given `id`.
|
|
266
|
+
// If no `id` is provided, it checks if the cellView has any highlighter.
|
|
267
|
+
has(cellView, id = null) {
|
|
268
|
+
const { cid } = cellView;
|
|
269
|
+
const { _views } = this;
|
|
270
|
+
const refs = _views[cid];
|
|
271
|
+
if (!refs) return false;
|
|
272
|
+
if (id === null) {
|
|
273
|
+
// any highlighter
|
|
274
|
+
for (let hid in refs) {
|
|
275
|
+
if (refs[hid] instanceof this) return true;
|
|
276
|
+
}
|
|
277
|
+
return false;
|
|
278
|
+
} else {
|
|
279
|
+
// single highlighter
|
|
280
|
+
if (id in refs) {
|
|
281
|
+
if (refs[id] instanceof this) return true;
|
|
282
|
+
}
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
|
|
244
287
|
add(cellView, nodeSelector, id, opt = {}) {
|
|
245
288
|
if (!id) throw new Error('dia.HighlighterView: An ID required.');
|
|
246
289
|
// Search the existing view amongst all the highlighters
|