@metadev/daga 4.2.13 → 4.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Changelog.md CHANGED
@@ -6,6 +6,12 @@ List of releases and changes.
6
6
 
7
7
  ## Next release Joyeuse
8
8
 
9
+ ## v. 4.2.14
10
+
11
+ - Fix `getViewCoordinates()` canvas method to return coordinates such that using those coordinates for the `translateTo()` method results in no changes [#364](https://github.com/metadevpro/daga/pull/364)
12
+ - Add `zoomAndPanTo()` method to canvas to enable zooming and panning with animation [#364](https://github.com/metadevpro/daga/pull/364)
13
+ - Enable configuring nodes and sections to be only resizable when selected [#365](https://github.com/metadevpro/daga/pull/365)
14
+
9
15
  ## v. 4.2.13
10
16
 
11
17
  - Fix check for whether a browser is Safari [#363](https://github.com/metadevpro/daga/pull/363)
package/index.cjs.js CHANGED
@@ -2977,6 +2977,11 @@ const getTopPadding$1 = config => {
2977
2977
  }
2978
2978
  };
2979
2979
 
2980
+ exports.ResizableMode = void 0;
2981
+ (function (ResizableMode) {
2982
+ ResizableMode[ResizableMode["OnlyWhenSelected"] = 0] = "OnlyWhenSelected";
2983
+ })(exports.ResizableMode || (exports.ResizableMode = {}));
2984
+
2980
2985
  /**
2981
2986
  * Default value of the default width of a diagram section.
2982
2987
  * @private
@@ -3181,12 +3186,15 @@ class DiagramSection extends DiagramElement {
3181
3186
  * @public
3182
3187
  */
3183
3188
  getResizableX() {
3184
- var _a, _b;
3189
+ var _a;
3185
3190
  const sectionType = this.type;
3186
3191
  if ((sectionType === null || sectionType === void 0 ? void 0 : sectionType.resizableX) !== undefined) {
3192
+ if (sectionType.resizableX === exports.ResizableMode.OnlyWhenSelected) {
3193
+ return this.selected;
3194
+ }
3187
3195
  return sectionType.resizableX;
3188
3196
  }
3189
- return ((_b = (_a = this.node) === null || _a === void 0 ? void 0 : _a.type) === null || _b === void 0 ? void 0 : _b.resizableX) || false;
3197
+ return ((_a = this.node) === null || _a === void 0 ? void 0 : _a.getResizableX()) || false;
3190
3198
  }
3191
3199
  /**
3192
3200
  * Returns whether this section can be resized vertically.
@@ -3195,12 +3203,15 @@ class DiagramSection extends DiagramElement {
3195
3203
  * @public
3196
3204
  */
3197
3205
  getResizableY() {
3198
- var _a, _b;
3206
+ var _a;
3199
3207
  const sectionType = this.type;
3200
3208
  if ((sectionType === null || sectionType === void 0 ? void 0 : sectionType.resizableY) !== undefined) {
3209
+ if (sectionType.resizableY === exports.ResizableMode.OnlyWhenSelected) {
3210
+ return this.selected;
3211
+ }
3201
3212
  return sectionType.resizableY;
3202
3213
  }
3203
- return ((_b = (_a = this.node) === null || _a === void 0 ? void 0 : _a.type) === null || _b === void 0 ? void 0 : _b.resizableY) || false;
3214
+ return ((_a = this.node) === null || _a === void 0 ? void 0 : _a.getResizableY()) || false;
3204
3215
  }
3205
3216
  /**
3206
3217
  * Get the port of this section which is closest to the given coordinates.
@@ -3711,6 +3722,28 @@ class DiagramNode extends DiagramElement {
3711
3722
  getPriority() {
3712
3723
  return this.type.priority;
3713
3724
  }
3725
+ /**
3726
+ * Returns whether this node can be resized horizontally.
3727
+ * @public
3728
+ */
3729
+ getResizableX() {
3730
+ const resizableX = this.type.resizableX;
3731
+ if (resizableX === exports.ResizableMode.OnlyWhenSelected) {
3732
+ return this.selected;
3733
+ }
3734
+ return resizableX;
3735
+ }
3736
+ /**
3737
+ * Returns whether this node can be resized vertically.
3738
+ * @public
3739
+ */
3740
+ getResizableY() {
3741
+ const resizableY = this.type.resizableY;
3742
+ if (resizableY === exports.ResizableMode.OnlyWhenSelected) {
3743
+ return this.selected;
3744
+ }
3745
+ return resizableY;
3746
+ }
3714
3747
  /**
3715
3748
  * Get the port of this node which is closest to the given coordinates.
3716
3749
  * @param coords A point in the diagram.
@@ -6686,6 +6719,34 @@ const getRelatedNodeOrItself = element => {
6686
6719
  }
6687
6720
  return element.rootElement instanceof DiagramNode || element.rootElement instanceof DiagramSection || element.rootElement instanceof DiagramPort ? getRelatedNodeOrItself(element.rootElement) : element;
6688
6721
  };
6722
+ const needsResizerX = element => {
6723
+ if (element instanceof DiagramNode) {
6724
+ return element.type.resizableX !== false;
6725
+ }
6726
+ if (element instanceof DiagramSection) {
6727
+ if (element.type !== undefined) {
6728
+ return element.type.resizableX !== false;
6729
+ }
6730
+ if (element.node !== undefined) {
6731
+ return needsResizerX(element.node);
6732
+ }
6733
+ }
6734
+ return false;
6735
+ };
6736
+ const needsResizerY = element => {
6737
+ if (element instanceof DiagramNode) {
6738
+ return element.type.resizableY !== false;
6739
+ }
6740
+ if (element instanceof DiagramSection) {
6741
+ if (element.type !== undefined) {
6742
+ return element.type.resizableY !== false;
6743
+ }
6744
+ if (element.node !== undefined) {
6745
+ return needsResizerY(element.node);
6746
+ }
6747
+ }
6748
+ return false;
6749
+ };
6689
6750
  const initializeLook = selection => {
6690
6751
  selection.filter('.shaped-look').append('path');
6691
6752
  selection.filter('.image-look').append('image').attr('preserveAspectRatio', 'none');
@@ -7771,7 +7832,14 @@ class DiagramCanvas {
7771
7832
  }
7772
7833
  }
7773
7834
  getViewCoordinates() {
7774
- return [this.zoomTransform.x, this.zoomTransform.y];
7835
+ var _a, _b, _c;
7836
+ const canvasViewBoundingBox = (_c = (_b = (_a = this.selectCanvasView()) === null || _a === void 0 ? void 0 : _a.select('rect')) === null || _b === void 0 ? void 0 : _b.node()) === null || _c === void 0 ? void 0 : _c.getBBox();
7837
+ /*
7838
+ transform the coordinates of the zoomTransform to the coordinates
7839
+ needed to point the zoomTransfrom to the center of the screen to
7840
+ ensure that canvas.translateTo(getViewCoordinates()) has no effect
7841
+ */
7842
+ return [((canvasViewBoundingBox.width + canvasViewBoundingBox.x) / 2 - this.zoomTransform.x) / this.zoomTransform.k, ((canvasViewBoundingBox.height + canvasViewBoundingBox.y) / 2 - this.zoomTransform.y) / this.zoomTransform.k];
7775
7843
  }
7776
7844
  translateBy(x, y) {
7777
7845
  if (!isNaN(x) && !isNaN(y)) {
@@ -7783,12 +7851,34 @@ class DiagramCanvas {
7783
7851
  this.zoomBehavior.translateTo(this.selectCanvasView(), x, y);
7784
7852
  }
7785
7853
  }
7786
- center() {
7854
+ zoomAndPanTo(x, y, z, duration) {
7855
+ if (!duration || duration <= 0) {
7856
+ this.zoomBehavior.scaleTo(this.selectCanvasView(), z);
7857
+ this.zoomBehavior.translateTo(this.selectCanvasView(), x, y);
7858
+ return;
7859
+ }
7860
+ this.zoomBehavior.interpolate(d3__namespace.interpolate);
7861
+ const [startingX, startingY] = this.getViewCoordinates();
7862
+ const startingZoom = this.getZoomLevel();
7863
+ const targetX = x,
7864
+ targetY = y,
7865
+ targetZoom = z;
7866
+ this.selectCanvasElements().transition().duration(duration).ease(d3__namespace.easeCubicInOut).tween('progress', () => {
7867
+ return value => {
7868
+ const currentX = value * (targetX - startingX) + startingX;
7869
+ const currentY = value * (targetY - startingY) + startingY;
7870
+ const currentZoom = value * (targetZoom - startingZoom) + startingZoom;
7871
+ this.zoomBehavior.scaleTo(this.selectCanvasView(), currentZoom);
7872
+ this.zoomBehavior.translateTo(this.selectCanvasView(), currentX, currentY);
7873
+ };
7874
+ });
7875
+ }
7876
+ center(nodeIds, maxZoomLevel, duration) {
7787
7877
  var _a;
7788
7878
  // if there are no nodes, we have nothing to do here
7789
7879
  if (this.model.nodes.length > 0) {
7790
7880
  const canvasViewBoundingBox = (_a = this.selectCanvasView().select('rect').node()) === null || _a === void 0 ? void 0 : _a.getBBox();
7791
- const nonRemovedNodes = this.model.nodes.all();
7881
+ const nonRemovedNodes = (nodeIds === null || nodeIds === void 0 ? void 0 : nodeIds.map(i => this.model.nodes.get(i)).filter(n => !!n)) || this.model.nodes.all();
7792
7882
  const minimumX = Math.min(...nonRemovedNodes.map(n => n.coords[0]));
7793
7883
  const maximumX = Math.max(...nonRemovedNodes.map(n => n.coords[0] + n.width));
7794
7884
  const averageX = (minimumX + maximumX) / 2;
@@ -7803,12 +7893,12 @@ class DiagramCanvas {
7803
7893
  * (windowRangeX / rangeX) is the zoom level to fit everything horizontally
7804
7894
  * (windowRangeY / rangeY) is the zoom level to fit everything vertically
7805
7895
  * the minimum between them is the zoom to fit everything in the screen both horizontally and vertically
7806
- * we also add 1 to the list so that if the zoom exceeds 1 it is set to 1
7896
+ * we also add maxZoomLevel to the list so that if the zoom exceeds maxZoomLevel it is set to maxZoomLevel
7897
+ * or 1 if maxZoomLevel is undefined
7807
7898
  * a zoom bigger than 1 means zooming in instead of out, and we don't want to zoom in if it's not necessary
7808
7899
  */
7809
- const zoom = Math.min(windowRangeX / rangeX, windowRangeY / rangeY, 1);
7810
- this.translateTo(averageX, averageY);
7811
- this.zoomTo(zoom);
7900
+ const zoom = Math.min(windowRangeX / rangeX, windowRangeY / rangeY, maxZoomLevel !== undefined ? maxZoomLevel : 1);
7901
+ this.zoomAndPanTo(averageX, averageY, zoom, duration);
7812
7902
  }
7813
7903
  }
7814
7904
  getClosestGridPoint(point) {
@@ -7864,7 +7954,7 @@ class DiagramCanvas {
7864
7954
  updateNodesInView(...ids) {
7865
7955
  let updateSelection = this.selectCanvasElements().selectAll('g.diagram-node').data(this.model.nodes.filter(e => this.priorityThreshold !== undefined ? e.getPriority() >= this.priorityThreshold : true), d => d.id);
7866
7956
  const exitSelection = updateSelection.exit();
7867
- const enterSelection = updateSelection.enter().append('g').attr('id', d => d.id).attr('class', d => `diagram-node${d.type.resizableX ? ' resizable-x' : ''}${d.type.resizableY ? ' resizable-y' : ''} ${d.type.defaultLook.lookType}`);
7957
+ const enterSelection = updateSelection.enter().append('g').attr('id', d => d.id).attr('class', d => `diagram-node${needsResizerX(d) ? ' resizable-x' : ''}${needsResizerY(d) ? ' resizable-y' : ''} ${d.type.defaultLook.lookType}`);
7868
7958
  if (ids && ids.length > 0) {
7869
7959
  updateSelection = updateSelection.filter(d => ids.includes(d.id));
7870
7960
  }
@@ -7928,27 +8018,27 @@ class DiagramCanvas {
7928
8018
  }));
7929
8019
  initializeLook(enterSelection);
7930
8020
  enterSelection.filter('.resizable-x').append('line').attr('class', 'left-resizer').attr('stroke', 'transparent').attr('stroke-width', `${RESIZER_THICKNESS}px`).on(exports.Events.MouseOver, (_event, d) => {
7931
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableX && !d.removed) {
8021
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableX() && !d.removed) {
7932
8022
  setCursorStyle(exports.CursorStyle.EWResize);
7933
8023
  }
7934
8024
  }).on(exports.Events.MouseOut, (_event, d) => {
7935
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableX && !d.removed) {
8025
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableX() && !d.removed) {
7936
8026
  setCursorStyle();
7937
8027
  }
7938
8028
  }).call(d3__namespace.drag().on(exports.DragEvents.Start, (_event, d) => {
7939
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableX && !d.removed) {
8029
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableX() && !d.removed) {
7940
8030
  setCursorStyle(exports.CursorStyle.EWResize);
7941
8031
  this.currentAction = new SetGeometryAction(this, exports.DiagramActions.StretchNode, d.id, d.getGeometry(), d.getGeometry());
7942
8032
  } else {
7943
8033
  setCursorStyle(exports.CursorStyle.NotAllowed);
7944
8034
  }
7945
8035
  }).on(exports.DragEvents.Drag, (event, d) => {
7946
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableX && !d.removed) {
8036
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableX() && !d.removed) {
7947
8037
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
7948
8038
  d.stretch(exports.Side.Left, d.coords[0] - pointerCoords[0]);
7949
8039
  }
7950
8040
  }).on(exports.DragEvents.End, (event, d) => {
7951
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableX && !d.removed && this.currentAction instanceof SetGeometryAction && this.currentAction.intent === exports.DiagramActions.StretchNode) {
8041
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableX() && !d.removed && this.currentAction instanceof SetGeometryAction && this.currentAction.intent === exports.DiagramActions.StretchNode) {
7952
8042
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
7953
8043
  if (this.snapToGrid) {
7954
8044
  pointerCoords = this.getClosestGridPoint([pointerCoords[0] - d.type.snapToGridOffset[0], pointerCoords[1] - d.type.snapToGridOffset[1]]);
@@ -7964,27 +8054,27 @@ class DiagramCanvas {
7964
8054
  setCursorStyle();
7965
8055
  }));
7966
8056
  enterSelection.filter('.resizable-y').append('line').attr('class', 'top-resizer').attr('stroke', 'transparent').attr('stroke-width', `${RESIZER_THICKNESS}px`).on(exports.Events.MouseOver, (_event, d) => {
7967
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableY && !d.removed) {
8057
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableY() && !d.removed) {
7968
8058
  setCursorStyle(exports.CursorStyle.NSResize);
7969
8059
  }
7970
8060
  }).on(exports.Events.MouseOut, (_event, d) => {
7971
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableY && !d.removed) {
8061
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableY() && !d.removed) {
7972
8062
  setCursorStyle();
7973
8063
  }
7974
8064
  }).call(d3__namespace.drag().on(exports.DragEvents.Start, (_event, d) => {
7975
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableY && !d.removed) {
8065
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableY() && !d.removed) {
7976
8066
  setCursorStyle(exports.CursorStyle.NSResize);
7977
8067
  this.currentAction = new SetGeometryAction(this, exports.DiagramActions.StretchNode, d.id, d.getGeometry(), d.getGeometry());
7978
8068
  } else {
7979
8069
  setCursorStyle(exports.CursorStyle.NotAllowed);
7980
8070
  }
7981
8071
  }).on(exports.DragEvents.Drag, (event, d) => {
7982
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableY && !d.removed) {
8072
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableY() && !d.removed) {
7983
8073
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
7984
8074
  d.stretch(exports.Side.Top, d.coords[1] - pointerCoords[1]);
7985
8075
  }
7986
8076
  }).on(exports.DragEvents.End, (event, d) => {
7987
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableY && !d.removed && this.currentAction instanceof SetGeometryAction && this.currentAction.intent === exports.DiagramActions.StretchNode) {
8077
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableY() && !d.removed && this.currentAction instanceof SetGeometryAction && this.currentAction.intent === exports.DiagramActions.StretchNode) {
7988
8078
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
7989
8079
  if (this.snapToGrid) {
7990
8080
  pointerCoords = this.getClosestGridPoint([pointerCoords[0] - d.type.snapToGridOffset[0], pointerCoords[1] - d.type.snapToGridOffset[1]]);
@@ -8000,27 +8090,27 @@ class DiagramCanvas {
8000
8090
  setCursorStyle();
8001
8091
  }));
8002
8092
  enterSelection.filter('.resizable-x').append('line').attr('class', 'right-resizer').attr('stroke', 'transparent').attr('stroke-width', `${RESIZER_THICKNESS}px`).on(exports.Events.MouseOver, (_event, d) => {
8003
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableX && !d.removed) {
8093
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableX() && !d.removed) {
8004
8094
  setCursorStyle(exports.CursorStyle.EWResize);
8005
8095
  }
8006
8096
  }).on(exports.Events.MouseOut, (_event, d) => {
8007
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableX && !d.removed) {
8097
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableX() && !d.removed) {
8008
8098
  setCursorStyle();
8009
8099
  }
8010
8100
  }).call(d3__namespace.drag().on(exports.DragEvents.Start, (_event, d) => {
8011
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableX && !d.removed) {
8101
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableX() && !d.removed) {
8012
8102
  setCursorStyle(exports.CursorStyle.EWResize);
8013
8103
  this.currentAction = new SetGeometryAction(this, exports.DiagramActions.StretchNode, d.id, d.getGeometry(), d.getGeometry());
8014
8104
  } else {
8015
8105
  setCursorStyle(exports.CursorStyle.NotAllowed);
8016
8106
  }
8017
8107
  }).on(exports.DragEvents.Drag, (event, d) => {
8018
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableX && !d.removed) {
8108
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableX() && !d.removed) {
8019
8109
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
8020
8110
  d.stretch(exports.Side.Right, pointerCoords[0] - (d.coords[0] + d.width));
8021
8111
  }
8022
8112
  }).on(exports.DragEvents.End, (event, d) => {
8023
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableX && !d.removed && this.currentAction instanceof SetGeometryAction && this.currentAction.intent === exports.DiagramActions.StretchNode) {
8113
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableX() && !d.removed && this.currentAction instanceof SetGeometryAction && this.currentAction.intent === exports.DiagramActions.StretchNode) {
8024
8114
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
8025
8115
  if (this.snapToGrid) {
8026
8116
  pointerCoords = this.getClosestGridPoint([pointerCoords[0] - d.type.snapToGridOffset[2], pointerCoords[1] - d.type.snapToGridOffset[3]]);
@@ -8036,27 +8126,27 @@ class DiagramCanvas {
8036
8126
  setCursorStyle();
8037
8127
  }));
8038
8128
  enterSelection.filter('.resizable-y').append('line').attr('class', 'bottom-resizer').attr('stroke', 'transparent').attr('stroke-width', `${RESIZER_THICKNESS}px`).on(exports.Events.MouseOver, (_event, d) => {
8039
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableY && !d.removed) {
8129
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableY() && !d.removed) {
8040
8130
  setCursorStyle(exports.CursorStyle.NSResize);
8041
8131
  }
8042
8132
  }).on(exports.Events.MouseOut, (_event, d) => {
8043
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableY && !d.removed) {
8133
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableY() && !d.removed) {
8044
8134
  setCursorStyle();
8045
8135
  }
8046
8136
  }).call(d3__namespace.drag().on(exports.DragEvents.Start, (_event, d) => {
8047
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableY && !d.removed) {
8137
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableY() && !d.removed) {
8048
8138
  setCursorStyle(exports.CursorStyle.NSResize);
8049
8139
  this.currentAction = new SetGeometryAction(this, exports.DiagramActions.StretchNode, d.id, d.getGeometry(), d.getGeometry());
8050
8140
  } else {
8051
8141
  setCursorStyle(exports.CursorStyle.NotAllowed);
8052
8142
  }
8053
8143
  }).on(exports.DragEvents.Drag, (event, d) => {
8054
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableY && !d.removed) {
8144
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableY() && !d.removed) {
8055
8145
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
8056
8146
  d.stretch(exports.Side.Bottom, pointerCoords[1] - (d.coords[1] + d.height));
8057
8147
  }
8058
8148
  }).on(exports.DragEvents.End, (event, d) => {
8059
- if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.type.resizableY && !d.removed && this.currentAction instanceof SetGeometryAction && this.currentAction.intent === exports.DiagramActions.StretchNode) {
8149
+ if (this.canUserPerformAction(exports.DiagramActions.StretchNode) && d.getResizableY() && !d.removed && this.currentAction instanceof SetGeometryAction && this.currentAction.intent === exports.DiagramActions.StretchNode) {
8060
8150
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
8061
8151
  if (this.snapToGrid) {
8062
8152
  if (this.snapToGrid) {
@@ -8075,17 +8165,17 @@ class DiagramCanvas {
8075
8165
  }));
8076
8166
  mergeSelection.attr('transform', d => `translate(${d.coords[0]},${d.coords[1]})`).attr('opacity', d => d.removed ? 0.5 : 1);
8077
8167
  updateLook(mergeSelection);
8078
- mergeSelection.filter('.resizable-x').select('line.left-resizer').attr('x1', RESIZER_THICKNESS / 2).attr('x2', RESIZER_THICKNESS / 2).attr('y1', 0).attr('y2', d => d.height);
8079
- mergeSelection.filter('.resizable-y').select('line.top-resizer').attr('x1', 0).attr('x2', d => d.width).attr('y1', RESIZER_THICKNESS / 2).attr('y2', RESIZER_THICKNESS / 2);
8080
- mergeSelection.filter('.resizable-x').select('line.right-resizer').attr('x1', d => d.width - RESIZER_THICKNESS / 2).attr('x2', d => d.width - RESIZER_THICKNESS / 2).attr('y1', 0).attr('y2', d => d.height);
8081
- mergeSelection.filter('.resizable-y').select('line.bottom-resizer').attr('x1', 0).attr('x2', d => d.width).attr('y1', d => d.height - RESIZER_THICKNESS / 2).attr('y2', d => d.height - RESIZER_THICKNESS / 2);
8168
+ mergeSelection.filter('.resizable-x').select('line.left-resizer').style('pointer-events', d => d.getResizableX() ? 'initial' : 'none').attr('x1', RESIZER_THICKNESS / 2).attr('x2', RESIZER_THICKNESS / 2).attr('y1', 0).attr('y2', d => d.height);
8169
+ mergeSelection.filter('.resizable-y').select('line.top-resizer').style('pointer-events', d => d.getResizableY() ? 'initial' : 'none').attr('x1', 0).attr('x2', d => d.width).attr('y1', RESIZER_THICKNESS / 2).attr('y2', RESIZER_THICKNESS / 2);
8170
+ mergeSelection.filter('.resizable-x').select('line.right-resizer').style('pointer-events', d => d.getResizableX() ? 'initial' : 'none').attr('x1', d => d.width - RESIZER_THICKNESS / 2).attr('x2', d => d.width - RESIZER_THICKNESS / 2).attr('y1', 0).attr('y2', d => d.height);
8171
+ mergeSelection.filter('.resizable-y').select('line.bottom-resizer').style('pointer-events', d => d.getResizableY() ? 'initial' : 'none').attr('x1', 0).attr('x2', d => d.width).attr('y1', d => d.height - RESIZER_THICKNESS / 2).attr('y2', d => d.height - RESIZER_THICKNESS / 2);
8082
8172
  }
8083
8173
  updateSectionsInView(...ids) {
8084
8174
  let updateSelection = this.selectCanvasElements().selectAll('g.diagram-section').data(this.model.sections.filter(e => this.priorityThreshold !== undefined ? e.getPriority() >= this.priorityThreshold : true), d => d.id);
8085
8175
  const exitSelection = updateSelection.exit();
8086
8176
  const enterSelection = updateSelection.enter().append('g').attr('id', d => d.id).attr('class', d => {
8087
8177
  var _a;
8088
- return `diagram-section${d.getResizableX() ? ' resizable-x' : ''}${d.getResizableY() ? ' resizable-y' : ''} ${(_a = d.look) === null || _a === void 0 ? void 0 : _a.lookType}`;
8178
+ return `diagram-section${needsResizerX(d) ? ' resizable-x' : ''}${needsResizerY(d) ? ' resizable-y' : ''} ${(_a = d.look) === null || _a === void 0 ? void 0 : _a.lookType}`;
8089
8179
  });
8090
8180
  if (ids && ids.length > 0) {
8091
8181
  updateSelection = updateSelection.filter(d => ids.includes(d.id));
@@ -8304,10 +8394,10 @@ class DiagramCanvas {
8304
8394
  }));
8305
8395
  mergeSelection.attr('transform', d => `translate(${d.coords[0]},${d.coords[1]})`).attr('opacity', d => d.removed ? 0.5 : 1);
8306
8396
  updateLook(mergeSelection);
8307
- mergeSelection.filter('.resizable-x').select('line.left-resizer').attr('x1', RESIZER_THICKNESS / 2).attr('x2', RESIZER_THICKNESS / 2).attr('y1', 0).attr('y2', d => d.height);
8308
- mergeSelection.filter('.resizable-y').select('line.top-resizer').attr('x1', 0).attr('x2', d => d.width).attr('y1', RESIZER_THICKNESS / 2).attr('y2', RESIZER_THICKNESS / 2);
8309
- mergeSelection.filter('.resizable-x').select('line.right-resizer').attr('x1', d => d.width - RESIZER_THICKNESS / 2).attr('x2', d => d.width - RESIZER_THICKNESS / 2).attr('y1', 0).attr('y2', d => d.height);
8310
- mergeSelection.filter('.resizable-y').select('line.bottom-resizer').attr('x1', 0).attr('x2', d => d.width).attr('y1', d => d.height - RESIZER_THICKNESS / 2).attr('y2', d => d.height - RESIZER_THICKNESS / 2);
8397
+ mergeSelection.filter('.resizable-x').select('line.left-resizer').style('pointer-events', d => d.getResizableX() ? 'initial' : 'none').attr('x1', RESIZER_THICKNESS / 2).attr('x2', RESIZER_THICKNESS / 2).attr('y1', 0).attr('y2', d => d.height);
8398
+ mergeSelection.filter('.resizable-y').select('line.top-resizer').style('pointer-events', d => d.getResizableY() ? 'initial' : 'none').attr('x1', 0).attr('x2', d => d.width).attr('y1', RESIZER_THICKNESS / 2).attr('y2', RESIZER_THICKNESS / 2);
8399
+ mergeSelection.filter('.resizable-x').select('line.right-resizer').style('pointer-events', d => d.getResizableX() ? 'initial' : 'none').attr('x1', d => d.width - RESIZER_THICKNESS / 2).attr('x2', d => d.width - RESIZER_THICKNESS / 2).attr('y1', 0).attr('y2', d => d.height);
8400
+ mergeSelection.filter('.resizable-y').select('line.bottom-resizer').style('pointer-events', d => d.getResizableY() ? 'initial' : 'none').attr('x1', 0).attr('x2', d => d.width).attr('y1', d => d.height - RESIZER_THICKNESS / 2).attr('y2', d => d.height - RESIZER_THICKNESS / 2);
8311
8401
  }
8312
8402
  updatePortsInView(...ids) {
8313
8403
  let updateSelection = this.selectCanvasElements().selectAll('g.diagram-port').data(this.model.ports.filter(e => this.priorityThreshold !== undefined ? e.getPriority() >= this.priorityThreshold : true), d => d.id);
package/index.esm.js CHANGED
@@ -2956,6 +2956,11 @@ const getTopPadding$1 = config => {
2956
2956
  }
2957
2957
  };
2958
2958
 
2959
+ var ResizableMode;
2960
+ (function (ResizableMode) {
2961
+ ResizableMode[ResizableMode["OnlyWhenSelected"] = 0] = "OnlyWhenSelected";
2962
+ })(ResizableMode || (ResizableMode = {}));
2963
+
2959
2964
  /**
2960
2965
  * Default value of the default width of a diagram section.
2961
2966
  * @private
@@ -3160,12 +3165,15 @@ class DiagramSection extends DiagramElement {
3160
3165
  * @public
3161
3166
  */
3162
3167
  getResizableX() {
3163
- var _a, _b;
3168
+ var _a;
3164
3169
  const sectionType = this.type;
3165
3170
  if ((sectionType === null || sectionType === void 0 ? void 0 : sectionType.resizableX) !== undefined) {
3171
+ if (sectionType.resizableX === ResizableMode.OnlyWhenSelected) {
3172
+ return this.selected;
3173
+ }
3166
3174
  return sectionType.resizableX;
3167
3175
  }
3168
- return ((_b = (_a = this.node) === null || _a === void 0 ? void 0 : _a.type) === null || _b === void 0 ? void 0 : _b.resizableX) || false;
3176
+ return ((_a = this.node) === null || _a === void 0 ? void 0 : _a.getResizableX()) || false;
3169
3177
  }
3170
3178
  /**
3171
3179
  * Returns whether this section can be resized vertically.
@@ -3174,12 +3182,15 @@ class DiagramSection extends DiagramElement {
3174
3182
  * @public
3175
3183
  */
3176
3184
  getResizableY() {
3177
- var _a, _b;
3185
+ var _a;
3178
3186
  const sectionType = this.type;
3179
3187
  if ((sectionType === null || sectionType === void 0 ? void 0 : sectionType.resizableY) !== undefined) {
3188
+ if (sectionType.resizableY === ResizableMode.OnlyWhenSelected) {
3189
+ return this.selected;
3190
+ }
3180
3191
  return sectionType.resizableY;
3181
3192
  }
3182
- return ((_b = (_a = this.node) === null || _a === void 0 ? void 0 : _a.type) === null || _b === void 0 ? void 0 : _b.resizableY) || false;
3193
+ return ((_a = this.node) === null || _a === void 0 ? void 0 : _a.getResizableY()) || false;
3183
3194
  }
3184
3195
  /**
3185
3196
  * Get the port of this section which is closest to the given coordinates.
@@ -3690,6 +3701,28 @@ class DiagramNode extends DiagramElement {
3690
3701
  getPriority() {
3691
3702
  return this.type.priority;
3692
3703
  }
3704
+ /**
3705
+ * Returns whether this node can be resized horizontally.
3706
+ * @public
3707
+ */
3708
+ getResizableX() {
3709
+ const resizableX = this.type.resizableX;
3710
+ if (resizableX === ResizableMode.OnlyWhenSelected) {
3711
+ return this.selected;
3712
+ }
3713
+ return resizableX;
3714
+ }
3715
+ /**
3716
+ * Returns whether this node can be resized vertically.
3717
+ * @public
3718
+ */
3719
+ getResizableY() {
3720
+ const resizableY = this.type.resizableY;
3721
+ if (resizableY === ResizableMode.OnlyWhenSelected) {
3722
+ return this.selected;
3723
+ }
3724
+ return resizableY;
3725
+ }
3693
3726
  /**
3694
3727
  * Get the port of this node which is closest to the given coordinates.
3695
3728
  * @param coords A point in the diagram.
@@ -6665,6 +6698,34 @@ const getRelatedNodeOrItself = element => {
6665
6698
  }
6666
6699
  return element.rootElement instanceof DiagramNode || element.rootElement instanceof DiagramSection || element.rootElement instanceof DiagramPort ? getRelatedNodeOrItself(element.rootElement) : element;
6667
6700
  };
6701
+ const needsResizerX = element => {
6702
+ if (element instanceof DiagramNode) {
6703
+ return element.type.resizableX !== false;
6704
+ }
6705
+ if (element instanceof DiagramSection) {
6706
+ if (element.type !== undefined) {
6707
+ return element.type.resizableX !== false;
6708
+ }
6709
+ if (element.node !== undefined) {
6710
+ return needsResizerX(element.node);
6711
+ }
6712
+ }
6713
+ return false;
6714
+ };
6715
+ const needsResizerY = element => {
6716
+ if (element instanceof DiagramNode) {
6717
+ return element.type.resizableY !== false;
6718
+ }
6719
+ if (element instanceof DiagramSection) {
6720
+ if (element.type !== undefined) {
6721
+ return element.type.resizableY !== false;
6722
+ }
6723
+ if (element.node !== undefined) {
6724
+ return needsResizerY(element.node);
6725
+ }
6726
+ }
6727
+ return false;
6728
+ };
6668
6729
  const initializeLook = selection => {
6669
6730
  selection.filter('.shaped-look').append('path');
6670
6731
  selection.filter('.image-look').append('image').attr('preserveAspectRatio', 'none');
@@ -7750,7 +7811,14 @@ class DiagramCanvas {
7750
7811
  }
7751
7812
  }
7752
7813
  getViewCoordinates() {
7753
- return [this.zoomTransform.x, this.zoomTransform.y];
7814
+ var _a, _b, _c;
7815
+ const canvasViewBoundingBox = (_c = (_b = (_a = this.selectCanvasView()) === null || _a === void 0 ? void 0 : _a.select('rect')) === null || _b === void 0 ? void 0 : _b.node()) === null || _c === void 0 ? void 0 : _c.getBBox();
7816
+ /*
7817
+ transform the coordinates of the zoomTransform to the coordinates
7818
+ needed to point the zoomTransfrom to the center of the screen to
7819
+ ensure that canvas.translateTo(getViewCoordinates()) has no effect
7820
+ */
7821
+ return [((canvasViewBoundingBox.width + canvasViewBoundingBox.x) / 2 - this.zoomTransform.x) / this.zoomTransform.k, ((canvasViewBoundingBox.height + canvasViewBoundingBox.y) / 2 - this.zoomTransform.y) / this.zoomTransform.k];
7754
7822
  }
7755
7823
  translateBy(x, y) {
7756
7824
  if (!isNaN(x) && !isNaN(y)) {
@@ -7762,12 +7830,34 @@ class DiagramCanvas {
7762
7830
  this.zoomBehavior.translateTo(this.selectCanvasView(), x, y);
7763
7831
  }
7764
7832
  }
7765
- center() {
7833
+ zoomAndPanTo(x, y, z, duration) {
7834
+ if (!duration || duration <= 0) {
7835
+ this.zoomBehavior.scaleTo(this.selectCanvasView(), z);
7836
+ this.zoomBehavior.translateTo(this.selectCanvasView(), x, y);
7837
+ return;
7838
+ }
7839
+ this.zoomBehavior.interpolate(d3.interpolate);
7840
+ const [startingX, startingY] = this.getViewCoordinates();
7841
+ const startingZoom = this.getZoomLevel();
7842
+ const targetX = x,
7843
+ targetY = y,
7844
+ targetZoom = z;
7845
+ this.selectCanvasElements().transition().duration(duration).ease(d3.easeCubicInOut).tween('progress', () => {
7846
+ return value => {
7847
+ const currentX = value * (targetX - startingX) + startingX;
7848
+ const currentY = value * (targetY - startingY) + startingY;
7849
+ const currentZoom = value * (targetZoom - startingZoom) + startingZoom;
7850
+ this.zoomBehavior.scaleTo(this.selectCanvasView(), currentZoom);
7851
+ this.zoomBehavior.translateTo(this.selectCanvasView(), currentX, currentY);
7852
+ };
7853
+ });
7854
+ }
7855
+ center(nodeIds, maxZoomLevel, duration) {
7766
7856
  var _a;
7767
7857
  // if there are no nodes, we have nothing to do here
7768
7858
  if (this.model.nodes.length > 0) {
7769
7859
  const canvasViewBoundingBox = (_a = this.selectCanvasView().select('rect').node()) === null || _a === void 0 ? void 0 : _a.getBBox();
7770
- const nonRemovedNodes = this.model.nodes.all();
7860
+ const nonRemovedNodes = (nodeIds === null || nodeIds === void 0 ? void 0 : nodeIds.map(i => this.model.nodes.get(i)).filter(n => !!n)) || this.model.nodes.all();
7771
7861
  const minimumX = Math.min(...nonRemovedNodes.map(n => n.coords[0]));
7772
7862
  const maximumX = Math.max(...nonRemovedNodes.map(n => n.coords[0] + n.width));
7773
7863
  const averageX = (minimumX + maximumX) / 2;
@@ -7782,12 +7872,12 @@ class DiagramCanvas {
7782
7872
  * (windowRangeX / rangeX) is the zoom level to fit everything horizontally
7783
7873
  * (windowRangeY / rangeY) is the zoom level to fit everything vertically
7784
7874
  * the minimum between them is the zoom to fit everything in the screen both horizontally and vertically
7785
- * we also add 1 to the list so that if the zoom exceeds 1 it is set to 1
7875
+ * we also add maxZoomLevel to the list so that if the zoom exceeds maxZoomLevel it is set to maxZoomLevel
7876
+ * or 1 if maxZoomLevel is undefined
7786
7877
  * a zoom bigger than 1 means zooming in instead of out, and we don't want to zoom in if it's not necessary
7787
7878
  */
7788
- const zoom = Math.min(windowRangeX / rangeX, windowRangeY / rangeY, 1);
7789
- this.translateTo(averageX, averageY);
7790
- this.zoomTo(zoom);
7879
+ const zoom = Math.min(windowRangeX / rangeX, windowRangeY / rangeY, maxZoomLevel !== undefined ? maxZoomLevel : 1);
7880
+ this.zoomAndPanTo(averageX, averageY, zoom, duration);
7791
7881
  }
7792
7882
  }
7793
7883
  getClosestGridPoint(point) {
@@ -7843,7 +7933,7 @@ class DiagramCanvas {
7843
7933
  updateNodesInView(...ids) {
7844
7934
  let updateSelection = this.selectCanvasElements().selectAll('g.diagram-node').data(this.model.nodes.filter(e => this.priorityThreshold !== undefined ? e.getPriority() >= this.priorityThreshold : true), d => d.id);
7845
7935
  const exitSelection = updateSelection.exit();
7846
- const enterSelection = updateSelection.enter().append('g').attr('id', d => d.id).attr('class', d => `diagram-node${d.type.resizableX ? ' resizable-x' : ''}${d.type.resizableY ? ' resizable-y' : ''} ${d.type.defaultLook.lookType}`);
7936
+ const enterSelection = updateSelection.enter().append('g').attr('id', d => d.id).attr('class', d => `diagram-node${needsResizerX(d) ? ' resizable-x' : ''}${needsResizerY(d) ? ' resizable-y' : ''} ${d.type.defaultLook.lookType}`);
7847
7937
  if (ids && ids.length > 0) {
7848
7938
  updateSelection = updateSelection.filter(d => ids.includes(d.id));
7849
7939
  }
@@ -7907,27 +7997,27 @@ class DiagramCanvas {
7907
7997
  }));
7908
7998
  initializeLook(enterSelection);
7909
7999
  enterSelection.filter('.resizable-x').append('line').attr('class', 'left-resizer').attr('stroke', 'transparent').attr('stroke-width', `${RESIZER_THICKNESS}px`).on(Events.MouseOver, (_event, d) => {
7910
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableX && !d.removed) {
8000
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableX() && !d.removed) {
7911
8001
  setCursorStyle(CursorStyle.EWResize);
7912
8002
  }
7913
8003
  }).on(Events.MouseOut, (_event, d) => {
7914
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableX && !d.removed) {
8004
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableX() && !d.removed) {
7915
8005
  setCursorStyle();
7916
8006
  }
7917
8007
  }).call(d3.drag().on(DragEvents.Start, (_event, d) => {
7918
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableX && !d.removed) {
8008
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableX() && !d.removed) {
7919
8009
  setCursorStyle(CursorStyle.EWResize);
7920
8010
  this.currentAction = new SetGeometryAction(this, DiagramActions.StretchNode, d.id, d.getGeometry(), d.getGeometry());
7921
8011
  } else {
7922
8012
  setCursorStyle(CursorStyle.NotAllowed);
7923
8013
  }
7924
8014
  }).on(DragEvents.Drag, (event, d) => {
7925
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableX && !d.removed) {
8015
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableX() && !d.removed) {
7926
8016
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
7927
8017
  d.stretch(Side.Left, d.coords[0] - pointerCoords[0]);
7928
8018
  }
7929
8019
  }).on(DragEvents.End, (event, d) => {
7930
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableX && !d.removed && this.currentAction instanceof SetGeometryAction && this.currentAction.intent === DiagramActions.StretchNode) {
8020
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableX() && !d.removed && this.currentAction instanceof SetGeometryAction && this.currentAction.intent === DiagramActions.StretchNode) {
7931
8021
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
7932
8022
  if (this.snapToGrid) {
7933
8023
  pointerCoords = this.getClosestGridPoint([pointerCoords[0] - d.type.snapToGridOffset[0], pointerCoords[1] - d.type.snapToGridOffset[1]]);
@@ -7943,27 +8033,27 @@ class DiagramCanvas {
7943
8033
  setCursorStyle();
7944
8034
  }));
7945
8035
  enterSelection.filter('.resizable-y').append('line').attr('class', 'top-resizer').attr('stroke', 'transparent').attr('stroke-width', `${RESIZER_THICKNESS}px`).on(Events.MouseOver, (_event, d) => {
7946
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableY && !d.removed) {
8036
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableY() && !d.removed) {
7947
8037
  setCursorStyle(CursorStyle.NSResize);
7948
8038
  }
7949
8039
  }).on(Events.MouseOut, (_event, d) => {
7950
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableY && !d.removed) {
8040
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableY() && !d.removed) {
7951
8041
  setCursorStyle();
7952
8042
  }
7953
8043
  }).call(d3.drag().on(DragEvents.Start, (_event, d) => {
7954
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableY && !d.removed) {
8044
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableY() && !d.removed) {
7955
8045
  setCursorStyle(CursorStyle.NSResize);
7956
8046
  this.currentAction = new SetGeometryAction(this, DiagramActions.StretchNode, d.id, d.getGeometry(), d.getGeometry());
7957
8047
  } else {
7958
8048
  setCursorStyle(CursorStyle.NotAllowed);
7959
8049
  }
7960
8050
  }).on(DragEvents.Drag, (event, d) => {
7961
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableY && !d.removed) {
8051
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableY() && !d.removed) {
7962
8052
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
7963
8053
  d.stretch(Side.Top, d.coords[1] - pointerCoords[1]);
7964
8054
  }
7965
8055
  }).on(DragEvents.End, (event, d) => {
7966
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableY && !d.removed && this.currentAction instanceof SetGeometryAction && this.currentAction.intent === DiagramActions.StretchNode) {
8056
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableY() && !d.removed && this.currentAction instanceof SetGeometryAction && this.currentAction.intent === DiagramActions.StretchNode) {
7967
8057
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
7968
8058
  if (this.snapToGrid) {
7969
8059
  pointerCoords = this.getClosestGridPoint([pointerCoords[0] - d.type.snapToGridOffset[0], pointerCoords[1] - d.type.snapToGridOffset[1]]);
@@ -7979,27 +8069,27 @@ class DiagramCanvas {
7979
8069
  setCursorStyle();
7980
8070
  }));
7981
8071
  enterSelection.filter('.resizable-x').append('line').attr('class', 'right-resizer').attr('stroke', 'transparent').attr('stroke-width', `${RESIZER_THICKNESS}px`).on(Events.MouseOver, (_event, d) => {
7982
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableX && !d.removed) {
8072
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableX() && !d.removed) {
7983
8073
  setCursorStyle(CursorStyle.EWResize);
7984
8074
  }
7985
8075
  }).on(Events.MouseOut, (_event, d) => {
7986
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableX && !d.removed) {
8076
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableX() && !d.removed) {
7987
8077
  setCursorStyle();
7988
8078
  }
7989
8079
  }).call(d3.drag().on(DragEvents.Start, (_event, d) => {
7990
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableX && !d.removed) {
8080
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableX() && !d.removed) {
7991
8081
  setCursorStyle(CursorStyle.EWResize);
7992
8082
  this.currentAction = new SetGeometryAction(this, DiagramActions.StretchNode, d.id, d.getGeometry(), d.getGeometry());
7993
8083
  } else {
7994
8084
  setCursorStyle(CursorStyle.NotAllowed);
7995
8085
  }
7996
8086
  }).on(DragEvents.Drag, (event, d) => {
7997
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableX && !d.removed) {
8087
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableX() && !d.removed) {
7998
8088
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
7999
8089
  d.stretch(Side.Right, pointerCoords[0] - (d.coords[0] + d.width));
8000
8090
  }
8001
8091
  }).on(DragEvents.End, (event, d) => {
8002
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableX && !d.removed && this.currentAction instanceof SetGeometryAction && this.currentAction.intent === DiagramActions.StretchNode) {
8092
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableX() && !d.removed && this.currentAction instanceof SetGeometryAction && this.currentAction.intent === DiagramActions.StretchNode) {
8003
8093
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
8004
8094
  if (this.snapToGrid) {
8005
8095
  pointerCoords = this.getClosestGridPoint([pointerCoords[0] - d.type.snapToGridOffset[2], pointerCoords[1] - d.type.snapToGridOffset[3]]);
@@ -8015,27 +8105,27 @@ class DiagramCanvas {
8015
8105
  setCursorStyle();
8016
8106
  }));
8017
8107
  enterSelection.filter('.resizable-y').append('line').attr('class', 'bottom-resizer').attr('stroke', 'transparent').attr('stroke-width', `${RESIZER_THICKNESS}px`).on(Events.MouseOver, (_event, d) => {
8018
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableY && !d.removed) {
8108
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableY() && !d.removed) {
8019
8109
  setCursorStyle(CursorStyle.NSResize);
8020
8110
  }
8021
8111
  }).on(Events.MouseOut, (_event, d) => {
8022
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableY && !d.removed) {
8112
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableY() && !d.removed) {
8023
8113
  setCursorStyle();
8024
8114
  }
8025
8115
  }).call(d3.drag().on(DragEvents.Start, (_event, d) => {
8026
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableY && !d.removed) {
8116
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableY() && !d.removed) {
8027
8117
  setCursorStyle(CursorStyle.NSResize);
8028
8118
  this.currentAction = new SetGeometryAction(this, DiagramActions.StretchNode, d.id, d.getGeometry(), d.getGeometry());
8029
8119
  } else {
8030
8120
  setCursorStyle(CursorStyle.NotAllowed);
8031
8121
  }
8032
8122
  }).on(DragEvents.Drag, (event, d) => {
8033
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableY && !d.removed) {
8123
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableY() && !d.removed) {
8034
8124
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
8035
8125
  d.stretch(Side.Bottom, pointerCoords[1] - (d.coords[1] + d.height));
8036
8126
  }
8037
8127
  }).on(DragEvents.End, (event, d) => {
8038
- if (this.canUserPerformAction(DiagramActions.StretchNode) && d.type.resizableY && !d.removed && this.currentAction instanceof SetGeometryAction && this.currentAction.intent === DiagramActions.StretchNode) {
8128
+ if (this.canUserPerformAction(DiagramActions.StretchNode) && d.getResizableY() && !d.removed && this.currentAction instanceof SetGeometryAction && this.currentAction.intent === DiagramActions.StretchNode) {
8039
8129
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
8040
8130
  if (this.snapToGrid) {
8041
8131
  if (this.snapToGrid) {
@@ -8054,17 +8144,17 @@ class DiagramCanvas {
8054
8144
  }));
8055
8145
  mergeSelection.attr('transform', d => `translate(${d.coords[0]},${d.coords[1]})`).attr('opacity', d => d.removed ? 0.5 : 1);
8056
8146
  updateLook(mergeSelection);
8057
- mergeSelection.filter('.resizable-x').select('line.left-resizer').attr('x1', RESIZER_THICKNESS / 2).attr('x2', RESIZER_THICKNESS / 2).attr('y1', 0).attr('y2', d => d.height);
8058
- mergeSelection.filter('.resizable-y').select('line.top-resizer').attr('x1', 0).attr('x2', d => d.width).attr('y1', RESIZER_THICKNESS / 2).attr('y2', RESIZER_THICKNESS / 2);
8059
- mergeSelection.filter('.resizable-x').select('line.right-resizer').attr('x1', d => d.width - RESIZER_THICKNESS / 2).attr('x2', d => d.width - RESIZER_THICKNESS / 2).attr('y1', 0).attr('y2', d => d.height);
8060
- mergeSelection.filter('.resizable-y').select('line.bottom-resizer').attr('x1', 0).attr('x2', d => d.width).attr('y1', d => d.height - RESIZER_THICKNESS / 2).attr('y2', d => d.height - RESIZER_THICKNESS / 2);
8147
+ mergeSelection.filter('.resizable-x').select('line.left-resizer').style('pointer-events', d => d.getResizableX() ? 'initial' : 'none').attr('x1', RESIZER_THICKNESS / 2).attr('x2', RESIZER_THICKNESS / 2).attr('y1', 0).attr('y2', d => d.height);
8148
+ mergeSelection.filter('.resizable-y').select('line.top-resizer').style('pointer-events', d => d.getResizableY() ? 'initial' : 'none').attr('x1', 0).attr('x2', d => d.width).attr('y1', RESIZER_THICKNESS / 2).attr('y2', RESIZER_THICKNESS / 2);
8149
+ mergeSelection.filter('.resizable-x').select('line.right-resizer').style('pointer-events', d => d.getResizableX() ? 'initial' : 'none').attr('x1', d => d.width - RESIZER_THICKNESS / 2).attr('x2', d => d.width - RESIZER_THICKNESS / 2).attr('y1', 0).attr('y2', d => d.height);
8150
+ mergeSelection.filter('.resizable-y').select('line.bottom-resizer').style('pointer-events', d => d.getResizableY() ? 'initial' : 'none').attr('x1', 0).attr('x2', d => d.width).attr('y1', d => d.height - RESIZER_THICKNESS / 2).attr('y2', d => d.height - RESIZER_THICKNESS / 2);
8061
8151
  }
8062
8152
  updateSectionsInView(...ids) {
8063
8153
  let updateSelection = this.selectCanvasElements().selectAll('g.diagram-section').data(this.model.sections.filter(e => this.priorityThreshold !== undefined ? e.getPriority() >= this.priorityThreshold : true), d => d.id);
8064
8154
  const exitSelection = updateSelection.exit();
8065
8155
  const enterSelection = updateSelection.enter().append('g').attr('id', d => d.id).attr('class', d => {
8066
8156
  var _a;
8067
- return `diagram-section${d.getResizableX() ? ' resizable-x' : ''}${d.getResizableY() ? ' resizable-y' : ''} ${(_a = d.look) === null || _a === void 0 ? void 0 : _a.lookType}`;
8157
+ return `diagram-section${needsResizerX(d) ? ' resizable-x' : ''}${needsResizerY(d) ? ' resizable-y' : ''} ${(_a = d.look) === null || _a === void 0 ? void 0 : _a.lookType}`;
8068
8158
  });
8069
8159
  if (ids && ids.length > 0) {
8070
8160
  updateSelection = updateSelection.filter(d => ids.includes(d.id));
@@ -8283,10 +8373,10 @@ class DiagramCanvas {
8283
8373
  }));
8284
8374
  mergeSelection.attr('transform', d => `translate(${d.coords[0]},${d.coords[1]})`).attr('opacity', d => d.removed ? 0.5 : 1);
8285
8375
  updateLook(mergeSelection);
8286
- mergeSelection.filter('.resizable-x').select('line.left-resizer').attr('x1', RESIZER_THICKNESS / 2).attr('x2', RESIZER_THICKNESS / 2).attr('y1', 0).attr('y2', d => d.height);
8287
- mergeSelection.filter('.resizable-y').select('line.top-resizer').attr('x1', 0).attr('x2', d => d.width).attr('y1', RESIZER_THICKNESS / 2).attr('y2', RESIZER_THICKNESS / 2);
8288
- mergeSelection.filter('.resizable-x').select('line.right-resizer').attr('x1', d => d.width - RESIZER_THICKNESS / 2).attr('x2', d => d.width - RESIZER_THICKNESS / 2).attr('y1', 0).attr('y2', d => d.height);
8289
- mergeSelection.filter('.resizable-y').select('line.bottom-resizer').attr('x1', 0).attr('x2', d => d.width).attr('y1', d => d.height - RESIZER_THICKNESS / 2).attr('y2', d => d.height - RESIZER_THICKNESS / 2);
8376
+ mergeSelection.filter('.resizable-x').select('line.left-resizer').style('pointer-events', d => d.getResizableX() ? 'initial' : 'none').attr('x1', RESIZER_THICKNESS / 2).attr('x2', RESIZER_THICKNESS / 2).attr('y1', 0).attr('y2', d => d.height);
8377
+ mergeSelection.filter('.resizable-y').select('line.top-resizer').style('pointer-events', d => d.getResizableY() ? 'initial' : 'none').attr('x1', 0).attr('x2', d => d.width).attr('y1', RESIZER_THICKNESS / 2).attr('y2', RESIZER_THICKNESS / 2);
8378
+ mergeSelection.filter('.resizable-x').select('line.right-resizer').style('pointer-events', d => d.getResizableX() ? 'initial' : 'none').attr('x1', d => d.width - RESIZER_THICKNESS / 2).attr('x2', d => d.width - RESIZER_THICKNESS / 2).attr('y1', 0).attr('y2', d => d.height);
8379
+ mergeSelection.filter('.resizable-y').select('line.bottom-resizer').style('pointer-events', d => d.getResizableY() ? 'initial' : 'none').attr('x1', 0).attr('x2', d => d.width).attr('y1', d => d.height - RESIZER_THICKNESS / 2).attr('y2', d => d.height - RESIZER_THICKNESS / 2);
8290
8380
  }
8291
8381
  updatePortsInView(...ids) {
8292
8382
  let updateSelection = this.selectCanvasElements().selectAll('g.diagram-port').data(this.model.ports.filter(e => this.priorityThreshold !== undefined ? e.getPriority() >= this.priorityThreshold : true), d => d.id);
@@ -10289,4 +10379,4 @@ const getLocationsOfNodes = model => {
10289
10379
  return locations;
10290
10380
  };
10291
10381
 
10292
- export { ACTION_STACK_SIZE, ActionStack, AddConnectionAction, AddNodeAction, AdjacencyLayout, ApplyLayoutAction, BreadthAdjacencyLayout, BreadthLayout, ClosedShape, CollabClient, Corner, CursorStyle, DIAGRAM_FIELD_DEFAULTS, DagaExporter, DagaImporter, DiagramActionMethod, DiagramActions, DiagramCanvas, DiagramConnection, DiagramConnectionSet, DiagramConnectionType, DiagramContextMenu, DiagramDecorator, DiagramDecoratorSet, DiagramDoubleClickEvent, DiagramElement, DiagramElementSet, DiagramEntitySet, DiagramEvent, DiagramEvents, DiagramField, DiagramFieldSet, DiagramHighlightedEvent, DiagramModel, DiagramNode, DiagramNodeSet, DiagramNodeType, DiagramObject, DiagramObjectSet, DiagramPort, DiagramPortSet, DiagramPortType, DiagramSecondaryClickEvent, DiagramSection, DiagramSectionSet, DiagramSelectionEvent, DiagramUserHighlight, DiagramUserSelection, DiagramZoomEvent, DragEvents, EditFieldAction, Events, ForceLayout, HorizontalAlign, HorizontalLayout, Keys, LineShape, LineStyle, MoveAction, PasteAction, PriorityLayout, Property, PropertySet, RemoveAction, SetGeometryAction, SetParentAction, Side, TreeLayout, Type, UpdateValuesAction, ValueSet, VerticalAlign, VerticalLayout, ZoomEvents, addIfNotExists, diff, equals, filterByOnlyAncestors, filterByOnlyDescendants, generalClosedPath, getBottomMargin, getBottomPadding$1 as getBottomPadding, getLeftMargin, getLeftPadding$1 as getLeftPadding, getLocationsOfNodes, getRightMargin, getRightPadding$1 as getRightPadding, getTopMargin, getTopPadding$1 as getTopPadding, isObject, layouts, linePath, lineStyleDasharray, removeIfExists, setCursorStyle };
10382
+ export { ACTION_STACK_SIZE, ActionStack, AddConnectionAction, AddNodeAction, AdjacencyLayout, ApplyLayoutAction, BreadthAdjacencyLayout, BreadthLayout, ClosedShape, CollabClient, Corner, CursorStyle, DIAGRAM_FIELD_DEFAULTS, DagaExporter, DagaImporter, DiagramActionMethod, DiagramActions, DiagramCanvas, DiagramConnection, DiagramConnectionSet, DiagramConnectionType, DiagramContextMenu, DiagramDecorator, DiagramDecoratorSet, DiagramDoubleClickEvent, DiagramElement, DiagramElementSet, DiagramEntitySet, DiagramEvent, DiagramEvents, DiagramField, DiagramFieldSet, DiagramHighlightedEvent, DiagramModel, DiagramNode, DiagramNodeSet, DiagramNodeType, DiagramObject, DiagramObjectSet, DiagramPort, DiagramPortSet, DiagramPortType, DiagramSecondaryClickEvent, DiagramSection, DiagramSectionSet, DiagramSelectionEvent, DiagramUserHighlight, DiagramUserSelection, DiagramZoomEvent, DragEvents, EditFieldAction, Events, ForceLayout, HorizontalAlign, HorizontalLayout, Keys, LineShape, LineStyle, MoveAction, PasteAction, PriorityLayout, Property, PropertySet, RemoveAction, ResizableMode, SetGeometryAction, SetParentAction, Side, TreeLayout, Type, UpdateValuesAction, ValueSet, VerticalAlign, VerticalLayout, ZoomEvents, addIfNotExists, diff, equals, filterByOnlyAncestors, filterByOnlyDescendants, generalClosedPath, getBottomMargin, getBottomPadding$1 as getBottomPadding, getLeftMargin, getLeftPadding$1 as getLeftPadding, getLocationsOfNodes, getRightMargin, getRightPadding$1 as getRightPadding, getTopMargin, getTopPadding$1 as getTopPadding, isObject, layouts, linePath, lineStyleDasharray, removeIfExists, setCursorStyle };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metadev/daga",
3
- "version": "4.2.13",
3
+ "version": "4.2.14",
4
4
  "dependencies": {},
5
5
  "peerDependencies": {
6
6
  "d3": "^7.9.0",
package/src/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export { CollabClient } from './lib/diagram/collab/collab-client';
9
9
  export type { CollabTimestamp, CollabTimestampSet } from './lib/diagram/collab/primitives';
10
10
  export type { CanvasConfig, GridConfig, GridStyle } from './lib/diagram/config/diagram-canvas-config';
11
11
  export type { ButtonsComponentConfig, ComponentsConfig, ConnectionTemplateConfig, ErrorsComponentConfig, NodeTemplateConfig, PaletteComponentConfig, PaletteSectionConfig, PropertyEditorComponentConfig } from './lib/diagram/config/diagram-components-config';
12
+ export { ResizableMode } from './lib/diagram/config/diagram-config';
12
13
  export type { ConnectionTypeConfig, DiagramConfig, FieldConfig, NodeTypeConfig, PortConfig, PortTypeConfig, SectionConfig, SectionGridConfig, UserActionConfig } from './lib/diagram/config/diagram-config';
13
14
  export type { ConnectionLook, ConnectionLookConfig, ImageLook, ImageLookConfig, Look, LookConfig, MarkerImageLook, MarkerImageLookConfig, ShapedLook, ShapedLookConfig, StretchableImageLook, StretchableImageLookConfig } from './lib/diagram/config/diagram-look-config';
14
15
  export { DagaExporter } from './lib/diagram/converters/daga-exporter';
@@ -23,6 +23,8 @@ export declare const isSecondaryButton: (event: MouseEvent) => boolean;
23
23
  export declare const getConnectionPath: (shape: LineShape | LineFunction, startCoords: Point, endCoords: Point, startDirection: Side | undefined, endDirection: Side | undefined, width: number, startMarkerWidth: number | undefined, endMarkerWidth: number | undefined) => string;
24
24
  export declare const setCursorStyle: (style?: CursorStyle) => void;
25
25
  export declare const getRelatedNodeOrItself: (element: DiagramNode | DiagramSection | DiagramPort | DiagramField) => DiagramNode | DiagramSection | DiagramPort | DiagramField;
26
+ export declare const needsResizerX: (element: DiagramNode | DiagramSection) => boolean;
27
+ export declare const needsResizerY: (element: DiagramNode | DiagramSection) => boolean;
26
28
  export declare const initializeLook: (selection: d3.Selection<SVGGElement, DiagramNode | DiagramSection | DiagramPort, d3.BaseType, unknown>) => void;
27
29
  export declare const SHAPED_LOOK_DEFAULTS: {
28
30
  fillColor: string;
@@ -105,7 +105,8 @@ export declare class DiagramCanvas implements Canvas {
105
105
  getViewCoordinates(): Point;
106
106
  translateBy(x: number, y: number): void;
107
107
  translateTo(x: number, y: number): void;
108
- center(): void;
108
+ zoomAndPanTo(x: number, y: number, z: number, duration?: number): void;
109
+ center(nodeIds?: string[], maxZoomLevel?: number, duration?: number): void;
109
110
  getClosestGridPoint(point: Point): Point;
110
111
  getCoordinatesOnScreen(): [Point, Point];
111
112
  private getEventHoldingCoordinates;
@@ -169,12 +169,12 @@ export interface NodeTypeConfig {
169
169
  * Whether the user can resize nodes of this type along the x axis.
170
170
  * @default false
171
171
  */
172
- resizableX?: boolean;
172
+ resizableX?: boolean | ResizableMode;
173
173
  /**
174
174
  * Whether the user can resize nodes of this type along the y axis.
175
175
  * @default false
176
176
  */
177
- resizableY?: boolean;
177
+ resizableY?: boolean | ResizableMode;
178
178
  /**
179
179
  * By how much the location of nodes of this type should be offset when snapping to grid in diagram units.
180
180
  * Each value corresponds to left, top, right and bottom respectively.
@@ -499,13 +499,13 @@ export interface SectionConfig {
499
499
  * If undefined, inherits from the parent node's resizableX setting.
500
500
  * @default undefined
501
501
  */
502
- resizableX?: boolean;
502
+ resizableX?: boolean | ResizableMode;
503
503
  /**
504
504
  * Whether this section can be resized vertically.
505
505
  * If undefined, inherits from the parent node's resizableY setting.
506
506
  * @default undefined
507
507
  */
508
- resizableY?: boolean;
508
+ resizableY?: boolean | ResizableMode;
509
509
  }
510
510
  /**
511
511
  * Configuration for a type of connection.
@@ -556,3 +556,6 @@ export interface ConnectionTypeConfig {
556
556
  */
557
557
  properties?: Property[];
558
558
  }
559
+ export declare enum ResizableMode {
560
+ OnlyWhenSelected = 0
561
+ }
@@ -1,7 +1,7 @@
1
1
  import { Point } from '../../util/canvas-util';
2
2
  import { Side } from '../../util/svg-util';
3
3
  import { CollabTimestamp } from '../collab/primitives';
4
- import { DecoratorConfig, FieldConfig, NodeTypeConfig, PortConfig } from '../config/diagram-config';
4
+ import { DecoratorConfig, FieldConfig, NodeTypeConfig, PortConfig, ResizableMode } from '../config/diagram-config';
5
5
  import { ImageLook, ImageLookConfig, ShapedLook, ShapedLookConfig, StretchableImageLook, StretchableImageLookConfig } from '../config/diagram-look-config';
6
6
  import { PropertySet } from '../property/property';
7
7
  import { ValueSet } from '../property/value';
@@ -75,8 +75,8 @@ export declare class DiagramNodeType implements DiagramEntity {
75
75
  defaultHeight: number;
76
76
  minWidth: number;
77
77
  minHeight: number;
78
- resizableX: boolean;
79
- resizableY: boolean;
78
+ resizableX: boolean | ResizableMode;
79
+ resizableY: boolean | ResizableMode;
80
80
  snapToGridOffset: [number, number, number, number];
81
81
  bottomPadding: number;
82
82
  leftPadding: number;
@@ -207,6 +207,16 @@ export declare class DiagramNode extends DiagramElement implements LabeledElemen
207
207
  updateInView(): void;
208
208
  raise(): void;
209
209
  getPriority(): number;
210
+ /**
211
+ * Returns whether this node can be resized horizontally.
212
+ * @public
213
+ */
214
+ getResizableX(): boolean;
215
+ /**
216
+ * Returns whether this node can be resized vertically.
217
+ * @public
218
+ */
219
+ getResizableY(): boolean;
210
220
  /**
211
221
  * Get the port of this node which is closest to the given coordinates.
212
222
  * @param coords A point in the diagram.
@@ -1,6 +1,6 @@
1
1
  import { Point } from '../../util/canvas-util';
2
2
  import { Side } from '../../util/svg-util';
3
- import { FieldConfig, PortConfig, SectionConfig, SectionGridConfig } from '../config/diagram-config';
3
+ import { FieldConfig, PortConfig, ResizableMode, SectionConfig, SectionGridConfig } from '../config/diagram-config';
4
4
  import { ImageLookConfig, ShapedLookConfig, StretchableImageLookConfig } from '../config/diagram-look-config';
5
5
  import { DiagramConnection } from './diagram-connection';
6
6
  import { DiagramDecorator } from './diagram-decorator';
@@ -61,8 +61,8 @@ export declare class DiagramSectionType {
61
61
  highlightedLook: NodeLook;
62
62
  selectedAndHighlightedLook: NodeLook;
63
63
  priority: number;
64
- resizableX: boolean | undefined;
65
- resizableY: boolean | undefined;
64
+ resizableX: boolean | ResizableMode | undefined;
65
+ resizableY: boolean | ResizableMode | undefined;
66
66
  constructor(options: SectionConfig);
67
67
  }
68
68
  /**
@@ -195,7 +195,8 @@ export interface Canvas {
195
195
  */
196
196
  zoomTo(level: number): void;
197
197
  /**
198
- * Gets the current coordinates of the view.
198
+ * Gets the current coordinates of the center of view.
199
+ * Ensures that calling `translateTo()` with the result causes no changes.
199
200
  * @public
200
201
  */
201
202
  getViewCoordinates(): Point;
@@ -207,17 +208,30 @@ export interface Canvas {
207
208
  */
208
209
  translateBy(x: number, y: number): void;
209
210
  /**
210
- * Translates the view to the given point.
211
+ * Translates the center of the view to the given point.
211
212
  * @public
212
213
  * @param x A coordinate along the x axis.
213
214
  * @param y A coordinate along the y axis.
214
215
  */
215
216
  translateTo(x: number, y: number): void;
217
+ /**
218
+ * Translates the center of the view to the given point and the given zoom level over the given duration of time.
219
+ * The duration may be set to `0` or `undefined` to make the move instant without animation.
220
+ * @public
221
+ * @param x A coordinate along the x axis.
222
+ * @param y A coordinate along the y axis.
223
+ * @param z A level of zoom.
224
+ * @param duration A duration for the animation in milliseconds. May be set to `0` or `undefined` to make it instant without animation.
225
+ */
226
+ zoomAndPanTo(x: number, y: number, z: number, duration?: number): void;
216
227
  /**
217
228
  * Centers by zooming and translating the view such that every element fits in the view.
218
229
  * @public
230
+ * @param nodeIds A list of node ids to center the view on. If `undefined`, it encompasses all nodes.
231
+ * @param maxZoomLevel The maximum zoom level that can be used when zooming into the elements. Can be used to prevent zooming in past a given level. `1` by default.
232
+ * @param duration A duration for the animation in milliseconds. May be set to `0` or `undefined` to make it instant without animation.
219
233
  */
220
- center(): void;
234
+ center(nodeIds?: string[], maxZoomLevel?: number, duration?: number): void;
221
235
  /**
222
236
  * Get the closest grid point to the given point.
223
237
  * @public