@joint/core 4.1.2 → 4.2.0-alpha.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 +1 -1
- package/dist/geometry.js +128 -123
- package/dist/geometry.min.js +2 -2
- package/dist/joint.d.ts +79 -16
- package/dist/joint.js +2249 -1730
- package/dist/joint.min.js +2 -2
- package/dist/joint.nowrap.js +2248 -1727
- package/dist/joint.nowrap.min.js +2 -2
- package/dist/vectorizer.js +469 -272
- package/dist/vectorizer.min.js +2 -2
- package/dist/version.mjs +1 -1
- package/package.json +28 -22
- package/src/V/create.mjs +51 -0
- package/src/V/index.mjs +69 -154
- package/src/V/namespace.mjs +9 -0
- package/src/V/transform.mjs +183 -0
- package/src/V/traverse.mjs +16 -0
- package/src/anchors/index.mjs +140 -33
- package/src/cellTools/Boundary.mjs +1 -1
- package/src/cellTools/Control.mjs +1 -1
- 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 +6 -2
- package/src/dia/CellView.mjs +47 -39
- package/src/dia/Element.mjs +79 -35
- package/src/dia/ElementView.mjs +9 -3
- package/src/dia/HighlighterView.mjs +32 -11
- package/src/dia/Paper.mjs +134 -22
- package/src/dia/PaperLayer.mjs +9 -2
- package/src/dia/attributes/text.mjs +12 -3
- package/src/dia/layers/GridLayer.mjs +5 -0
- package/src/dia/ports.mjs +152 -39
- package/src/env/index.mjs +1 -1
- package/src/g/rect.mjs +7 -0
- package/src/highlighters/stroke.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/Dom/methods.mjs +2 -2
- package/src/util/util.mjs +1 -1
- package/src/util/utilHelpers.mjs +2 -0
- package/types/geometry.d.ts +2 -0
- package/types/joint.d.ts +81 -20
- package/src/V/annotation.mjs +0 -0
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,85 @@ 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 (missing properties = 0).
|
|
425
|
+
minBBox = new Rect(opt.minRect);
|
|
426
|
+
}
|
|
427
|
+
|
|
407
428
|
const elementsBBox = this.graph.getCellsBBox(opt.elements);
|
|
408
|
-
// If no `opt.elements` were provided, do nothing.
|
|
409
|
-
if (!elementsBBox)
|
|
429
|
+
// If no `opt.elements` were provided, do nothing (but if `opt.minRect` was provided, set that as this element's bbox instead).
|
|
430
|
+
if (!elementsBBox) {
|
|
431
|
+
this._setBBox(minBBox, opt);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
410
434
|
|
|
411
435
|
const { expandOnly, shrinkOnly } = opt;
|
|
412
|
-
// This combination is meaningless, do nothing.
|
|
413
|
-
if (expandOnly && shrinkOnly)
|
|
436
|
+
// This combination is meaningless, do nothing (but if `opt.minRect` was provided, set that as this element's bbox instead).
|
|
437
|
+
if (expandOnly && shrinkOnly) {
|
|
438
|
+
this._setBBox(minBBox, opt);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
414
441
|
|
|
415
442
|
// Calculate new size and position of this element based on:
|
|
416
443
|
// - union of bboxes of `opt.elements`
|
|
417
|
-
// - inflated by `opt.padding` (
|
|
444
|
+
// - inflated by normalized `opt.padding` (missing sides = 0)
|
|
418
445
|
let { x, y, width, height } = elementsBBox;
|
|
419
446
|
const { left, right, top, bottom } = normalizeSides(opt.padding);
|
|
420
447
|
x -= left;
|
|
421
448
|
y -= top;
|
|
422
449
|
width += left + right;
|
|
423
450
|
height += bottom + top;
|
|
424
|
-
let
|
|
451
|
+
let contentBBox = new Rect(x, y, width, height);
|
|
425
452
|
|
|
426
453
|
if (expandOnly) {
|
|
427
454
|
// Non-shrinking is enforced by taking union of this element's current bbox with bbox calculated from `opt.elements`.
|
|
428
|
-
|
|
455
|
+
contentBBox = this.getBBox().union(contentBBox);
|
|
429
456
|
|
|
430
457
|
} else if (shrinkOnly) {
|
|
431
458
|
// 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
|
|
459
|
+
const intersectionBBox = this.getBBox().intersect(contentBBox);
|
|
460
|
+
// If all children are outside this element's current bbox, then `intersectionBBox` is `null`.
|
|
461
|
+
// That does not make sense, do nothing (but if `opt.minRect` was provided, set that as this element's bbox instead).
|
|
462
|
+
if (!intersectionBBox) {
|
|
463
|
+
this._setBBox(minBBox, opt);
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
435
466
|
|
|
436
|
-
|
|
467
|
+
contentBBox = intersectionBBox;
|
|
437
468
|
}
|
|
438
469
|
|
|
439
470
|
// Set the new size and position of this element.
|
|
471
|
+
// - if `opt.minRect` was provided, add it via union to calculated bbox
|
|
472
|
+
let resultBBox = contentBBox;
|
|
473
|
+
if (minBBox) {
|
|
474
|
+
resultBBox = resultBBox.union(minBBox);
|
|
475
|
+
}
|
|
476
|
+
this._setBBox(resultBBox, opt);
|
|
477
|
+
},
|
|
478
|
+
|
|
479
|
+
_setBBox: function(bbox, opt) {
|
|
480
|
+
|
|
481
|
+
if (!bbox) return;
|
|
482
|
+
|
|
483
|
+
const { x, y, width, height } = bbox;
|
|
440
484
|
this.set({
|
|
441
|
-
position: { x
|
|
442
|
-
size: { width
|
|
485
|
+
position: { x, y },
|
|
486
|
+
size: { width, height }
|
|
443
487
|
}, opt);
|
|
444
488
|
},
|
|
445
489
|
|
|
@@ -451,7 +495,7 @@ export const Element = Cell.extend({
|
|
|
451
495
|
|
|
452
496
|
if (origin) {
|
|
453
497
|
|
|
454
|
-
var center = this.
|
|
498
|
+
var center = this.getCenter();
|
|
455
499
|
var size = this.get('size');
|
|
456
500
|
var position = this.get('position');
|
|
457
501
|
center.rotate(origin, this.get('angle') - angle);
|
|
@@ -497,6 +541,11 @@ export const Element = Cell.extend({
|
|
|
497
541
|
return bbox;
|
|
498
542
|
},
|
|
499
543
|
|
|
544
|
+
getCenter: function() {
|
|
545
|
+
const { position: { x, y }, size: { width, height }} = this.attributes;
|
|
546
|
+
return new Point(x + width / 2, y + height / 2);
|
|
547
|
+
},
|
|
548
|
+
|
|
500
549
|
getPointFromConnectedLink: function(link, endType) {
|
|
501
550
|
// Center of the model
|
|
502
551
|
var bbox = this.getBBox();
|
|
@@ -506,14 +555,9 @@ export const Element = Cell.extend({
|
|
|
506
555
|
if (!endDef) return center;
|
|
507
556
|
var portId = endDef.port;
|
|
508
557
|
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;
|
|
558
|
+
return this.getPortCenter(portId);
|
|
515
559
|
}
|
|
560
|
+
|
|
516
561
|
});
|
|
517
562
|
|
|
518
563
|
assign(Element.prototype, elementPortPrototype);
|
|
519
|
-
|
package/src/dia/ElementView.mjs
CHANGED
|
@@ -804,17 +804,23 @@ export const ElementView = CellView.extend({
|
|
|
804
804
|
|
|
805
805
|
// Drag Handlers
|
|
806
806
|
|
|
807
|
+
snapToGrid: function(evt, x, y) {
|
|
808
|
+
const grid = this.paper.options.gridSize;
|
|
809
|
+
return {
|
|
810
|
+
x: snapToGrid(x, grid),
|
|
811
|
+
y: snapToGrid(y, grid)
|
|
812
|
+
};
|
|
813
|
+
},
|
|
814
|
+
|
|
807
815
|
drag: function(evt, x, y) {
|
|
808
816
|
|
|
809
817
|
var paper = this.paper;
|
|
810
|
-
var grid = paper.options.gridSize;
|
|
811
818
|
var element = this.model;
|
|
812
819
|
var data = this.eventData(evt);
|
|
813
820
|
var { pointerOffset, restrictedArea, embedding } = data;
|
|
814
821
|
|
|
815
822
|
// Make sure the new element's position always snaps to the current grid
|
|
816
|
-
|
|
817
|
-
var elY = snapToGrid(y + pointerOffset.y, grid);
|
|
823
|
+
const { x: elX, y: elY } = this.snapToGrid(evt, x + pointerOffset.x, y + pointerOffset.y);
|
|
818
824
|
|
|
819
825
|
element.position(elX, elY, { restrictedArea, deep: true, ui: true });
|
|
820
826
|
|
|
@@ -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();
|
package/src/dia/Paper.mjs
CHANGED
|
@@ -387,6 +387,9 @@ export const Paper = View.extend({
|
|
|
387
387
|
// to mitigate the differences between the model and view geometry.
|
|
388
388
|
DEFAULT_FIND_BUFFER: 200,
|
|
389
389
|
|
|
390
|
+
// Default layer to insert the cell views into.
|
|
391
|
+
DEFAULT_CELL_LAYER: LayersNames.CELLS,
|
|
392
|
+
|
|
390
393
|
init: function() {
|
|
391
394
|
|
|
392
395
|
const { options } = this;
|
|
@@ -399,7 +402,11 @@ export const Paper = View.extend({
|
|
|
399
402
|
const model = this.model = options.model || new Graph;
|
|
400
403
|
|
|
401
404
|
// Layers (SVGGroups)
|
|
402
|
-
this._layers = {
|
|
405
|
+
this._layers = {
|
|
406
|
+
viewsMap: {},
|
|
407
|
+
namesMap: {},
|
|
408
|
+
order: [],
|
|
409
|
+
};
|
|
403
410
|
|
|
404
411
|
this.cloneOptions();
|
|
405
412
|
this.render();
|
|
@@ -471,7 +478,10 @@ export const Paper = View.extend({
|
|
|
471
478
|
|
|
472
479
|
onCellChange: function(cell, opt) {
|
|
473
480
|
if (cell === this.model.attributes.cells) return;
|
|
474
|
-
if (
|
|
481
|
+
if (
|
|
482
|
+
cell.hasChanged('layer') ||
|
|
483
|
+
(cell.hasChanged('z') && this.options.sorting === sortingTypes.APPROX)
|
|
484
|
+
) {
|
|
475
485
|
const view = this.findViewByModel(cell);
|
|
476
486
|
if (view) this.requestViewUpdate(view, view.FLAG_INSERT, view.UPDATE_PRIORITY, opt);
|
|
477
487
|
}
|
|
@@ -588,19 +598,122 @@ export const Paper = View.extend({
|
|
|
588
598
|
},
|
|
589
599
|
|
|
590
600
|
hasLayerView(layerName) {
|
|
591
|
-
return (layerName in this._layers);
|
|
601
|
+
return (layerName in this._layers.viewsMap);
|
|
592
602
|
},
|
|
593
603
|
|
|
594
604
|
getLayerView(layerName) {
|
|
595
|
-
const { _layers } = this;
|
|
596
|
-
if (layerName in
|
|
597
|
-
throw new Error(`dia.Paper: Unknown layer "${layerName}"
|
|
605
|
+
const { _layers: { viewsMap }} = this;
|
|
606
|
+
if (layerName in viewsMap) return viewsMap[layerName];
|
|
607
|
+
throw new Error(`dia.Paper: Unknown layer "${layerName}".`);
|
|
598
608
|
},
|
|
599
609
|
|
|
600
610
|
getLayerNode(layerName) {
|
|
601
611
|
return this.getLayerView(layerName).el;
|
|
602
612
|
},
|
|
603
613
|
|
|
614
|
+
_removeLayer(layerView) {
|
|
615
|
+
this._unregisterLayer(layerView);
|
|
616
|
+
layerView.remove();
|
|
617
|
+
},
|
|
618
|
+
|
|
619
|
+
_unregisterLayer(layerView) {
|
|
620
|
+
const { _layers: { viewsMap, namesMap, order }} = this;
|
|
621
|
+
const layerName = this._getLayerName(layerView);
|
|
622
|
+
order.splice(order.indexOf(layerName), 1);
|
|
623
|
+
delete namesMap[layerView.cid];
|
|
624
|
+
delete viewsMap[layerName];
|
|
625
|
+
},
|
|
626
|
+
|
|
627
|
+
_registerLayer(layerName, layerView, beforeLayerView) {
|
|
628
|
+
const { _layers: { viewsMap, namesMap, order }} = this;
|
|
629
|
+
if (beforeLayerView) {
|
|
630
|
+
const beforeLayerName = this._getLayerName(beforeLayerView);
|
|
631
|
+
order.splice(order.indexOf(beforeLayerName), 0, layerName);
|
|
632
|
+
} else {
|
|
633
|
+
order.push(layerName);
|
|
634
|
+
}
|
|
635
|
+
viewsMap[layerName] = layerView;
|
|
636
|
+
namesMap[layerView.cid] = layerName;
|
|
637
|
+
},
|
|
638
|
+
|
|
639
|
+
_getLayerView(layer) {
|
|
640
|
+
const { _layers: { namesMap, viewsMap }} = this;
|
|
641
|
+
if (layer instanceof PaperLayer) {
|
|
642
|
+
if (layer.cid in namesMap) return layer;
|
|
643
|
+
return null;
|
|
644
|
+
}
|
|
645
|
+
if (layer in viewsMap) return viewsMap[layer];
|
|
646
|
+
return null;
|
|
647
|
+
},
|
|
648
|
+
|
|
649
|
+
_getLayerName(layerView) {
|
|
650
|
+
const { _layers: { namesMap }} = this;
|
|
651
|
+
return namesMap[layerView.cid];
|
|
652
|
+
},
|
|
653
|
+
|
|
654
|
+
_requireLayerView(layer) {
|
|
655
|
+
const layerView = this._getLayerView(layer);
|
|
656
|
+
if (!layerView) {
|
|
657
|
+
if (layer instanceof PaperLayer) {
|
|
658
|
+
throw new Error('dia.Paper: The layer is not registered.');
|
|
659
|
+
} else {
|
|
660
|
+
throw new Error(`dia.Paper: Unknown layer "${layer}".`);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return layerView;
|
|
664
|
+
},
|
|
665
|
+
|
|
666
|
+
hasLayer(layer) {
|
|
667
|
+
return this._getLayerView(layer) !== null;
|
|
668
|
+
},
|
|
669
|
+
|
|
670
|
+
removeLayer(layer) {
|
|
671
|
+
const layerView = this._requireLayerView(layer);
|
|
672
|
+
if (!layerView.isEmpty()) {
|
|
673
|
+
throw new Error('dia.Paper: The layer is not empty.');
|
|
674
|
+
}
|
|
675
|
+
this._removeLayer(layerView);
|
|
676
|
+
},
|
|
677
|
+
|
|
678
|
+
addLayer(layerName, layerView, options = {}) {
|
|
679
|
+
if (!layerName || typeof layerName !== 'string') {
|
|
680
|
+
throw new Error('dia.Paper: The layer name must be provided.');
|
|
681
|
+
}
|
|
682
|
+
if (this._getLayerView(layerName)) {
|
|
683
|
+
throw new Error(`dia.Paper: The layer "${layerName}" already exists.`);
|
|
684
|
+
}
|
|
685
|
+
if (!(layerView instanceof PaperLayer)) {
|
|
686
|
+
throw new Error('dia.Paper: The layer view is not an instance of dia.PaperLayer.');
|
|
687
|
+
}
|
|
688
|
+
const { insertBefore } = options;
|
|
689
|
+
if (!insertBefore) {
|
|
690
|
+
this._registerLayer(layerName, layerView, null);
|
|
691
|
+
this.layers.appendChild(layerView.el);
|
|
692
|
+
} else {
|
|
693
|
+
const beforeLayerView = this._requireLayerView(insertBefore);
|
|
694
|
+
this._registerLayer(layerName, layerView, beforeLayerView);
|
|
695
|
+
this.layers.insertBefore(layerView.el, beforeLayerView.el);
|
|
696
|
+
}
|
|
697
|
+
},
|
|
698
|
+
|
|
699
|
+
moveLayer(layer, insertBefore) {
|
|
700
|
+
const layerView = this._requireLayerView(layer);
|
|
701
|
+
if (layerView === this._getLayerView(insertBefore)) return;
|
|
702
|
+
const layerName = this._getLayerName(layerView);
|
|
703
|
+
this._unregisterLayer(layerView);
|
|
704
|
+
this.addLayer(layerName, layerView, { insertBefore });
|
|
705
|
+
},
|
|
706
|
+
|
|
707
|
+
getLayerNames() {
|
|
708
|
+
// Returns a sorted array of layer names.
|
|
709
|
+
return this._layers.order.slice();
|
|
710
|
+
},
|
|
711
|
+
|
|
712
|
+
getLayers() {
|
|
713
|
+
// Returns a sorted array of layer views.
|
|
714
|
+
return this.getLayerNames().map(name => this.getLayerView(name));
|
|
715
|
+
},
|
|
716
|
+
|
|
604
717
|
render: function() {
|
|
605
718
|
|
|
606
719
|
this.renderChildren();
|
|
@@ -645,14 +758,15 @@ export const Paper = View.extend({
|
|
|
645
758
|
}
|
|
646
759
|
},
|
|
647
760
|
|
|
761
|
+
renderLayer: function(name) {
|
|
762
|
+
const layerView = this.createLayer(name);
|
|
763
|
+
this.addLayer(name, layerView);
|
|
764
|
+
return layerView;
|
|
765
|
+
},
|
|
766
|
+
|
|
648
767
|
renderLayers: function(layers = defaultLayers) {
|
|
649
768
|
this.removeLayers();
|
|
650
|
-
|
|
651
|
-
layers.forEach(({ name, sorted }) => {
|
|
652
|
-
const layerView = this.createLayer(name);
|
|
653
|
-
this.layers.appendChild(layerView.el);
|
|
654
|
-
this._layers[name] = layerView;
|
|
655
|
-
});
|
|
769
|
+
layers.forEach(({ name }) => this.renderLayer(name));
|
|
656
770
|
// Throws an exception if doesn't exist
|
|
657
771
|
const cellsLayerView = this.getLayerView(LayersNames.CELLS);
|
|
658
772
|
const toolsLayerView = this.getLayerView(LayersNames.TOOLS);
|
|
@@ -670,18 +784,13 @@ export const Paper = View.extend({
|
|
|
670
784
|
},
|
|
671
785
|
|
|
672
786
|
removeLayers: function() {
|
|
673
|
-
const { _layers } = this;
|
|
674
|
-
Object.
|
|
675
|
-
_layers[name].remove();
|
|
676
|
-
delete _layers[name];
|
|
677
|
-
});
|
|
787
|
+
const { _layers: { viewsMap }} = this;
|
|
788
|
+
Object.values(viewsMap).forEach(layerView => this._removeLayer(layerView));
|
|
678
789
|
},
|
|
679
790
|
|
|
680
791
|
resetLayers: function() {
|
|
681
|
-
const { _layers } = this;
|
|
682
|
-
Object.
|
|
683
|
-
_layers[name].removePivots();
|
|
684
|
-
});
|
|
792
|
+
const { _layers: { viewsMap }} = this;
|
|
793
|
+
Object.values(viewsMap).forEach(layerView => layerView.removePivots());
|
|
685
794
|
},
|
|
686
795
|
|
|
687
796
|
update: function() {
|
|
@@ -1796,8 +1905,11 @@ export const Paper = View.extend({
|
|
|
1796
1905
|
},
|
|
1797
1906
|
|
|
1798
1907
|
insertView: function(view, isInitialInsert) {
|
|
1799
|
-
const layerView = this.getLayerView(LayersNames.CELLS);
|
|
1800
1908
|
const { el, model } = view;
|
|
1909
|
+
|
|
1910
|
+
const layerName = model.get('layer') || this.DEFAULT_CELL_LAYER;
|
|
1911
|
+
const layerView = this.getLayerView(layerName);
|
|
1912
|
+
|
|
1801
1913
|
switch (this.options.sorting) {
|
|
1802
1914
|
case sortingTypes.APPROX:
|
|
1803
1915
|
layerView.insertSortedNode(el, model.get('z'));
|
package/src/dia/PaperLayer.mjs
CHANGED
|
@@ -22,7 +22,9 @@ export const PaperLayer = View.extend({
|
|
|
22
22
|
},
|
|
23
23
|
|
|
24
24
|
className: function() {
|
|
25
|
-
|
|
25
|
+
const { name } = this.options;
|
|
26
|
+
if (!name) return null;
|
|
27
|
+
return addClassNamePrefix(`${name}-layer`);
|
|
26
28
|
},
|
|
27
29
|
|
|
28
30
|
init: function() {
|
|
@@ -70,6 +72,11 @@ export const PaperLayer = View.extend({
|
|
|
70
72
|
const { el, pivotNodes } = this;
|
|
71
73
|
for (let z in pivotNodes) el.removeChild(pivotNodes[z]);
|
|
72
74
|
this.pivotNodes = {};
|
|
73
|
-
}
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
isEmpty: function() {
|
|
78
|
+
// Check if the layer has any child elements (pivot comments are not counted).
|
|
79
|
+
return this.el.children.length === 0;
|
|
80
|
+
},
|
|
74
81
|
|
|
75
82
|
});
|
|
@@ -148,10 +148,19 @@ const textAttributesNS = {
|
|
|
148
148
|
// TODO: change the `lineHeight` to breakText option.
|
|
149
149
|
wrapFontAttributes.lineHeight = attrs['line-height'];
|
|
150
150
|
|
|
151
|
+
let svgDocument = this.paper.svg;
|
|
152
|
+
if (!svgDocument.checkVisibility()) {
|
|
153
|
+
// If the paper is visible, we can utilize
|
|
154
|
+
// its SVG element to measure the text size
|
|
155
|
+
// when breaking the text.
|
|
156
|
+
// Otherwise, we need to create a temporary
|
|
157
|
+
// SVG document and append it to the DOM,
|
|
158
|
+
// (the default behavior of `breakText`).
|
|
159
|
+
svgDocument = null;
|
|
160
|
+
}
|
|
161
|
+
|
|
151
162
|
wrappedText = breakTextFn('' + text, size, wrapFontAttributes, {
|
|
152
|
-
|
|
153
|
-
// instead of creating a temporary one over again.
|
|
154
|
-
svgDocument: this.paper.svg,
|
|
163
|
+
svgDocument,
|
|
155
164
|
ellipsis: value.ellipsis,
|
|
156
165
|
hyphen: value.hyphen,
|
|
157
166
|
separator: value.separator,
|