@metadev/daga 4.2.1 → 4.2.3

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/Changelog.md CHANGED
@@ -6,6 +6,16 @@ List of releases and changes.
6
6
 
7
7
  ## Next release Joyeuse
8
8
 
9
+ ## v. 4.2.3
10
+
11
+ - Enable toggling whether individual sections can be highlighted or entire nodes [#308](https://github.com/metadevpro/daga/pull/308)
12
+ - Enable toggling whether connections tighten automatically and whether they can loop and share ports [#309](https://github.com/metadevpro/daga/pull/309)
13
+ - Enable toggling background color of connection label [#310](https://github.com/metadevpro/daga/pull/310)
14
+
15
+ ## v. 4.2.2
16
+
17
+ - Fix zoom on double click [#307](https://github.com/metadevpro/daga/pull/307)
18
+
9
19
  ## v. 4.2.1
10
20
 
11
21
  - Apply zoom when scrolling over diagram elements [#278](https://github.com/metadevpro/daga/issues/278) [#290](https://github.com/metadevpro/daga/pull/290)
package/index.cjs.js CHANGED
@@ -2138,11 +2138,14 @@ class DiagramConnection extends DiagramElement {
2138
2138
  * @public
2139
2139
  */
2140
2140
  tighten() {
2141
- var _a, _b;
2142
- if (((_a = this.start) === null || _a === void 0 ? void 0 : _a.rootElement) && this.end) {
2141
+ var _a, _b, _c, _d, _e;
2142
+ const allowConnectionLoops = ((_a = this.model.canvas) === null || _a === void 0 ? void 0 : _a.allowConnectionLoops) || false;
2143
+ const allowSharingPorts = ((_b = this.model.canvas) === null || _b === void 0 ? void 0 : _b.allowSharingPorts) !== false;
2144
+ const allowSharingBothPorts = ((_c = this.model.canvas) === null || _c === void 0 ? void 0 : _c.allowSharingBothPorts) || false;
2145
+ if (((_d = this.start) === null || _d === void 0 ? void 0 : _d.rootElement) && this.end) {
2143
2146
  const alternativeStartPortsSortedByDistanceAscending = this.start.rootElement.ports.map(p => [p, p.distanceTo(this.end.coords)]).sort((a, b) => a[1] - b[1]).map(a => a[0]);
2144
2147
  checkAlternativeStartPorts: for (const alternativeStartPort of alternativeStartPortsSortedByDistanceAscending) {
2145
- if (alternativeStartPort === this.end) {
2148
+ if (!allowConnectionLoops && alternativeStartPort === this.end) {
2146
2149
  // alternative start port not valid, it is the same as the end port
2147
2150
  continue checkAlternativeStartPorts;
2148
2151
  }
@@ -2150,7 +2153,11 @@ class DiagramConnection extends DiagramElement {
2150
2153
  // alternative start port not valid, it doesn't allow outgoing connections
2151
2154
  continue checkAlternativeStartPorts;
2152
2155
  }
2153
- {
2156
+ if (!allowSharingPorts && (alternativeStartPort.incomingConnections.length === 1 && alternativeStartPort.incomingConnections[0] !== this || alternativeStartPort.incomingConnections.length > 1 || alternativeStartPort.outgoingConnections.length === 1 && alternativeStartPort.outgoingConnections[0] !== this || alternativeStartPort.outgoingConnections.length > 1)) {
2157
+ // alternative start port not valid, it already has other connections
2158
+ continue checkAlternativeStartPorts;
2159
+ }
2160
+ if (!allowSharingBothPorts) {
2154
2161
  for (const connection of alternativeStartPort.outgoingConnections) {
2155
2162
  if (connection !== this && connection.end === this.end) {
2156
2163
  // alternative start port not valid, there is a connection whose start and end matches the alternative start port and this connection's end
@@ -2173,10 +2180,10 @@ class DiagramConnection extends DiagramElement {
2173
2180
  }
2174
2181
  }
2175
2182
  }
2176
- if (((_b = this.end) === null || _b === void 0 ? void 0 : _b.rootElement) && this.start) {
2183
+ if (((_e = this.end) === null || _e === void 0 ? void 0 : _e.rootElement) && this.start) {
2177
2184
  const alternativeEndPortsSortedByDistanceAscending = this.end.rootElement.ports.map(p => [p, p.distanceTo(this.start.coords)]).sort((a, b) => a[1] - b[1]).map(a => a[0]);
2178
2185
  checkAlternativeEndPorts: for (const alternativeEndPort of alternativeEndPortsSortedByDistanceAscending) {
2179
- if (alternativeEndPort === this.start) {
2186
+ if (!allowConnectionLoops && alternativeEndPort === this.start) {
2180
2187
  // alternative end port not valid, it is the same as the end port
2181
2188
  continue checkAlternativeEndPorts;
2182
2189
  }
@@ -2184,7 +2191,11 @@ class DiagramConnection extends DiagramElement {
2184
2191
  // alternative end port not valid, it doesn't allow incoming connections
2185
2192
  continue checkAlternativeEndPorts;
2186
2193
  }
2187
- {
2194
+ if (!allowSharingPorts && (alternativeEndPort.outgoingConnections.length === 1 && alternativeEndPort.outgoingConnections[0] !== this || alternativeEndPort.outgoingConnections.length > 1 || alternativeEndPort.incomingConnections.length === 1 && alternativeEndPort.incomingConnections[0] !== this || alternativeEndPort.incomingConnections.length > 1)) {
2195
+ // alternative end port not valid, it already has other connections
2196
+ continue checkAlternativeEndPorts;
2197
+ }
2198
+ if (!allowSharingBothPorts) {
2188
2199
  for (const connection of alternativeEndPort.incomingConnections) {
2189
2200
  if (connection !== this && connection.start === this.start) {
2190
2201
  // alternative end port not valid, there is a connection whose start and end matches the alternative end port and this connection's start
@@ -2292,6 +2303,7 @@ const DIAGRAM_FIELD_DEFAULTS = {
2292
2303
  fontFamily: "'Wonder Unit Sans', sans-serif",
2293
2304
  color: '#000000',
2294
2305
  selectedColor: '#000000',
2306
+ backgroundColor: '#00000000',
2295
2307
  horizontalAlign: exports.HorizontalAlign.Center,
2296
2308
  verticalAlign: exports.VerticalAlign.Center,
2297
2309
  orientation: exports.Side.Top,
@@ -2948,6 +2960,7 @@ class DiagramSection extends DiagramElement {
2948
2960
  * @public
2949
2961
  */
2950
2962
  setGeometry(geometry) {
2963
+ var _a;
2951
2964
  const oldCoordsX = [this.coords[0], this.coords[0] + this.width];
2952
2965
  const oldCoordsY = [this.coords[1], this.coords[1] + this.height];
2953
2966
  this.coords = [...geometry.coords];
@@ -2971,8 +2984,10 @@ class DiagramSection extends DiagramElement {
2971
2984
  for (const decorator of this.decorators) {
2972
2985
  decorator.move(translatePointWithAnchors(decorator.coords, oldCoordsX, oldCoordsY, newCoordsX, newCoordsY, decorator.anchorPointX, decorator.anchorPointY));
2973
2986
  }
2987
+ if (((_a = this.model.canvas) === null || _a === void 0 ? void 0 : _a.autoTightenConnections) !== false) {
2988
+ this.getConnections().forEach(c => c.tighten());
2989
+ }
2974
2990
  // Update canvas.
2975
- this.getConnections().forEach(c => c.tighten());
2976
2991
  this.updateInView();
2977
2992
  }
2978
2993
  }
@@ -3691,6 +3706,7 @@ class DiagramNode extends DiagramElement {
3691
3706
  * @public
3692
3707
  */
3693
3708
  setGeometry(geometry) {
3709
+ var _a;
3694
3710
  this.raise();
3695
3711
  const oldCoordsX = [this.coords[0], this.coords[0] + this.width];
3696
3712
  const oldCoordsY = [this.coords[1], this.coords[1] + this.height];
@@ -3728,8 +3744,10 @@ class DiagramNode extends DiagramElement {
3728
3744
  for (const decorator of this.decorators) {
3729
3745
  decorator.move(translatePointWithAnchors(decorator.coords, oldCoordsX, oldCoordsY, newCoordsX, newCoordsY, decorator.anchorPointX, decorator.anchorPointY));
3730
3746
  }
3747
+ if (((_a = this.model.canvas) === null || _a === void 0 ? void 0 : _a.autoTightenConnections) !== false) {
3748
+ this.getConnections().forEach(c => c.tighten());
3749
+ }
3731
3750
  // Update canvas.
3732
- this.getConnections().forEach(c => c.tighten());
3733
3751
  this.updateInView();
3734
3752
  }
3735
3753
  /**
@@ -6423,10 +6441,11 @@ class DiagramUserHighlight extends DiagramElementSet {
6423
6441
  * @public
6424
6442
  * @param canvas A canvas.
6425
6443
  */
6426
- constructor(canvas) {
6444
+ constructor(canvas, highlightSections = true) {
6427
6445
  super();
6428
6446
  this.focus = undefined;
6429
6447
  this.canvas = canvas;
6448
+ this.highlightSections = highlightSections;
6430
6449
  }
6431
6450
  /**
6432
6451
  * Gets the focus of the user highlight, which is the element where the current user highlight started regardless of which other elements were highlighted as a consequence.
@@ -6452,6 +6471,20 @@ class DiagramUserHighlight extends DiagramElementSet {
6452
6471
  add(element) {
6453
6472
  super.add(element);
6454
6473
  if (element instanceof DiagramNode) {
6474
+ if (!this.highlightSections) {
6475
+ for (const section of element.sections) {
6476
+ super.add(section);
6477
+ for (const port of section.ports) {
6478
+ super.add(port);
6479
+ this.canvas.updatePortsInView(port.id);
6480
+ }
6481
+ if (section.label) {
6482
+ super.add(section.label);
6483
+ this.canvas.updateFieldsInView(section.label.id);
6484
+ }
6485
+ this.canvas.updateSectionsInView(section.id);
6486
+ }
6487
+ }
6455
6488
  for (const port of element.ports) {
6456
6489
  super.add(port);
6457
6490
  this.canvas.updatePortsInView(port.id);
@@ -6462,15 +6495,19 @@ class DiagramUserHighlight extends DiagramElementSet {
6462
6495
  }
6463
6496
  this.canvas.updateNodesInView(element.id);
6464
6497
  } else if (element instanceof DiagramSection) {
6465
- for (const port of element.ports) {
6466
- super.add(port);
6467
- this.canvas.updatePortsInView(port.id);
6468
- }
6469
- if (element.label) {
6470
- super.add(element.label);
6471
- this.canvas.updateFieldsInView(element.label.id);
6498
+ if (!this.highlightSections && element.node) {
6499
+ this.add(element.node);
6500
+ } else {
6501
+ for (const port of element.ports) {
6502
+ super.add(port);
6503
+ this.canvas.updatePortsInView(port.id);
6504
+ }
6505
+ if (element.label) {
6506
+ super.add(element.label);
6507
+ this.canvas.updateFieldsInView(element.label.id);
6508
+ }
6509
+ this.canvas.updateSectionsInView(element.id);
6472
6510
  }
6473
- this.canvas.updateSectionsInView(element.id);
6474
6511
  } else if (element instanceof DiagramPort) {
6475
6512
  if (element.label) {
6476
6513
  super.add(element.label);
@@ -6939,7 +6976,6 @@ const RESIZER_THICKNESS = 6;
6939
6976
  */
6940
6977
  const ACTION_STACK_SIZE = 25;
6941
6978
  const UNFINISHED_CONNECTION_ID = 'diagram-connection-unfinished';
6942
- const MAX_DISTANCE_TO_PORT = 100;
6943
6979
  class DiagramCanvas {
6944
6980
  get connectionType() {
6945
6981
  return this._connectionType;
@@ -6957,7 +6993,7 @@ class DiagramCanvas {
6957
6993
  * @param config The configuration object used to set the parameters of this canvas.
6958
6994
  */
6959
6995
  constructor(parentComponent, config) {
6960
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
6996
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5;
6961
6997
  this.backgroundPatternId = `daga-background-pattern-id-${DiagramCanvas.canvasCount++}`;
6962
6998
  this.zoomTransform = d3__namespace.zoomIdentity;
6963
6999
  // used to distinguish drags from clicks when dragging elements and during multiple selection
@@ -6973,19 +7009,24 @@ class DiagramCanvas {
6973
7009
  this.parentComponent = parentComponent;
6974
7010
  this.model = new DiagramModel(this, undefined, config.name || 'unnamed', '', config.type || '', config.properties || []);
6975
7011
  this.userSelection = new DiagramUserSelection(this);
6976
- this.userHighlight = new DiagramUserHighlight(this);
6977
- this.contextMenu = new DiagramContextMenu(this, (_a = config.canvas) === null || _a === void 0 ? void 0 : _a.contextMenu);
6978
- this.backgroundColor = ((_b = config.canvas) === null || _b === void 0 ? void 0 : _b.backgroundColor) || '#FFFFFF';
6979
- this.gridStyle = (_e = (_d = (_c = config.canvas) === null || _c === void 0 ? void 0 : _c.grid) === null || _d === void 0 ? void 0 : _d.style) !== null && _e !== void 0 ? _e : GRID_DEFAULTS.style;
6980
- this.gridSize = ((_g = (_f = config.canvas) === null || _f === void 0 ? void 0 : _f.grid) === null || _g === void 0 ? void 0 : _g.enabled) === false || ((_h = config.canvas) === null || _h === void 0 ? void 0 : _h.grid) === undefined ? 0 : Math.abs(((_k = (_j = config.canvas) === null || _j === void 0 ? void 0 : _j.grid) === null || _k === void 0 ? void 0 : _k.spacing) || GRID_DEFAULTS.spacing);
6981
- this.gridThickness = Math.abs(((_m = (_l = config.canvas) === null || _l === void 0 ? void 0 : _l.grid) === null || _m === void 0 ? void 0 : _m.thickness) || GRID_DEFAULTS.thickness);
6982
- this.gridColor = ((_p = (_o = config.canvas) === null || _o === void 0 ? void 0 : _o.grid) === null || _p === void 0 ? void 0 : _p.color) || GRID_DEFAULTS.color;
6983
- this.snapToGrid = ((_r = (_q = config.canvas) === null || _q === void 0 ? void 0 : _q.grid) === null || _r === void 0 ? void 0 : _r.enabled) === false || ((_s = config.canvas) === null || _s === void 0 ? void 0 : _s.grid) === undefined ? false : ((_u = (_t = config.canvas) === null || _t === void 0 ? void 0 : _t.grid) === null || _u === void 0 ? void 0 : _u.snap) || GRID_DEFAULTS.snap;
6984
- this.zoomFactor = ((_v = config.canvas) === null || _v === void 0 ? void 0 : _v.zoomFactor) || 2;
6985
- this.panRate = ((_w = config.canvas) === null || _w === void 0 ? void 0 : _w.panRate) || 100;
6986
- this.inferConnectionType = config.inferConnectionType || false;
7012
+ this.userHighlight = new DiagramUserHighlight(this, ((_a = config.canvas) === null || _a === void 0 ? void 0 : _a.highlightSections) !== false);
7013
+ this.contextMenu = new DiagramContextMenu(this, (_b = config.canvas) === null || _b === void 0 ? void 0 : _b.contextMenu);
7014
+ this.backgroundColor = ((_c = config.canvas) === null || _c === void 0 ? void 0 : _c.backgroundColor) || '#FFFFFF';
7015
+ this.gridStyle = (_f = (_e = (_d = config.canvas) === null || _d === void 0 ? void 0 : _d.grid) === null || _e === void 0 ? void 0 : _e.style) !== null && _f !== void 0 ? _f : GRID_DEFAULTS.style;
7016
+ this.gridSize = ((_h = (_g = config.canvas) === null || _g === void 0 ? void 0 : _g.grid) === null || _h === void 0 ? void 0 : _h.enabled) === false || ((_j = config.canvas) === null || _j === void 0 ? void 0 : _j.grid) === undefined ? 0 : Math.abs(((_l = (_k = config.canvas) === null || _k === void 0 ? void 0 : _k.grid) === null || _l === void 0 ? void 0 : _l.spacing) || GRID_DEFAULTS.spacing);
7017
+ this.gridThickness = Math.abs(((_o = (_m = config.canvas) === null || _m === void 0 ? void 0 : _m.grid) === null || _o === void 0 ? void 0 : _o.thickness) || GRID_DEFAULTS.thickness);
7018
+ this.gridColor = ((_q = (_p = config.canvas) === null || _p === void 0 ? void 0 : _p.grid) === null || _q === void 0 ? void 0 : _q.color) || GRID_DEFAULTS.color;
7019
+ this.snapToGrid = ((_s = (_r = config.canvas) === null || _r === void 0 ? void 0 : _r.grid) === null || _s === void 0 ? void 0 : _s.enabled) === false || ((_t = config.canvas) === null || _t === void 0 ? void 0 : _t.grid) === undefined ? false : ((_v = (_u = config.canvas) === null || _u === void 0 ? void 0 : _u.grid) === null || _v === void 0 ? void 0 : _v.snap) || GRID_DEFAULTS.snap;
7020
+ this.zoomFactor = ((_w = config.canvas) === null || _w === void 0 ? void 0 : _w.zoomFactor) || 2;
7021
+ this.panRate = ((_x = config.canvas) === null || _x === void 0 ? void 0 : _x.panRate) || 100;
7022
+ this.inferConnectionType = ((_y = config.connectionSettings) === null || _y === void 0 ? void 0 : _y.inferConnectionType) || false;
7023
+ this.autoTightenConnections = ((_z = config.connectionSettings) === null || _z === void 0 ? void 0 : _z.autoTighten) !== false;
7024
+ this.allowConnectionLoops = ((_0 = config.connectionSettings) === null || _0 === void 0 ? void 0 : _0.allowLoops) || false;
7025
+ this.allowSharingPorts = ((_1 = config.connectionSettings) === null || _1 === void 0 ? void 0 : _1.sharePorts) !== false;
7026
+ this.allowSharingBothPorts = ((_2 = config.connectionSettings) === null || _2 === void 0 ? void 0 : _2.shareBothPorts) || false;
7027
+ this.portHighlightRadius = ((_3 = config.connectionSettings) === null || _3 === void 0 ? void 0 : _3.portHighlightRadius) || 100;
6987
7028
  this.multipleSelectionOn = false;
6988
- this.priorityThresholds = ((_x = config.canvas) === null || _x === void 0 ? void 0 : _x.priorityThresholds) || [];
7029
+ this.priorityThresholds = ((_4 = config.canvas) === null || _4 === void 0 ? void 0 : _4.priorityThresholds) || [];
6989
7030
  this.priorityThreshold = this.priorityThresholds ? this.priorityThresholds[0] : undefined;
6990
7031
  this.layoutFormat = config.layoutFormat;
6991
7032
  this.userActions = config.userActions || {};
@@ -7012,7 +7053,7 @@ class DiagramCanvas {
7012
7053
  const connectionType = new DiagramConnectionType(Object.assign(Object.assign({}, config.connectionTypeDefaults), connectionTypeConfig));
7013
7054
  this.model.connections.types.add(connectionType);
7014
7055
  }
7015
- this._connectionType = config.defaultConnection !== undefined ? this.model.connections.types.get(config.defaultConnection) : undefined;
7056
+ this._connectionType = ((_5 = config === null || config === void 0 ? void 0 : config.connectionSettings) === null || _5 === void 0 ? void 0 : _5.defaultConnection) !== undefined ? this.model.connections.types.get(config.connectionSettings.defaultConnection) : undefined;
7016
7057
  }
7017
7058
  }
7018
7059
  addValidator(validator) {
@@ -7190,7 +7231,10 @@ class DiagramCanvas {
7190
7231
  }
7191
7232
  });
7192
7233
  const canvasView = this.selectSVGElement().append('g').attr('class', 'daga-canvas-view').attr('width', `100%`).attr('height', `100%`);
7193
- canvasView.call(this.zoomBehavior = d3__namespace.zoom().on(exports.ZoomEvents.Zoom, event => {
7234
+ canvasView.call(this.zoomBehavior = d3__namespace.zoom().filter(event => {
7235
+ // Disable double-click zoom by filtering out dblclick events
7236
+ return event.type !== exports.Events.DoubleClick;
7237
+ }).on(exports.ZoomEvents.Zoom, event => {
7194
7238
  if (event.sourceEvent) {
7195
7239
  // zoom event was triggered by user
7196
7240
  if (!this.canUserPerformAction(exports.DiagramActions.Zoom)) {
@@ -7857,7 +7901,7 @@ class DiagramCanvas {
7857
7901
  if (this.multipleSelectionOn || this.secondaryButton) {
7858
7902
  this.startMultipleSelection(event);
7859
7903
  } else {
7860
- if (this.canUserPerformAction(exports.DiagramActions.AddConnection) && !d.removed) {
7904
+ if (this.canUserPerformAction(exports.DiagramActions.AddConnection) && (this.allowSharingPorts || d.incomingConnections.length === 0 && d.outgoingConnections.length === 0) && !d.removed) {
7861
7905
  setCursorStyle(exports.CursorStyle.Grabbing);
7862
7906
  this.startConnection(d);
7863
7907
  // should be true after having called this.startConnection()
@@ -7903,7 +7947,7 @@ class DiagramCanvas {
7903
7947
  closestPortFound = port;
7904
7948
  }
7905
7949
  }
7906
- if (closestPortFound && minDistanceFound < MAX_DISTANCE_TO_PORT) {
7950
+ if (closestPortFound && minDistanceFound < this.portHighlightRadius) {
7907
7951
  this.userHighlight.focusOn(closestPortFound);
7908
7952
  } else {
7909
7953
  this.userHighlight.clear();
@@ -8308,6 +8352,77 @@ class DiagramCanvas {
8308
8352
  const labelConfiguration = Object.assign(Object.assign({}, DIAGRAM_FIELD_DEFAULTS), connection.type.label);
8309
8353
  if (pathNode) {
8310
8354
  const pathLength = pathNode.getTotalLength();
8355
+ let startLabelShiftX = 0;
8356
+ let startLabelShiftY = 0;
8357
+ let middleLabelShiftX = 0;
8358
+ let middleLabelShiftY = 0;
8359
+ let endLabelShiftX = 0;
8360
+ let endLabelShiftY = 0;
8361
+ if (labelConfiguration.backgroundColor === '#00000000') {
8362
+ // background color is transparent / not set, so we find an alternative position for the label
8363
+ const deltaX = connection.endCoords[0] - connection.startCoords[0];
8364
+ const deltaY = connection.endCoords[1] - connection.startCoords[1];
8365
+ switch (connection.startDirection) {
8366
+ case exports.Side.Top:
8367
+ startLabelShiftX = deltaX >= 0 ? 1 : -1;
8368
+ middleLabelShiftX = startLabelShiftX;
8369
+ endLabelShiftX = startLabelShiftX;
8370
+ startLabelShiftY = -1;
8371
+ break;
8372
+ case exports.Side.Bottom:
8373
+ startLabelShiftX = deltaX >= 0 ? 1 : -1;
8374
+ middleLabelShiftX = startLabelShiftX;
8375
+ endLabelShiftX = startLabelShiftX;
8376
+ startLabelShiftY = 1;
8377
+ break;
8378
+ case exports.Side.Left:
8379
+ startLabelShiftX = -1;
8380
+ startLabelShiftY = deltaY > 0 ? 1 : -1;
8381
+ middleLabelShiftY = startLabelShiftY;
8382
+ endLabelShiftY = startLabelShiftY;
8383
+ break;
8384
+ case exports.Side.Right:
8385
+ startLabelShiftX = 1;
8386
+ startLabelShiftY = deltaY > 0 ? 1 : -1;
8387
+ middleLabelShiftY = startLabelShiftY;
8388
+ endLabelShiftY = startLabelShiftY;
8389
+ break;
8390
+ default:
8391
+ startLabelShiftX = 1;
8392
+ middleLabelShiftX = startLabelShiftX;
8393
+ endLabelShiftX = startLabelShiftX;
8394
+ startLabelShiftY = -1;
8395
+ middleLabelShiftY = startLabelShiftY;
8396
+ endLabelShiftY = startLabelShiftY;
8397
+ }
8398
+ switch (connection.endDirection) {
8399
+ case exports.Side.Top:
8400
+ endLabelShiftX = deltaX >= 0 ? 1 : -1;
8401
+ middleLabelShiftX = endLabelShiftX;
8402
+ endLabelShiftY = 1;
8403
+ break;
8404
+ case exports.Side.Bottom:
8405
+ endLabelShiftX = deltaX >= 0 ? 1 : -1;
8406
+ middleLabelShiftX = endLabelShiftX;
8407
+ endLabelShiftY = -1;
8408
+ break;
8409
+ case exports.Side.Left:
8410
+ endLabelShiftX = -1;
8411
+ endLabelShiftY = deltaY > 0 ? 1 : -1;
8412
+ middleLabelShiftY = endLabelShiftY;
8413
+ break;
8414
+ case exports.Side.Right:
8415
+ endLabelShiftX = 1;
8416
+ endLabelShiftY = deltaY > 0 ? 1 : -1;
8417
+ middleLabelShiftY = endLabelShiftY;
8418
+ break;
8419
+ default:
8420
+ endLabelShiftX = 1;
8421
+ middleLabelShiftX = endLabelShiftX;
8422
+ endLabelShiftY = -1;
8423
+ middleLabelShiftY = endLabelShiftY;
8424
+ }
8425
+ }
8311
8426
  // bind start labels
8312
8427
  connectionSelection.select('g.diagram-connection-start-label text').attr('x', 0).attr('y', labelConfiguration.fontSize / 3).attr('text-anchor', 'middle').attr('font-family', labelConfiguration.fontFamily).attr('font-size', labelConfiguration.fontSize).attr('fill', connection.selected ? labelConfiguration.selectedColor : labelConfiguration.color).style('font-kerning', 'none').text(connection.startLabel);
8313
8428
  const startLabelBoundingRect = (_a = connectionSelection.select('g.diagram-connection-start-label text').node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
@@ -8316,8 +8431,8 @@ class DiagramCanvas {
8316
8431
  const boundingWidth = !connection.startLabel ? 0 : startLabelBoundingRect.width / this.zoomTransform.k + getLeftPadding$1(labelConfiguration) + getRightPadding$1(labelConfiguration);
8317
8432
  const boundingHeight = !connection.startLabel ? 0 : startLabelBoundingRect.height / this.zoomTransform.k + getTopPadding$1(labelConfiguration) + getBottomPadding$1(labelConfiguration);
8318
8433
  const pathStartLabelPoint = pathNode.getPointAtLength(Math.max(getLeftMargin(labelConfiguration) + boundingWidth / 2, getRightMargin(labelConfiguration) + boundingWidth / 2, getTopMargin(labelConfiguration) + boundingHeight / 2, getBottomMargin(labelConfiguration) + boundingHeight / 2));
8319
- connectionSelection.select('g.diagram-connection-start-label path').attr('d', pillPath(-boundingWidth / 2, -boundingHeight / 2, boundingWidth, boundingHeight)).attr('fill', connection.look.color).attr('stroke', 'none');
8320
- connectionSelection.select('g.diagram-connection-start-label').attr('transform', `translate(${pathStartLabelPoint.x},${pathStartLabelPoint.y})`);
8434
+ connectionSelection.select('g.diagram-connection-start-label path').attr('d', pillPath(-boundingWidth / 2, -boundingHeight / 2, boundingWidth, boundingHeight)).attr('fill', labelConfiguration.backgroundColor).attr('stroke', 'none');
8435
+ connectionSelection.select('g.diagram-connection-start-label').attr('transform', `translate(${pathStartLabelPoint.x + startLabelShiftX * boundingWidth},${pathStartLabelPoint.y + startLabelShiftY * boundingHeight})`);
8321
8436
  }
8322
8437
  // bind middle labels
8323
8438
  connectionSelection.select('g.diagram-connection-middle-label text').attr('x', 0).attr('y', labelConfiguration.fontSize / 3).attr('text-anchor', 'middle').attr('font-family', labelConfiguration.fontFamily).attr('font-size', labelConfiguration.fontSize).attr('fill', connection.selected ? labelConfiguration.selectedColor : labelConfiguration.color).style('font-kerning', 'none').text(connection.middleLabel);
@@ -8327,8 +8442,8 @@ class DiagramCanvas {
8327
8442
  const boundingWidth = !connection.middleLabel ? 0 : middleLabelBoundingRect.width / this.zoomTransform.k + getLeftPadding$1(labelConfiguration) + getRightPadding$1(labelConfiguration);
8328
8443
  const boundingHeight = !connection.middleLabel ? 0 : middleLabelBoundingRect.height / this.zoomTransform.k + getTopPadding$1(labelConfiguration) + getBottomPadding$1(labelConfiguration);
8329
8444
  const pathMiddleLabelPoint = pathNode.getPointAtLength(pathLength / 2);
8330
- connectionSelection.select('g.diagram-connection-middle-label path').attr('d', pillPath(-boundingWidth / 2, -boundingHeight / 2, boundingWidth, boundingHeight)).attr('fill', connection.look.color).attr('stroke', 'none');
8331
- connectionSelection.select('g.diagram-connection-middle-label').attr('transform', `translate(${pathMiddleLabelPoint.x},${pathMiddleLabelPoint.y})`);
8445
+ connectionSelection.select('g.diagram-connection-middle-label path').attr('d', pillPath(-boundingWidth / 2, -boundingHeight / 2, boundingWidth, boundingHeight)).attr('fill', labelConfiguration.backgroundColor).attr('stroke', 'none');
8446
+ connectionSelection.select('g.diagram-connection-middle-label').attr('transform', `translate(${pathMiddleLabelPoint.x + middleLabelShiftX * boundingWidth},${pathMiddleLabelPoint.y + middleLabelShiftY * boundingHeight})`);
8332
8447
  }
8333
8448
  // bind end labels
8334
8449
  connectionSelection.select('g.diagram-connection-end-label text').attr('x', 0).attr('y', labelConfiguration.fontSize / 3).attr('text-anchor', 'middle').attr('font-family', labelConfiguration.fontFamily).attr('font-size', labelConfiguration.fontSize).attr('fill', connection.selected ? labelConfiguration.selectedColor : labelConfiguration.color).style('font-kerning', 'none').text(connection.endLabel);
@@ -8338,8 +8453,8 @@ class DiagramCanvas {
8338
8453
  const boundingWidth = !connection.endLabel ? 0 : endLabelBoundingRect.width / this.zoomTransform.k + getLeftPadding$1(labelConfiguration) + getRightPadding$1(labelConfiguration);
8339
8454
  const boundingHeight = !connection.endLabel ? 0 : endLabelBoundingRect.height / this.zoomTransform.k + getTopPadding$1(labelConfiguration) + getBottomPadding$1(labelConfiguration);
8340
8455
  const pathEndLabelPoint = pathNode.getPointAtLength(pathLength - Math.max(getLeftMargin(labelConfiguration) + boundingWidth / 2, getRightMargin(labelConfiguration) + boundingWidth / 2, getTopMargin(labelConfiguration) + boundingHeight / 2, getBottomMargin(labelConfiguration) + boundingHeight / 2));
8341
- connectionSelection.select('g.diagram-connection-end-label path').attr('d', pillPath(-boundingWidth / 2, -boundingHeight / 2, boundingWidth, boundingHeight)).attr('fill', connection.look.color).attr('stroke', 'none');
8342
- connectionSelection.select('g.diagram-connection-end-label').attr('transform', `translate(${pathEndLabelPoint.x},${pathEndLabelPoint.y})`);
8456
+ connectionSelection.select('g.diagram-connection-end-label path').attr('d', pillPath(-boundingWidth / 2, -boundingHeight / 2, boundingWidth, boundingHeight)).attr('fill', labelConfiguration.backgroundColor).attr('stroke', 'none');
8457
+ connectionSelection.select('g.diagram-connection-end-label').attr('transform', `translate(${pathEndLabelPoint.x + endLabelShiftX * boundingWidth},${pathEndLabelPoint.y + endLabelShiftY * boundingHeight})`);
8343
8458
  }
8344
8459
  }
8345
8460
  }
@@ -8511,51 +8626,61 @@ class DiagramCanvas {
8511
8626
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
8512
8627
  this.userHighlight.clear();
8513
8628
  if (this.unfinishedConnection !== undefined) {
8514
- if (this.unfinishedConnection.start !== port) {
8515
- if (this.unfinishedConnection.type.canStartFromType(((_d = (_c = (_b = (_a = this.unfinishedConnection) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.getNode()) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.id) || '') && ((_f = (_e = this.unfinishedConnection) === null || _e === void 0 ? void 0 : _e.start) === null || _f === void 0 ? void 0 : _f.allowsOutgoing) && this.unfinishedConnection.type.canFinishOnType(((_h = (_g = port.getNode()) === null || _g === void 0 ? void 0 : _g.type) === null || _h === void 0 ? void 0 : _h.id) || '') && port.allowsIncoming) {
8516
- const addConnectionAction = new AddConnectionAction(this, this.unfinishedConnection.type, (_j = this.unfinishedConnection.start) === null || _j === void 0 ? void 0 : _j.id, port.id);
8517
- // clean up the previous unfinished connection
8518
- this.dropConnection();
8519
- addConnectionAction.do();
8520
- this.actionStack.add(addConnectionAction);
8521
- } else if (this.unfinishedConnection.type.canFinishOnType(((_o = (_m = (_l = (_k = this.unfinishedConnection) === null || _k === void 0 ? void 0 : _k.start) === null || _l === void 0 ? void 0 : _l.getNode()) === null || _m === void 0 ? void 0 : _m.type) === null || _o === void 0 ? void 0 : _o.id) || '') && ((_q = (_p = this.unfinishedConnection) === null || _p === void 0 ? void 0 : _p.start) === null || _q === void 0 ? void 0 : _q.allowsIncoming) && this.unfinishedConnection.type.canStartFromType(((_s = (_r = port.getNode()) === null || _r === void 0 ? void 0 : _r.type) === null || _s === void 0 ? void 0 : _s.id) || '') && port.allowsOutgoing) {
8522
- const addConnectionAction = new AddConnectionAction(this, this.unfinishedConnection.type, port.id, (_t = this.unfinishedConnection.start) === null || _t === void 0 ? void 0 : _t.id);
8523
- // clean up the previous unfinished connection
8524
- this.dropConnection();
8525
- addConnectionAction.do();
8526
- this.actionStack.add(addConnectionAction);
8527
- } else {
8528
- if (this.inferConnectionType) {
8529
- let differentConnectionType = this.model.connections.types.all().find(t => {
8629
+ if (!this.allowConnectionLoops && this.unfinishedConnection.start === port) {
8630
+ this.dropConnection();
8631
+ return;
8632
+ }
8633
+ if (!this.allowSharingPorts && (port.incomingConnections.length > 0 || port.outgoingConnections.length > 0)) {
8634
+ this.dropConnection();
8635
+ return;
8636
+ }
8637
+ if (!this.allowSharingBothPorts && this.model.connections.find(c => {
8638
+ var _a, _b;
8639
+ return c.start === ((_a = this.unfinishedConnection) === null || _a === void 0 ? void 0 : _a.start) && c.end === port || c.end === ((_b = this.unfinishedConnection) === null || _b === void 0 ? void 0 : _b.start) && c.start === port;
8640
+ }) !== undefined) {
8641
+ this.dropConnection();
8642
+ return;
8643
+ }
8644
+ if (this.unfinishedConnection.type.canStartFromType(((_d = (_c = (_b = (_a = this.unfinishedConnection) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.getNode()) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.id) || '') && ((_f = (_e = this.unfinishedConnection) === null || _e === void 0 ? void 0 : _e.start) === null || _f === void 0 ? void 0 : _f.allowsOutgoing) && this.unfinishedConnection.type.canFinishOnType(((_h = (_g = port.getNode()) === null || _g === void 0 ? void 0 : _g.type) === null || _h === void 0 ? void 0 : _h.id) || '') && port.allowsIncoming) {
8645
+ const addConnectionAction = new AddConnectionAction(this, this.unfinishedConnection.type, (_j = this.unfinishedConnection.start) === null || _j === void 0 ? void 0 : _j.id, port.id);
8646
+ // clean up the previous unfinished connection
8647
+ this.dropConnection();
8648
+ addConnectionAction.do();
8649
+ this.actionStack.add(addConnectionAction);
8650
+ } else if (this.unfinishedConnection.type.canFinishOnType(((_o = (_m = (_l = (_k = this.unfinishedConnection) === null || _k === void 0 ? void 0 : _k.start) === null || _l === void 0 ? void 0 : _l.getNode()) === null || _m === void 0 ? void 0 : _m.type) === null || _o === void 0 ? void 0 : _o.id) || '') && ((_q = (_p = this.unfinishedConnection) === null || _p === void 0 ? void 0 : _p.start) === null || _q === void 0 ? void 0 : _q.allowsIncoming) && this.unfinishedConnection.type.canStartFromType(((_s = (_r = port.getNode()) === null || _r === void 0 ? void 0 : _r.type) === null || _s === void 0 ? void 0 : _s.id) || '') && port.allowsOutgoing) {
8651
+ const addConnectionAction = new AddConnectionAction(this, this.unfinishedConnection.type, port.id, (_t = this.unfinishedConnection.start) === null || _t === void 0 ? void 0 : _t.id);
8652
+ // clean up the previous unfinished connection
8653
+ this.dropConnection();
8654
+ addConnectionAction.do();
8655
+ this.actionStack.add(addConnectionAction);
8656
+ } else {
8657
+ if (this.inferConnectionType) {
8658
+ let differentConnectionType = this.model.connections.types.all().find(t => {
8659
+ var _a, _b, _c, _d, _e, _f, _g, _h;
8660
+ return t.canStartFromType(((_d = (_c = (_b = (_a = this.unfinishedConnection) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.getNode()) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.id) || '') && ((_f = (_e = this.unfinishedConnection) === null || _e === void 0 ? void 0 : _e.start) === null || _f === void 0 ? void 0 : _f.allowsOutgoing) && t.canFinishOnType(((_h = (_g = port.getNode()) === null || _g === void 0 ? void 0 : _g.type) === null || _h === void 0 ? void 0 : _h.id) || '') && port.allowsIncoming;
8661
+ });
8662
+ let invertConnection = false;
8663
+ if (differentConnectionType === undefined) {
8664
+ differentConnectionType = this.model.connections.types.all().find(t => {
8530
8665
  var _a, _b, _c, _d, _e, _f, _g, _h;
8531
- return t.canStartFromType(((_d = (_c = (_b = (_a = this.unfinishedConnection) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.getNode()) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.id) || '') && ((_f = (_e = this.unfinishedConnection) === null || _e === void 0 ? void 0 : _e.start) === null || _f === void 0 ? void 0 : _f.allowsOutgoing) && t.canFinishOnType(((_h = (_g = port.getNode()) === null || _g === void 0 ? void 0 : _g.type) === null || _h === void 0 ? void 0 : _h.id) || '') && port.allowsIncoming;
8666
+ return t.canFinishOnType(((_d = (_c = (_b = (_a = this.unfinishedConnection) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.getNode()) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.id) || '') && ((_f = (_e = this.unfinishedConnection) === null || _e === void 0 ? void 0 : _e.start) === null || _f === void 0 ? void 0 : _f.allowsIncoming) && t.canStartFromType(((_h = (_g = port.getNode()) === null || _g === void 0 ? void 0 : _g.type) === null || _h === void 0 ? void 0 : _h.id) || '') && port.allowsOutgoing;
8532
8667
  });
8533
- let invertConnection = false;
8534
- if (differentConnectionType === undefined) {
8535
- differentConnectionType = this.model.connections.types.all().find(t => {
8536
- var _a, _b, _c, _d, _e, _f, _g, _h;
8537
- return t.canFinishOnType(((_d = (_c = (_b = (_a = this.unfinishedConnection) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.getNode()) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.id) || '') && ((_f = (_e = this.unfinishedConnection) === null || _e === void 0 ? void 0 : _e.start) === null || _f === void 0 ? void 0 : _f.allowsIncoming) && t.canStartFromType(((_h = (_g = port.getNode()) === null || _g === void 0 ? void 0 : _g.type) === null || _h === void 0 ? void 0 : _h.id) || '') && port.allowsOutgoing;
8538
- });
8539
- invertConnection = true;
8540
- }
8541
- if (differentConnectionType !== undefined) {
8542
- const addConnectionAction = new AddConnectionAction(this, differentConnectionType, invertConnection ? port.id : (_u = this.unfinishedConnection.start) === null || _u === void 0 ? void 0 : _u.id, invertConnection ? (_v = this.unfinishedConnection.start) === null || _v === void 0 ? void 0 : _v.id : port.id);
8543
- // clean up the previous unfinished connection
8544
- this.dropConnection();
8545
- addConnectionAction.do();
8546
- this.actionStack.add(addConnectionAction);
8547
- } else {
8548
- // error: connection target of wrong type and no allowed type can be found
8549
- this.dropConnection();
8550
- }
8668
+ invertConnection = true;
8669
+ }
8670
+ if (differentConnectionType !== undefined) {
8671
+ const addConnectionAction = new AddConnectionAction(this, differentConnectionType, invertConnection ? port.id : (_u = this.unfinishedConnection.start) === null || _u === void 0 ? void 0 : _u.id, invertConnection ? (_v = this.unfinishedConnection.start) === null || _v === void 0 ? void 0 : _v.id : port.id);
8672
+ // clean up the previous unfinished connection
8673
+ this.dropConnection();
8674
+ addConnectionAction.do();
8675
+ this.actionStack.add(addConnectionAction);
8551
8676
  } else {
8552
- // error: connection target of wrong type and can't guess for a different type
8677
+ // error: connection target of wrong type and no allowed type can be found
8553
8678
  this.dropConnection();
8554
8679
  }
8680
+ } else {
8681
+ // error: connection target of wrong type and can't guess for a different type
8682
+ this.dropConnection();
8555
8683
  }
8556
- } else {
8557
- // error: start port of a connection can't also be the end port
8558
- this.dropConnection();
8559
8684
  }
8560
8685
  }
8561
8686
  }
package/index.esm.js CHANGED
@@ -2117,11 +2117,14 @@ class DiagramConnection extends DiagramElement {
2117
2117
  * @public
2118
2118
  */
2119
2119
  tighten() {
2120
- var _a, _b;
2121
- if (((_a = this.start) === null || _a === void 0 ? void 0 : _a.rootElement) && this.end) {
2120
+ var _a, _b, _c, _d, _e;
2121
+ const allowConnectionLoops = ((_a = this.model.canvas) === null || _a === void 0 ? void 0 : _a.allowConnectionLoops) || false;
2122
+ const allowSharingPorts = ((_b = this.model.canvas) === null || _b === void 0 ? void 0 : _b.allowSharingPorts) !== false;
2123
+ const allowSharingBothPorts = ((_c = this.model.canvas) === null || _c === void 0 ? void 0 : _c.allowSharingBothPorts) || false;
2124
+ if (((_d = this.start) === null || _d === void 0 ? void 0 : _d.rootElement) && this.end) {
2122
2125
  const alternativeStartPortsSortedByDistanceAscending = this.start.rootElement.ports.map(p => [p, p.distanceTo(this.end.coords)]).sort((a, b) => a[1] - b[1]).map(a => a[0]);
2123
2126
  checkAlternativeStartPorts: for (const alternativeStartPort of alternativeStartPortsSortedByDistanceAscending) {
2124
- if (alternativeStartPort === this.end) {
2127
+ if (!allowConnectionLoops && alternativeStartPort === this.end) {
2125
2128
  // alternative start port not valid, it is the same as the end port
2126
2129
  continue checkAlternativeStartPorts;
2127
2130
  }
@@ -2129,7 +2132,11 @@ class DiagramConnection extends DiagramElement {
2129
2132
  // alternative start port not valid, it doesn't allow outgoing connections
2130
2133
  continue checkAlternativeStartPorts;
2131
2134
  }
2132
- {
2135
+ if (!allowSharingPorts && (alternativeStartPort.incomingConnections.length === 1 && alternativeStartPort.incomingConnections[0] !== this || alternativeStartPort.incomingConnections.length > 1 || alternativeStartPort.outgoingConnections.length === 1 && alternativeStartPort.outgoingConnections[0] !== this || alternativeStartPort.outgoingConnections.length > 1)) {
2136
+ // alternative start port not valid, it already has other connections
2137
+ continue checkAlternativeStartPorts;
2138
+ }
2139
+ if (!allowSharingBothPorts) {
2133
2140
  for (const connection of alternativeStartPort.outgoingConnections) {
2134
2141
  if (connection !== this && connection.end === this.end) {
2135
2142
  // alternative start port not valid, there is a connection whose start and end matches the alternative start port and this connection's end
@@ -2152,10 +2159,10 @@ class DiagramConnection extends DiagramElement {
2152
2159
  }
2153
2160
  }
2154
2161
  }
2155
- if (((_b = this.end) === null || _b === void 0 ? void 0 : _b.rootElement) && this.start) {
2162
+ if (((_e = this.end) === null || _e === void 0 ? void 0 : _e.rootElement) && this.start) {
2156
2163
  const alternativeEndPortsSortedByDistanceAscending = this.end.rootElement.ports.map(p => [p, p.distanceTo(this.start.coords)]).sort((a, b) => a[1] - b[1]).map(a => a[0]);
2157
2164
  checkAlternativeEndPorts: for (const alternativeEndPort of alternativeEndPortsSortedByDistanceAscending) {
2158
- if (alternativeEndPort === this.start) {
2165
+ if (!allowConnectionLoops && alternativeEndPort === this.start) {
2159
2166
  // alternative end port not valid, it is the same as the end port
2160
2167
  continue checkAlternativeEndPorts;
2161
2168
  }
@@ -2163,7 +2170,11 @@ class DiagramConnection extends DiagramElement {
2163
2170
  // alternative end port not valid, it doesn't allow incoming connections
2164
2171
  continue checkAlternativeEndPorts;
2165
2172
  }
2166
- {
2173
+ if (!allowSharingPorts && (alternativeEndPort.outgoingConnections.length === 1 && alternativeEndPort.outgoingConnections[0] !== this || alternativeEndPort.outgoingConnections.length > 1 || alternativeEndPort.incomingConnections.length === 1 && alternativeEndPort.incomingConnections[0] !== this || alternativeEndPort.incomingConnections.length > 1)) {
2174
+ // alternative end port not valid, it already has other connections
2175
+ continue checkAlternativeEndPorts;
2176
+ }
2177
+ if (!allowSharingBothPorts) {
2167
2178
  for (const connection of alternativeEndPort.incomingConnections) {
2168
2179
  if (connection !== this && connection.start === this.start) {
2169
2180
  // alternative end port not valid, there is a connection whose start and end matches the alternative end port and this connection's start
@@ -2271,6 +2282,7 @@ const DIAGRAM_FIELD_DEFAULTS = {
2271
2282
  fontFamily: "'Wonder Unit Sans', sans-serif",
2272
2283
  color: '#000000',
2273
2284
  selectedColor: '#000000',
2285
+ backgroundColor: '#00000000',
2274
2286
  horizontalAlign: HorizontalAlign.Center,
2275
2287
  verticalAlign: VerticalAlign.Center,
2276
2288
  orientation: Side.Top,
@@ -2927,6 +2939,7 @@ class DiagramSection extends DiagramElement {
2927
2939
  * @public
2928
2940
  */
2929
2941
  setGeometry(geometry) {
2942
+ var _a;
2930
2943
  const oldCoordsX = [this.coords[0], this.coords[0] + this.width];
2931
2944
  const oldCoordsY = [this.coords[1], this.coords[1] + this.height];
2932
2945
  this.coords = [...geometry.coords];
@@ -2950,8 +2963,10 @@ class DiagramSection extends DiagramElement {
2950
2963
  for (const decorator of this.decorators) {
2951
2964
  decorator.move(translatePointWithAnchors(decorator.coords, oldCoordsX, oldCoordsY, newCoordsX, newCoordsY, decorator.anchorPointX, decorator.anchorPointY));
2952
2965
  }
2966
+ if (((_a = this.model.canvas) === null || _a === void 0 ? void 0 : _a.autoTightenConnections) !== false) {
2967
+ this.getConnections().forEach(c => c.tighten());
2968
+ }
2953
2969
  // Update canvas.
2954
- this.getConnections().forEach(c => c.tighten());
2955
2970
  this.updateInView();
2956
2971
  }
2957
2972
  }
@@ -3670,6 +3685,7 @@ class DiagramNode extends DiagramElement {
3670
3685
  * @public
3671
3686
  */
3672
3687
  setGeometry(geometry) {
3688
+ var _a;
3673
3689
  this.raise();
3674
3690
  const oldCoordsX = [this.coords[0], this.coords[0] + this.width];
3675
3691
  const oldCoordsY = [this.coords[1], this.coords[1] + this.height];
@@ -3707,8 +3723,10 @@ class DiagramNode extends DiagramElement {
3707
3723
  for (const decorator of this.decorators) {
3708
3724
  decorator.move(translatePointWithAnchors(decorator.coords, oldCoordsX, oldCoordsY, newCoordsX, newCoordsY, decorator.anchorPointX, decorator.anchorPointY));
3709
3725
  }
3726
+ if (((_a = this.model.canvas) === null || _a === void 0 ? void 0 : _a.autoTightenConnections) !== false) {
3727
+ this.getConnections().forEach(c => c.tighten());
3728
+ }
3710
3729
  // Update canvas.
3711
- this.getConnections().forEach(c => c.tighten());
3712
3730
  this.updateInView();
3713
3731
  }
3714
3732
  /**
@@ -6402,10 +6420,11 @@ class DiagramUserHighlight extends DiagramElementSet {
6402
6420
  * @public
6403
6421
  * @param canvas A canvas.
6404
6422
  */
6405
- constructor(canvas) {
6423
+ constructor(canvas, highlightSections = true) {
6406
6424
  super();
6407
6425
  this.focus = undefined;
6408
6426
  this.canvas = canvas;
6427
+ this.highlightSections = highlightSections;
6409
6428
  }
6410
6429
  /**
6411
6430
  * Gets the focus of the user highlight, which is the element where the current user highlight started regardless of which other elements were highlighted as a consequence.
@@ -6431,6 +6450,20 @@ class DiagramUserHighlight extends DiagramElementSet {
6431
6450
  add(element) {
6432
6451
  super.add(element);
6433
6452
  if (element instanceof DiagramNode) {
6453
+ if (!this.highlightSections) {
6454
+ for (const section of element.sections) {
6455
+ super.add(section);
6456
+ for (const port of section.ports) {
6457
+ super.add(port);
6458
+ this.canvas.updatePortsInView(port.id);
6459
+ }
6460
+ if (section.label) {
6461
+ super.add(section.label);
6462
+ this.canvas.updateFieldsInView(section.label.id);
6463
+ }
6464
+ this.canvas.updateSectionsInView(section.id);
6465
+ }
6466
+ }
6434
6467
  for (const port of element.ports) {
6435
6468
  super.add(port);
6436
6469
  this.canvas.updatePortsInView(port.id);
@@ -6441,15 +6474,19 @@ class DiagramUserHighlight extends DiagramElementSet {
6441
6474
  }
6442
6475
  this.canvas.updateNodesInView(element.id);
6443
6476
  } else if (element instanceof DiagramSection) {
6444
- for (const port of element.ports) {
6445
- super.add(port);
6446
- this.canvas.updatePortsInView(port.id);
6447
- }
6448
- if (element.label) {
6449
- super.add(element.label);
6450
- this.canvas.updateFieldsInView(element.label.id);
6477
+ if (!this.highlightSections && element.node) {
6478
+ this.add(element.node);
6479
+ } else {
6480
+ for (const port of element.ports) {
6481
+ super.add(port);
6482
+ this.canvas.updatePortsInView(port.id);
6483
+ }
6484
+ if (element.label) {
6485
+ super.add(element.label);
6486
+ this.canvas.updateFieldsInView(element.label.id);
6487
+ }
6488
+ this.canvas.updateSectionsInView(element.id);
6451
6489
  }
6452
- this.canvas.updateSectionsInView(element.id);
6453
6490
  } else if (element instanceof DiagramPort) {
6454
6491
  if (element.label) {
6455
6492
  super.add(element.label);
@@ -6918,7 +6955,6 @@ const RESIZER_THICKNESS = 6;
6918
6955
  */
6919
6956
  const ACTION_STACK_SIZE = 25;
6920
6957
  const UNFINISHED_CONNECTION_ID = 'diagram-connection-unfinished';
6921
- const MAX_DISTANCE_TO_PORT = 100;
6922
6958
  class DiagramCanvas {
6923
6959
  get connectionType() {
6924
6960
  return this._connectionType;
@@ -6936,7 +6972,7 @@ class DiagramCanvas {
6936
6972
  * @param config The configuration object used to set the parameters of this canvas.
6937
6973
  */
6938
6974
  constructor(parentComponent, config) {
6939
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
6975
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5;
6940
6976
  this.backgroundPatternId = `daga-background-pattern-id-${DiagramCanvas.canvasCount++}`;
6941
6977
  this.zoomTransform = d3.zoomIdentity;
6942
6978
  // used to distinguish drags from clicks when dragging elements and during multiple selection
@@ -6952,19 +6988,24 @@ class DiagramCanvas {
6952
6988
  this.parentComponent = parentComponent;
6953
6989
  this.model = new DiagramModel(this, undefined, config.name || 'unnamed', '', config.type || '', config.properties || []);
6954
6990
  this.userSelection = new DiagramUserSelection(this);
6955
- this.userHighlight = new DiagramUserHighlight(this);
6956
- this.contextMenu = new DiagramContextMenu(this, (_a = config.canvas) === null || _a === void 0 ? void 0 : _a.contextMenu);
6957
- this.backgroundColor = ((_b = config.canvas) === null || _b === void 0 ? void 0 : _b.backgroundColor) || '#FFFFFF';
6958
- this.gridStyle = (_e = (_d = (_c = config.canvas) === null || _c === void 0 ? void 0 : _c.grid) === null || _d === void 0 ? void 0 : _d.style) !== null && _e !== void 0 ? _e : GRID_DEFAULTS.style;
6959
- this.gridSize = ((_g = (_f = config.canvas) === null || _f === void 0 ? void 0 : _f.grid) === null || _g === void 0 ? void 0 : _g.enabled) === false || ((_h = config.canvas) === null || _h === void 0 ? void 0 : _h.grid) === undefined ? 0 : Math.abs(((_k = (_j = config.canvas) === null || _j === void 0 ? void 0 : _j.grid) === null || _k === void 0 ? void 0 : _k.spacing) || GRID_DEFAULTS.spacing);
6960
- this.gridThickness = Math.abs(((_m = (_l = config.canvas) === null || _l === void 0 ? void 0 : _l.grid) === null || _m === void 0 ? void 0 : _m.thickness) || GRID_DEFAULTS.thickness);
6961
- this.gridColor = ((_p = (_o = config.canvas) === null || _o === void 0 ? void 0 : _o.grid) === null || _p === void 0 ? void 0 : _p.color) || GRID_DEFAULTS.color;
6962
- this.snapToGrid = ((_r = (_q = config.canvas) === null || _q === void 0 ? void 0 : _q.grid) === null || _r === void 0 ? void 0 : _r.enabled) === false || ((_s = config.canvas) === null || _s === void 0 ? void 0 : _s.grid) === undefined ? false : ((_u = (_t = config.canvas) === null || _t === void 0 ? void 0 : _t.grid) === null || _u === void 0 ? void 0 : _u.snap) || GRID_DEFAULTS.snap;
6963
- this.zoomFactor = ((_v = config.canvas) === null || _v === void 0 ? void 0 : _v.zoomFactor) || 2;
6964
- this.panRate = ((_w = config.canvas) === null || _w === void 0 ? void 0 : _w.panRate) || 100;
6965
- this.inferConnectionType = config.inferConnectionType || false;
6991
+ this.userHighlight = new DiagramUserHighlight(this, ((_a = config.canvas) === null || _a === void 0 ? void 0 : _a.highlightSections) !== false);
6992
+ this.contextMenu = new DiagramContextMenu(this, (_b = config.canvas) === null || _b === void 0 ? void 0 : _b.contextMenu);
6993
+ this.backgroundColor = ((_c = config.canvas) === null || _c === void 0 ? void 0 : _c.backgroundColor) || '#FFFFFF';
6994
+ this.gridStyle = (_f = (_e = (_d = config.canvas) === null || _d === void 0 ? void 0 : _d.grid) === null || _e === void 0 ? void 0 : _e.style) !== null && _f !== void 0 ? _f : GRID_DEFAULTS.style;
6995
+ this.gridSize = ((_h = (_g = config.canvas) === null || _g === void 0 ? void 0 : _g.grid) === null || _h === void 0 ? void 0 : _h.enabled) === false || ((_j = config.canvas) === null || _j === void 0 ? void 0 : _j.grid) === undefined ? 0 : Math.abs(((_l = (_k = config.canvas) === null || _k === void 0 ? void 0 : _k.grid) === null || _l === void 0 ? void 0 : _l.spacing) || GRID_DEFAULTS.spacing);
6996
+ this.gridThickness = Math.abs(((_o = (_m = config.canvas) === null || _m === void 0 ? void 0 : _m.grid) === null || _o === void 0 ? void 0 : _o.thickness) || GRID_DEFAULTS.thickness);
6997
+ this.gridColor = ((_q = (_p = config.canvas) === null || _p === void 0 ? void 0 : _p.grid) === null || _q === void 0 ? void 0 : _q.color) || GRID_DEFAULTS.color;
6998
+ this.snapToGrid = ((_s = (_r = config.canvas) === null || _r === void 0 ? void 0 : _r.grid) === null || _s === void 0 ? void 0 : _s.enabled) === false || ((_t = config.canvas) === null || _t === void 0 ? void 0 : _t.grid) === undefined ? false : ((_v = (_u = config.canvas) === null || _u === void 0 ? void 0 : _u.grid) === null || _v === void 0 ? void 0 : _v.snap) || GRID_DEFAULTS.snap;
6999
+ this.zoomFactor = ((_w = config.canvas) === null || _w === void 0 ? void 0 : _w.zoomFactor) || 2;
7000
+ this.panRate = ((_x = config.canvas) === null || _x === void 0 ? void 0 : _x.panRate) || 100;
7001
+ this.inferConnectionType = ((_y = config.connectionSettings) === null || _y === void 0 ? void 0 : _y.inferConnectionType) || false;
7002
+ this.autoTightenConnections = ((_z = config.connectionSettings) === null || _z === void 0 ? void 0 : _z.autoTighten) !== false;
7003
+ this.allowConnectionLoops = ((_0 = config.connectionSettings) === null || _0 === void 0 ? void 0 : _0.allowLoops) || false;
7004
+ this.allowSharingPorts = ((_1 = config.connectionSettings) === null || _1 === void 0 ? void 0 : _1.sharePorts) !== false;
7005
+ this.allowSharingBothPorts = ((_2 = config.connectionSettings) === null || _2 === void 0 ? void 0 : _2.shareBothPorts) || false;
7006
+ this.portHighlightRadius = ((_3 = config.connectionSettings) === null || _3 === void 0 ? void 0 : _3.portHighlightRadius) || 100;
6966
7007
  this.multipleSelectionOn = false;
6967
- this.priorityThresholds = ((_x = config.canvas) === null || _x === void 0 ? void 0 : _x.priorityThresholds) || [];
7008
+ this.priorityThresholds = ((_4 = config.canvas) === null || _4 === void 0 ? void 0 : _4.priorityThresholds) || [];
6968
7009
  this.priorityThreshold = this.priorityThresholds ? this.priorityThresholds[0] : undefined;
6969
7010
  this.layoutFormat = config.layoutFormat;
6970
7011
  this.userActions = config.userActions || {};
@@ -6991,7 +7032,7 @@ class DiagramCanvas {
6991
7032
  const connectionType = new DiagramConnectionType(Object.assign(Object.assign({}, config.connectionTypeDefaults), connectionTypeConfig));
6992
7033
  this.model.connections.types.add(connectionType);
6993
7034
  }
6994
- this._connectionType = config.defaultConnection !== undefined ? this.model.connections.types.get(config.defaultConnection) : undefined;
7035
+ this._connectionType = ((_5 = config === null || config === void 0 ? void 0 : config.connectionSettings) === null || _5 === void 0 ? void 0 : _5.defaultConnection) !== undefined ? this.model.connections.types.get(config.connectionSettings.defaultConnection) : undefined;
6995
7036
  }
6996
7037
  }
6997
7038
  addValidator(validator) {
@@ -7169,7 +7210,10 @@ class DiagramCanvas {
7169
7210
  }
7170
7211
  });
7171
7212
  const canvasView = this.selectSVGElement().append('g').attr('class', 'daga-canvas-view').attr('width', `100%`).attr('height', `100%`);
7172
- canvasView.call(this.zoomBehavior = d3.zoom().on(ZoomEvents.Zoom, event => {
7213
+ canvasView.call(this.zoomBehavior = d3.zoom().filter(event => {
7214
+ // Disable double-click zoom by filtering out dblclick events
7215
+ return event.type !== Events.DoubleClick;
7216
+ }).on(ZoomEvents.Zoom, event => {
7173
7217
  if (event.sourceEvent) {
7174
7218
  // zoom event was triggered by user
7175
7219
  if (!this.canUserPerformAction(DiagramActions.Zoom)) {
@@ -7836,7 +7880,7 @@ class DiagramCanvas {
7836
7880
  if (this.multipleSelectionOn || this.secondaryButton) {
7837
7881
  this.startMultipleSelection(event);
7838
7882
  } else {
7839
- if (this.canUserPerformAction(DiagramActions.AddConnection) && !d.removed) {
7883
+ if (this.canUserPerformAction(DiagramActions.AddConnection) && (this.allowSharingPorts || d.incomingConnections.length === 0 && d.outgoingConnections.length === 0) && !d.removed) {
7840
7884
  setCursorStyle(CursorStyle.Grabbing);
7841
7885
  this.startConnection(d);
7842
7886
  // should be true after having called this.startConnection()
@@ -7882,7 +7926,7 @@ class DiagramCanvas {
7882
7926
  closestPortFound = port;
7883
7927
  }
7884
7928
  }
7885
- if (closestPortFound && minDistanceFound < MAX_DISTANCE_TO_PORT) {
7929
+ if (closestPortFound && minDistanceFound < this.portHighlightRadius) {
7886
7930
  this.userHighlight.focusOn(closestPortFound);
7887
7931
  } else {
7888
7932
  this.userHighlight.clear();
@@ -8287,6 +8331,77 @@ class DiagramCanvas {
8287
8331
  const labelConfiguration = Object.assign(Object.assign({}, DIAGRAM_FIELD_DEFAULTS), connection.type.label);
8288
8332
  if (pathNode) {
8289
8333
  const pathLength = pathNode.getTotalLength();
8334
+ let startLabelShiftX = 0;
8335
+ let startLabelShiftY = 0;
8336
+ let middleLabelShiftX = 0;
8337
+ let middleLabelShiftY = 0;
8338
+ let endLabelShiftX = 0;
8339
+ let endLabelShiftY = 0;
8340
+ if (labelConfiguration.backgroundColor === '#00000000') {
8341
+ // background color is transparent / not set, so we find an alternative position for the label
8342
+ const deltaX = connection.endCoords[0] - connection.startCoords[0];
8343
+ const deltaY = connection.endCoords[1] - connection.startCoords[1];
8344
+ switch (connection.startDirection) {
8345
+ case Side.Top:
8346
+ startLabelShiftX = deltaX >= 0 ? 1 : -1;
8347
+ middleLabelShiftX = startLabelShiftX;
8348
+ endLabelShiftX = startLabelShiftX;
8349
+ startLabelShiftY = -1;
8350
+ break;
8351
+ case Side.Bottom:
8352
+ startLabelShiftX = deltaX >= 0 ? 1 : -1;
8353
+ middleLabelShiftX = startLabelShiftX;
8354
+ endLabelShiftX = startLabelShiftX;
8355
+ startLabelShiftY = 1;
8356
+ break;
8357
+ case Side.Left:
8358
+ startLabelShiftX = -1;
8359
+ startLabelShiftY = deltaY > 0 ? 1 : -1;
8360
+ middleLabelShiftY = startLabelShiftY;
8361
+ endLabelShiftY = startLabelShiftY;
8362
+ break;
8363
+ case Side.Right:
8364
+ startLabelShiftX = 1;
8365
+ startLabelShiftY = deltaY > 0 ? 1 : -1;
8366
+ middleLabelShiftY = startLabelShiftY;
8367
+ endLabelShiftY = startLabelShiftY;
8368
+ break;
8369
+ default:
8370
+ startLabelShiftX = 1;
8371
+ middleLabelShiftX = startLabelShiftX;
8372
+ endLabelShiftX = startLabelShiftX;
8373
+ startLabelShiftY = -1;
8374
+ middleLabelShiftY = startLabelShiftY;
8375
+ endLabelShiftY = startLabelShiftY;
8376
+ }
8377
+ switch (connection.endDirection) {
8378
+ case Side.Top:
8379
+ endLabelShiftX = deltaX >= 0 ? 1 : -1;
8380
+ middleLabelShiftX = endLabelShiftX;
8381
+ endLabelShiftY = 1;
8382
+ break;
8383
+ case Side.Bottom:
8384
+ endLabelShiftX = deltaX >= 0 ? 1 : -1;
8385
+ middleLabelShiftX = endLabelShiftX;
8386
+ endLabelShiftY = -1;
8387
+ break;
8388
+ case Side.Left:
8389
+ endLabelShiftX = -1;
8390
+ endLabelShiftY = deltaY > 0 ? 1 : -1;
8391
+ middleLabelShiftY = endLabelShiftY;
8392
+ break;
8393
+ case Side.Right:
8394
+ endLabelShiftX = 1;
8395
+ endLabelShiftY = deltaY > 0 ? 1 : -1;
8396
+ middleLabelShiftY = endLabelShiftY;
8397
+ break;
8398
+ default:
8399
+ endLabelShiftX = 1;
8400
+ middleLabelShiftX = endLabelShiftX;
8401
+ endLabelShiftY = -1;
8402
+ middleLabelShiftY = endLabelShiftY;
8403
+ }
8404
+ }
8290
8405
  // bind start labels
8291
8406
  connectionSelection.select('g.diagram-connection-start-label text').attr('x', 0).attr('y', labelConfiguration.fontSize / 3).attr('text-anchor', 'middle').attr('font-family', labelConfiguration.fontFamily).attr('font-size', labelConfiguration.fontSize).attr('fill', connection.selected ? labelConfiguration.selectedColor : labelConfiguration.color).style('font-kerning', 'none').text(connection.startLabel);
8292
8407
  const startLabelBoundingRect = (_a = connectionSelection.select('g.diagram-connection-start-label text').node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
@@ -8295,8 +8410,8 @@ class DiagramCanvas {
8295
8410
  const boundingWidth = !connection.startLabel ? 0 : startLabelBoundingRect.width / this.zoomTransform.k + getLeftPadding$1(labelConfiguration) + getRightPadding$1(labelConfiguration);
8296
8411
  const boundingHeight = !connection.startLabel ? 0 : startLabelBoundingRect.height / this.zoomTransform.k + getTopPadding$1(labelConfiguration) + getBottomPadding$1(labelConfiguration);
8297
8412
  const pathStartLabelPoint = pathNode.getPointAtLength(Math.max(getLeftMargin(labelConfiguration) + boundingWidth / 2, getRightMargin(labelConfiguration) + boundingWidth / 2, getTopMargin(labelConfiguration) + boundingHeight / 2, getBottomMargin(labelConfiguration) + boundingHeight / 2));
8298
- connectionSelection.select('g.diagram-connection-start-label path').attr('d', pillPath(-boundingWidth / 2, -boundingHeight / 2, boundingWidth, boundingHeight)).attr('fill', connection.look.color).attr('stroke', 'none');
8299
- connectionSelection.select('g.diagram-connection-start-label').attr('transform', `translate(${pathStartLabelPoint.x},${pathStartLabelPoint.y})`);
8413
+ connectionSelection.select('g.diagram-connection-start-label path').attr('d', pillPath(-boundingWidth / 2, -boundingHeight / 2, boundingWidth, boundingHeight)).attr('fill', labelConfiguration.backgroundColor).attr('stroke', 'none');
8414
+ connectionSelection.select('g.diagram-connection-start-label').attr('transform', `translate(${pathStartLabelPoint.x + startLabelShiftX * boundingWidth},${pathStartLabelPoint.y + startLabelShiftY * boundingHeight})`);
8300
8415
  }
8301
8416
  // bind middle labels
8302
8417
  connectionSelection.select('g.diagram-connection-middle-label text').attr('x', 0).attr('y', labelConfiguration.fontSize / 3).attr('text-anchor', 'middle').attr('font-family', labelConfiguration.fontFamily).attr('font-size', labelConfiguration.fontSize).attr('fill', connection.selected ? labelConfiguration.selectedColor : labelConfiguration.color).style('font-kerning', 'none').text(connection.middleLabel);
@@ -8306,8 +8421,8 @@ class DiagramCanvas {
8306
8421
  const boundingWidth = !connection.middleLabel ? 0 : middleLabelBoundingRect.width / this.zoomTransform.k + getLeftPadding$1(labelConfiguration) + getRightPadding$1(labelConfiguration);
8307
8422
  const boundingHeight = !connection.middleLabel ? 0 : middleLabelBoundingRect.height / this.zoomTransform.k + getTopPadding$1(labelConfiguration) + getBottomPadding$1(labelConfiguration);
8308
8423
  const pathMiddleLabelPoint = pathNode.getPointAtLength(pathLength / 2);
8309
- connectionSelection.select('g.diagram-connection-middle-label path').attr('d', pillPath(-boundingWidth / 2, -boundingHeight / 2, boundingWidth, boundingHeight)).attr('fill', connection.look.color).attr('stroke', 'none');
8310
- connectionSelection.select('g.diagram-connection-middle-label').attr('transform', `translate(${pathMiddleLabelPoint.x},${pathMiddleLabelPoint.y})`);
8424
+ connectionSelection.select('g.diagram-connection-middle-label path').attr('d', pillPath(-boundingWidth / 2, -boundingHeight / 2, boundingWidth, boundingHeight)).attr('fill', labelConfiguration.backgroundColor).attr('stroke', 'none');
8425
+ connectionSelection.select('g.diagram-connection-middle-label').attr('transform', `translate(${pathMiddleLabelPoint.x + middleLabelShiftX * boundingWidth},${pathMiddleLabelPoint.y + middleLabelShiftY * boundingHeight})`);
8311
8426
  }
8312
8427
  // bind end labels
8313
8428
  connectionSelection.select('g.diagram-connection-end-label text').attr('x', 0).attr('y', labelConfiguration.fontSize / 3).attr('text-anchor', 'middle').attr('font-family', labelConfiguration.fontFamily).attr('font-size', labelConfiguration.fontSize).attr('fill', connection.selected ? labelConfiguration.selectedColor : labelConfiguration.color).style('font-kerning', 'none').text(connection.endLabel);
@@ -8317,8 +8432,8 @@ class DiagramCanvas {
8317
8432
  const boundingWidth = !connection.endLabel ? 0 : endLabelBoundingRect.width / this.zoomTransform.k + getLeftPadding$1(labelConfiguration) + getRightPadding$1(labelConfiguration);
8318
8433
  const boundingHeight = !connection.endLabel ? 0 : endLabelBoundingRect.height / this.zoomTransform.k + getTopPadding$1(labelConfiguration) + getBottomPadding$1(labelConfiguration);
8319
8434
  const pathEndLabelPoint = pathNode.getPointAtLength(pathLength - Math.max(getLeftMargin(labelConfiguration) + boundingWidth / 2, getRightMargin(labelConfiguration) + boundingWidth / 2, getTopMargin(labelConfiguration) + boundingHeight / 2, getBottomMargin(labelConfiguration) + boundingHeight / 2));
8320
- connectionSelection.select('g.diagram-connection-end-label path').attr('d', pillPath(-boundingWidth / 2, -boundingHeight / 2, boundingWidth, boundingHeight)).attr('fill', connection.look.color).attr('stroke', 'none');
8321
- connectionSelection.select('g.diagram-connection-end-label').attr('transform', `translate(${pathEndLabelPoint.x},${pathEndLabelPoint.y})`);
8435
+ connectionSelection.select('g.diagram-connection-end-label path').attr('d', pillPath(-boundingWidth / 2, -boundingHeight / 2, boundingWidth, boundingHeight)).attr('fill', labelConfiguration.backgroundColor).attr('stroke', 'none');
8436
+ connectionSelection.select('g.diagram-connection-end-label').attr('transform', `translate(${pathEndLabelPoint.x + endLabelShiftX * boundingWidth},${pathEndLabelPoint.y + endLabelShiftY * boundingHeight})`);
8322
8437
  }
8323
8438
  }
8324
8439
  }
@@ -8490,51 +8605,61 @@ class DiagramCanvas {
8490
8605
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
8491
8606
  this.userHighlight.clear();
8492
8607
  if (this.unfinishedConnection !== undefined) {
8493
- if (this.unfinishedConnection.start !== port) {
8494
- if (this.unfinishedConnection.type.canStartFromType(((_d = (_c = (_b = (_a = this.unfinishedConnection) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.getNode()) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.id) || '') && ((_f = (_e = this.unfinishedConnection) === null || _e === void 0 ? void 0 : _e.start) === null || _f === void 0 ? void 0 : _f.allowsOutgoing) && this.unfinishedConnection.type.canFinishOnType(((_h = (_g = port.getNode()) === null || _g === void 0 ? void 0 : _g.type) === null || _h === void 0 ? void 0 : _h.id) || '') && port.allowsIncoming) {
8495
- const addConnectionAction = new AddConnectionAction(this, this.unfinishedConnection.type, (_j = this.unfinishedConnection.start) === null || _j === void 0 ? void 0 : _j.id, port.id);
8496
- // clean up the previous unfinished connection
8497
- this.dropConnection();
8498
- addConnectionAction.do();
8499
- this.actionStack.add(addConnectionAction);
8500
- } else if (this.unfinishedConnection.type.canFinishOnType(((_o = (_m = (_l = (_k = this.unfinishedConnection) === null || _k === void 0 ? void 0 : _k.start) === null || _l === void 0 ? void 0 : _l.getNode()) === null || _m === void 0 ? void 0 : _m.type) === null || _o === void 0 ? void 0 : _o.id) || '') && ((_q = (_p = this.unfinishedConnection) === null || _p === void 0 ? void 0 : _p.start) === null || _q === void 0 ? void 0 : _q.allowsIncoming) && this.unfinishedConnection.type.canStartFromType(((_s = (_r = port.getNode()) === null || _r === void 0 ? void 0 : _r.type) === null || _s === void 0 ? void 0 : _s.id) || '') && port.allowsOutgoing) {
8501
- const addConnectionAction = new AddConnectionAction(this, this.unfinishedConnection.type, port.id, (_t = this.unfinishedConnection.start) === null || _t === void 0 ? void 0 : _t.id);
8502
- // clean up the previous unfinished connection
8503
- this.dropConnection();
8504
- addConnectionAction.do();
8505
- this.actionStack.add(addConnectionAction);
8506
- } else {
8507
- if (this.inferConnectionType) {
8508
- let differentConnectionType = this.model.connections.types.all().find(t => {
8608
+ if (!this.allowConnectionLoops && this.unfinishedConnection.start === port) {
8609
+ this.dropConnection();
8610
+ return;
8611
+ }
8612
+ if (!this.allowSharingPorts && (port.incomingConnections.length > 0 || port.outgoingConnections.length > 0)) {
8613
+ this.dropConnection();
8614
+ return;
8615
+ }
8616
+ if (!this.allowSharingBothPorts && this.model.connections.find(c => {
8617
+ var _a, _b;
8618
+ return c.start === ((_a = this.unfinishedConnection) === null || _a === void 0 ? void 0 : _a.start) && c.end === port || c.end === ((_b = this.unfinishedConnection) === null || _b === void 0 ? void 0 : _b.start) && c.start === port;
8619
+ }) !== undefined) {
8620
+ this.dropConnection();
8621
+ return;
8622
+ }
8623
+ if (this.unfinishedConnection.type.canStartFromType(((_d = (_c = (_b = (_a = this.unfinishedConnection) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.getNode()) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.id) || '') && ((_f = (_e = this.unfinishedConnection) === null || _e === void 0 ? void 0 : _e.start) === null || _f === void 0 ? void 0 : _f.allowsOutgoing) && this.unfinishedConnection.type.canFinishOnType(((_h = (_g = port.getNode()) === null || _g === void 0 ? void 0 : _g.type) === null || _h === void 0 ? void 0 : _h.id) || '') && port.allowsIncoming) {
8624
+ const addConnectionAction = new AddConnectionAction(this, this.unfinishedConnection.type, (_j = this.unfinishedConnection.start) === null || _j === void 0 ? void 0 : _j.id, port.id);
8625
+ // clean up the previous unfinished connection
8626
+ this.dropConnection();
8627
+ addConnectionAction.do();
8628
+ this.actionStack.add(addConnectionAction);
8629
+ } else if (this.unfinishedConnection.type.canFinishOnType(((_o = (_m = (_l = (_k = this.unfinishedConnection) === null || _k === void 0 ? void 0 : _k.start) === null || _l === void 0 ? void 0 : _l.getNode()) === null || _m === void 0 ? void 0 : _m.type) === null || _o === void 0 ? void 0 : _o.id) || '') && ((_q = (_p = this.unfinishedConnection) === null || _p === void 0 ? void 0 : _p.start) === null || _q === void 0 ? void 0 : _q.allowsIncoming) && this.unfinishedConnection.type.canStartFromType(((_s = (_r = port.getNode()) === null || _r === void 0 ? void 0 : _r.type) === null || _s === void 0 ? void 0 : _s.id) || '') && port.allowsOutgoing) {
8630
+ const addConnectionAction = new AddConnectionAction(this, this.unfinishedConnection.type, port.id, (_t = this.unfinishedConnection.start) === null || _t === void 0 ? void 0 : _t.id);
8631
+ // clean up the previous unfinished connection
8632
+ this.dropConnection();
8633
+ addConnectionAction.do();
8634
+ this.actionStack.add(addConnectionAction);
8635
+ } else {
8636
+ if (this.inferConnectionType) {
8637
+ let differentConnectionType = this.model.connections.types.all().find(t => {
8638
+ var _a, _b, _c, _d, _e, _f, _g, _h;
8639
+ return t.canStartFromType(((_d = (_c = (_b = (_a = this.unfinishedConnection) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.getNode()) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.id) || '') && ((_f = (_e = this.unfinishedConnection) === null || _e === void 0 ? void 0 : _e.start) === null || _f === void 0 ? void 0 : _f.allowsOutgoing) && t.canFinishOnType(((_h = (_g = port.getNode()) === null || _g === void 0 ? void 0 : _g.type) === null || _h === void 0 ? void 0 : _h.id) || '') && port.allowsIncoming;
8640
+ });
8641
+ let invertConnection = false;
8642
+ if (differentConnectionType === undefined) {
8643
+ differentConnectionType = this.model.connections.types.all().find(t => {
8509
8644
  var _a, _b, _c, _d, _e, _f, _g, _h;
8510
- return t.canStartFromType(((_d = (_c = (_b = (_a = this.unfinishedConnection) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.getNode()) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.id) || '') && ((_f = (_e = this.unfinishedConnection) === null || _e === void 0 ? void 0 : _e.start) === null || _f === void 0 ? void 0 : _f.allowsOutgoing) && t.canFinishOnType(((_h = (_g = port.getNode()) === null || _g === void 0 ? void 0 : _g.type) === null || _h === void 0 ? void 0 : _h.id) || '') && port.allowsIncoming;
8645
+ return t.canFinishOnType(((_d = (_c = (_b = (_a = this.unfinishedConnection) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.getNode()) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.id) || '') && ((_f = (_e = this.unfinishedConnection) === null || _e === void 0 ? void 0 : _e.start) === null || _f === void 0 ? void 0 : _f.allowsIncoming) && t.canStartFromType(((_h = (_g = port.getNode()) === null || _g === void 0 ? void 0 : _g.type) === null || _h === void 0 ? void 0 : _h.id) || '') && port.allowsOutgoing;
8511
8646
  });
8512
- let invertConnection = false;
8513
- if (differentConnectionType === undefined) {
8514
- differentConnectionType = this.model.connections.types.all().find(t => {
8515
- var _a, _b, _c, _d, _e, _f, _g, _h;
8516
- return t.canFinishOnType(((_d = (_c = (_b = (_a = this.unfinishedConnection) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.getNode()) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.id) || '') && ((_f = (_e = this.unfinishedConnection) === null || _e === void 0 ? void 0 : _e.start) === null || _f === void 0 ? void 0 : _f.allowsIncoming) && t.canStartFromType(((_h = (_g = port.getNode()) === null || _g === void 0 ? void 0 : _g.type) === null || _h === void 0 ? void 0 : _h.id) || '') && port.allowsOutgoing;
8517
- });
8518
- invertConnection = true;
8519
- }
8520
- if (differentConnectionType !== undefined) {
8521
- const addConnectionAction = new AddConnectionAction(this, differentConnectionType, invertConnection ? port.id : (_u = this.unfinishedConnection.start) === null || _u === void 0 ? void 0 : _u.id, invertConnection ? (_v = this.unfinishedConnection.start) === null || _v === void 0 ? void 0 : _v.id : port.id);
8522
- // clean up the previous unfinished connection
8523
- this.dropConnection();
8524
- addConnectionAction.do();
8525
- this.actionStack.add(addConnectionAction);
8526
- } else {
8527
- // error: connection target of wrong type and no allowed type can be found
8528
- this.dropConnection();
8529
- }
8647
+ invertConnection = true;
8648
+ }
8649
+ if (differentConnectionType !== undefined) {
8650
+ const addConnectionAction = new AddConnectionAction(this, differentConnectionType, invertConnection ? port.id : (_u = this.unfinishedConnection.start) === null || _u === void 0 ? void 0 : _u.id, invertConnection ? (_v = this.unfinishedConnection.start) === null || _v === void 0 ? void 0 : _v.id : port.id);
8651
+ // clean up the previous unfinished connection
8652
+ this.dropConnection();
8653
+ addConnectionAction.do();
8654
+ this.actionStack.add(addConnectionAction);
8530
8655
  } else {
8531
- // error: connection target of wrong type and can't guess for a different type
8656
+ // error: connection target of wrong type and no allowed type can be found
8532
8657
  this.dropConnection();
8533
8658
  }
8659
+ } else {
8660
+ // error: connection target of wrong type and can't guess for a different type
8661
+ this.dropConnection();
8534
8662
  }
8535
- } else {
8536
- // error: start port of a connection can't also be the end port
8537
- this.dropConnection();
8538
8663
  }
8539
8664
  }
8540
8665
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metadev/daga",
3
- "version": "4.2.1",
3
+ "version": "4.2.3",
4
4
  "dependencies": {},
5
5
  "peerDependencies": {
6
6
  "d3": "^7.9.0",
@@ -49,10 +49,15 @@ export declare class DiagramCanvas implements Canvas {
49
49
  zoomFactor: number;
50
50
  panRate: number;
51
51
  inferConnectionType: boolean;
52
- multipleSelectionOn: boolean;
53
52
  private _connectionType?;
54
53
  get connectionType(): DiagramConnectionType | undefined;
55
54
  set connectionType(value: DiagramConnectionType | undefined);
55
+ autoTightenConnections: boolean;
56
+ allowConnectionLoops: boolean;
57
+ allowSharingPorts: boolean;
58
+ allowSharingBothPorts: boolean;
59
+ portHighlightRadius: number;
60
+ multipleSelectionOn: boolean;
56
61
  layoutFormat?: string;
57
62
  validators: DiagramValidator[];
58
63
  userActions: {
@@ -8,12 +8,16 @@ import { DiagramElement, DiagramElementSet } from '../model/diagram-element';
8
8
  export declare class DiagramUserHighlight extends DiagramElementSet<DiagramElement> {
9
9
  private readonly canvas;
10
10
  private focus?;
11
+ /**
12
+ * Whether sections can be highlighted individually or the whole node must be highlighted when highlighting a section.
13
+ */
14
+ private highlightSections;
11
15
  /**
12
16
  * Constructs a user highlight object.
13
17
  * @public
14
18
  * @param canvas A canvas.
15
19
  */
16
- constructor(canvas: Canvas);
20
+ constructor(canvas: Canvas, highlightSections?: boolean);
17
21
  /**
18
22
  * Gets the focus of the user highlight, which is the element where the current user highlight started regardless of which other elements were highlighted as a consequence.
19
23
  * @public
@@ -30,6 +30,11 @@ export interface CanvasConfig {
30
30
  * @default 100
31
31
  */
32
32
  panRate?: number;
33
+ /**
34
+ * Whether sections can be highlighted individually or the whole node must be highlighted when highlighting a section.
35
+ * @default true
36
+ */
37
+ highlightSections?: boolean;
33
38
  /**
34
39
  * Possible values of the priority threshold that the user can toggle between to hide nodes whose priority value is below it.
35
40
  * If it is possible to toggle the priority threshold via the filter button, it should have at least two values.
@@ -26,15 +26,10 @@ export interface DiagramConfig {
26
26
  */
27
27
  canvas?: CanvasConfig;
28
28
  /**
29
- * When true, connections made by the user will have their type assumed based on the type of the source and target nodes.
30
- * @default false
31
- */
32
- inferConnectionType?: boolean;
33
- /**
34
- * Id of the type of connection that is initially selected for the user to draw when drawing connections. By default, no type is selected.
35
- * @default undefined
29
+ * Configuration regarding the general settings of connections.
30
+ * @default {}
36
31
  */
37
- defaultConnection?: string;
32
+ connectionSettings?: ConnectionSettingsConfig;
38
33
  /**
39
34
  * Name of the layout format used to arrange the nodes of this diagram when pressing the layout button in the diagram buttons component.
40
35
  * @default undefined
@@ -88,6 +83,47 @@ export interface DiagramConfig {
88
83
  */
89
84
  properties?: Property[];
90
85
  }
86
+ /**
87
+ * Configuration regarding the general settings of connections.
88
+ * @public
89
+ */
90
+ export interface ConnectionSettingsConfig {
91
+ /**
92
+ * When true, connections made by the user will have their type assumed based on the type of the source and target nodes.
93
+ * @default false
94
+ */
95
+ inferConnectionType?: boolean;
96
+ /**
97
+ * Id of the type of connection that is initially selected for the user to draw when drawing connections. By default, no type is selected.
98
+ * @default undefined
99
+ */
100
+ defaultConnection?: string;
101
+ /**
102
+ * Automatically try to change the start and end ports of a connection to minimize the distance between them.
103
+ * @default true
104
+ */
105
+ autoTighten?: boolean;
106
+ /**
107
+ * Whether a connection can use the same port as start and end.
108
+ * @default false
109
+ */
110
+ allowLoops?: boolean;
111
+ /**
112
+ * Whether the same port can be used by two different connections.
113
+ * @default true
114
+ */
115
+ sharePorts?: boolean;
116
+ /**
117
+ * Whether two connections can use the same ports.
118
+ * @default false
119
+ */
120
+ shareBothPorts?: boolean;
121
+ /**
122
+ * Within what radius the closest port may be highlighted when drawing a new connection as a guide to the user.
123
+ * @default 100
124
+ */
125
+ portHighlightRadius?: number;
126
+ }
91
127
  /**
92
128
  * Configuration for which user actions are allowed in a diagram.
93
129
  * @public
@@ -314,6 +350,11 @@ export interface FieldConfig {
314
350
  * @default '#000000'
315
351
  */
316
352
  color?: string;
353
+ /**
354
+ * The background color of the text of this field.
355
+ * @default '#00000000'
356
+ */
357
+ backgroundColor?: string;
317
358
  /**
318
359
  * The color of the text of this field when selected.
319
360
  * @default '#000000'
@@ -36,22 +36,6 @@ export declare const DIAGRAM_CONNECTION_TYPE_DEFAULTS: {
36
36
  endTypes: never[];
37
37
  properties: never[];
38
38
  };
39
- /**
40
- * Whether a connection can have the same port as both its start port and its end port.
41
- */
42
- export declare const CAN_A_CONNECTION_END_ON_THE_SAME_PORT_IT_STARTS = false;
43
- /**
44
- * Whether a connection can have the same ports as another connection.
45
- */
46
- export declare const CAN_A_CONNECTION_HAVE_THE_SAME_PORTS_AS_ANOTHER = false;
47
- /**
48
- * Whether a port can have multiple connections.
49
- */
50
- export declare const CAN_CONNECTIONS_SHARE_PORTS = true;
51
- /**
52
- * Whether tightening connections is allowed.
53
- */
54
- export declare const CAN_TIGHTEN_CONNECTIONS = true;
55
39
  /**
56
40
  * The types allowed for the look of the markers of a connection.
57
41
  * @public
@@ -17,6 +17,7 @@ export declare const DIAGRAM_FIELD_DEFAULTS: {
17
17
  fontFamily: string;
18
18
  color: string;
19
19
  selectedColor: string;
20
+ backgroundColor: string;
20
21
  horizontalAlign: HorizontalAlign;
21
22
  verticalAlign: VerticalAlign;
22
23
  orientation: Side;
@@ -98,15 +98,40 @@ export interface Canvas {
98
98
  * @public
99
99
  */
100
100
  inferConnectionType: boolean;
101
- /**
102
- * If true, the next drag action will create a multiple selection rectangle.
103
- */
104
- multipleSelectionOn: boolean;
105
101
  /**
106
102
  * When a new connection is made, it should have this type.
107
103
  * @public
108
104
  */
109
105
  connectionType?: DiagramConnectionType;
106
+ /**
107
+ * Whether the start and end ports should automatically change to try to minimize the distance between them.
108
+ * @public
109
+ */
110
+ autoTightenConnections: boolean;
111
+ /**
112
+ * Whether a connection can use the same port as start and end.
113
+ * @public
114
+ */
115
+ allowConnectionLoops: boolean;
116
+ /**
117
+ * Whether the same port can be used by two different connections.
118
+ * @public
119
+ */
120
+ allowSharingPorts: boolean;
121
+ /**
122
+ * Whether two connections can use the same ports.
123
+ * @public
124
+ */
125
+ allowSharingBothPorts: boolean;
126
+ /**
127
+ * Within what radius the closest port may be highlighted when drawing a new connection as a guide to the user.
128
+ * @public
129
+ */
130
+ portHighlightRadius: number;
131
+ /**
132
+ * If true, the next drag action will create a multiple selection rectangle.
133
+ */
134
+ multipleSelectionOn: boolean;
110
135
  /**
111
136
  * Identifier of the current layout format used by the diagram.
112
137
  * @public