@logicflow/extension 1.2.8 → 1.2.10

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.
@@ -139,6 +139,35 @@ var Control = /** @class */ (function (_super) {
139
139
  }
140
140
  return resize;
141
141
  };
142
+ _this.updateEdgePointByAnchors = function () {
143
+ // https://github.com/didi/LogicFlow/issues/807
144
+ // https://github.com/didi/LogicFlow/issues/875
145
+ // 之前的做法,比如Rect是使用getRectResizeEdgePoint()计算边的point缩放后的位置
146
+ // getRectResizeEdgePoint()考虑了瞄点在四条边以及在4个圆角的情况
147
+ // 使用的是一种等比例缩放的模式,比如:
148
+ // const pct = (y - beforeNode.y) / (beforeNode.height / 2 - radius)
149
+ // afterPoint.y = afterNode.y + (afterNode.height / 2 - radius) * pct
150
+ // 但是用户自定义的getDefaultAnchor()不一定是按照比例编写的
151
+ // 它可能是 x: x + 20:每次缩放都会保持在x右边20的位置,因此用户自定义瞄点时,然后产生无法跟随的问题
152
+ // 现在的做法是:直接获取用户自定义瞄点的位置,然后用这个位置作为边的新的起点,而不是自己进行计算
153
+ var _a = _this.nodeModel, id = _a.id, anchors = _a.anchors;
154
+ var edges = _this.getNodeEdges(id);
155
+ // 更新边
156
+ edges.sourceEdges.forEach(function (item) {
157
+ var anchorItem = anchors.find(function (anchor) { return anchor.id === item.sourceAnchorId; });
158
+ item.updateStartPoint({
159
+ x: anchorItem.x,
160
+ y: anchorItem.y,
161
+ });
162
+ });
163
+ edges.targetEdges.forEach(function (item) {
164
+ var anchorItem = anchors.find(function (anchor) { return anchor.id === item.targetAnchorId; });
165
+ item.updateEndPoint({
166
+ x: anchorItem.x,
167
+ y: anchorItem.y,
168
+ });
169
+ });
170
+ };
142
171
  // 矩形更新
143
172
  _this.updateRect = function (_a) {
144
173
  var deltaX = _a.deltaX, deltaY = _a.deltaY;
@@ -189,23 +218,8 @@ var Control = /** @class */ (function (_super) {
189
218
  height: _this.nodeModel.height,
190
219
  radius: radius,
191
220
  };
192
- var params = {
193
- point: '',
194
- beforeNode: beforeNode,
195
- afterNode: afterNode,
196
- };
197
221
  // 更新边
198
- var afterPoint;
199
- edges.sourceEdges.forEach(function (item) {
200
- params.point = item.startPoint;
201
- afterPoint = Util_1.getRectResizeEdgePoint(params);
202
- item.updateStartPoint(afterPoint);
203
- });
204
- edges.targetEdges.forEach(function (item) {
205
- params.point = item.endPoint;
206
- afterPoint = Util_1.getRectResizeEdgePoint(params);
207
- item.updateEndPoint(afterPoint);
208
- });
222
+ _this.updateEdgePointByAnchors();
209
223
  _this.eventEmit({ beforeNode: beforeNode, afterNode: afterNode });
210
224
  };
211
225
  // 椭圆更新
@@ -254,23 +268,8 @@ var Control = /** @class */ (function (_super) {
254
268
  x: _this.nodeModel.x,
255
269
  y: _this.nodeModel.y,
256
270
  };
257
- var params = {
258
- point: {},
259
- beforeNode: beforeNode,
260
- afterNode: afterNode,
261
- };
262
271
  // 更新边
263
- var afterPoint;
264
- edges.sourceEdges.forEach(function (item) {
265
- params.point = item.startPoint;
266
- afterPoint = Util_1.getEllipseResizeEdgePoint(params);
267
- item.updateStartPoint(afterPoint);
268
- });
269
- edges.targetEdges.forEach(function (item) {
270
- params.point = item.endPoint;
271
- afterPoint = Util_1.getEllipseResizeEdgePoint(params);
272
- item.updateEndPoint(afterPoint);
273
- });
272
+ _this.updateEdgePointByAnchors();
274
273
  _this.eventEmit({ beforeNode: __assign(__assign({}, beforeNode), { rx: rx, ry: ry }), afterNode: afterNode });
275
274
  };
276
275
  // 菱形更新
@@ -318,24 +317,8 @@ var Control = /** @class */ (function (_super) {
318
317
  x: _this.nodeModel.x,
319
318
  y: _this.nodeModel.y,
320
319
  };
321
- var params = {
322
- point: {},
323
- beforeNode: beforeNode,
324
- afterNode: afterNode,
325
- };
326
320
  // 更新边
327
- var afterPoint;
328
- var edges = _this.getNodeEdges(id);
329
- edges.sourceEdges.forEach(function (item) {
330
- params.point = item.startPoint;
331
- afterPoint = Util_1.getDiamondResizeEdgePoint(params);
332
- item.updateStartPoint(afterPoint);
333
- });
334
- edges.targetEdges.forEach(function (item) {
335
- params.point = item.endPoint;
336
- afterPoint = Util_1.getDiamondResizeEdgePoint(params);
337
- item.updateEndPoint(afterPoint);
338
- });
321
+ _this.updateEdgePointByAnchors();
339
322
  _this.eventEmit({ beforeNode: beforeNode, afterNode: afterNode });
340
323
  };
341
324
  _this.eventEmit = function (_a) {
@@ -367,6 +350,9 @@ var Control = /** @class */ (function (_super) {
367
350
  * 由于将拖拽放大缩小改成丝滑模式,这个时候需要在拖拽结束的时候,将节点的位置更新到grid上.
368
351
  */
369
352
  _this.onDragEnd = function () {
353
+ // 先触发onDragging()->更新边->再触发用户自定义的getDefaultAnchor(),所以onDragging()拿到的anchors是滞后的
354
+ // 为了正确设置最终的位置,应该在拖拽结束的时候,再设置一次边的Point位置,此时拿到的anchors是最新的
355
+ _this.updateEdgePointByAnchors();
370
356
  var _a = _this.graphModel.gridSize, gridSize = _a === void 0 ? 1 : _a;
371
357
  var x = gridSize * Math.round(_this.nodeModel.x / gridSize);
372
358
  var y = gridSize * Math.round(_this.nodeModel.y / gridSize);
@@ -91,7 +91,7 @@ var Control = /** @class */ (function () {
91
91
  };
92
92
  Control.prototype.removeItem = function (key) {
93
93
  var index = this.controlItems.findIndex(function (item) { return item.key === key; });
94
- return this.controlItems.splice(index, 1)[0];
94
+ return index == -1 ? null : this.controlItems.splice(index, 1)[0];
95
95
  };
96
96
  Control.prototype.getControlTool = function () {
97
97
  var _this = this;
@@ -128,7 +128,7 @@ var Menu = /** @class */ (function () {
128
128
  }, true);
129
129
  // 通过事件控制菜单的显示和隐藏
130
130
  this.lf.on('node:contextmenu', function (_a) {
131
- var data = _a.data, position = _a.position;
131
+ var data = _a.data, position = _a.position, e = _a.e;
132
132
  var _b = position.domOverlayPosition, x = _b.x, y = _b.y;
133
133
  var id = data.id;
134
134
  var model = _this.lf.graphModel.getNodeModelById(id);
@@ -145,10 +145,15 @@ var Menu = /** @class */ (function () {
145
145
  menuList = _this.menuTypeMap.get(DefaultNodeMenuKey);
146
146
  }
147
147
  _this.__currentData = data;
148
- _this.showMenu(x, y, menuList);
148
+ _this.showMenu(x, y, menuList, {
149
+ width: model.width,
150
+ height: model.height,
151
+ clientX: e.clientX,
152
+ clientY: e.clientY,
153
+ });
149
154
  });
150
155
  this.lf.on('edge:contextmenu', function (_a) {
151
- var data = _a.data, position = _a.position;
156
+ var data = _a.data, position = _a.position, e = _a.e;
152
157
  var _b = position.domOverlayPosition, x = _b.x, y = _b.y;
153
158
  var id = data.id;
154
159
  var model = _this.lf.graphModel.getEdgeModelById(id);
@@ -165,7 +170,12 @@ var Menu = /** @class */ (function () {
165
170
  menuList = _this.menuTypeMap.get(DefaultEdgeMenuKey);
166
171
  }
167
172
  _this.__currentData = data;
168
- _this.showMenu(x, y, menuList);
173
+ _this.showMenu(x, y, menuList, {
174
+ width: model.width,
175
+ height: model.height,
176
+ clientX: e.clientX,
177
+ clientY: e.clientY,
178
+ });
169
179
  });
170
180
  this.lf.on('blank:contextmenu', function (_a) {
171
181
  var position = _a.position;
@@ -195,7 +205,7 @@ var Menu = /** @class */ (function () {
195
205
  (_a = this === null || this === void 0 ? void 0 : this.__container) === null || _a === void 0 ? void 0 : _a.removeChild(this.__menuDOM);
196
206
  this.__menuDOM = null;
197
207
  };
198
- Menu.prototype.showMenu = function (x, y, menuList) {
208
+ Menu.prototype.showMenu = function (x, y, menuList, options) {
199
209
  if (!menuList || !menuList.length)
200
210
  return;
201
211
  var menu = this.__menuDOM;
@@ -206,8 +216,67 @@ var Menu = /** @class */ (function () {
206
216
  if (!menu.children.length)
207
217
  return;
208
218
  menu.style.display = 'block';
209
- menu.style.top = y + "px";
210
- menu.style.left = x + "px";
219
+ if (!options) {
220
+ menu.style.top = y + "px";
221
+ menu.style.left = x + "px";
222
+ return;
223
+ }
224
+ // https://github.com/didi/LogicFlow/issues/1019
225
+ // 根据边界判断菜单的left 和 top
226
+ var width = options.width, height = options.height, clientX = options.clientX, clientY = options.clientY;
227
+ var graphModel = this.lf.graphModel;
228
+ var menuWidth = menu.offsetWidth;
229
+ var menuIsRightShow = true;
230
+ // ======先进行可视屏幕范围的判断=======
231
+ // 浏览器窗口可视区域兼容性写法
232
+ // eslint-disable-next-line max-len
233
+ var windowMaxX = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
234
+ var rightDistance = windowMaxX - clientX;
235
+ // ======先进行可视屏幕范围的判断=======
236
+ // ========再进行画布范围的判断========
237
+ var graphRect = graphModel.rootEl.getBoundingClientRect();
238
+ var graphMaxX = graphRect.left + graphRect.width;
239
+ if (graphMaxX < windowMaxX) {
240
+ // 画布右边小于可视屏幕范围的最右边,取画布右边作为极限值,计算出当前触摸点距离右边极限值的距离
241
+ rightDistance = graphMaxX - clientX;
242
+ }
243
+ // ========再进行画布范围的判断========
244
+ // 根据当前触摸点距离右边的距离 跟 menuWidth进行比较
245
+ if (rightDistance < menuWidth) {
246
+ // 空间不足够,显示在左边
247
+ menuIsRightShow = false;
248
+ }
249
+ if (menuIsRightShow) {
250
+ menu.style.left = x + "px";
251
+ }
252
+ else {
253
+ menu.style.left = (x - width) + "px";
254
+ }
255
+ var menuHeight = menu.offsetHeight;
256
+ var menuIsBottomShow = true;
257
+ // ======先进行可视屏幕范围的判断=======
258
+ // 浏览器窗口可视区域兼容性写法
259
+ // eslint-disable-next-line max-len
260
+ var windowMaxY = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
261
+ var bottomDistance = windowMaxY - clientY;
262
+ // ======先进行可视屏幕范围的判断=======
263
+ // ========再进行画布范围的判断========
264
+ var graphMaxY = graphRect.top + graphRect.height;
265
+ if (graphMaxY < windowMaxY) {
266
+ // 画布底部小于可视屏幕范围的最底边,取画布底部作为极限值,计算出当前触摸点距离底部极限值的距离
267
+ bottomDistance = graphMaxY - clientY;
268
+ }
269
+ // ========再进行画布范围的判断========
270
+ if (bottomDistance < menuHeight) {
271
+ // 如果下边距离太小,无法显示menu,则向上显示
272
+ menuIsBottomShow = false;
273
+ }
274
+ if (menuIsBottomShow) {
275
+ menu.style.top = y + "px";
276
+ }
277
+ else {
278
+ menu.style.top = (y - height) + "px";
279
+ }
211
280
  };
212
281
  /**
213
282
  * 设置指定类型元素的菜单
@@ -56,6 +56,10 @@ var GroupNodeModel = /** @class */ (function (_super) {
56
56
  _this.isGroup = true;
57
57
  _this.unfoldedWidth = defaultWidth;
58
58
  _this.unfoldedHight = defaultHeight;
59
+ /**
60
+ * children元素上一次折叠的状态缓存
61
+ */
62
+ _this.childrenLastFoldStatus = {};
59
63
  return _this;
60
64
  }
61
65
  GroupNodeModel.prototype.initNodeData = function (data) {
@@ -103,6 +107,11 @@ var GroupNodeModel = /** @class */ (function (_super) {
103
107
  */
104
108
  GroupNodeModel.prototype.foldGroup = function (isFolded) {
105
109
  var _this = this;
110
+ if (isFolded === this.isFolded) {
111
+ // 防止多次调用同样的状态设置
112
+ // 如果this.isFolded=false,同时触发foldGroup(false),会导致下面的childrenLastFoldStatus状态错乱
113
+ return;
114
+ }
106
115
  this.setProperty('isFolded', isFolded);
107
116
  this.isFolded = isFolded;
108
117
  // step 1
@@ -124,10 +133,26 @@ var GroupNodeModel = /** @class */ (function (_super) {
124
133
  var allEdges = this.incoming.edges.concat(this.outgoing.edges);
125
134
  this.children.forEach(function (elementId) {
126
135
  var nodeModel = _this.graphModel.getElement(elementId);
136
+ var foldStatus = nodeModel.isFolded;
127
137
  // FIX: https://github.com/didi/LogicFlow/issues/1007
128
138
  if (nodeModel.isGroup && !nodeModel.isFolded) {
139
+ // 正常情况下,parent折叠后,children应该折叠
140
+ // 因此当parent准备展开时,children的值目前肯定是折叠状态,也就是nodeModel.isFolded=true,这个代码块不会触发
141
+ // 只有当parent准备折叠时,children目前状态才有可能是展开,即nodeModel.isFolded=false,这个代码块触发,此时isFolded=true,触发children也进行折叠
129
142
  nodeModel.foldGroup(isFolded);
130
143
  }
144
+ if (nodeModel.isGroup && !isFolded) {
145
+ // 当parent准备展开时,children的值应该恢复到折叠前的状态
146
+ var lastFoldStatus = _this.childrenLastFoldStatus[elementId];
147
+ if (lastFoldStatus !== undefined && lastFoldStatus !== nodeModel.isFolded) {
148
+ // https://github.com/didi/LogicFlow/issues/1145
149
+ // 当parent准备展开时,children的值肯定是折叠,也就是nodeModel.isFolded=true
150
+ // 当parent准备展开时,如果children之前的状态是展开,则恢复展开状态
151
+ nodeModel.foldGroup(lastFoldStatus);
152
+ }
153
+ }
154
+ // 存储parent触发children改变折叠状态前的状态
155
+ _this.childrenLastFoldStatus[elementId] = foldStatus;
131
156
  nodeModel.visible = !isFolded;
132
157
  allEdges = allEdges.concat(nodeModel.incoming.edges.concat(nodeModel.outgoing.edges));
133
158
  });
@@ -154,7 +154,7 @@ var Snapshot = /** @class */ (function () {
154
154
  var transformModel = graphModel.transformModel;
155
155
  var SCALE_X = transformModel.SCALE_X, SCALE_Y = transformModel.SCALE_Y, TRANSLATE_X = transformModel.TRANSLATE_X, TRANSLATE_Y = transformModel.TRANSLATE_Y;
156
156
  // offset值加10,保证图形不会紧贴着下载图片的左边和上边
157
- copy.lastChild.style.transform = "matrix(1, 0, 0, 1, " + (-offsetX + 10 + TRANSLATE_X) + ", " + (-offsetY + 10 + TRANSLATE_Y) + ")";
157
+ copy.lastChild.style.transform = "matrix(1, 0, 0, 1, " + ((-offsetX + TRANSLATE_X) * (1 / SCALE_X) + 10) + ", " + ((-offsetY + TRANSLATE_Y) * (1 / SCALE_Y) + 10) + ")";
158
158
  var bboxWidth = Math.ceil(bbox.width / SCALE_X);
159
159
  var bboxHeight = Math.ceil(bbox.height / SCALE_Y);
160
160
  // width,height 值加40,保证图形不会紧贴着下载图片的右边和下边
@@ -36,6 +36,7 @@ declare class Control extends Component<IProps> {
36
36
  deltaX: any;
37
37
  deltaY: any;
38
38
  };
39
+ updateEdgePointByAnchors: () => void;
39
40
  updateRect: ({ deltaX, deltaY }: {
40
41
  deltaX: any;
41
42
  deltaY: any;
@@ -41,7 +41,7 @@ var __read = (this && this.__read) || function (o, n) {
41
41
  import { h, Component } from 'preact';
42
42
  import { LogicFlowUtil } from '@logicflow/core';
43
43
  import Rect from '../BasicShape/Rect';
44
- import { getDiamondResizeEdgePoint, getEllipseResizeEdgePoint, getRectResizeEdgePoint, ModelType } from './Util';
44
+ import { ModelType } from './Util';
45
45
  var StepDrag = LogicFlowUtil.StepDrag;
46
46
  var Control = /** @class */ (function (_super) {
47
47
  __extends(Control, _super);
@@ -137,6 +137,35 @@ var Control = /** @class */ (function (_super) {
137
137
  }
138
138
  return resize;
139
139
  };
140
+ _this.updateEdgePointByAnchors = function () {
141
+ // https://github.com/didi/LogicFlow/issues/807
142
+ // https://github.com/didi/LogicFlow/issues/875
143
+ // 之前的做法,比如Rect是使用getRectResizeEdgePoint()计算边的point缩放后的位置
144
+ // getRectResizeEdgePoint()考虑了瞄点在四条边以及在4个圆角的情况
145
+ // 使用的是一种等比例缩放的模式,比如:
146
+ // const pct = (y - beforeNode.y) / (beforeNode.height / 2 - radius)
147
+ // afterPoint.y = afterNode.y + (afterNode.height / 2 - radius) * pct
148
+ // 但是用户自定义的getDefaultAnchor()不一定是按照比例编写的
149
+ // 它可能是 x: x + 20:每次缩放都会保持在x右边20的位置,因此用户自定义瞄点时,然后产生无法跟随的问题
150
+ // 现在的做法是:直接获取用户自定义瞄点的位置,然后用这个位置作为边的新的起点,而不是自己进行计算
151
+ var _a = _this.nodeModel, id = _a.id, anchors = _a.anchors;
152
+ var edges = _this.getNodeEdges(id);
153
+ // 更新边
154
+ edges.sourceEdges.forEach(function (item) {
155
+ var anchorItem = anchors.find(function (anchor) { return anchor.id === item.sourceAnchorId; });
156
+ item.updateStartPoint({
157
+ x: anchorItem.x,
158
+ y: anchorItem.y,
159
+ });
160
+ });
161
+ edges.targetEdges.forEach(function (item) {
162
+ var anchorItem = anchors.find(function (anchor) { return anchor.id === item.targetAnchorId; });
163
+ item.updateEndPoint({
164
+ x: anchorItem.x,
165
+ y: anchorItem.y,
166
+ });
167
+ });
168
+ };
140
169
  // 矩形更新
141
170
  _this.updateRect = function (_a) {
142
171
  var deltaX = _a.deltaX, deltaY = _a.deltaY;
@@ -187,23 +216,8 @@ var Control = /** @class */ (function (_super) {
187
216
  height: _this.nodeModel.height,
188
217
  radius: radius,
189
218
  };
190
- var params = {
191
- point: '',
192
- beforeNode: beforeNode,
193
- afterNode: afterNode,
194
- };
195
219
  // 更新边
196
- var afterPoint;
197
- edges.sourceEdges.forEach(function (item) {
198
- params.point = item.startPoint;
199
- afterPoint = getRectResizeEdgePoint(params);
200
- item.updateStartPoint(afterPoint);
201
- });
202
- edges.targetEdges.forEach(function (item) {
203
- params.point = item.endPoint;
204
- afterPoint = getRectResizeEdgePoint(params);
205
- item.updateEndPoint(afterPoint);
206
- });
220
+ _this.updateEdgePointByAnchors();
207
221
  _this.eventEmit({ beforeNode: beforeNode, afterNode: afterNode });
208
222
  };
209
223
  // 椭圆更新
@@ -252,23 +266,8 @@ var Control = /** @class */ (function (_super) {
252
266
  x: _this.nodeModel.x,
253
267
  y: _this.nodeModel.y,
254
268
  };
255
- var params = {
256
- point: {},
257
- beforeNode: beforeNode,
258
- afterNode: afterNode,
259
- };
260
269
  // 更新边
261
- var afterPoint;
262
- edges.sourceEdges.forEach(function (item) {
263
- params.point = item.startPoint;
264
- afterPoint = getEllipseResizeEdgePoint(params);
265
- item.updateStartPoint(afterPoint);
266
- });
267
- edges.targetEdges.forEach(function (item) {
268
- params.point = item.endPoint;
269
- afterPoint = getEllipseResizeEdgePoint(params);
270
- item.updateEndPoint(afterPoint);
271
- });
270
+ _this.updateEdgePointByAnchors();
272
271
  _this.eventEmit({ beforeNode: __assign(__assign({}, beforeNode), { rx: rx, ry: ry }), afterNode: afterNode });
273
272
  };
274
273
  // 菱形更新
@@ -316,24 +315,8 @@ var Control = /** @class */ (function (_super) {
316
315
  x: _this.nodeModel.x,
317
316
  y: _this.nodeModel.y,
318
317
  };
319
- var params = {
320
- point: {},
321
- beforeNode: beforeNode,
322
- afterNode: afterNode,
323
- };
324
318
  // 更新边
325
- var afterPoint;
326
- var edges = _this.getNodeEdges(id);
327
- edges.sourceEdges.forEach(function (item) {
328
- params.point = item.startPoint;
329
- afterPoint = getDiamondResizeEdgePoint(params);
330
- item.updateStartPoint(afterPoint);
331
- });
332
- edges.targetEdges.forEach(function (item) {
333
- params.point = item.endPoint;
334
- afterPoint = getDiamondResizeEdgePoint(params);
335
- item.updateEndPoint(afterPoint);
336
- });
319
+ _this.updateEdgePointByAnchors();
337
320
  _this.eventEmit({ beforeNode: beforeNode, afterNode: afterNode });
338
321
  };
339
322
  _this.eventEmit = function (_a) {
@@ -365,6 +348,9 @@ var Control = /** @class */ (function (_super) {
365
348
  * 由于将拖拽放大缩小改成丝滑模式,这个时候需要在拖拽结束的时候,将节点的位置更新到grid上.
366
349
  */
367
350
  _this.onDragEnd = function () {
351
+ // 先触发onDragging()->更新边->再触发用户自定义的getDefaultAnchor(),所以onDragging()拿到的anchors是滞后的
352
+ // 为了正确设置最终的位置,应该在拖拽结束的时候,再设置一次边的Point位置,此时拿到的anchors是最新的
353
+ _this.updateEdgePointByAnchors();
368
354
  var _a = _this.graphModel.gridSize, gridSize = _a === void 0 ? 1 : _a;
369
355
  var x = gridSize * Math.round(_this.nodeModel.x / gridSize);
370
356
  var y = gridSize * Math.round(_this.nodeModel.y / gridSize);
@@ -88,7 +88,7 @@ var Control = /** @class */ (function () {
88
88
  };
89
89
  Control.prototype.removeItem = function (key) {
90
90
  var index = this.controlItems.findIndex(function (item) { return item.key === key; });
91
- return this.controlItems.splice(index, 1)[0];
91
+ return index == -1 ? null : this.controlItems.splice(index, 1)[0];
92
92
  };
93
93
  Control.prototype.getControlTool = function () {
94
94
  var _this = this;
@@ -125,7 +125,7 @@ var Menu = /** @class */ (function () {
125
125
  }, true);
126
126
  // 通过事件控制菜单的显示和隐藏
127
127
  this.lf.on('node:contextmenu', function (_a) {
128
- var data = _a.data, position = _a.position;
128
+ var data = _a.data, position = _a.position, e = _a.e;
129
129
  var _b = position.domOverlayPosition, x = _b.x, y = _b.y;
130
130
  var id = data.id;
131
131
  var model = _this.lf.graphModel.getNodeModelById(id);
@@ -142,10 +142,15 @@ var Menu = /** @class */ (function () {
142
142
  menuList = _this.menuTypeMap.get(DefaultNodeMenuKey);
143
143
  }
144
144
  _this.__currentData = data;
145
- _this.showMenu(x, y, menuList);
145
+ _this.showMenu(x, y, menuList, {
146
+ width: model.width,
147
+ height: model.height,
148
+ clientX: e.clientX,
149
+ clientY: e.clientY,
150
+ });
146
151
  });
147
152
  this.lf.on('edge:contextmenu', function (_a) {
148
- var data = _a.data, position = _a.position;
153
+ var data = _a.data, position = _a.position, e = _a.e;
149
154
  var _b = position.domOverlayPosition, x = _b.x, y = _b.y;
150
155
  var id = data.id;
151
156
  var model = _this.lf.graphModel.getEdgeModelById(id);
@@ -162,7 +167,12 @@ var Menu = /** @class */ (function () {
162
167
  menuList = _this.menuTypeMap.get(DefaultEdgeMenuKey);
163
168
  }
164
169
  _this.__currentData = data;
165
- _this.showMenu(x, y, menuList);
170
+ _this.showMenu(x, y, menuList, {
171
+ width: model.width,
172
+ height: model.height,
173
+ clientX: e.clientX,
174
+ clientY: e.clientY,
175
+ });
166
176
  });
167
177
  this.lf.on('blank:contextmenu', function (_a) {
168
178
  var position = _a.position;
@@ -192,7 +202,7 @@ var Menu = /** @class */ (function () {
192
202
  (_a = this === null || this === void 0 ? void 0 : this.__container) === null || _a === void 0 ? void 0 : _a.removeChild(this.__menuDOM);
193
203
  this.__menuDOM = null;
194
204
  };
195
- Menu.prototype.showMenu = function (x, y, menuList) {
205
+ Menu.prototype.showMenu = function (x, y, menuList, options) {
196
206
  if (!menuList || !menuList.length)
197
207
  return;
198
208
  var menu = this.__menuDOM;
@@ -203,8 +213,67 @@ var Menu = /** @class */ (function () {
203
213
  if (!menu.children.length)
204
214
  return;
205
215
  menu.style.display = 'block';
206
- menu.style.top = y + "px";
207
- menu.style.left = x + "px";
216
+ if (!options) {
217
+ menu.style.top = y + "px";
218
+ menu.style.left = x + "px";
219
+ return;
220
+ }
221
+ // https://github.com/didi/LogicFlow/issues/1019
222
+ // 根据边界判断菜单的left 和 top
223
+ var width = options.width, height = options.height, clientX = options.clientX, clientY = options.clientY;
224
+ var graphModel = this.lf.graphModel;
225
+ var menuWidth = menu.offsetWidth;
226
+ var menuIsRightShow = true;
227
+ // ======先进行可视屏幕范围的判断=======
228
+ // 浏览器窗口可视区域兼容性写法
229
+ // eslint-disable-next-line max-len
230
+ var windowMaxX = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
231
+ var rightDistance = windowMaxX - clientX;
232
+ // ======先进行可视屏幕范围的判断=======
233
+ // ========再进行画布范围的判断========
234
+ var graphRect = graphModel.rootEl.getBoundingClientRect();
235
+ var graphMaxX = graphRect.left + graphRect.width;
236
+ if (graphMaxX < windowMaxX) {
237
+ // 画布右边小于可视屏幕范围的最右边,取画布右边作为极限值,计算出当前触摸点距离右边极限值的距离
238
+ rightDistance = graphMaxX - clientX;
239
+ }
240
+ // ========再进行画布范围的判断========
241
+ // 根据当前触摸点距离右边的距离 跟 menuWidth进行比较
242
+ if (rightDistance < menuWidth) {
243
+ // 空间不足够,显示在左边
244
+ menuIsRightShow = false;
245
+ }
246
+ if (menuIsRightShow) {
247
+ menu.style.left = x + "px";
248
+ }
249
+ else {
250
+ menu.style.left = (x - width) + "px";
251
+ }
252
+ var menuHeight = menu.offsetHeight;
253
+ var menuIsBottomShow = true;
254
+ // ======先进行可视屏幕范围的判断=======
255
+ // 浏览器窗口可视区域兼容性写法
256
+ // eslint-disable-next-line max-len
257
+ var windowMaxY = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
258
+ var bottomDistance = windowMaxY - clientY;
259
+ // ======先进行可视屏幕范围的判断=======
260
+ // ========再进行画布范围的判断========
261
+ var graphMaxY = graphRect.top + graphRect.height;
262
+ if (graphMaxY < windowMaxY) {
263
+ // 画布底部小于可视屏幕范围的最底边,取画布底部作为极限值,计算出当前触摸点距离底部极限值的距离
264
+ bottomDistance = graphMaxY - clientY;
265
+ }
266
+ // ========再进行画布范围的判断========
267
+ if (bottomDistance < menuHeight) {
268
+ // 如果下边距离太小,无法显示menu,则向上显示
269
+ menuIsBottomShow = false;
270
+ }
271
+ if (menuIsBottomShow) {
272
+ menu.style.top = y + "px";
273
+ }
274
+ else {
275
+ menu.style.top = (y - height) + "px";
276
+ }
208
277
  };
209
278
  /**
210
279
  * 设置指定类型元素的菜单
@@ -32,6 +32,10 @@ declare class GroupNodeModel extends RectResize.model {
32
32
  isFolded: boolean;
33
33
  unfoldedWidth: number;
34
34
  unfoldedHight: number;
35
+ /**
36
+ * children元素上一次折叠的状态缓存
37
+ */
38
+ childrenLastFoldStatus: Record<string, boolean>;
35
39
  initNodeData(data: any): void;
36
40
  getResizeOutlineStyle(): {
37
41
  fill: string;
@@ -54,6 +54,10 @@ var GroupNodeModel = /** @class */ (function (_super) {
54
54
  _this.isGroup = true;
55
55
  _this.unfoldedWidth = defaultWidth;
56
56
  _this.unfoldedHight = defaultHeight;
57
+ /**
58
+ * children元素上一次折叠的状态缓存
59
+ */
60
+ _this.childrenLastFoldStatus = {};
57
61
  return _this;
58
62
  }
59
63
  GroupNodeModel.prototype.initNodeData = function (data) {
@@ -101,6 +105,11 @@ var GroupNodeModel = /** @class */ (function (_super) {
101
105
  */
102
106
  GroupNodeModel.prototype.foldGroup = function (isFolded) {
103
107
  var _this = this;
108
+ if (isFolded === this.isFolded) {
109
+ // 防止多次调用同样的状态设置
110
+ // 如果this.isFolded=false,同时触发foldGroup(false),会导致下面的childrenLastFoldStatus状态错乱
111
+ return;
112
+ }
104
113
  this.setProperty('isFolded', isFolded);
105
114
  this.isFolded = isFolded;
106
115
  // step 1
@@ -122,10 +131,26 @@ var GroupNodeModel = /** @class */ (function (_super) {
122
131
  var allEdges = this.incoming.edges.concat(this.outgoing.edges);
123
132
  this.children.forEach(function (elementId) {
124
133
  var nodeModel = _this.graphModel.getElement(elementId);
134
+ var foldStatus = nodeModel.isFolded;
125
135
  // FIX: https://github.com/didi/LogicFlow/issues/1007
126
136
  if (nodeModel.isGroup && !nodeModel.isFolded) {
137
+ // 正常情况下,parent折叠后,children应该折叠
138
+ // 因此当parent准备展开时,children的值目前肯定是折叠状态,也就是nodeModel.isFolded=true,这个代码块不会触发
139
+ // 只有当parent准备折叠时,children目前状态才有可能是展开,即nodeModel.isFolded=false,这个代码块触发,此时isFolded=true,触发children也进行折叠
127
140
  nodeModel.foldGroup(isFolded);
128
141
  }
142
+ if (nodeModel.isGroup && !isFolded) {
143
+ // 当parent准备展开时,children的值应该恢复到折叠前的状态
144
+ var lastFoldStatus = _this.childrenLastFoldStatus[elementId];
145
+ if (lastFoldStatus !== undefined && lastFoldStatus !== nodeModel.isFolded) {
146
+ // https://github.com/didi/LogicFlow/issues/1145
147
+ // 当parent准备展开时,children的值肯定是折叠,也就是nodeModel.isFolded=true
148
+ // 当parent准备展开时,如果children之前的状态是展开,则恢复展开状态
149
+ nodeModel.foldGroup(lastFoldStatus);
150
+ }
151
+ }
152
+ // 存储parent触发children改变折叠状态前的状态
153
+ _this.childrenLastFoldStatus[elementId] = foldStatus;
129
154
  nodeModel.visible = !isFolded;
130
155
  allEdges = allEdges.concat(nodeModel.incoming.edges.concat(nodeModel.outgoing.edges));
131
156
  });
@@ -151,7 +151,7 @@ var Snapshot = /** @class */ (function () {
151
151
  var transformModel = graphModel.transformModel;
152
152
  var SCALE_X = transformModel.SCALE_X, SCALE_Y = transformModel.SCALE_Y, TRANSLATE_X = transformModel.TRANSLATE_X, TRANSLATE_Y = transformModel.TRANSLATE_Y;
153
153
  // offset值加10,保证图形不会紧贴着下载图片的左边和上边
154
- copy.lastChild.style.transform = "matrix(1, 0, 0, 1, " + (-offsetX + 10 + TRANSLATE_X) + ", " + (-offsetY + 10 + TRANSLATE_Y) + ")";
154
+ copy.lastChild.style.transform = "matrix(1, 0, 0, 1, " + ((-offsetX + TRANSLATE_X) * (1 / SCALE_X) + 10) + ", " + ((-offsetY + TRANSLATE_Y) * (1 / SCALE_Y) + 10) + ")";
155
155
  var bboxWidth = Math.ceil(bbox.width / SCALE_X);
156
156
  var bboxHeight = Math.ceil(bbox.height / SCALE_Y);
157
157
  // width,height 值加40,保证图形不会紧贴着下载图片的右边和下边