@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.esm.js CHANGED
@@ -1001,6 +1001,10 @@ class DiagramEntitySet {
1001
1001
  }
1002
1002
  }
1003
1003
 
1004
+ const escapeSelector = selector => {
1005
+ return selector.replace(/([!"#$%&'()*+,\-./:;<=>?@[\\\]^`{|}])/g, '\\$1');
1006
+ };
1007
+
1004
1008
  /**
1005
1009
  * Default priority value for diagram elements.
1006
1010
  * @private
@@ -1062,7 +1066,7 @@ class DiagramElement {
1062
1066
  */
1063
1067
  select() {
1064
1068
  var _a, _b;
1065
- return (_b = (_a = this.model.canvas) === null || _a === undefined ? undefined : _a.selectCanvasView()) === null || _b === undefined ? undefined : _b.select(`g#${this.id}`);
1069
+ return (_b = (_a = this.model.canvas) === null || _a === undefined ? undefined : _a.selectCanvasView()) === null || _b === undefined ? undefined : _b.select(`[id='${escapeSelector(this.id)}']`);
1066
1070
  }
1067
1071
  }
1068
1072
  class DiagramElementSet extends DiagramEntitySet {
@@ -1142,24 +1146,6 @@ class DiagramElementSet extends DiagramEntitySet {
1142
1146
  }
1143
1147
  }
1144
1148
 
1145
- /**
1146
- * Returns whether the incoming timestamp wins over the existing timestamp.
1147
- *
1148
- * In the DiagramModel, timestamps that have never been set are left null;
1149
- * `timestampWins` treats that as an "initial timestamp" that loses to all LogicalTimestamps.
1150
- */
1151
- function timestampWins(incoming, existing) {
1152
- if (!existing) return true;
1153
- if (incoming[0] > existing[0]) return true;
1154
- if (incoming[0] === existing[0]) {
1155
- // In case of equality, declare the incoming timestamp the "winner".
1156
- // This occurs if a client reuses a timestamp for multiple actions in a row,
1157
- // in which case the last created should win.
1158
- return incoming[1] >= existing[1];
1159
- }
1160
- return false;
1161
- }
1162
-
1163
1149
  /**
1164
1150
  * A property which is part of a property set and defines what values a value in a value set can take.
1165
1151
  * @public
@@ -1276,6 +1262,121 @@ class PropertySet {
1276
1262
  return this.propertyList.length > 0;
1277
1263
  }
1278
1264
  }
1265
+
1266
+ /**
1267
+ * Returns whether the incoming timestamp wins over the existing timestamp.
1268
+ *
1269
+ * In the DiagramModel, timestamps that have never been set are left null;
1270
+ * `timestampWins` treats that as an "initial timestamp" that loses to all LogicalTimestamps.
1271
+ */
1272
+ function timestampWins(incoming, existing) {
1273
+ if (!existing) return true;
1274
+ if (incoming[0] > existing[0]) return true;
1275
+ if (incoming[0] === existing[0]) {
1276
+ // In case of equality, declare the incoming timestamp the "winner".
1277
+ // This occurs if a client reuses a timestamp for multiple actions in a row,
1278
+ // in which case the last created should win.
1279
+ return incoming[1] >= existing[1];
1280
+ }
1281
+ return false;
1282
+ }
1283
+
1284
+ /**
1285
+ * Checks if the given value is not empty.
1286
+ * @private
1287
+ * @param a A value.
1288
+ * @returns `true` if the given value is not `undefined`, `null`, `''`, `[]` or `{}`; `false` otherwise.
1289
+ */
1290
+ const empty = a => {
1291
+ return a === undefined || a === null || a === '' || a instanceof Array && a.length === 0 || a instanceof Object && Object.keys(a).length === 0;
1292
+ };
1293
+ /**
1294
+ * Checks whether the given values are equal.
1295
+ * @public
1296
+ * @param a A value.
1297
+ * @param b A value.
1298
+ * @returns `true` if the given values are equal, `false` otherwise.
1299
+ */
1300
+ const equals = (a, b) => {
1301
+ return a === b || JSON.stringify(a) === JSON.stringify(b);
1302
+ };
1303
+ /**
1304
+ * Calculates the differences between the two given objects and returns two objects containing the differences in each relative to the other.
1305
+ *
1306
+ * 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.
1307
+ *
1308
+ * This function is recursive, that is, if the value under the key is an object, the function will be applied to that value recursively.
1309
+ *
1310
+ * @public
1311
+ * @param a An object.
1312
+ * @param b An object.
1313
+ * @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.
1314
+ */
1315
+ const diff = (a, b) => {
1316
+ const aDiff = {};
1317
+ const bDiff = {};
1318
+ const allKeys = [];
1319
+ for (const key in a) {
1320
+ allKeys.push(key);
1321
+ }
1322
+ for (const key in b) {
1323
+ if (!(key in a)) {
1324
+ allKeys.push(key);
1325
+ }
1326
+ }
1327
+ for (const key of allKeys) {
1328
+ if (isObject(a[key]) && isObject(b[key])) {
1329
+ const diffAB = diff(a[key], b[key]);
1330
+ // only add the key if differences are detected
1331
+ if (Object.keys(diffAB[0]).length > 0 && Object.keys(diffAB[1]).length > 0) {
1332
+ aDiff[key] = diffAB[0];
1333
+ bDiff[key] = diffAB[1];
1334
+ }
1335
+ } else {
1336
+ if (!equals(a[key], b[key])) {
1337
+ aDiff[key] = a[key];
1338
+ bDiff[key] = b[key];
1339
+ }
1340
+ }
1341
+ }
1342
+ return [aDiff, bDiff];
1343
+ };
1344
+ /**
1345
+ * Calculates the differences between the two given values of a valueset and returns two objects containing the differences in each relative to the other.
1346
+ *
1347
+ * @param a An object.
1348
+ * @param b An object.
1349
+ * @param valueSet A ValueSet to use as reference for the keys and types of each property.
1350
+ * @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.
1351
+ */
1352
+ const diffProperties = (a, b, valueSet) => {
1353
+ const aDiff = {};
1354
+ const bDiff = {};
1355
+ for (const key in valueSet.propertySet.propertyMap) {
1356
+ if (valueSet.propertySet.propertyMap[key].type === Type.Object) {
1357
+ const diffAB = diffProperties(a[key], b[key], valueSet.getSubValueSet(key));
1358
+ // only add the key if differences are detected
1359
+ if (Object.keys(diffAB[0]).length > 0 && Object.keys(diffAB[1]).length > 0) {
1360
+ aDiff[key] = diffAB[0];
1361
+ bDiff[key] = diffAB[1];
1362
+ }
1363
+ } else {
1364
+ if (!equals(a[key], b[key])) {
1365
+ aDiff[key] = a[key];
1366
+ bDiff[key] = b[key];
1367
+ }
1368
+ }
1369
+ }
1370
+ return [aDiff, bDiff];
1371
+ };
1372
+ /**
1373
+ * Checks if the given value is an object.
1374
+ * @public
1375
+ * @param x A value.
1376
+ * @returns `true` if the given value is an object, `false` otherwise.
1377
+ */
1378
+ const isObject = x => x !== undefined && x !== null && x.constructor === Object;
1379
+
1279
1380
  /**
1280
1381
  * A set of values corresponding to a set of properties.
1281
1382
  * @public
@@ -1627,101 +1728,6 @@ class ValueSet {
1627
1728
  }
1628
1729
  }
1629
1730
  }
1630
- /**
1631
- * Checks if the given value is not empty.
1632
- * @private
1633
- * @param a A value.
1634
- * @returns `true` if the given value is not `undefined`, `null`, `''`, `[]` or `{}`; `false` otherwise.
1635
- */
1636
- const empty = a => {
1637
- return a === undefined || a === null || a === '' || a instanceof Array && a.length === 0 || a instanceof Object && Object.keys(a).length === 0;
1638
- };
1639
- /**
1640
- * Checks whether the given values are equal.
1641
- * @public
1642
- * @param a A value.
1643
- * @param b A value.
1644
- * @returns `true` if the given values are equal, `false` otherwise.
1645
- */
1646
- const equals = (a, b) => {
1647
- return a === b || JSON.stringify(a) === JSON.stringify(b);
1648
- };
1649
- /**
1650
- * Calculates the differences between the two given objects and returns two objects containing the differences in each relative to the other.
1651
- *
1652
- * 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.
1653
- *
1654
- * This function is recursive, that is, if the value under the key is an object, the function will be applied to that value recursively.
1655
- *
1656
- * @public
1657
- * @param a An object.
1658
- * @param b An object.
1659
- * @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.
1660
- */
1661
- const diff = (a, b) => {
1662
- const aDiff = {};
1663
- const bDiff = {};
1664
- const allKeys = [];
1665
- for (const key in a) {
1666
- allKeys.push(key);
1667
- }
1668
- for (const key in b) {
1669
- if (!(key in a)) {
1670
- allKeys.push(key);
1671
- }
1672
- }
1673
- for (const key of allKeys) {
1674
- if (isObject(a[key]) && isObject(b[key])) {
1675
- const diffAB = diff(a[key], b[key]);
1676
- // only add the key if differences are detected
1677
- if (Object.keys(diffAB[0]).length > 0 && Object.keys(diffAB[1]).length > 0) {
1678
- aDiff[key] = diffAB[0];
1679
- bDiff[key] = diffAB[1];
1680
- }
1681
- } else {
1682
- if (!equals(a[key], b[key])) {
1683
- aDiff[key] = a[key];
1684
- bDiff[key] = b[key];
1685
- }
1686
- }
1687
- }
1688
- return [aDiff, bDiff];
1689
- };
1690
- /**
1691
- * Calculates the differences between the two given values of a valueset and returns two objects containing the differences in each relative to the other.
1692
- *
1693
- * @param a An object.
1694
- * @param b An object.
1695
- * @param valueSet A ValueSet to use as reference for the keys and types of each property.
1696
- * @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.
1697
- */
1698
- const diffProperties = (a, b, valueSet) => {
1699
- const aDiff = {};
1700
- const bDiff = {};
1701
- for (const key in valueSet.propertySet.propertyMap) {
1702
- if (valueSet.propertySet.propertyMap[key].type === Type.Object) {
1703
- const diffAB = diffProperties(a[key], b[key], valueSet.getSubValueSet(key));
1704
- // only add the key if differences are detected
1705
- if (Object.keys(diffAB[0]).length > 0 && Object.keys(diffAB[1]).length > 0) {
1706
- aDiff[key] = diffAB[0];
1707
- bDiff[key] = diffAB[1];
1708
- }
1709
- } else {
1710
- if (!equals(a[key], b[key])) {
1711
- aDiff[key] = a[key];
1712
- bDiff[key] = b[key];
1713
- }
1714
- }
1715
- }
1716
- return [aDiff, bDiff];
1717
- };
1718
- /**
1719
- * Checks if the given value is an object.
1720
- * @public
1721
- * @param x A value.
1722
- * @returns `true` if the given value is an object, `false` otherwise.
1723
- */
1724
- const isObject = x => x !== undefined && x !== null && x.constructor === Object;
1725
1731
 
1726
1732
  /**
1727
1733
  * Default values of the parameters of a diagram connection.
@@ -2269,10 +2275,6 @@ class DiagramField extends DiagramElement {
2269
2275
  this.editable = editable;
2270
2276
  this.fit = fit;
2271
2277
  }
2272
- select() {
2273
- var _a, _b;
2274
- return (_b = (_a = this.model.canvas) === null || _a === undefined ? undefined : _a.selectCanvasView()) === null || _b === undefined ? undefined : _b.select(`foreignObject#${this.id}`);
2275
- }
2276
2278
  get removed() {
2277
2279
  return this.selfRemoved || this.rootElement !== undefined && this.rootElement.removed;
2278
2280
  }
@@ -2962,6 +2964,7 @@ const DIAGRAM_NODE_TYPE_DEFAULTS = {
2962
2964
  padding: 0,
2963
2965
  label: null,
2964
2966
  ports: [],
2967
+ decorators: [],
2965
2968
  sectionGrid: null,
2966
2969
  look: DIAGRAM_NODE_LOOK_DEFAULTS,
2967
2970
  isUnique: false,
@@ -2992,6 +2995,7 @@ class DiagramNodeType {
2992
2995
  this.topPadding = getTopPadding(values);
2993
2996
  this.label = values.label;
2994
2997
  this.ports = values.ports;
2998
+ this.decorators = values.decorators;
2995
2999
  this.sectionGrid = values.sectionGrid ? new DiagramSectionGrid(values.sectionGrid) : null;
2996
3000
  const looks = extractLooksFromConfig(values.look);
2997
3001
  this.defaultLook = looks.defaultLook;
@@ -3633,7 +3637,7 @@ class DiagramNodeSet extends DiagramElementSet {
3633
3637
  for (let i = 0; i < nodeType.ports.length; ++i) {
3634
3638
  const portConfig = nodeType.ports[i];
3635
3639
  const portType = portConfig.type !== undefined ? this.model.ports.types.get(portConfig.type) : undefined;
3636
- 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}`);
3640
+ 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}`);
3637
3641
  if ((_e = port.type) === null || _e === undefined ? undefined : _e.label) {
3638
3642
  const labelConfiguration = Object.assign(Object.assign({}, DIAGRAM_FIELD_DEFAULTS), (_f = port.type) === null || _f === undefined ? undefined : _f.label);
3639
3643
  const labelWidth = 6 * labelConfiguration.fontSize + getLeftPadding$1(labelConfiguration) + getRightPadding$1(labelConfiguration);
@@ -3660,6 +3664,13 @@ class DiagramNodeSet extends DiagramElementSet {
3660
3664
  const labelConfiguration = Object.assign(Object.assign({}, DIAGRAM_FIELD_DEFAULTS), nodeType.label);
3661
3665
  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);
3662
3666
  }
3667
+ // add node decorators
3668
+ if (nodeType.decorators.length > 0) {
3669
+ for (let i = 0; i < nodeType.decorators.length; ++i) {
3670
+ const decoratorConfig = nodeType.decorators[i];
3671
+ 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}`);
3672
+ }
3673
+ }
3663
3674
  node.valueSet.resetValues();
3664
3675
  (_g = node.model.canvas) === null || _g === undefined ? undefined : _g.fitNodeInView(node.id);
3665
3676
  return node;
@@ -4188,6 +4199,13 @@ class DagaImporter {
4188
4199
  newNode.width = node.width;
4189
4200
  newNode.height = node.height;
4190
4201
  if (node.label) {
4202
+ // add node decorators
4203
+ if (newNodeType.decorators) {
4204
+ for (let i = 0; i < newNodeType.decorators.length; ++i) {
4205
+ const decoratorConfig = newNodeType.decorators[i];
4206
+ 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}`);
4207
+ }
4208
+ }
4191
4209
  // add node label
4192
4210
  if (newNodeType.label) {
4193
4211
  const labelConfiguration = Object.assign(Object.assign({}, DIAGRAM_FIELD_DEFAULTS), newNodeType.label);
@@ -5614,10 +5632,6 @@ class DiagramDecorator extends DiagramElement {
5614
5632
  this.priority = priority;
5615
5633
  this.html = html;
5616
5634
  }
5617
- select() {
5618
- var _a, _b;
5619
- return (_b = (_a = this.model.canvas) === null || _a === undefined ? undefined : _a.selectCanvasView()) === null || _b === undefined ? undefined : _b.select(`foreignObject#${this.id}`);
5620
- }
5621
5635
  get removed() {
5622
5636
  return this.selfRemoved || this.rootElement !== undefined && this.rootElement.removed;
5623
5637
  }
@@ -5707,10 +5721,6 @@ class DiagramObject extends DiagramElement {
5707
5721
  this.priority = priority;
5708
5722
  this.html = html;
5709
5723
  }
5710
- select() {
5711
- var _a, _b;
5712
- return (_b = (_a = this.model.canvas) === null || _a === undefined ? undefined : _a.selectCanvasView()) === null || _b === undefined ? undefined : _b.select(`foreignObject#${this.id}`);
5713
- }
5714
5724
  get removed() {
5715
5725
  return this.selfRemoved;
5716
5726
  }
@@ -5917,12 +5927,41 @@ const updateLook = selection => {
5917
5927
  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);
5918
5928
  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);
5919
5929
  };
5930
+ const GRID_DEFAULTS = {
5931
+ enabled: true,
5932
+ style: 'dots',
5933
+ color: 'rgba(0, 0, 0, 0.1)',
5934
+ snap: false,
5935
+ spacing: 10,
5936
+ thickness: 0.05
5937
+ };
5938
+ const initializeGrid = (canvas, canvasView, backgroundPatternId) => {
5939
+ const canvasDefs = canvasView.append('defs');
5940
+ if (canvas.gridSize > 0 && isFinite(canvas.gridSize)) {
5941
+ 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');
5942
+ canvasBackgroundPattern.append('rect').attr('x', 0).attr('y', 0).attr('width', canvas.gridSize).attr('height', canvas.gridSize).attr('fill', canvas.backgroundColor);
5943
+ switch (canvas.gridStyle) {
5944
+ case 'dots':
5945
+ canvasBackgroundPattern.append('circle').attr('cx', canvas.gridSize / 2).attr('cy', canvas.gridSize / 2).attr('r', canvas.gridSize * canvas.gridThickness).attr('fill', canvas.gridColor);
5946
+ break;
5947
+ case 'lines':
5948
+ 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);
5949
+ 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);
5950
+ 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);
5951
+ break;
5952
+ }
5953
+ canvasView.select('rect').attr('fill', `url(#${backgroundPatternId})`);
5954
+ }
5955
+ };
5920
5956
 
5921
5957
  const CONTEXT_MENU_MENU_RADIUS = 96;
5922
5958
  const CONTEXT_MENU_BUTTON_RADIUS = 32;
5923
5959
  const CONTEXT_MENU_TOTAL_RADIUS = CONTEXT_MENU_MENU_RADIUS + CONTEXT_MENU_BUTTON_RADIUS;
5924
5960
  const CONTEXT_MENU_BUTTON_PADDING_RADIANS = Math.PI / 4;
5925
5961
  const CONTEXT_MENU_ANIMATION_DURATION_MS = 100;
5962
+ const CONTEXT_MENU_DEFAULTS = {
5963
+ customButtons: []
5964
+ };
5926
5965
  /**
5927
5966
  * Stores the functionality regarding the context menu of a diagram canvas.
5928
5967
  * @public
@@ -5934,8 +5973,9 @@ class DiagramContextMenu {
5934
5973
  * @public
5935
5974
  * @param canvas A canvas.
5936
5975
  */
5937
- constructor(canvas) {
5976
+ constructor(canvas, config) {
5938
5977
  this.canvas = canvas;
5978
+ this.config = config || CONTEXT_MENU_DEFAULTS;
5939
5979
  }
5940
5980
  /**
5941
5981
  * Opens the context menu at the location determined by the given mouse event.
@@ -5962,10 +6002,9 @@ class DiagramContextMenu {
5962
6002
  buttons.push({
5963
6003
  name: 'CUT',
5964
6004
  imageClass: 'daga-cut',
5965
- onPress: buttonPressEvent => {
5966
- buttonPressEvent.preventDefault();
5967
- this.canvas.userSelection.cutToClipboard();
5968
- this.canvas.cancelAllUserActions();
6005
+ onPress: canvas => {
6006
+ canvas.userSelection.cutToClipboard();
6007
+ canvas.cancelAllUserActions();
5969
6008
  }
5970
6009
  });
5971
6010
  }
@@ -5973,10 +6012,9 @@ class DiagramContextMenu {
5973
6012
  buttons.push({
5974
6013
  name: 'COPY',
5975
6014
  imageClass: 'daga-copy',
5976
- onPress: buttonPressEvent => {
5977
- buttonPressEvent.preventDefault();
5978
- this.canvas.userSelection.copyToClipboard();
5979
- this.canvas.cancelAllUserActions();
6015
+ onPress: canvas => {
6016
+ canvas.userSelection.copyToClipboard();
6017
+ canvas.cancelAllUserActions();
5980
6018
  }
5981
6019
  });
5982
6020
  }
@@ -5984,10 +6022,9 @@ class DiagramContextMenu {
5984
6022
  buttons.push({
5985
6023
  name: 'PASTE',
5986
6024
  imageClass: 'daga-paste',
5987
- onPress: buttonPressEvent => {
5988
- buttonPressEvent.preventDefault();
5989
- this.canvas.userSelection.pasteFromClipboard(this.canvas.getClosestGridPoint(coordsRelativeToCanvas));
5990
- this.canvas.cancelAllUserActions();
6025
+ onPress: canvas => {
6026
+ canvas.userSelection.pasteFromClipboard(canvas.getClosestGridPoint(coordsRelativeToCanvas));
6027
+ canvas.cancelAllUserActions();
5991
6028
  }
5992
6029
  });
5993
6030
  }
@@ -5995,13 +6032,17 @@ class DiagramContextMenu {
5995
6032
  buttons.push({
5996
6033
  name: 'DELETE',
5997
6034
  imageClass: 'daga-delete',
5998
- onPress: buttonPressEvent => {
5999
- buttonPressEvent.preventDefault();
6000
- this.canvas.userSelection.removeFromModel();
6001
- this.canvas.cancelAllUserActions();
6035
+ onPress: canvas => {
6036
+ canvas.userSelection.removeFromModel();
6037
+ canvas.cancelAllUserActions();
6002
6038
  }
6003
6039
  });
6004
6040
  }
6041
+ for (const customButton of this.config.customButtons || CONTEXT_MENU_DEFAULTS.customButtons) {
6042
+ if (customButton.condition !== undefined ? customButton.condition(this.canvas) : true) {
6043
+ buttons.push(customButton);
6044
+ }
6045
+ }
6005
6046
  // show something if there are no options available
6006
6047
  if (buttons.length === 0) {
6007
6048
  buttons.push({
@@ -6014,20 +6055,22 @@ class DiagramContextMenu {
6014
6055
  const button = buttons[i];
6015
6056
  const buttonOnPress = button.onPress;
6016
6057
  const angle = (i + 0.5 - buttons.length / 2) * CONTEXT_MENU_BUTTON_PADDING_RADIANS;
6017
- 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')
6018
- // eslint-disable-next-line @typescript-eslint/no-empty-function
6019
- .on(Events.Click, event => {
6058
+ 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(Events.Click, event => {
6020
6059
  if (buttonOnPress) {
6021
- buttonOnPress(event);
6060
+ event.preventDefault();
6061
+ buttonOnPress(this.canvas);
6022
6062
  }
6023
- })
6024
- // eslint-disable-next-line @typescript-eslint/no-empty-function
6025
- .on(Events.KeyDown, event => {
6063
+ }).on(Events.KeyDown, event => {
6026
6064
  if (buttonOnPress && event.key === Keys.Enter) {
6027
- buttonOnPress(event);
6065
+ event.preventDefault();
6066
+ buttonOnPress(this.canvas);
6028
6067
  }
6029
6068
  });
6030
- 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);
6069
+ if (button.imageClass !== undefined) {
6070
+ 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);
6071
+ } else if (button.image !== undefined) {
6072
+ 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);
6073
+ }
6031
6074
  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);
6032
6075
  buttonContainer.transition().ease(d3.easeLinear).duration(CONTEXT_MENU_ANIMATION_DURATION_MS).tween('progress', () => {
6033
6076
  return value => {
@@ -6079,7 +6122,7 @@ class DiagramUserHighlight extends DiagramElementSet {
6079
6122
  focusOn(element) {
6080
6123
  this.clear();
6081
6124
  this.focus = element;
6082
- if (element instanceof DiagramField && element.rootElement) {
6125
+ if ((element instanceof DiagramField || element instanceof DiagramDecorator) && element.rootElement) {
6083
6126
  this.focusOn(element.rootElement);
6084
6127
  } else {
6085
6128
  this.add(element);
@@ -6567,7 +6610,7 @@ class DiagramCanvas {
6567
6610
  * @param config The configuration object used to set the parameters of this canvas.
6568
6611
  */
6569
6612
  constructor(parentComponent, config) {
6570
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
6613
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
6571
6614
  this.backgroundPatternId = `daga-background-pattern-id-${DiagramCanvas.canvasCount++}`;
6572
6615
  this.zoomTransform = d3.zoomIdentity;
6573
6616
  // used to distinguish drags from clicks when dragging elements and during multiple selection
@@ -6584,17 +6627,18 @@ class DiagramCanvas {
6584
6627
  this.model = new DiagramModel(this, undefined, config.name || 'unnamed', '', config.type || '', config.properties || []);
6585
6628
  this.userSelection = new DiagramUserSelection(this);
6586
6629
  this.userHighlight = new DiagramUserHighlight(this);
6587
- this.contextMenu = new DiagramContextMenu(this);
6588
- this.backgroundColor = ((_a = config.canvas) === null || _a === undefined ? undefined : _a.backgroundColor) || '#FFFFFF';
6589
- 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);
6590
- this.gridThickness = Math.abs(((_h = (_g = config.canvas) === null || _g === undefined ? undefined : _g.grid) === null || _h === undefined ? undefined : _h.thickness) || 0.05);
6591
- this.gridColor = ((_k = (_j = config.canvas) === null || _j === undefined ? undefined : _j.grid) === null || _k === undefined ? undefined : _k.color) || 'rgba(0, 0, 0, 0.1)';
6592
- 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;
6593
- this.zoomFactor = ((_r = config.canvas) === null || _r === undefined ? undefined : _r.zoomFactor) || 2;
6594
- this.panRate = ((_s = config.canvas) === null || _s === undefined ? undefined : _s.panRate) || 100;
6630
+ this.contextMenu = new DiagramContextMenu(this, (_a = config.canvas) === null || _a === undefined ? undefined : _a.contextMenu);
6631
+ this.backgroundColor = ((_b = config.canvas) === null || _b === undefined ? undefined : _b.backgroundColor) || '#FFFFFF';
6632
+ 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;
6633
+ 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);
6634
+ this.gridThickness = Math.abs(((_m = (_l = config.canvas) === null || _l === undefined ? undefined : _l.grid) === null || _m === undefined ? undefined : _m.thickness) || GRID_DEFAULTS.thickness);
6635
+ this.gridColor = ((_p = (_o = config.canvas) === null || _o === undefined ? undefined : _o.grid) === null || _p === undefined ? undefined : _p.color) || GRID_DEFAULTS.color;
6636
+ 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;
6637
+ this.zoomFactor = ((_v = config.canvas) === null || _v === undefined ? undefined : _v.zoomFactor) || 2;
6638
+ this.panRate = ((_w = config.canvas) === null || _w === undefined ? undefined : _w.panRate) || 100;
6595
6639
  this.inferConnectionType = config.inferConnectionType || false;
6596
6640
  this.multipleSelectionOn = false;
6597
- this.priorityThresholds = ((_t = config.canvas) === null || _t === undefined ? undefined : _t.priorityThresholds) || [];
6641
+ this.priorityThresholds = ((_x = config.canvas) === null || _x === undefined ? undefined : _x.priorityThresholds) || [];
6598
6642
  this.priorityThreshold = this.priorityThresholds ? this.priorityThresholds[0] : undefined;
6599
6643
  this.layoutFormat = config.layoutFormat;
6600
6644
  this.userActions = config.userActions || {};
@@ -6721,23 +6765,29 @@ class DiagramCanvas {
6721
6765
  }
6722
6766
  }
6723
6767
  if (event.ctrlKey && event.key === 'c') {
6724
- event.preventDefault();
6725
- // copy
6726
- this.userSelection.copyToClipboard();
6727
- this.cancelAllUserActions();
6768
+ if (this.canUserPerformAction(DiagramActions.Clipboard)) {
6769
+ event.preventDefault();
6770
+ // copy
6771
+ this.userSelection.copyToClipboard();
6772
+ this.cancelAllUserActions();
6773
+ }
6728
6774
  }
6729
6775
  if (event.ctrlKey && event.key === 'x') {
6730
- event.preventDefault();
6731
- // cut
6732
- this.userSelection.cutToClipboard();
6733
- this.cancelAllUserActions();
6776
+ if (this.canUserPerformAction(DiagramActions.Clipboard) && this.canUserPerformAction(DiagramActions.Remove)) {
6777
+ event.preventDefault();
6778
+ // cut
6779
+ this.userSelection.cutToClipboard();
6780
+ this.cancelAllUserActions();
6781
+ }
6734
6782
  }
6735
6783
  if (event.ctrlKey && event.key === 'v') {
6736
- event.preventDefault();
6737
- // paste
6738
- const coordinateRange = this.getCoordinatesOnScreen();
6739
- this.userSelection.pasteFromClipboard(this.getClosestGridPoint([(coordinateRange[0][0] + coordinateRange[1][0]) / 2, (coordinateRange[0][1] + coordinateRange[1][1]) / 2]));
6740
- this.cancelAllUserActions();
6784
+ if (this.canUserPerformAction(DiagramActions.Paste)) {
6785
+ event.preventDefault();
6786
+ // paste
6787
+ const coordinateRange = this.getCoordinatesOnScreen();
6788
+ this.userSelection.pasteFromClipboard(this.getClosestGridPoint([(coordinateRange[0][0] + coordinateRange[1][0]) / 2, (coordinateRange[0][1] + coordinateRange[1][1]) / 2]));
6789
+ this.cancelAllUserActions();
6790
+ }
6741
6791
  }
6742
6792
  if (event.ctrlKey && event.key === 'y') {
6743
6793
  event.preventDefault();
@@ -6750,38 +6800,50 @@ class DiagramCanvas {
6750
6800
  this.actionStack.undo();
6751
6801
  }
6752
6802
  if (event.key === '+') {
6753
- event.preventDefault();
6754
- // zoom in
6755
- this.zoomBy(this.zoomFactor);
6803
+ if (this.canUserPerformAction(DiagramActions.Zoom)) {
6804
+ event.preventDefault();
6805
+ // zoom in
6806
+ this.zoomBy(this.zoomFactor);
6807
+ }
6756
6808
  }
6757
6809
  if (event.key === '-') {
6758
- event.preventDefault();
6759
- // zoom out
6760
- this.zoomBy(1 / this.zoomFactor);
6810
+ if (this.canUserPerformAction(DiagramActions.Zoom)) {
6811
+ event.preventDefault();
6812
+ // zoom out
6813
+ this.zoomBy(1 / this.zoomFactor);
6814
+ }
6761
6815
  }
6762
6816
  if (event.key === Keys.ArrowLeft) {
6763
- event.preventDefault();
6764
- // move left, faster if we're zoomed out and slower if we're zoomed in
6765
- this.translateBy(this.panRate / this.zoomTransform.k, 0);
6817
+ if (this.canUserPerformAction(DiagramActions.Zoom)) {
6818
+ event.preventDefault();
6819
+ // move left, faster if we're zoomed out and slower if we're zoomed in
6820
+ this.translateBy(this.panRate / this.zoomTransform.k, 0);
6821
+ }
6766
6822
  }
6767
6823
  if (event.key === Keys.ArrowRight) {
6768
- event.preventDefault();
6769
- // move right, faster if we're zoomed out and slower if we're zoomed in
6770
- this.translateBy(-this.panRate / this.zoomTransform.k, 0);
6824
+ if (this.canUserPerformAction(DiagramActions.Zoom)) {
6825
+ event.preventDefault();
6826
+ // move right, faster if we're zoomed out and slower if we're zoomed in
6827
+ this.translateBy(-this.panRate / this.zoomTransform.k, 0);
6828
+ }
6771
6829
  }
6772
6830
  if (event.key === Keys.ArrowDown) {
6773
- event.preventDefault();
6774
- // move down, faster if we're zoomed out and slower if we're zoomed in
6775
- this.translateBy(0, -this.panRate / this.zoomTransform.k);
6831
+ if (this.canUserPerformAction(DiagramActions.Zoom)) {
6832
+ event.preventDefault();
6833
+ // move down, faster if we're zoomed out and slower if we're zoomed in
6834
+ this.translateBy(0, -this.panRate / this.zoomTransform.k);
6835
+ }
6776
6836
  }
6777
6837
  if (event.key === Keys.ArrowUp) {
6778
- event.preventDefault();
6779
- // move up, faster if we're zoomed out and slower if we're zoomed in
6780
- this.translateBy(0, this.panRate / this.zoomTransform.k);
6838
+ if (this.canUserPerformAction(DiagramActions.Zoom)) {
6839
+ event.preventDefault();
6840
+ // move up, faster if we're zoomed out and slower if we're zoomed in
6841
+ this.translateBy(0, this.panRate / this.zoomTransform.k);
6842
+ }
6781
6843
  }
6782
6844
  });
6783
6845
  const canvasView = this.selectSVGElement().append('g').attr('class', 'daga-canvas-view').attr('width', `100%`).attr('height', `100%`);
6784
- 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(Events.MouseMove, event => {
6846
+ canvasView.append('rect').attr('x', 0).attr('y', 0).attr('width', `100%`).attr('height', `100%`).attr('fill', this.backgroundColor).attr('stroke-width', '0').on(Events.MouseMove, event => {
6785
6847
  if (this.unfinishedConnection !== undefined) {
6786
6848
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
6787
6849
  this.unfinishedConnection.endCoords = pointerCoords;
@@ -6837,13 +6899,7 @@ class DiagramCanvas {
6837
6899
  }).on(ZoomEvents.End, () => {
6838
6900
  setCursorStyle();
6839
6901
  }));
6840
- const canvasDefs = canvasView.append('defs');
6841
- if (this.gridSize > 0 && isFinite(this.gridSize)) {
6842
- 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');
6843
- canvasBackgroundPattern.append('rect').attr('x', 0).attr('y', 0).attr('width', this.gridSize).attr('height', this.gridSize).attr('fill', this.backgroundColor);
6844
- canvasBackgroundPattern.append('circle').attr('cx', this.gridSize / 2).attr('cy', this.gridSize / 2).attr('r', this.gridSize * this.gridThickness).attr('fill', this.gridColor);
6845
- canvasBackground.attr('fill', `url(#${this.backgroundPatternId})`);
6846
- }
6902
+ initializeGrid(this, canvasView, this.backgroundPatternId);
6847
6903
  canvasView.append('g').attr('class', 'daga-canvas-elements');
6848
6904
  }
6849
6905
  zoomBy(factor) {
@@ -7796,11 +7852,17 @@ class DiagramCanvas {
7796
7852
  this.secondaryButton = isSecondaryButton(event);
7797
7853
  return true;
7798
7854
  }).on(DragEvents.Start, event => {
7799
- this.startMultipleSelection(event);
7855
+ if (this.multipleSelectionOn || this.secondaryButton) {
7856
+ this.startMultipleSelection(event);
7857
+ }
7800
7858
  }).on(DragEvents.Drag, event => {
7801
- this.continueMultipleSelection(event);
7859
+ if (this.multipleSelectionOn || this.secondaryButton) {
7860
+ this.continueMultipleSelection(event);
7861
+ }
7802
7862
  }).on(DragEvents.End, event => {
7803
- this.finishMultipleSelection(event);
7863
+ if (this.multipleSelectionOn || this.secondaryButton) {
7864
+ this.finishMultipleSelection(event);
7865
+ }
7804
7866
  }));
7805
7867
  }
7806
7868
  updateDecoratorsInView(...ids) {
@@ -7813,7 +7875,23 @@ class DiagramCanvas {
7813
7875
  const mergeSelection = enterSelection.merge(updateSelection);
7814
7876
  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);
7815
7877
  exitSelection.remove();
7816
- enterSelection.on(Events.ContextMenu, (event, d) => {
7878
+ enterSelection.on(Events.MouseOver, (_event, d) => {
7879
+ if (!this.dragging) {
7880
+ this.userHighlight.focusOn(d);
7881
+ this.diagramEvent$.next(new DiagramHighlightedEvent(d));
7882
+ }
7883
+ }).on(Events.Click, (event, d) => {
7884
+ if (!event.ctrlKey && !event.shiftKey) {
7885
+ const deselectedElements = this.userSelection.all();
7886
+ this.userSelection.clear();
7887
+ this.diagramEvent$.next(new DiagramSelectionEvent(deselectedElements, false));
7888
+ }
7889
+ if (d.rootElement) {
7890
+ const elementToBeToggled = getRelatedNodeOrItself(d.rootElement);
7891
+ this.userSelection.toggle(elementToBeToggled);
7892
+ this.diagramEvent$.next(new DiagramSelectionEvent([elementToBeToggled], elementToBeToggled.selected));
7893
+ }
7894
+ }).on(Events.ContextMenu, (event, d) => {
7817
7895
  if (this.dragging) {
7818
7896
  event.preventDefault();
7819
7897
  event.stopPropagation();
@@ -7823,8 +7901,15 @@ class DiagramCanvas {
7823
7901
  const diagramEvent = new DiagramSecondaryClickEvent(event, d);
7824
7902
  this.diagramEvent$.next(diagramEvent);
7825
7903
  if (!diagramEvent.defaultPrevented && this.canUserPerformAction(DiagramActions.ContextMenu)) {
7826
- event.preventDefault();
7827
- this.contextMenu.open(event);
7904
+ if (d.rootElement) {
7905
+ event.preventDefault();
7906
+ const elementToSelect = getRelatedNodeOrItself(d.rootElement);
7907
+ this.userHighlight.focusOn(elementToSelect);
7908
+ this.diagramEvent$.next(new DiagramHighlightedEvent(elementToSelect));
7909
+ this.userSelection.add(elementToSelect);
7910
+ this.diagramEvent$.next(new DiagramSelectionEvent([elementToSelect], true));
7911
+ this.contextMenu.open(event);
7912
+ }
7828
7913
  }
7829
7914
  }).on(Events.DoubleClick, (event, d) => {
7830
7915
  const diagramEvent = new DiagramDoubleClickEvent(event, d);
@@ -7832,12 +7917,55 @@ class DiagramCanvas {
7832
7917
  }).call(d3.drag().filter(event => {
7833
7918
  this.secondaryButton = isSecondaryButton(event);
7834
7919
  return true;
7835
- }).on(DragEvents.Start, event => {
7836
- this.startMultipleSelection(event);
7837
- }).on(DragEvents.Drag, event => {
7838
- this.continueMultipleSelection(event);
7839
- }).on(DragEvents.End, event => {
7840
- this.finishMultipleSelection(event);
7920
+ }).on(DragEvents.Start, (event, d) => {
7921
+ if (this.multipleSelectionOn || this.secondaryButton) {
7922
+ this.startMultipleSelection(event);
7923
+ } else {
7924
+ let node;
7925
+ if (d.rootElement instanceof DiagramNode) {
7926
+ node = d.rootElement;
7927
+ } else if (d.rootElement instanceof DiagramSection) {
7928
+ node = d.rootElement.node;
7929
+ }
7930
+ if (node) {
7931
+ this.startMovingNode(event, node);
7932
+ } else {
7933
+ setCursorStyle(CursorStyle.NotAllowed);
7934
+ }
7935
+ }
7936
+ }).on(DragEvents.Drag, (event, d) => {
7937
+ if (this.multipleSelectionOn || this.secondaryButton) {
7938
+ this.continueMultipleSelection(event);
7939
+ } else {
7940
+ let node;
7941
+ if (d.rootElement instanceof DiagramNode) {
7942
+ node = d.rootElement;
7943
+ } else if (d.rootElement instanceof DiagramSection) {
7944
+ node = d.rootElement.node;
7945
+ }
7946
+ if (node) {
7947
+ this.continueMovingNode(event, node);
7948
+ } else {
7949
+ setCursorStyle(CursorStyle.NotAllowed);
7950
+ }
7951
+ }
7952
+ }).on(DragEvents.End, (event, d) => {
7953
+ if (this.multipleSelectionOn || this.secondaryButton) {
7954
+ this.finishMultipleSelection(event);
7955
+ } else {
7956
+ let node;
7957
+ if (d.rootElement instanceof DiagramNode) {
7958
+ node = d.rootElement;
7959
+ } else if (d.rootElement instanceof DiagramSection) {
7960
+ node = d.rootElement.node;
7961
+ }
7962
+ if (node) {
7963
+ this.finishMovingNode(event, node);
7964
+ } else {
7965
+ setCursorStyle();
7966
+ }
7967
+ }
7968
+ this.secondaryButton = false;
7841
7969
  }));
7842
7970
  }
7843
7971
  updateConnectionLabelsInView(connection) {
@@ -8255,18 +8383,18 @@ class DiagramCanvas {
8255
8383
  }
8256
8384
  startMultipleSelection(event) {
8257
8385
  this.draggingFrom = this.getPointerLocationRelativeToCanvas(event);
8258
- /* TODO: would be a good idea to build the multipleSelectionContainer
8259
- * from selectRoot() instead of selectCanvasElements()
8260
- * to avoid having the thickness of the rectangle be affected by zoom
8261
- */
8262
- this.multipleSelectionContainer = this.selectCanvasElements().append('rect').attr('stroke', '#0E74B6').attr('fill', 'rgba(14, 116, 182, 0.06)');
8386
+ // we put the multiple selection rectangle in the diagram svg element
8387
+ // so it's not affected by the zoom level in the diagram elements
8388
+ this.multipleSelectionContainer = this.selectSVGElement().append('rect').attr('stroke', '#0E74B6').attr('fill', 'rgba(14, 116, 182, 0.06)');
8263
8389
  }
8264
8390
  continueMultipleSelection(event) {
8265
8391
  var _a, _b, _c, _d;
8266
8392
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
8267
8393
  if (this.draggingFrom[0] !== pointerCoords[0] || this.draggingFrom[1] !== pointerCoords[1]) {
8268
8394
  setCursorStyle(CursorStyle.Crosshair);
8269
- (_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]));
8395
+ // since the multiple selection rectangle is not affected by zoom,
8396
+ // we compensate its coordinates based in the zoom transform to draw it
8397
+ (_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);
8270
8398
  this.dragging = true;
8271
8399
  }
8272
8400
  }