@logicflow/extension 2.0.0-beta.2 → 2.0.0-beta.4

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 (44) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/dist/index.min.js +2 -2
  3. package/es/components/menu/index.d.ts +1 -1
  4. package/es/components/menu/index.js +9 -10
  5. package/es/components/menu/index.js.map +1 -1
  6. package/es/index.d.ts +1 -0
  7. package/es/index.js +1 -0
  8. package/es/index.js.map +1 -1
  9. package/es/insert-node-in-polyline/index.js +3 -3
  10. package/es/insert-node-in-polyline/index.js.map +1 -1
  11. package/es/materials/node-selection/index.d.ts +2 -1
  12. package/es/materials/node-selection/index.js +64 -56
  13. package/es/materials/node-selection/index.js.map +1 -1
  14. package/es/tools/snapshot/index.d.ts +101 -11
  15. package/es/tools/snapshot/index.js +331 -147
  16. package/es/tools/snapshot/index.js.map +1 -1
  17. package/es/tools/snapshot/utils.d.ts +35 -0
  18. package/es/tools/snapshot/utils.js +238 -0
  19. package/es/tools/snapshot/utils.js.map +1 -0
  20. package/lib/components/menu/index.d.ts +1 -1
  21. package/lib/components/menu/index.js +9 -10
  22. package/lib/components/menu/index.js.map +1 -1
  23. package/lib/index.d.ts +1 -0
  24. package/lib/index.js +1 -0
  25. package/lib/index.js.map +1 -1
  26. package/lib/insert-node-in-polyline/index.js +2 -2
  27. package/lib/insert-node-in-polyline/index.js.map +1 -1
  28. package/lib/materials/node-selection/index.d.ts +2 -1
  29. package/lib/materials/node-selection/index.js +63 -55
  30. package/lib/materials/node-selection/index.js.map +1 -1
  31. package/lib/tools/snapshot/index.d.ts +101 -11
  32. package/lib/tools/snapshot/index.js +331 -147
  33. package/lib/tools/snapshot/index.js.map +1 -1
  34. package/lib/tools/snapshot/utils.d.ts +35 -0
  35. package/lib/tools/snapshot/utils.js +247 -0
  36. package/lib/tools/snapshot/utils.js.map +1 -0
  37. package/package.json +7 -4
  38. package/src/components/menu/index.ts +16 -13
  39. package/src/index.ts +1 -0
  40. package/src/insert-node-in-polyline/index.ts +3 -3
  41. package/src/materials/node-selection/index.ts +72 -69
  42. package/src/tools/snapshot/README.md +130 -5
  43. package/src/tools/snapshot/index.ts +264 -98
  44. package/src/tools/snapshot/utils.ts +163 -0
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __read = (this && this.__read) || function (o, n) {
39
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
40
+ if (!m) return o;
41
+ var i = m.call(o), r, ar = [], e;
42
+ try {
43
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
44
+ }
45
+ catch (error) { e = { error: error }; }
46
+ finally {
47
+ try {
48
+ if (r && !r.done && (m = i["return"])) m.call(i);
49
+ }
50
+ finally { if (e) throw e.error; }
51
+ }
52
+ return ar;
53
+ };
54
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
55
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
56
+ if (ar || !(i in from)) {
57
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
58
+ ar[i] = from[i];
59
+ }
60
+ }
61
+ return to.concat(ar || Array.prototype.slice.call(from));
62
+ };
63
+ Object.defineProperty(exports, "__esModule", { value: true });
64
+ exports.copyCanvas = exports.updateImageSource = exports.updateBackgroundImageWithBase64Image = exports.updateImageSrcOrHrefWithBase64Image = exports.convertImageToBase64 = exports.isTextPlainBase64 = void 0;
65
+ /**
66
+ * 图片缓存, 已请求过的图片直接从缓存中获取
67
+ */
68
+ var imageCache = {};
69
+ /**
70
+ * 当获取图片失败时会返回失败信息,是 text/plain 类型的数据
71
+ * @param str - 图片内容
72
+ * @returns
73
+ */
74
+ function isTextPlainBase64(str) {
75
+ return str.startsWith('data:text/plain');
76
+ }
77
+ exports.isTextPlainBase64 = isTextPlainBase64;
78
+ /**
79
+ * 将网络图片转为 base64
80
+ * @param url - 图片地址
81
+ * @returns
82
+ */
83
+ function convertImageToBase64(url) {
84
+ return __awaiter(this, void 0, void 0, function () {
85
+ return __generator(this, function (_a) {
86
+ if (imageCache[url]) {
87
+ return [2 /*return*/, imageCache[url]];
88
+ }
89
+ return [2 /*return*/, new Promise(function (resolve, reject) {
90
+ try {
91
+ fetch(url)
92
+ .then(function (response) { return response.blob(); })
93
+ .then(function (blob) {
94
+ var reader = new FileReader();
95
+ reader.onloadend = function () {
96
+ resolve((imageCache[url] = reader.result));
97
+ };
98
+ reader.onerror = reject;
99
+ reader.readAsDataURL(blob);
100
+ })
101
+ .catch(function () {
102
+ resolve((imageCache[url] = url));
103
+ });
104
+ }
105
+ catch (error) {
106
+ // 如果转换失败,后续大概率仍然会失败,因此直接缓存
107
+ return (imageCache[url] = url);
108
+ }
109
+ })];
110
+ });
111
+ });
112
+ }
113
+ exports.convertImageToBase64 = convertImageToBase64;
114
+ /**
115
+ * 使用 base64 的图片替换 img 标签的 src 或 image 标签的 href
116
+ * @param node - html 节点或 svg 节点
117
+ */
118
+ function updateImageSrcOrHrefWithBase64Image(node, attrName) {
119
+ return __awaiter(this, void 0, void 0, function () {
120
+ var url, base64Image, error_1;
121
+ return __generator(this, function (_a) {
122
+ switch (_a.label) {
123
+ case 0:
124
+ _a.trys.push([0, 2, , 3]);
125
+ url = node.getAttribute(attrName) || '';
126
+ // 已经是 base64 图片,不需要处理
127
+ if (url.startsWith('data:')) {
128
+ return [2 /*return*/];
129
+ }
130
+ return [4 /*yield*/, convertImageToBase64(url)];
131
+ case 1:
132
+ base64Image = _a.sent();
133
+ if (isTextPlainBase64(base64Image)) {
134
+ return [2 /*return*/];
135
+ }
136
+ node.setAttribute(attrName, base64Image);
137
+ return [3 /*break*/, 3];
138
+ case 2:
139
+ error_1 = _a.sent();
140
+ console.error(error_1);
141
+ return [3 /*break*/, 3];
142
+ case 3: return [2 /*return*/];
143
+ }
144
+ });
145
+ });
146
+ }
147
+ exports.updateImageSrcOrHrefWithBase64Image = updateImageSrcOrHrefWithBase64Image;
148
+ /**
149
+ * 使用 base64 的图片替换背景图片
150
+ * @param node - html 节点
151
+ * @param styleAttr - 样式属性名称
152
+ */
153
+ function updateBackgroundImageWithBase64Image(node, url) {
154
+ return __awaiter(this, void 0, void 0, function () {
155
+ var base64Image, error_2;
156
+ return __generator(this, function (_a) {
157
+ switch (_a.label) {
158
+ case 0:
159
+ _a.trys.push([0, 2, , 3]);
160
+ // 已经是 base64 图片,不需要处理
161
+ if (url.startsWith('data:')) {
162
+ return [2 /*return*/];
163
+ }
164
+ return [4 /*yield*/, convertImageToBase64(url)];
165
+ case 1:
166
+ base64Image = _a.sent();
167
+ if (isTextPlainBase64(base64Image)) {
168
+ return [2 /*return*/];
169
+ }
170
+ node.style.backgroundImage = "url(".concat(base64Image, ")");
171
+ return [3 /*break*/, 3];
172
+ case 2:
173
+ error_2 = _a.sent();
174
+ console.error(error_2);
175
+ return [3 /*break*/, 3];
176
+ case 3: return [2 /*return*/];
177
+ }
178
+ });
179
+ });
180
+ }
181
+ exports.updateBackgroundImageWithBase64Image = updateBackgroundImageWithBase64Image;
182
+ /**
183
+ * 更新图片数据
184
+ * @param node - 节点
185
+ */
186
+ function updateImageSource(node) {
187
+ return __awaiter(this, void 0, void 0, function () {
188
+ var nodes, nodePtr, promises, _a, background, backgroundImage, backgroundUrlMatch, imageUrl, backgroundImageUrlMatch, imageUrl;
189
+ return __generator(this, function (_b) {
190
+ switch (_b.label) {
191
+ case 0:
192
+ nodes = [node];
193
+ promises = [];
194
+ while (nodes.length) {
195
+ nodePtr = nodes.shift();
196
+ if (nodePtr.children.length) {
197
+ nodes.push.apply(nodes, __spreadArray([], __read(nodePtr.children), false));
198
+ }
199
+ if (nodePtr instanceof HTMLElement) {
200
+ _a = nodePtr.style, background = _a.background, backgroundImage = _a.backgroundImage;
201
+ backgroundUrlMatch = background.match(/url\(["']?(.*?)["']?\)/);
202
+ if (backgroundUrlMatch && backgroundUrlMatch[1]) {
203
+ imageUrl = backgroundUrlMatch[1];
204
+ promises.push(updateBackgroundImageWithBase64Image(nodePtr, imageUrl));
205
+ }
206
+ backgroundImageUrlMatch = backgroundImage.match(/url\(["']?(.*?)["']?\)/);
207
+ if (backgroundImageUrlMatch && backgroundImageUrlMatch[1]) {
208
+ imageUrl = backgroundImageUrlMatch[1];
209
+ promises.push(updateBackgroundImageWithBase64Image(nodePtr, imageUrl));
210
+ }
211
+ }
212
+ // 如果有 img 和 image 标签,尝试将 src 和 href 替换为 base64 图片
213
+ if (nodePtr instanceof HTMLImageElement) {
214
+ promises.push(updateImageSrcOrHrefWithBase64Image(nodePtr, 'src'));
215
+ }
216
+ else if (nodePtr instanceof SVGImageElement) {
217
+ promises.push(updateImageSrcOrHrefWithBase64Image(nodePtr, 'href'));
218
+ }
219
+ }
220
+ return [4 /*yield*/, Promise.all(promises)];
221
+ case 1:
222
+ _b.sent();
223
+ return [2 /*return*/];
224
+ }
225
+ });
226
+ });
227
+ }
228
+ exports.updateImageSource = updateImageSource;
229
+ /**
230
+ * 重新复制canvas 用于在不裁剪原canvas的基础上通过拉伸方式达到自定义宽高目的
231
+ * @param originCanvas HTMLCanvasElement
232
+ * @param targetWidth number
233
+ * @param targetHeight number
234
+ */
235
+ function copyCanvas(originCanvas, targetWidth, targetHeight) {
236
+ var newCanvas = document.createElement('canvas');
237
+ newCanvas.width = targetWidth;
238
+ newCanvas.height = targetHeight;
239
+ var newCtx = newCanvas.getContext('2d');
240
+ if (newCtx) {
241
+ // 注意: 自定义宽高时,可能会拉伸图形,这时候padding也会被拉伸导致不准确
242
+ newCtx.drawImage(originCanvas, 0, 0, originCanvas.width, originCanvas.height, 0, 0, targetWidth, targetHeight);
243
+ }
244
+ return newCanvas;
245
+ }
246
+ exports.copyCanvas = copyCanvas;
247
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/tools/snapshot/utils.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logicflow/extension",
3
- "version": "2.0.0-beta.2",
3
+ "version": "2.0.0-beta.4",
4
4
  "description": "LogicFlow Extensions",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.js",
@@ -20,12 +20,15 @@
20
20
  "author": "Logicflow-Team",
21
21
  "license": "Apache-2.0",
22
22
  "peerDependencies": {
23
- "@logicflow/core": "2.0.0-beta.2"
23
+ "@logicflow/core": "2.0.0-beta.4"
24
24
  },
25
25
  "dependencies": {
26
+ "lodash-es": "^4.17.21",
26
27
  "@antv/hierarchy": "^0.6.11",
27
- "preact": "^10.17.1",
28
- "@logicflow/core": "2.0.0-beta.2"
28
+ "@logicflow/core": "2.0.0-beta.4"
29
+ },
30
+ "devDependencies": {
31
+ "preact": "^10.17.1"
29
32
  },
30
33
  "scripts": {
31
34
  "clean:turbo": "rss",
@@ -1,5 +1,9 @@
1
1
  import LogicFlow from '@logicflow/core'
2
2
 
3
+ import GraphData = LogicFlow.GraphData
4
+ import NodeData = LogicFlow.NodeData
5
+ import EdgeData = LogicFlow.EdgeData
6
+
3
7
  type SetType = 'add' | 'reset'
4
8
 
5
9
  export type MenuItem = {
@@ -25,7 +29,7 @@ class Menu {
25
29
  private __container?: HTMLElement
26
30
  private __menuDOM?: HTMLElement
27
31
  private menuTypeMap?: Map<string, MenuItem[]>
28
- private __currentData: any
32
+ private __currentData: EdgeData | NodeData | GraphData | null = null
29
33
  static pluginName = 'menu'
30
34
 
31
35
  constructor({ lf }) {
@@ -107,7 +111,7 @@ class Menu {
107
111
  this.menuTypeMap?.set(DefaultSelectionMenuKey, DefaultSelectionMenu)
108
112
  }
109
113
 
110
- render(lf, container) {
114
+ render(lf: LogicFlow, container: HTMLElement) {
111
115
  if (lf.options.isSilentMode) return
112
116
  this.__container = container
113
117
  this.__currentData = null // 当前展示的菜单所属元素的model数据
@@ -147,7 +151,7 @@ class Menu {
147
151
  )
148
152
  }
149
153
  // 通过事件控制菜单的显示和隐藏
150
- this.lf.on('node:contextmenu', ({ data, position, e }: any) => {
154
+ this.lf.on('node:contextmenu', ({ data, position, e }) => {
151
155
  const {
152
156
  domOverlayPosition: { x, y },
153
157
  } = position
@@ -157,14 +161,14 @@ class Menu {
157
161
  if (!model) return
158
162
  let menuList: any = []
159
163
  const typeMenus = this.menuTypeMap?.get(model.type)
160
- // 如果单个节点自定义了节点,以单个节点自定义为准
164
+ // 1.如果单个节点自定义了菜单,以单个节点自定义为准
161
165
  if (model && model.menu && Array.isArray(model.menu)) {
162
166
  menuList = model.menu
163
167
  } else if (typeMenus) {
164
- // 如果定义当前节点类型的元素
168
+ // 2.如果当前节点类型定义了菜单,再取该配置
165
169
  menuList = typeMenus
166
170
  } else {
167
- // 最后取全局默认
171
+ // 3.最后取全局默认
168
172
  menuList = this.menuTypeMap?.get(DefaultNodeMenuKey)
169
173
  }
170
174
  this.__currentData = data
@@ -175,7 +179,7 @@ class Menu {
175
179
  clientY: e.clientY,
176
180
  })
177
181
  })
178
- this.lf.on('edge:contextmenu', ({ data, position, e }: any) => {
182
+ this.lf.on('edge:contextmenu', ({ data, position, e }) => {
179
183
  const {
180
184
  domOverlayPosition: { x, y },
181
185
  } = position
@@ -183,15 +187,13 @@ class Menu {
183
187
  const model = this.lf.graphModel.getEdgeModelById(id)
184
188
  if (!model) return
185
189
  let menuList: any = []
186
- const typeMenus = this.menuTypeMap?.get(model.type) ?? []
187
- // 如果单个节点自定义了边
190
+ const typeMenus = this.menuTypeMap?.get(model.type)
191
+ // 菜单优先级: model.menu > typeMenus > defaultEdgeMenu,注释同上节点
188
192
  if (model && model.menu && Array.isArray(model.menu)) {
189
193
  menuList = model.menu
190
194
  } else if (typeMenus) {
191
- // 如果定义当前边类型的元素
192
195
  menuList = typeMenus
193
196
  } else {
194
- // 最后取全局默认
195
197
  menuList = this.menuTypeMap?.get(DefaultEdgeMenuKey) ?? []
196
198
  }
197
199
  this.__currentData = data
@@ -202,14 +204,14 @@ class Menu {
202
204
  clientY: e.clientY,
203
205
  })
204
206
  })
205
- this.lf.on('blank:contextmenu', ({ position }: any) => {
207
+ this.lf.on('blank:contextmenu', ({ position }) => {
206
208
  const menuList = this.menuTypeMap?.get(DefaultGraphMenuKey) ?? []
207
209
  const {
208
210
  domOverlayPosition: { x, y },
209
211
  } = position
210
212
  this.showMenu(x, y, menuList)
211
213
  })
212
- this.lf.on('selection:contextmenu', ({ data, position }: any) => {
214
+ this.lf.on('selection:contextmenu', ({ data, position }) => {
213
215
  const menuList = this.menuTypeMap?.get(DefaultSelectionMenuKey)
214
216
  const {
215
217
  domOverlayPosition: { x, y },
@@ -217,6 +219,7 @@ class Menu {
217
219
  this.__currentData = data
218
220
  this.showMenu(x, y, menuList)
219
221
  })
222
+
220
223
  this.lf.on('node:mousedown', () => {
221
224
  this.__menuDOM!.style.display = 'none'
222
225
  })
package/src/index.ts CHANGED
@@ -14,6 +14,7 @@ export * from './components/selection-select'
14
14
  export * from './components/mini-map'
15
15
  export * from './materials/curved-edge'
16
16
  export * from './materials/group'
17
+ export * from './materials/node-selection'
17
18
  export * from './NodeResize'
18
19
  export * from './tools/flow-path'
19
20
  export * from './tools/auto-layout'
@@ -2,7 +2,7 @@ import LogicFlow, {
2
2
  BaseNodeModel,
3
3
  PolylineEdgeModel,
4
4
  EventType,
5
- formateAnchorConnectValidateData,
5
+ formatAnchorConnectValidateData,
6
6
  } from '@logicflow/core'
7
7
  import { cloneDeep } from 'lodash-es'
8
8
  import { isNodeInSegment } from './edge'
@@ -85,9 +85,9 @@ export class InsertNodeInPolyline {
85
85
  )
86
86
 
87
87
  const { isAllPass: isSourcePass, msg: sourceMsg } =
88
- formateAnchorConnectValidateData(sourceRuleResultData)
88
+ formatAnchorConnectValidateData(sourceRuleResultData)
89
89
  const { isAllPass: isTargetPass, msg: targetMsg } =
90
- formateAnchorConnectValidateData(targetRuleResultData)
90
+ formatAnchorConnectValidateData(targetRuleResultData)
91
91
 
92
92
  return {
93
93
  isPass: isSourcePass && isTargetPass,
@@ -2,17 +2,6 @@ import { get } from 'lodash-es'
2
2
  import { h, PolygonNode, PolygonNodeModel } from '@logicflow/core'
3
3
 
4
4
  class NodeSelectionView extends PolygonNode {
5
- d = 10
6
-
7
- getShapeStyle() {
8
- // 设置边框为虚线
9
- const style = this.props.model.getNodeStyle()
10
- // @ts-ignore
11
- style.strokeDashArray = '10 5'
12
-
13
- return style
14
- }
15
-
16
5
  getLabelShape(): h.JSX.Element {
17
6
  const { id, x, y, width, height, properties } = this.props.model
18
7
  const style = this.props.model.getNodeStyle()
@@ -21,38 +10,38 @@ class NodeSelectionView extends PolygonNode {
21
10
  {
22
11
  x: x - width / 2,
23
12
  y: y - height / 2,
13
+ width: 50,
14
+ height: 24,
24
15
  style: 'z-index: 0; background: none; overflow: auto;',
25
16
  },
26
- properties.labelText
27
- ? h(
28
- 'text',
29
- {
30
- x: 0,
31
- y: -5,
32
- width: 50,
33
- height: 24,
34
- fontSize: '16px',
35
- fill: style.stroke,
36
- },
37
- '方案',
38
- )
39
- : '',
40
- properties.disabledDelete
41
- ? ''
42
- : h(
43
- 'text',
44
- {
45
- x: properties.labelText ? 50 : 0,
46
- y: -5,
47
- width: 50,
48
- height: 24,
49
- fontSize: '24px',
50
- cursor: 'pointer',
51
- fill: style.stroke,
52
- onclick: this.handleCustomDeleteIconClick.bind(this, id),
53
- },
54
- 'x',
55
- ),
17
+ [
18
+ properties.labelText
19
+ ? h(
20
+ 'text',
21
+ {
22
+ x: 0,
23
+ y: -5,
24
+ fontSize: '16px',
25
+ fill: style.stroke,
26
+ },
27
+ properties.labelText,
28
+ )
29
+ : '',
30
+ properties.disabledDelete
31
+ ? ''
32
+ : h(
33
+ 'text',
34
+ {
35
+ x: properties.labelText ? 50 : 0,
36
+ y: -5,
37
+ fontSize: '24px',
38
+ cursor: 'pointer',
39
+ fill: style.stroke,
40
+ onclick: this.handleCustomDeleteIconClick.bind(this, id),
41
+ },
42
+ 'x',
43
+ ),
44
+ ],
56
45
  )
57
46
  }
58
47
 
@@ -72,7 +61,7 @@ class NodeSelectionView extends PolygonNode {
72
61
  this.getLabelShape(),
73
62
  ])
74
63
  }
75
-
64
+ // 避免点击时,该节点置于最高层,挡住内部节点
76
65
  toFront() {}
77
66
 
78
67
  /**
@@ -88,36 +77,46 @@ class NodeSelectionView extends PolygonNode {
88
77
  class NodeSelectionModel extends PolygonNodeModel {
89
78
  d = 10
90
79
 
91
- setAttributes() {
92
- // 默认不显示
93
- this.points = []
94
-
95
- this.text = {
80
+ initNodeData(data) {
81
+ data.text = {
96
82
  value: '',
97
- x: 0,
98
- y: 0,
83
+ x: data.x,
84
+ y: data.y,
99
85
  draggable: false,
100
86
  editable: false,
101
87
  }
102
- this.stroke = this.properties.active_color || '#008000'
88
+ super.initNodeData(data)
103
89
  this.zIndex = 0
104
- this.draggable = false
105
- this.anchorsOffset = [[0, 0]]
90
+ this.draggable = true
91
+ }
106
92
 
107
- // TODO: 确认此处为何使用 setTimeout, 是初始化时该设置未生效吗?
108
- if ((this.properties.node_selection_ids as string[]).length > 1) {
93
+ setAttributes() {
94
+ // 默认不显示
95
+ this.points = []
96
+
97
+ // 图render的时候,会把所有nodes数据实例化,全部实例化完成后,放到nodesMap里。
98
+ // 节点的setAttributes在实例化的时候执行第一次
99
+ // updatePointsByNodes中的getNodeModelById方法,是从nodesMap取的数据,第一次就拿不到,所以要加setTimeout
100
+ if ((this.properties?.node_selection_ids as string[]).length > 1) {
109
101
  setTimeout(() => {
110
- this.updatePointsByNodes(this.properties.node_selection_ids)
102
+ this.updatePointsByNodes(this.properties?.node_selection_ids || [])
111
103
  })
112
104
  }
113
105
  }
114
106
 
107
+ getNodeStyle() {
108
+ const style = super.getNodeStyle()
109
+ style.stroke = this.properties.strokeColor || '#008000'
110
+ style.strokeDasharray = '10 5'
111
+ return style
112
+ }
113
+
115
114
  getDefaultAnchor() {
116
115
  return []
117
116
  }
118
117
 
119
118
  /**
120
- * 更新points
119
+ * 更新points - 多边形顶点坐标集合
121
120
  * @param points
122
121
  */
123
122
  updatePoints(points) {
@@ -125,7 +124,7 @@ class NodeSelectionModel extends PolygonNodeModel {
125
124
  }
126
125
 
127
126
  /**
128
- * 更新x y
127
+ * 更新x y - 多边形中点坐标
129
128
  */
130
129
  updateCoordinate({ x, y }) {
131
130
  this.x = x
@@ -133,10 +132,9 @@ class NodeSelectionModel extends PolygonNodeModel {
133
132
  }
134
133
 
135
134
  /**
136
- * 更新points
135
+ * 计算新的 points 和 x y
137
136
  */
138
137
  updatePointsByNodes(nodesIds) {
139
- // TODO: 临时方案矩形
140
138
  const points: [number, number][] = []
141
139
  let minX = Infinity
142
140
  let minY = Infinity
@@ -164,7 +162,7 @@ class NodeSelectionModel extends PolygonNodeModel {
164
162
  }
165
163
  }
166
164
 
167
- export class NodeSelection {
165
+ class NodeSelection {
168
166
  static pluginName = 'node-selection'
169
167
  lf // lf 实例
170
168
  selectNodes: any[] = [] // 选择的nodes
@@ -237,26 +235,20 @@ export class NodeSelection {
237
235
 
238
236
  lf.on('node:click', (val) => {
239
237
  if (!val.e.shiftKey || val.data.type === 'node-selection') return
240
-
241
238
  this.currentClickNode = val.data
242
239
 
243
240
  // 如果selectNodesIds中已存在此节点,则取消选中此节点
244
- let isUnSelected = false
241
+ let hasExists = false
245
242
  if (this.selectNodesIds.includes(val.data.id)) {
246
243
  this.lf.getNodeModelById(val.data.id).setSelected(false)
247
- isUnSelected = true
244
+ hasExists = true
248
245
  }
249
246
 
250
247
  // 获取所有被选中的节点,获取到的数组是无序的
251
248
  const { nodes } = lf.getSelectElements(true)
252
- // 使用插件时判断是否允许使用node-selection
253
- if (lf.disableNodeSelection && lf.disableNodeSelection(nodes)) {
254
- return
255
- }
256
249
  this.selectNodes = nodes
257
-
258
250
  if (this.selectNodes.length === 1) {
259
- if (!isUnSelected) {
251
+ if (!hasExists) {
260
252
  this.addNodeSelection()
261
253
  } else {
262
254
  this.updateNodeSelection()
@@ -265,7 +257,18 @@ export class NodeSelection {
265
257
  this.updateNodeSelection()
266
258
  }
267
259
  })
260
+ lf.graphModel.addNodeMoveRules((model, deltaX, deltaY) => {
261
+ if (model.type === 'node-selection') {
262
+ // 如果移动的是分组,那么分组的子节点也跟着移动。
263
+ const nodeIds = model.properties.node_selection_ids
264
+ lf.graphModel.moveNodes(nodeIds, deltaX, deltaY, true)
265
+ return true
266
+ }
267
+ return true
268
+ })
268
269
  }
269
270
  }
270
271
 
271
272
  export default NodeSelection
273
+
274
+ export { NodeSelection }