@metadev/daga 4.0.0 → 4.0.2

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.
Files changed (29) hide show
  1. package/Changelog.md +21 -0
  2. package/index.cjs.js +349 -221
  3. package/index.esm.js +349 -221
  4. package/package.json +1 -1
  5. package/src/index.d.ts +8 -4
  6. package/src/lib/diagram/canvas/diagram-canvas-util.d.ts +10 -0
  7. package/src/lib/diagram/canvas/diagram-canvas.d.ts +2 -0
  8. package/src/lib/diagram/canvas/diagram-context-menu.d.ts +6 -1
  9. package/src/lib/diagram/canvas/diagram-user-selection.d.ts +1 -1
  10. package/src/lib/diagram/collab/collab-action.d.ts +1 -1
  11. package/src/lib/diagram/config/diagram-canvas-config.d.ts +103 -0
  12. package/src/lib/diagram/config/diagram-components-config.d.ts +249 -0
  13. package/src/lib/diagram/config/diagram-config.d.ts +35 -312
  14. package/src/lib/diagram/model/diagram-connection.d.ts +3 -2
  15. package/src/lib/diagram/model/diagram-decorator.d.ts +0 -1
  16. package/src/lib/diagram/model/diagram-field.d.ts +0 -1
  17. package/src/lib/diagram/model/diagram-model.d.ts +2 -1
  18. package/src/lib/diagram/model/diagram-node.d.ts +6 -3
  19. package/src/lib/diagram/model/diagram-object.d.ts +0 -1
  20. package/src/lib/diagram/model/diagram-port.d.ts +1 -1
  21. package/src/lib/diagram/model/diagram-section.d.ts +1 -1
  22. package/src/lib/diagram/property/property-util.d.ts +61 -0
  23. package/src/lib/diagram/property/property.d.ts +146 -0
  24. package/src/lib/diagram/property/value.d.ts +163 -0
  25. package/src/lib/interfaces/canvas.d.ts +6 -0
  26. package/src/lib/interfaces/property-editor.d.ts +1 -1
  27. package/src/lib/util/html-util.d.ts +1 -0
  28. package/src/lib/diagram/model/diagram-property.d.ts +0 -368
  29. /package/src/lib/diagram/config/{diagram-look.d.ts → diagram-look-config.d.ts} +0 -0
package/index.cjs.js CHANGED
@@ -1022,6 +1022,10 @@ class DiagramEntitySet {
1022
1022
  }
1023
1023
  }
1024
1024
 
1025
+ const escapeSelector = selector => {
1026
+ return selector.replace(/([!"#$%&'()*+,\-./:;<=>?@[\\\]^`{|}])/g, '\\$1');
1027
+ };
1028
+
1025
1029
  /**
1026
1030
  * Default priority value for diagram elements.
1027
1031
  * @private
@@ -1083,7 +1087,7 @@ class DiagramElement {
1083
1087
  */
1084
1088
  select() {
1085
1089
  var _a, _b;
1086
- return (_b = (_a = this.model.canvas) === null || _a === undefined ? undefined : _a.selectCanvasView()) === null || _b === undefined ? undefined : _b.select(`g#${this.id}`);
1090
+ return (_b = (_a = this.model.canvas) === null || _a === undefined ? undefined : _a.selectCanvasView()) === null || _b === undefined ? undefined : _b.select(`[id='${escapeSelector(this.id)}']`);
1087
1091
  }
1088
1092
  }
1089
1093
  class DiagramElementSet extends DiagramEntitySet {
@@ -1163,24 +1167,6 @@ class DiagramElementSet extends DiagramEntitySet {
1163
1167
  }
1164
1168
  }
1165
1169
 
1166
- /**
1167
- * Returns whether the incoming timestamp wins over the existing timestamp.
1168
- *
1169
- * In the DiagramModel, timestamps that have never been set are left null;
1170
- * `timestampWins` treats that as an "initial timestamp" that loses to all LogicalTimestamps.
1171
- */
1172
- function timestampWins(incoming, existing) {
1173
- if (!existing) return true;
1174
- if (incoming[0] > existing[0]) return true;
1175
- if (incoming[0] === existing[0]) {
1176
- // In case of equality, declare the incoming timestamp the "winner".
1177
- // This occurs if a client reuses a timestamp for multiple actions in a row,
1178
- // in which case the last created should win.
1179
- return incoming[1] >= existing[1];
1180
- }
1181
- return false;
1182
- }
1183
-
1184
1170
  /**
1185
1171
  * A property which is part of a property set and defines what values a value in a value set can take.
1186
1172
  * @public
@@ -1297,6 +1283,121 @@ class PropertySet {
1297
1283
  return this.propertyList.length > 0;
1298
1284
  }
1299
1285
  }
1286
+
1287
+ /**
1288
+ * Returns whether the incoming timestamp wins over the existing timestamp.
1289
+ *
1290
+ * In the DiagramModel, timestamps that have never been set are left null;
1291
+ * `timestampWins` treats that as an "initial timestamp" that loses to all LogicalTimestamps.
1292
+ */
1293
+ function timestampWins(incoming, existing) {
1294
+ if (!existing) return true;
1295
+ if (incoming[0] > existing[0]) return true;
1296
+ if (incoming[0] === existing[0]) {
1297
+ // In case of equality, declare the incoming timestamp the "winner".
1298
+ // This occurs if a client reuses a timestamp for multiple actions in a row,
1299
+ // in which case the last created should win.
1300
+ return incoming[1] >= existing[1];
1301
+ }
1302
+ return false;
1303
+ }
1304
+
1305
+ /**
1306
+ * Checks if the given value is not empty.
1307
+ * @private
1308
+ * @param a A value.
1309
+ * @returns `true` if the given value is not `undefined`, `null`, `''`, `[]` or `{}`; `false` otherwise.
1310
+ */
1311
+ const empty = a => {
1312
+ return a === undefined || a === null || a === '' || a instanceof Array && a.length === 0 || a instanceof Object && Object.keys(a).length === 0;
1313
+ };
1314
+ /**
1315
+ * Checks whether the given values are equal.
1316
+ * @public
1317
+ * @param a A value.
1318
+ * @param b A value.
1319
+ * @returns `true` if the given values are equal, `false` otherwise.
1320
+ */
1321
+ const equals = (a, b) => {
1322
+ return a === b || JSON.stringify(a) === JSON.stringify(b);
1323
+ };
1324
+ /**
1325
+ * Calculates the differences between the two given objects and returns two objects containing the differences in each relative to the other.
1326
+ *
1327
+ * For each key that holds a different value in the two objects, the resulting objects will contain the differences in the values under that key.
1328
+ *
1329
+ * This function is recursive, that is, if the value under the key is an object, the function will be applied to that value recursively.
1330
+ *
1331
+ * @public
1332
+ * @param a An object.
1333
+ * @param b An object.
1334
+ * @returns A tuple of two objects with each containing the keys that have a different value in the corresponding argument compared to the other argument.
1335
+ */
1336
+ const diff = (a, b) => {
1337
+ const aDiff = {};
1338
+ const bDiff = {};
1339
+ const allKeys = [];
1340
+ for (const key in a) {
1341
+ allKeys.push(key);
1342
+ }
1343
+ for (const key in b) {
1344
+ if (!(key in a)) {
1345
+ allKeys.push(key);
1346
+ }
1347
+ }
1348
+ for (const key of allKeys) {
1349
+ if (isObject(a[key]) && isObject(b[key])) {
1350
+ const diffAB = diff(a[key], b[key]);
1351
+ // only add the key if differences are detected
1352
+ if (Object.keys(diffAB[0]).length > 0 && Object.keys(diffAB[1]).length > 0) {
1353
+ aDiff[key] = diffAB[0];
1354
+ bDiff[key] = diffAB[1];
1355
+ }
1356
+ } else {
1357
+ if (!equals(a[key], b[key])) {
1358
+ aDiff[key] = a[key];
1359
+ bDiff[key] = b[key];
1360
+ }
1361
+ }
1362
+ }
1363
+ return [aDiff, bDiff];
1364
+ };
1365
+ /**
1366
+ * Calculates the differences between the two given values of a valueset and returns two objects containing the differences in each relative to the other.
1367
+ *
1368
+ * @param a An object.
1369
+ * @param b An object.
1370
+ * @param valueSet A ValueSet to use as reference for the keys and types of each property.
1371
+ * @returns A tuple of two objects with each containing the keys that have a different value in the corresponding argument compared to the other argument.
1372
+ */
1373
+ const diffProperties = (a, b, valueSet) => {
1374
+ const aDiff = {};
1375
+ const bDiff = {};
1376
+ for (const key in valueSet.propertySet.propertyMap) {
1377
+ if (valueSet.propertySet.propertyMap[key].type === exports.Type.Object) {
1378
+ const diffAB = diffProperties(a[key], b[key], valueSet.getSubValueSet(key));
1379
+ // only add the key if differences are detected
1380
+ if (Object.keys(diffAB[0]).length > 0 && Object.keys(diffAB[1]).length > 0) {
1381
+ aDiff[key] = diffAB[0];
1382
+ bDiff[key] = diffAB[1];
1383
+ }
1384
+ } else {
1385
+ if (!equals(a[key], b[key])) {
1386
+ aDiff[key] = a[key];
1387
+ bDiff[key] = b[key];
1388
+ }
1389
+ }
1390
+ }
1391
+ return [aDiff, bDiff];
1392
+ };
1393
+ /**
1394
+ * Checks if the given value is an object.
1395
+ * @public
1396
+ * @param x A value.
1397
+ * @returns `true` if the given value is an object, `false` otherwise.
1398
+ */
1399
+ const isObject = x => x !== undefined && x !== null && x.constructor === Object;
1400
+
1300
1401
  /**
1301
1402
  * A set of values corresponding to a set of properties.
1302
1403
  * @public
@@ -1648,101 +1749,6 @@ class ValueSet {
1648
1749
  }
1649
1750
  }
1650
1751
  }
1651
- /**
1652
- * Checks if the given value is not empty.
1653
- * @private
1654
- * @param a A value.
1655
- * @returns `true` if the given value is not `undefined`, `null`, `''`, `[]` or `{}`; `false` otherwise.
1656
- */
1657
- const empty = a => {
1658
- return a === undefined || a === null || a === '' || a instanceof Array && a.length === 0 || a instanceof Object && Object.keys(a).length === 0;
1659
- };
1660
- /**
1661
- * Checks whether the given values are equal.
1662
- * @public
1663
- * @param a A value.
1664
- * @param b A value.
1665
- * @returns `true` if the given values are equal, `false` otherwise.
1666
- */
1667
- const equals = (a, b) => {
1668
- return a === b || JSON.stringify(a) === JSON.stringify(b);
1669
- };
1670
- /**
1671
- * Calculates the differences between the two given objects and returns two objects containing the differences in each relative to the other.
1672
- *
1673
- * For each key that holds a different value in the two objects, the resulting objects will contain the differences in the values under that key.
1674
- *
1675
- * This function is recursive, that is, if the value under the key is an object, the function will be applied to that value recursively.
1676
- *
1677
- * @public
1678
- * @param a An object.
1679
- * @param b An object.
1680
- * @returns A tuple of two objects with each containing the keys that have a different value in the corresponding argument compared to the other argument.
1681
- */
1682
- const diff = (a, b) => {
1683
- const aDiff = {};
1684
- const bDiff = {};
1685
- const allKeys = [];
1686
- for (const key in a) {
1687
- allKeys.push(key);
1688
- }
1689
- for (const key in b) {
1690
- if (!(key in a)) {
1691
- allKeys.push(key);
1692
- }
1693
- }
1694
- for (const key of allKeys) {
1695
- if (isObject(a[key]) && isObject(b[key])) {
1696
- const diffAB = diff(a[key], b[key]);
1697
- // only add the key if differences are detected
1698
- if (Object.keys(diffAB[0]).length > 0 && Object.keys(diffAB[1]).length > 0) {
1699
- aDiff[key] = diffAB[0];
1700
- bDiff[key] = diffAB[1];
1701
- }
1702
- } else {
1703
- if (!equals(a[key], b[key])) {
1704
- aDiff[key] = a[key];
1705
- bDiff[key] = b[key];
1706
- }
1707
- }
1708
- }
1709
- return [aDiff, bDiff];
1710
- };
1711
- /**
1712
- * Calculates the differences between the two given values of a valueset and returns two objects containing the differences in each relative to the other.
1713
- *
1714
- * @param a An object.
1715
- * @param b An object.
1716
- * @param valueSet A ValueSet to use as reference for the keys and types of each property.
1717
- * @returns A tuple of two objects with each containing the keys that have a different value in the corresponding argument compared to the other argument.
1718
- */
1719
- const diffProperties = (a, b, valueSet) => {
1720
- const aDiff = {};
1721
- const bDiff = {};
1722
- for (const key in valueSet.propertySet.propertyMap) {
1723
- if (valueSet.propertySet.propertyMap[key].type === exports.Type.Object) {
1724
- const diffAB = diffProperties(a[key], b[key], valueSet.getSubValueSet(key));
1725
- // only add the key if differences are detected
1726
- if (Object.keys(diffAB[0]).length > 0 && Object.keys(diffAB[1]).length > 0) {
1727
- aDiff[key] = diffAB[0];
1728
- bDiff[key] = diffAB[1];
1729
- }
1730
- } else {
1731
- if (!equals(a[key], b[key])) {
1732
- aDiff[key] = a[key];
1733
- bDiff[key] = b[key];
1734
- }
1735
- }
1736
- }
1737
- return [aDiff, bDiff];
1738
- };
1739
- /**
1740
- * Checks if the given value is an object.
1741
- * @public
1742
- * @param x A value.
1743
- * @returns `true` if the given value is an object, `false` otherwise.
1744
- */
1745
- const isObject = x => x !== undefined && x !== null && x.constructor === Object;
1746
1752
 
1747
1753
  /**
1748
1754
  * Default values of the parameters of a diagram connection.
@@ -2290,10 +2296,6 @@ class DiagramField extends DiagramElement {
2290
2296
  this.editable = editable;
2291
2297
  this.fit = fit;
2292
2298
  }
2293
- select() {
2294
- var _a, _b;
2295
- return (_b = (_a = this.model.canvas) === null || _a === undefined ? undefined : _a.selectCanvasView()) === null || _b === undefined ? undefined : _b.select(`foreignObject#${this.id}`);
2296
- }
2297
2299
  get removed() {
2298
2300
  return this.selfRemoved || this.rootElement !== undefined && this.rootElement.removed;
2299
2301
  }
@@ -2983,6 +2985,7 @@ const DIAGRAM_NODE_TYPE_DEFAULTS = {
2983
2985
  padding: 0,
2984
2986
  label: null,
2985
2987
  ports: [],
2988
+ decorators: [],
2986
2989
  sectionGrid: null,
2987
2990
  look: DIAGRAM_NODE_LOOK_DEFAULTS,
2988
2991
  isUnique: false,
@@ -3013,6 +3016,7 @@ class DiagramNodeType {
3013
3016
  this.topPadding = getTopPadding(values);
3014
3017
  this.label = values.label;
3015
3018
  this.ports = values.ports;
3019
+ this.decorators = values.decorators;
3016
3020
  this.sectionGrid = values.sectionGrid ? new DiagramSectionGrid(values.sectionGrid) : null;
3017
3021
  const looks = extractLooksFromConfig(values.look);
3018
3022
  this.defaultLook = looks.defaultLook;
@@ -3654,7 +3658,7 @@ class DiagramNodeSet extends DiagramElementSet {
3654
3658
  for (let i = 0; i < nodeType.ports.length; ++i) {
3655
3659
  const portConfig = nodeType.ports[i];
3656
3660
  const portType = portConfig.type !== undefined ? this.model.ports.types.get(portConfig.type) : undefined;
3657
- const port = this.model.ports.new(portType, node, [node.coords[0] + portConfig.coords[0], node.coords[1] + portConfig.coords[1]], portConfig.connectionPoint !== undefined ? [node.coords[0] + (portConfig.connectionPoint[0] || 0), node.coords[1] + (portConfig.connectionPoint[1] || 0)] : undefined, portConfig.direction, `${node.id}_${i}`);
3661
+ const port = this.model.ports.new(portType, node, [node.coords[0] + portConfig.coords[0], node.coords[1] + portConfig.coords[1]], portConfig.connectionPoint !== undefined ? [node.coords[0] + (portConfig.connectionPoint[0] || 0), node.coords[1] + (portConfig.connectionPoint[1] || 0)] : undefined, portConfig.direction, `${node.id}_port_${i}`);
3658
3662
  if ((_e = port.type) === null || _e === undefined ? undefined : _e.label) {
3659
3663
  const labelConfiguration = Object.assign(Object.assign({}, DIAGRAM_FIELD_DEFAULTS), (_f = port.type) === null || _f === undefined ? undefined : _f.label);
3660
3664
  const labelWidth = 6 * labelConfiguration.fontSize + getLeftPadding$1(labelConfiguration) + getRightPadding$1(labelConfiguration);
@@ -3681,6 +3685,13 @@ class DiagramNodeSet extends DiagramElementSet {
3681
3685
  const labelConfiguration = Object.assign(Object.assign({}, DIAGRAM_FIELD_DEFAULTS), nodeType.label);
3682
3686
  this.model.fields.new(node, [node.coords[0] + getLeftMargin(labelConfiguration), node.coords[1] + getTopMargin(labelConfiguration)], labelConfiguration.fontSize, labelConfiguration.fontFamily, labelConfiguration.color, labelConfiguration.selectedColor, node.width - getLeftMargin(labelConfiguration) - getRightMargin(labelConfiguration), node.height - getTopMargin(labelConfiguration) - getBottomMargin(labelConfiguration), labelConfiguration.horizontalAlign, labelConfiguration.verticalAlign, '', labelConfiguration.editable, labelConfiguration.fit);
3683
3687
  }
3688
+ // add node decorators
3689
+ if (nodeType.decorators.length > 0) {
3690
+ for (let i = 0; i < nodeType.decorators.length; ++i) {
3691
+ const decoratorConfig = nodeType.decorators[i];
3692
+ this.model.decorators.new(node, [node.coords[0] + decoratorConfig.coords[0], node.coords[1] + decoratorConfig.coords[1]], decoratorConfig.width, decoratorConfig.height, node.getPriority(), decoratorConfig.html, `${node.id}_decorator_${i}`);
3693
+ }
3694
+ }
3684
3695
  node.valueSet.resetValues();
3685
3696
  (_g = node.model.canvas) === null || _g === undefined ? undefined : _g.fitNodeInView(node.id);
3686
3697
  return node;
@@ -4209,6 +4220,13 @@ class DagaImporter {
4209
4220
  newNode.width = node.width;
4210
4221
  newNode.height = node.height;
4211
4222
  if (node.label) {
4223
+ // add node decorators
4224
+ if (newNodeType.decorators) {
4225
+ for (let i = 0; i < newNodeType.decorators.length; ++i) {
4226
+ const decoratorConfig = newNodeType.decorators[i];
4227
+ model.decorators.new(newNode, [newNode.coords[0] + decoratorConfig.coords[0], newNode.coords[1] + decoratorConfig.coords[1]], decoratorConfig.width, decoratorConfig.height, newNode.getPriority(), decoratorConfig.html, `${newNode.id}_decorator_${i}`);
4228
+ }
4229
+ }
4212
4230
  // add node label
4213
4231
  if (newNodeType.label) {
4214
4232
  const labelConfiguration = Object.assign(Object.assign({}, DIAGRAM_FIELD_DEFAULTS), newNodeType.label);
@@ -5635,10 +5653,6 @@ class DiagramDecorator extends DiagramElement {
5635
5653
  this.priority = priority;
5636
5654
  this.html = html;
5637
5655
  }
5638
- select() {
5639
- var _a, _b;
5640
- return (_b = (_a = this.model.canvas) === null || _a === undefined ? undefined : _a.selectCanvasView()) === null || _b === undefined ? undefined : _b.select(`foreignObject#${this.id}`);
5641
- }
5642
5656
  get removed() {
5643
5657
  return this.selfRemoved || this.rootElement !== undefined && this.rootElement.removed;
5644
5658
  }
@@ -5728,10 +5742,6 @@ class DiagramObject extends DiagramElement {
5728
5742
  this.priority = priority;
5729
5743
  this.html = html;
5730
5744
  }
5731
- select() {
5732
- var _a, _b;
5733
- return (_b = (_a = this.model.canvas) === null || _a === undefined ? undefined : _a.selectCanvasView()) === null || _b === undefined ? undefined : _b.select(`foreignObject#${this.id}`);
5734
- }
5735
5745
  get removed() {
5736
5746
  return this.selfRemoved;
5737
5747
  }
@@ -5938,12 +5948,41 @@ const updateLook = selection => {
5938
5948
  selection.filter('.stretchable-image-look').select('image.bottom-image').attr('x', d => d.look.leftMargin).attr('y', d => d.height - d.look.bottomMargin).attr('width', d => d.width - d.look.rightMargin - d.look.leftMargin).attr('height', d => d.look.bottomMargin).attr('href', d => d.look.backgroundImageBottom);
5939
5949
  selection.filter('.stretchable-image-look').select('image.bottom-right-image').attr('x', d => d.width - d.look.rightMargin).attr('y', d => d.height - d.look.bottomMargin).attr('width', d => d.look.rightMargin).attr('height', d => d.look.bottomMargin).attr('href', d => d.look.backgroundImageBottomRight);
5940
5950
  };
5951
+ const GRID_DEFAULTS = {
5952
+ enabled: true,
5953
+ style: 'dots',
5954
+ color: 'rgba(0, 0, 0, 0.1)',
5955
+ snap: false,
5956
+ spacing: 10,
5957
+ thickness: 0.05
5958
+ };
5959
+ const initializeGrid = (canvas, canvasView, backgroundPatternId) => {
5960
+ const canvasDefs = canvasView.append('defs');
5961
+ if (canvas.gridSize > 0 && isFinite(canvas.gridSize)) {
5962
+ const canvasBackgroundPattern = canvasDefs.append('pattern').attr('id', backgroundPatternId).attr('x', -canvas.gridSize / 2).attr('y', -canvas.gridSize / 2).attr('width', canvas.gridSize).attr('height', canvas.gridSize).attr('patternUnits', 'userSpaceOnUse');
5963
+ canvasBackgroundPattern.append('rect').attr('x', 0).attr('y', 0).attr('width', canvas.gridSize).attr('height', canvas.gridSize).attr('fill', canvas.backgroundColor);
5964
+ switch (canvas.gridStyle) {
5965
+ case 'dots':
5966
+ canvasBackgroundPattern.append('circle').attr('cx', canvas.gridSize / 2).attr('cy', canvas.gridSize / 2).attr('r', canvas.gridSize * canvas.gridThickness).attr('fill', canvas.gridColor);
5967
+ break;
5968
+ case 'lines':
5969
+ canvasBackgroundPattern.append('line').attr('x1', canvas.gridSize / 2).attr('x2', canvas.gridSize / 2).attr('y1', 0).attr('y2', canvas.gridSize).attr('stroke-width', canvas.gridSize * canvas.gridThickness).attr('stroke', canvas.gridColor);
5970
+ canvasBackgroundPattern.append('line').attr('x1', 0).attr('x2', (canvas.gridSize - canvas.gridSize * canvas.gridThickness) / 2).attr('y1', canvas.gridSize / 2).attr('y2', canvas.gridSize / 2).attr('stroke-width', canvas.gridSize * canvas.gridThickness).attr('stroke', canvas.gridColor);
5971
+ canvasBackgroundPattern.append('line').attr('x1', (canvas.gridSize + canvas.gridSize * canvas.gridThickness) / 2).attr('x2', canvas.gridSize).attr('y1', canvas.gridSize / 2).attr('y2', canvas.gridSize / 2).attr('stroke-width', canvas.gridSize * canvas.gridThickness).attr('stroke', canvas.gridColor);
5972
+ break;
5973
+ }
5974
+ canvasView.select('rect').attr('fill', `url(#${backgroundPatternId})`);
5975
+ }
5976
+ };
5941
5977
 
5942
5978
  const CONTEXT_MENU_MENU_RADIUS = 96;
5943
5979
  const CONTEXT_MENU_BUTTON_RADIUS = 32;
5944
5980
  const CONTEXT_MENU_TOTAL_RADIUS = CONTEXT_MENU_MENU_RADIUS + CONTEXT_MENU_BUTTON_RADIUS;
5945
5981
  const CONTEXT_MENU_BUTTON_PADDING_RADIANS = Math.PI / 4;
5946
5982
  const CONTEXT_MENU_ANIMATION_DURATION_MS = 100;
5983
+ const CONTEXT_MENU_DEFAULTS = {
5984
+ customButtons: []
5985
+ };
5947
5986
  /**
5948
5987
  * Stores the functionality regarding the context menu of a diagram canvas.
5949
5988
  * @public
@@ -5955,8 +5994,9 @@ class DiagramContextMenu {
5955
5994
  * @public
5956
5995
  * @param canvas A canvas.
5957
5996
  */
5958
- constructor(canvas) {
5997
+ constructor(canvas, config) {
5959
5998
  this.canvas = canvas;
5999
+ this.config = config || CONTEXT_MENU_DEFAULTS;
5960
6000
  }
5961
6001
  /**
5962
6002
  * Opens the context menu at the location determined by the given mouse event.
@@ -5983,10 +6023,9 @@ class DiagramContextMenu {
5983
6023
  buttons.push({
5984
6024
  name: 'CUT',
5985
6025
  imageClass: 'daga-cut',
5986
- onPress: buttonPressEvent => {
5987
- buttonPressEvent.preventDefault();
5988
- this.canvas.userSelection.cutToClipboard();
5989
- this.canvas.cancelAllUserActions();
6026
+ onPress: canvas => {
6027
+ canvas.userSelection.cutToClipboard();
6028
+ canvas.cancelAllUserActions();
5990
6029
  }
5991
6030
  });
5992
6031
  }
@@ -5994,10 +6033,9 @@ class DiagramContextMenu {
5994
6033
  buttons.push({
5995
6034
  name: 'COPY',
5996
6035
  imageClass: 'daga-copy',
5997
- onPress: buttonPressEvent => {
5998
- buttonPressEvent.preventDefault();
5999
- this.canvas.userSelection.copyToClipboard();
6000
- this.canvas.cancelAllUserActions();
6036
+ onPress: canvas => {
6037
+ canvas.userSelection.copyToClipboard();
6038
+ canvas.cancelAllUserActions();
6001
6039
  }
6002
6040
  });
6003
6041
  }
@@ -6005,10 +6043,9 @@ class DiagramContextMenu {
6005
6043
  buttons.push({
6006
6044
  name: 'PASTE',
6007
6045
  imageClass: 'daga-paste',
6008
- onPress: buttonPressEvent => {
6009
- buttonPressEvent.preventDefault();
6010
- this.canvas.userSelection.pasteFromClipboard(this.canvas.getClosestGridPoint(coordsRelativeToCanvas));
6011
- this.canvas.cancelAllUserActions();
6046
+ onPress: canvas => {
6047
+ canvas.userSelection.pasteFromClipboard(canvas.getClosestGridPoint(coordsRelativeToCanvas));
6048
+ canvas.cancelAllUserActions();
6012
6049
  }
6013
6050
  });
6014
6051
  }
@@ -6016,13 +6053,17 @@ class DiagramContextMenu {
6016
6053
  buttons.push({
6017
6054
  name: 'DELETE',
6018
6055
  imageClass: 'daga-delete',
6019
- onPress: buttonPressEvent => {
6020
- buttonPressEvent.preventDefault();
6021
- this.canvas.userSelection.removeFromModel();
6022
- this.canvas.cancelAllUserActions();
6056
+ onPress: canvas => {
6057
+ canvas.userSelection.removeFromModel();
6058
+ canvas.cancelAllUserActions();
6023
6059
  }
6024
6060
  });
6025
6061
  }
6062
+ for (const customButton of this.config.customButtons || CONTEXT_MENU_DEFAULTS.customButtons) {
6063
+ if (customButton.condition !== undefined ? customButton.condition(this.canvas) : true) {
6064
+ buttons.push(customButton);
6065
+ }
6066
+ }
6026
6067
  // show something if there are no options available
6027
6068
  if (buttons.length === 0) {
6028
6069
  buttons.push({
@@ -6035,20 +6076,22 @@ class DiagramContextMenu {
6035
6076
  const button = buttons[i];
6036
6077
  const buttonOnPress = button.onPress;
6037
6078
  const angle = (i + 0.5 - buttons.length / 2) * CONTEXT_MENU_BUTTON_PADDING_RADIANS;
6038
- const buttonContainer = contextMenuContainer.append('xhtml:div').attr('class', `daga-context-menu-button ${button.imageClass}-button${button.onPress != undefined ? ' daga-clickable' : ''}`).attr('tabindex', 0).style('position', 'absolute').style('box-sizing', 'border-box').style('width', `${2 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('height', `${2 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('border-radius', `${CONTEXT_MENU_BUTTON_RADIUS}px`).style('pointer-events', 'auto')
6039
- // eslint-disable-next-line @typescript-eslint/no-empty-function
6040
- .on(exports.Events.Click, event => {
6079
+ const buttonContainer = contextMenuContainer.append('xhtml:div').attr('class', `daga-context-menu-button ${button.onPress !== undefined ? ' daga-clickable' : ''}`).attr('tabindex', 0).style('position', 'absolute').style('box-sizing', 'border-box').style('width', `${2 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('height', `${2 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('border-radius', `${CONTEXT_MENU_BUTTON_RADIUS}px`).style('pointer-events', 'auto').on(exports.Events.Click, event => {
6041
6080
  if (buttonOnPress) {
6042
- buttonOnPress(event);
6081
+ event.preventDefault();
6082
+ buttonOnPress(this.canvas);
6043
6083
  }
6044
- })
6045
- // eslint-disable-next-line @typescript-eslint/no-empty-function
6046
- .on(exports.Events.KeyDown, event => {
6084
+ }).on(exports.Events.KeyDown, event => {
6047
6085
  if (buttonOnPress && event.key === exports.Keys.Enter) {
6048
- buttonOnPress(event);
6086
+ event.preventDefault();
6087
+ buttonOnPress(this.canvas);
6049
6088
  }
6050
6089
  });
6051
- buttonContainer.append('xhtml:div').style('position', 'absolute').style('left', `${0.75 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('top', `${0.5 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('width', `${0.5 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('height', `${0.5 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('background-size', 'contain').style('background-repeat', 'no-repeat').attr('class', button.imageClass);
6090
+ if (button.imageClass !== undefined) {
6091
+ buttonContainer.append('xhtml:div').style('position', 'absolute').style('left', `${0.75 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('top', `${0.5 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('width', `${0.5 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('height', `${0.5 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('background-size', 'contain').style('background-repeat', 'no-repeat').attr('class', button.imageClass);
6092
+ } else if (button.image !== undefined) {
6093
+ buttonContainer.append('xhtml:img').style('position', 'absolute').style('left', `${0.75 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('top', `${0.5 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('width', `${0.5 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('height', `${0.5 * CONTEXT_MENU_BUTTON_RADIUS}px`).attr('src', button.image);
6094
+ }
6052
6095
  buttonContainer.append('xhtml:span').style('position', 'absolute').style('left', `${0.2 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('top', `${1.1 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('text-align', 'center').style('width', `${1.6 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('height', `${0.35 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('margin', '0').style('font-size', `${0.35 * CONTEXT_MENU_BUTTON_RADIUS}px`).style('font-weight', '700').style('user-select', 'none').text(button.name);
6053
6096
  buttonContainer.transition().ease(d3__namespace.easeLinear).duration(CONTEXT_MENU_ANIMATION_DURATION_MS).tween('progress', () => {
6054
6097
  return value => {
@@ -6100,7 +6143,7 @@ class DiagramUserHighlight extends DiagramElementSet {
6100
6143
  focusOn(element) {
6101
6144
  this.clear();
6102
6145
  this.focus = element;
6103
- if (element instanceof DiagramField && element.rootElement) {
6146
+ if ((element instanceof DiagramField || element instanceof DiagramDecorator) && element.rootElement) {
6104
6147
  this.focusOn(element.rootElement);
6105
6148
  } else {
6106
6149
  this.add(element);
@@ -6588,7 +6631,7 @@ class DiagramCanvas {
6588
6631
  * @param config The configuration object used to set the parameters of this canvas.
6589
6632
  */
6590
6633
  constructor(parentComponent, config) {
6591
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
6634
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
6592
6635
  this.backgroundPatternId = `daga-background-pattern-id-${DiagramCanvas.canvasCount++}`;
6593
6636
  this.zoomTransform = d3__namespace.zoomIdentity;
6594
6637
  // used to distinguish drags from clicks when dragging elements and during multiple selection
@@ -6605,17 +6648,18 @@ class DiagramCanvas {
6605
6648
  this.model = new DiagramModel(this, undefined, config.name || 'unnamed', '', config.type || '', config.properties || []);
6606
6649
  this.userSelection = new DiagramUserSelection(this);
6607
6650
  this.userHighlight = new DiagramUserHighlight(this);
6608
- this.contextMenu = new DiagramContextMenu(this);
6609
- this.backgroundColor = ((_a = config.canvas) === null || _a === undefined ? undefined : _a.backgroundColor) || '#FFFFFF';
6610
- this.gridSize = ((_c = (_b = config.canvas) === null || _b === undefined ? undefined : _b.grid) === null || _c === undefined ? undefined : _c.enabled) === false || ((_d = config.canvas) === null || _d === undefined ? undefined : _d.grid) === undefined ? 0 : Math.abs(((_f = (_e = config.canvas) === null || _e === undefined ? undefined : _e.grid) === null || _f === undefined ? undefined : _f.spacing) || 10);
6611
- this.gridThickness = Math.abs(((_h = (_g = config.canvas) === null || _g === undefined ? undefined : _g.grid) === null || _h === undefined ? undefined : _h.thickness) || 0.05);
6612
- this.gridColor = ((_k = (_j = config.canvas) === null || _j === undefined ? undefined : _j.grid) === null || _k === undefined ? undefined : _k.color) || 'rgba(0, 0, 0, 0.1)';
6613
- this.snapToGrid = ((_m = (_l = config.canvas) === null || _l === undefined ? undefined : _l.grid) === null || _m === undefined ? undefined : _m.enabled) === false || ((_o = config.canvas) === null || _o === undefined ? undefined : _o.grid) === undefined ? false : ((_q = (_p = config.canvas) === null || _p === undefined ? undefined : _p.grid) === null || _q === undefined ? undefined : _q.snap) || false;
6614
- this.zoomFactor = ((_r = config.canvas) === null || _r === undefined ? undefined : _r.zoomFactor) || 2;
6615
- this.panRate = ((_s = config.canvas) === null || _s === undefined ? undefined : _s.panRate) || 100;
6651
+ this.contextMenu = new DiagramContextMenu(this, (_a = config.canvas) === null || _a === undefined ? undefined : _a.contextMenu);
6652
+ this.backgroundColor = ((_b = config.canvas) === null || _b === undefined ? undefined : _b.backgroundColor) || '#FFFFFF';
6653
+ this.gridStyle = (_e = (_d = (_c = config.canvas) === null || _c === undefined ? undefined : _c.grid) === null || _d === undefined ? undefined : _d.style) !== null && _e !== undefined ? _e : GRID_DEFAULTS.style;
6654
+ this.gridSize = ((_g = (_f = config.canvas) === null || _f === undefined ? undefined : _f.grid) === null || _g === undefined ? undefined : _g.enabled) === false || ((_h = config.canvas) === null || _h === undefined ? undefined : _h.grid) === undefined ? 0 : Math.abs(((_k = (_j = config.canvas) === null || _j === undefined ? undefined : _j.grid) === null || _k === undefined ? undefined : _k.spacing) || GRID_DEFAULTS.spacing);
6655
+ this.gridThickness = Math.abs(((_m = (_l = config.canvas) === null || _l === undefined ? undefined : _l.grid) === null || _m === undefined ? undefined : _m.thickness) || GRID_DEFAULTS.thickness);
6656
+ this.gridColor = ((_p = (_o = config.canvas) === null || _o === undefined ? undefined : _o.grid) === null || _p === undefined ? undefined : _p.color) || GRID_DEFAULTS.color;
6657
+ this.snapToGrid = ((_r = (_q = config.canvas) === null || _q === undefined ? undefined : _q.grid) === null || _r === undefined ? undefined : _r.enabled) === false || ((_s = config.canvas) === null || _s === undefined ? undefined : _s.grid) === undefined ? false : ((_u = (_t = config.canvas) === null || _t === undefined ? undefined : _t.grid) === null || _u === undefined ? undefined : _u.snap) || GRID_DEFAULTS.snap;
6658
+ this.zoomFactor = ((_v = config.canvas) === null || _v === undefined ? undefined : _v.zoomFactor) || 2;
6659
+ this.panRate = ((_w = config.canvas) === null || _w === undefined ? undefined : _w.panRate) || 100;
6616
6660
  this.inferConnectionType = config.inferConnectionType || false;
6617
6661
  this.multipleSelectionOn = false;
6618
- this.priorityThresholds = ((_t = config.canvas) === null || _t === undefined ? undefined : _t.priorityThresholds) || [];
6662
+ this.priorityThresholds = ((_x = config.canvas) === null || _x === undefined ? undefined : _x.priorityThresholds) || [];
6619
6663
  this.priorityThreshold = this.priorityThresholds ? this.priorityThresholds[0] : undefined;
6620
6664
  this.layoutFormat = config.layoutFormat;
6621
6665
  this.userActions = config.userActions || {};
@@ -6742,23 +6786,29 @@ class DiagramCanvas {
6742
6786
  }
6743
6787
  }
6744
6788
  if (event.ctrlKey && event.key === 'c') {
6745
- event.preventDefault();
6746
- // copy
6747
- this.userSelection.copyToClipboard();
6748
- this.cancelAllUserActions();
6789
+ if (this.canUserPerformAction(exports.DiagramActions.Clipboard)) {
6790
+ event.preventDefault();
6791
+ // copy
6792
+ this.userSelection.copyToClipboard();
6793
+ this.cancelAllUserActions();
6794
+ }
6749
6795
  }
6750
6796
  if (event.ctrlKey && event.key === 'x') {
6751
- event.preventDefault();
6752
- // cut
6753
- this.userSelection.cutToClipboard();
6754
- this.cancelAllUserActions();
6797
+ if (this.canUserPerformAction(exports.DiagramActions.Clipboard) && this.canUserPerformAction(exports.DiagramActions.Remove)) {
6798
+ event.preventDefault();
6799
+ // cut
6800
+ this.userSelection.cutToClipboard();
6801
+ this.cancelAllUserActions();
6802
+ }
6755
6803
  }
6756
6804
  if (event.ctrlKey && event.key === 'v') {
6757
- event.preventDefault();
6758
- // paste
6759
- const coordinateRange = this.getCoordinatesOnScreen();
6760
- this.userSelection.pasteFromClipboard(this.getClosestGridPoint([(coordinateRange[0][0] + coordinateRange[1][0]) / 2, (coordinateRange[0][1] + coordinateRange[1][1]) / 2]));
6761
- this.cancelAllUserActions();
6805
+ if (this.canUserPerformAction(exports.DiagramActions.Paste)) {
6806
+ event.preventDefault();
6807
+ // paste
6808
+ const coordinateRange = this.getCoordinatesOnScreen();
6809
+ this.userSelection.pasteFromClipboard(this.getClosestGridPoint([(coordinateRange[0][0] + coordinateRange[1][0]) / 2, (coordinateRange[0][1] + coordinateRange[1][1]) / 2]));
6810
+ this.cancelAllUserActions();
6811
+ }
6762
6812
  }
6763
6813
  if (event.ctrlKey && event.key === 'y') {
6764
6814
  event.preventDefault();
@@ -6771,38 +6821,50 @@ class DiagramCanvas {
6771
6821
  this.actionStack.undo();
6772
6822
  }
6773
6823
  if (event.key === '+') {
6774
- event.preventDefault();
6775
- // zoom in
6776
- this.zoomBy(this.zoomFactor);
6824
+ if (this.canUserPerformAction(exports.DiagramActions.Zoom)) {
6825
+ event.preventDefault();
6826
+ // zoom in
6827
+ this.zoomBy(this.zoomFactor);
6828
+ }
6777
6829
  }
6778
6830
  if (event.key === '-') {
6779
- event.preventDefault();
6780
- // zoom out
6781
- this.zoomBy(1 / this.zoomFactor);
6831
+ if (this.canUserPerformAction(exports.DiagramActions.Zoom)) {
6832
+ event.preventDefault();
6833
+ // zoom out
6834
+ this.zoomBy(1 / this.zoomFactor);
6835
+ }
6782
6836
  }
6783
6837
  if (event.key === exports.Keys.ArrowLeft) {
6784
- event.preventDefault();
6785
- // move left, faster if we're zoomed out and slower if we're zoomed in
6786
- this.translateBy(this.panRate / this.zoomTransform.k, 0);
6838
+ if (this.canUserPerformAction(exports.DiagramActions.Zoom)) {
6839
+ event.preventDefault();
6840
+ // move left, faster if we're zoomed out and slower if we're zoomed in
6841
+ this.translateBy(this.panRate / this.zoomTransform.k, 0);
6842
+ }
6787
6843
  }
6788
6844
  if (event.key === exports.Keys.ArrowRight) {
6789
- event.preventDefault();
6790
- // move right, faster if we're zoomed out and slower if we're zoomed in
6791
- this.translateBy(-this.panRate / this.zoomTransform.k, 0);
6845
+ if (this.canUserPerformAction(exports.DiagramActions.Zoom)) {
6846
+ event.preventDefault();
6847
+ // move right, faster if we're zoomed out and slower if we're zoomed in
6848
+ this.translateBy(-this.panRate / this.zoomTransform.k, 0);
6849
+ }
6792
6850
  }
6793
6851
  if (event.key === exports.Keys.ArrowDown) {
6794
- event.preventDefault();
6795
- // move down, faster if we're zoomed out and slower if we're zoomed in
6796
- this.translateBy(0, -this.panRate / this.zoomTransform.k);
6852
+ if (this.canUserPerformAction(exports.DiagramActions.Zoom)) {
6853
+ event.preventDefault();
6854
+ // move down, faster if we're zoomed out and slower if we're zoomed in
6855
+ this.translateBy(0, -this.panRate / this.zoomTransform.k);
6856
+ }
6797
6857
  }
6798
6858
  if (event.key === exports.Keys.ArrowUp) {
6799
- event.preventDefault();
6800
- // move up, faster if we're zoomed out and slower if we're zoomed in
6801
- this.translateBy(0, this.panRate / this.zoomTransform.k);
6859
+ if (this.canUserPerformAction(exports.DiagramActions.Zoom)) {
6860
+ event.preventDefault();
6861
+ // move up, faster if we're zoomed out and slower if we're zoomed in
6862
+ this.translateBy(0, this.panRate / this.zoomTransform.k);
6863
+ }
6802
6864
  }
6803
6865
  });
6804
6866
  const canvasView = this.selectSVGElement().append('g').attr('class', 'daga-canvas-view').attr('width', `100%`).attr('height', `100%`);
6805
- const canvasBackground = canvasView.append('rect').attr('x', 0).attr('y', 0).attr('width', `100%`).attr('height', `100%`).attr('fill', this.backgroundColor).attr('stroke-width', '0').on(exports.Events.MouseMove, event => {
6867
+ canvasView.append('rect').attr('x', 0).attr('y', 0).attr('width', `100%`).attr('height', `100%`).attr('fill', this.backgroundColor).attr('stroke-width', '0').on(exports.Events.MouseMove, event => {
6806
6868
  if (this.unfinishedConnection !== undefined) {
6807
6869
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
6808
6870
  this.unfinishedConnection.endCoords = pointerCoords;
@@ -6858,13 +6920,7 @@ class DiagramCanvas {
6858
6920
  }).on(exports.ZoomEvents.End, () => {
6859
6921
  setCursorStyle();
6860
6922
  }));
6861
- const canvasDefs = canvasView.append('defs');
6862
- if (this.gridSize > 0 && isFinite(this.gridSize)) {
6863
- const canvasBackgroundPattern = canvasDefs.append('pattern').attr('id', this.backgroundPatternId).attr('x', -this.gridSize / 2).attr('y', -this.gridSize / 2).attr('width', this.gridSize).attr('height', this.gridSize).attr('patternUnits', 'userSpaceOnUse');
6864
- canvasBackgroundPattern.append('rect').attr('x', 0).attr('y', 0).attr('width', this.gridSize).attr('height', this.gridSize).attr('fill', this.backgroundColor);
6865
- canvasBackgroundPattern.append('circle').attr('cx', this.gridSize / 2).attr('cy', this.gridSize / 2).attr('r', this.gridSize * this.gridThickness).attr('fill', this.gridColor);
6866
- canvasBackground.attr('fill', `url(#${this.backgroundPatternId})`);
6867
- }
6923
+ initializeGrid(this, canvasView, this.backgroundPatternId);
6868
6924
  canvasView.append('g').attr('class', 'daga-canvas-elements');
6869
6925
  }
6870
6926
  zoomBy(factor) {
@@ -7817,11 +7873,17 @@ class DiagramCanvas {
7817
7873
  this.secondaryButton = isSecondaryButton(event);
7818
7874
  return true;
7819
7875
  }).on(exports.DragEvents.Start, event => {
7820
- this.startMultipleSelection(event);
7876
+ if (this.multipleSelectionOn || this.secondaryButton) {
7877
+ this.startMultipleSelection(event);
7878
+ }
7821
7879
  }).on(exports.DragEvents.Drag, event => {
7822
- this.continueMultipleSelection(event);
7880
+ if (this.multipleSelectionOn || this.secondaryButton) {
7881
+ this.continueMultipleSelection(event);
7882
+ }
7823
7883
  }).on(exports.DragEvents.End, event => {
7824
- this.finishMultipleSelection(event);
7884
+ if (this.multipleSelectionOn || this.secondaryButton) {
7885
+ this.finishMultipleSelection(event);
7886
+ }
7825
7887
  }));
7826
7888
  }
7827
7889
  updateDecoratorsInView(...ids) {
@@ -7834,7 +7896,23 @@ class DiagramCanvas {
7834
7896
  const mergeSelection = enterSelection.merge(updateSelection);
7835
7897
  mergeSelection.attr('width', d => `${d.width}px`).attr('height', d => `${d.height}px`).attr('transform', d => `translate(${d.coords[0]},${d.coords[1]})`).html(d => d.html);
7836
7898
  exitSelection.remove();
7837
- enterSelection.on(exports.Events.ContextMenu, (event, d) => {
7899
+ enterSelection.on(exports.Events.MouseOver, (_event, d) => {
7900
+ if (!this.dragging) {
7901
+ this.userHighlight.focusOn(d);
7902
+ this.diagramEvent$.next(new DiagramHighlightedEvent(d));
7903
+ }
7904
+ }).on(exports.Events.Click, (event, d) => {
7905
+ if (!event.ctrlKey && !event.shiftKey) {
7906
+ const deselectedElements = this.userSelection.all();
7907
+ this.userSelection.clear();
7908
+ this.diagramEvent$.next(new DiagramSelectionEvent(deselectedElements, false));
7909
+ }
7910
+ if (d.rootElement) {
7911
+ const elementToBeToggled = getRelatedNodeOrItself(d.rootElement);
7912
+ this.userSelection.toggle(elementToBeToggled);
7913
+ this.diagramEvent$.next(new DiagramSelectionEvent([elementToBeToggled], elementToBeToggled.selected));
7914
+ }
7915
+ }).on(exports.Events.ContextMenu, (event, d) => {
7838
7916
  if (this.dragging) {
7839
7917
  event.preventDefault();
7840
7918
  event.stopPropagation();
@@ -7844,8 +7922,15 @@ class DiagramCanvas {
7844
7922
  const diagramEvent = new DiagramSecondaryClickEvent(event, d);
7845
7923
  this.diagramEvent$.next(diagramEvent);
7846
7924
  if (!diagramEvent.defaultPrevented && this.canUserPerformAction(exports.DiagramActions.ContextMenu)) {
7847
- event.preventDefault();
7848
- this.contextMenu.open(event);
7925
+ if (d.rootElement) {
7926
+ event.preventDefault();
7927
+ const elementToSelect = getRelatedNodeOrItself(d.rootElement);
7928
+ this.userHighlight.focusOn(elementToSelect);
7929
+ this.diagramEvent$.next(new DiagramHighlightedEvent(elementToSelect));
7930
+ this.userSelection.add(elementToSelect);
7931
+ this.diagramEvent$.next(new DiagramSelectionEvent([elementToSelect], true));
7932
+ this.contextMenu.open(event);
7933
+ }
7849
7934
  }
7850
7935
  }).on(exports.Events.DoubleClick, (event, d) => {
7851
7936
  const diagramEvent = new DiagramDoubleClickEvent(event, d);
@@ -7853,12 +7938,55 @@ class DiagramCanvas {
7853
7938
  }).call(d3__namespace.drag().filter(event => {
7854
7939
  this.secondaryButton = isSecondaryButton(event);
7855
7940
  return true;
7856
- }).on(exports.DragEvents.Start, event => {
7857
- this.startMultipleSelection(event);
7858
- }).on(exports.DragEvents.Drag, event => {
7859
- this.continueMultipleSelection(event);
7860
- }).on(exports.DragEvents.End, event => {
7861
- this.finishMultipleSelection(event);
7941
+ }).on(exports.DragEvents.Start, (event, d) => {
7942
+ if (this.multipleSelectionOn || this.secondaryButton) {
7943
+ this.startMultipleSelection(event);
7944
+ } else {
7945
+ let node;
7946
+ if (d.rootElement instanceof DiagramNode) {
7947
+ node = d.rootElement;
7948
+ } else if (d.rootElement instanceof DiagramSection) {
7949
+ node = d.rootElement.node;
7950
+ }
7951
+ if (node) {
7952
+ this.startMovingNode(event, node);
7953
+ } else {
7954
+ setCursorStyle(exports.CursorStyle.NotAllowed);
7955
+ }
7956
+ }
7957
+ }).on(exports.DragEvents.Drag, (event, d) => {
7958
+ if (this.multipleSelectionOn || this.secondaryButton) {
7959
+ this.continueMultipleSelection(event);
7960
+ } else {
7961
+ let node;
7962
+ if (d.rootElement instanceof DiagramNode) {
7963
+ node = d.rootElement;
7964
+ } else if (d.rootElement instanceof DiagramSection) {
7965
+ node = d.rootElement.node;
7966
+ }
7967
+ if (node) {
7968
+ this.continueMovingNode(event, node);
7969
+ } else {
7970
+ setCursorStyle(exports.CursorStyle.NotAllowed);
7971
+ }
7972
+ }
7973
+ }).on(exports.DragEvents.End, (event, d) => {
7974
+ if (this.multipleSelectionOn || this.secondaryButton) {
7975
+ this.finishMultipleSelection(event);
7976
+ } else {
7977
+ let node;
7978
+ if (d.rootElement instanceof DiagramNode) {
7979
+ node = d.rootElement;
7980
+ } else if (d.rootElement instanceof DiagramSection) {
7981
+ node = d.rootElement.node;
7982
+ }
7983
+ if (node) {
7984
+ this.finishMovingNode(event, node);
7985
+ } else {
7986
+ setCursorStyle();
7987
+ }
7988
+ }
7989
+ this.secondaryButton = false;
7862
7990
  }));
7863
7991
  }
7864
7992
  updateConnectionLabelsInView(connection) {
@@ -8276,18 +8404,18 @@ class DiagramCanvas {
8276
8404
  }
8277
8405
  startMultipleSelection(event) {
8278
8406
  this.draggingFrom = this.getPointerLocationRelativeToCanvas(event);
8279
- /* TODO: would be a good idea to build the multipleSelectionContainer
8280
- * from selectRoot() instead of selectCanvasElements()
8281
- * to avoid having the thickness of the rectangle be affected by zoom
8282
- */
8283
- this.multipleSelectionContainer = this.selectCanvasElements().append('rect').attr('stroke', '#0E74B6').attr('fill', 'rgba(14, 116, 182, 0.06)');
8407
+ // we put the multiple selection rectangle in the diagram svg element
8408
+ // so it's not affected by the zoom level in the diagram elements
8409
+ this.multipleSelectionContainer = this.selectSVGElement().append('rect').attr('stroke', '#0E74B6').attr('fill', 'rgba(14, 116, 182, 0.06)');
8284
8410
  }
8285
8411
  continueMultipleSelection(event) {
8286
8412
  var _a, _b, _c, _d;
8287
8413
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
8288
8414
  if (this.draggingFrom[0] !== pointerCoords[0] || this.draggingFrom[1] !== pointerCoords[1]) {
8289
8415
  setCursorStyle(exports.CursorStyle.Crosshair);
8290
- (_d = (_c = (_b = (_a = this.multipleSelectionContainer) === null || _a === undefined ? undefined : _a.attr('x', Math.min(this.draggingFrom[0], pointerCoords[0]))) === null || _b === undefined ? undefined : _b.attr('y', Math.min(this.draggingFrom[1], pointerCoords[1]))) === null || _c === undefined ? undefined : _c.attr('width', Math.abs(this.draggingFrom[0] - pointerCoords[0]))) === null || _d === undefined ? undefined : _d.attr('height', Math.abs(this.draggingFrom[1] - pointerCoords[1]));
8416
+ // since the multiple selection rectangle is not affected by zoom,
8417
+ // we compensate its coordinates based in the zoom transform to draw it
8418
+ (_d = (_c = (_b = (_a = this.multipleSelectionContainer) === null || _a === undefined ? undefined : _a.attr('x', Math.min(this.draggingFrom[0], pointerCoords[0]) * this.zoomTransform.k + this.zoomTransform.x)) === null || _b === undefined ? undefined : _b.attr('y', Math.min(this.draggingFrom[1], pointerCoords[1]) * this.zoomTransform.k + this.zoomTransform.y)) === null || _c === undefined ? undefined : _c.attr('width', Math.abs(this.draggingFrom[0] - pointerCoords[0]) * this.zoomTransform.k)) === null || _d === undefined ? undefined : _d.attr('height', Math.abs(this.draggingFrom[1] - pointerCoords[1]) * this.zoomTransform.k);
8291
8419
  this.dragging = true;
8292
8420
  }
8293
8421
  }