@flowgram-vue/document 0.2.0
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/LICENSE +22 -0
- package/dist/index.cjs +3233 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +1728 -0
- package/dist/index.js +3199 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
- package/src/datas/flow-node-render-data.ts +227 -0
- package/src/datas/flow-node-transform-data.ts +337 -0
- package/src/datas/flow-node-transition-data.ts +182 -0
- package/src/datas/index.ts +8 -0
- package/src/entities/flow-document-transformer-entity.ts +125 -0
- package/src/entities/flow-node-entity.ts +415 -0
- package/src/entities/flow-renderer-state-entity.ts +127 -0
- package/src/entities/index.ts +8 -0
- package/src/flow-document-config.ts +42 -0
- package/src/flow-document-container-module.ts +33 -0
- package/src/flow-document-contribution.ts +22 -0
- package/src/flow-document-options.ts +79 -0
- package/src/flow-document.ts +766 -0
- package/src/flow-render-tree.ts +236 -0
- package/src/flow-virtual-tree.ts +244 -0
- package/src/index.ts +16 -0
- package/src/layout/horizontal-fixed-layout.ts +198 -0
- package/src/layout/index.ts +7 -0
- package/src/layout/vertical-fixed-layout.ts +198 -0
- package/src/services/flow-drag-service.ts +211 -0
- package/src/services/flow-group-service/flow-group-controller.ts +120 -0
- package/src/services/flow-group-service/flow-group-service.ts +107 -0
- package/src/services/flow-group-service/flow-group-utils.ts +104 -0
- package/src/services/flow-group-service/index.ts +7 -0
- package/src/services/flow-operation-base-service.ts +333 -0
- package/src/services/index.ts +8 -0
- package/src/typings/flow-group.ts +8 -0
- package/src/typings/flow-layout.ts +72 -0
- package/src/typings/flow-node-register.ts +356 -0
- package/src/typings/flow-operation.ts +312 -0
- package/src/typings/flow-transition.ts +104 -0
- package/src/typings/flow.ts +70 -0
- package/src/typings/index.ts +11 -0
- package/src/utils/get-default-spacing.ts +21 -0
- package/src/utils/index.ts +6 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Event, type Rectangle } from '@flowgram-vue/utils';
|
|
7
|
+
import { Entity, type EntityOpts } from '@flowgram-vue/core';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
FlowLayoutDefault,
|
|
11
|
+
FlowNodeJSON,
|
|
12
|
+
FlowNodeMeta,
|
|
13
|
+
FlowNodeRegistry,
|
|
14
|
+
FlowNodeType,
|
|
15
|
+
} from '../typings';
|
|
16
|
+
import type { FlowDocument } from '../flow-document';
|
|
17
|
+
import { FlowNodeRenderData, FlowNodeTransformData } from '../datas';
|
|
18
|
+
|
|
19
|
+
export interface FlowNodeEntityConfig extends EntityOpts {
|
|
20
|
+
document: FlowDocument;
|
|
21
|
+
flowNodeType: FlowNodeType;
|
|
22
|
+
originParent?: FlowNodeEntity;
|
|
23
|
+
meta?: FlowNodeMeta;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface FlowNodeInitData {
|
|
27
|
+
originParent?: FlowNodeEntity;
|
|
28
|
+
parent?: FlowNodeEntity;
|
|
29
|
+
hidden?: boolean;
|
|
30
|
+
meta?: FlowNodeMeta;
|
|
31
|
+
index?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class FlowNodeEntity extends Entity<FlowNodeEntityConfig> {
|
|
35
|
+
private _memoLocalCache = new Map<string, any>();
|
|
36
|
+
|
|
37
|
+
private _memoGlobalCache = new Map<string, any>();
|
|
38
|
+
|
|
39
|
+
static type = 'FlowNodeEntity';
|
|
40
|
+
|
|
41
|
+
private _registerCache?: FlowNodeRegistry;
|
|
42
|
+
|
|
43
|
+
private _metaCache?: Required<FlowNodeMeta>;
|
|
44
|
+
|
|
45
|
+
metaFromJSON?: FlowNodeMeta;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 真实的父节点,条件块在内部会创建一些空的块节点,这些块需要关联它真实的父亲节点
|
|
49
|
+
*/
|
|
50
|
+
originParent?: FlowNodeEntity;
|
|
51
|
+
|
|
52
|
+
flowNodeType: FlowNodeType = 'unknown'; // 流程类型
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 是否隐藏
|
|
56
|
+
*/
|
|
57
|
+
private _hidden = false;
|
|
58
|
+
|
|
59
|
+
index = -1;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 文档引用
|
|
63
|
+
*/
|
|
64
|
+
document: FlowDocument;
|
|
65
|
+
|
|
66
|
+
constructor(conf: FlowNodeEntityConfig) {
|
|
67
|
+
super(conf);
|
|
68
|
+
this.document = conf.document;
|
|
69
|
+
this.flowNodeType = conf.flowNodeType;
|
|
70
|
+
this.originParent = conf.originParent;
|
|
71
|
+
this.metaFromJSON = conf.meta;
|
|
72
|
+
this.onDispose(() => {
|
|
73
|
+
this.document.originTree
|
|
74
|
+
.getChildren(this)
|
|
75
|
+
.slice()
|
|
76
|
+
.forEach((child) => {
|
|
77
|
+
child.dispose();
|
|
78
|
+
});
|
|
79
|
+
this.document.originTree.remove(this, false);
|
|
80
|
+
this.originParent = undefined;
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
initData(initConf: FlowNodeInitData): void {
|
|
85
|
+
if (initConf.originParent !== this.originParent) {
|
|
86
|
+
this.originParent = initConf.originParent;
|
|
87
|
+
this._registerCache = undefined;
|
|
88
|
+
}
|
|
89
|
+
if (initConf.parent) {
|
|
90
|
+
initConf.parent.addChild(this, initConf.index);
|
|
91
|
+
}
|
|
92
|
+
// TODO 这个 meta 不会触发 data 数据更新
|
|
93
|
+
if (initConf.meta !== this.metaFromJSON) {
|
|
94
|
+
this._metaCache = undefined;
|
|
95
|
+
this.metaFromJSON = initConf.meta;
|
|
96
|
+
}
|
|
97
|
+
this._hidden = !!(this.getNodeMeta().hidden || initConf.hidden);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
get isStart(): boolean {
|
|
101
|
+
return this.getNodeMeta().isStart;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
get isFirst(): boolean {
|
|
105
|
+
return !this.pre;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
get isLast(): boolean {
|
|
109
|
+
return !this.next;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 子节点采用水平布局
|
|
114
|
+
*/
|
|
115
|
+
get isInlineBlocks(): boolean {
|
|
116
|
+
const originIsInlineBlocks = this.getNodeMeta().isInlineBlocks;
|
|
117
|
+
return typeof originIsInlineBlocks === 'function'
|
|
118
|
+
? originIsInlineBlocks(this)
|
|
119
|
+
: originIsInlineBlocks;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 水平节点
|
|
124
|
+
*/
|
|
125
|
+
get isInlineBlock(): boolean {
|
|
126
|
+
const parent = this.document.renderTree.getParent(this);
|
|
127
|
+
return !!(parent && parent.isInlineBlocks);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* 节点结束标记
|
|
132
|
+
* - 当前节点是结束节点
|
|
133
|
+
* - 当前节点最后一个节点包含结束标记
|
|
134
|
+
* - 当前节点为 inlineBlock,每一个 block 包含结束标记
|
|
135
|
+
*
|
|
136
|
+
* 由子元素确定,因此使用 memoLocal
|
|
137
|
+
*/
|
|
138
|
+
get isNodeEnd(): boolean {
|
|
139
|
+
return this.memoLocal<boolean>('isNodeEnd', () => {
|
|
140
|
+
if (this.getNodeMeta().isNodeEnd) {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (this.isInlineBlocks && this.collapsedChildren.length) {
|
|
145
|
+
return this.collapsedChildren.every((child) => child.isNodeEnd);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (this.lastCollapsedChild) {
|
|
149
|
+
return this.lastCollapsedChild.isNodeEnd;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return false;
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 添加 子节点
|
|
158
|
+
*
|
|
159
|
+
* @param child 插入节点
|
|
160
|
+
*/
|
|
161
|
+
addChild(child: FlowNodeEntity, index?: number) {
|
|
162
|
+
if (child.parent === this) return;
|
|
163
|
+
this.document.originTree.addChild(this, child, index);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
get hasChild(): boolean {
|
|
167
|
+
return this.children.length > 0;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
get pre(): FlowNodeEntity | undefined {
|
|
171
|
+
return this.document.renderTree.getPre(this);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
get next(): FlowNodeEntity | undefined {
|
|
175
|
+
return this.document.renderTree.getNext(this);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
get parent(): FlowNodeEntity | undefined {
|
|
179
|
+
return this.document.renderTree.getParent(this);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
getNodeRegistry<M extends FlowNodeRegistry = FlowNodeRegistry & { meta: FlowNodeMeta }>(): M {
|
|
183
|
+
if (this._registerCache) return this._registerCache as M;
|
|
184
|
+
this._registerCache = this.document.getNodeRegistry(this.flowNodeType, this.originParent);
|
|
185
|
+
return this._registerCache as M;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @deprecated
|
|
190
|
+
* use getNodeRegistry instead
|
|
191
|
+
*/
|
|
192
|
+
getNodeRegister<M extends FlowNodeRegistry = FlowNodeRegistry>(): M {
|
|
193
|
+
return this.getNodeRegistry<M>();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
getNodeMeta<M extends FlowNodeMeta = FlowNodeMeta>(): M & Required<FlowNodeMeta> {
|
|
197
|
+
if (this._metaCache) return this._metaCache as M & Required<FlowNodeMeta>;
|
|
198
|
+
if (this.metaFromJSON) {
|
|
199
|
+
this._metaCache = {
|
|
200
|
+
...this.getNodeRegistry().meta,
|
|
201
|
+
...this.metaFromJSON,
|
|
202
|
+
} as M & Required<FlowNodeMeta>;
|
|
203
|
+
} else {
|
|
204
|
+
this._metaCache = this.getNodeRegistry().meta as M & Required<FlowNodeMeta>;
|
|
205
|
+
}
|
|
206
|
+
return this._metaCache as M & Required<FlowNodeMeta>;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* 获取所有子节点,包含 child 及其所有兄弟节点
|
|
211
|
+
*/
|
|
212
|
+
get allChildren(): FlowNodeEntity[] {
|
|
213
|
+
const children: FlowNodeEntity[] = [];
|
|
214
|
+
for (const child of this.children) {
|
|
215
|
+
children.push(child);
|
|
216
|
+
children.push(...child.allChildren);
|
|
217
|
+
}
|
|
218
|
+
return children;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 获取所有收起的子节点,包含 child 及其所有兄弟节点
|
|
223
|
+
*/
|
|
224
|
+
get allCollapsedChildren(): FlowNodeEntity[] {
|
|
225
|
+
const children: FlowNodeEntity[] = [];
|
|
226
|
+
for (const child of this.collapsedChildren) {
|
|
227
|
+
children.push(child);
|
|
228
|
+
children.push(...child.allCollapsedChildren);
|
|
229
|
+
}
|
|
230
|
+
return children;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
*
|
|
235
|
+
* Get child blocks
|
|
236
|
+
*
|
|
237
|
+
* use `blocks` instead
|
|
238
|
+
* @deprecated
|
|
239
|
+
*/
|
|
240
|
+
|
|
241
|
+
get collapsedChildren(): FlowNodeEntity[] {
|
|
242
|
+
return this.document.renderTree.getCollapsedChildren(this);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Get child blocks
|
|
247
|
+
*/
|
|
248
|
+
get blocks(): FlowNodeEntity[] {
|
|
249
|
+
return this.collapsedChildren;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Get last block
|
|
254
|
+
*/
|
|
255
|
+
get lastBlock(): FlowNodeEntity | undefined {
|
|
256
|
+
return this.lastCollapsedChild;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* use `lastBlock` instead
|
|
261
|
+
*/
|
|
262
|
+
get lastCollapsedChild(): FlowNodeEntity | undefined {
|
|
263
|
+
const { collapsedChildren } = this;
|
|
264
|
+
return collapsedChildren[collapsedChildren.length - 1];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* 获取子节点,如果子节点收起来,则会返回 空数组
|
|
269
|
+
*/
|
|
270
|
+
get children(): FlowNodeEntity[] {
|
|
271
|
+
return this.document.renderTree.getChildren(this);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
get lastChild(): FlowNodeEntity | undefined {
|
|
275
|
+
const { children } = this;
|
|
276
|
+
return children[children.length - 1];
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
get firstChild(): FlowNodeEntity | undefined {
|
|
280
|
+
return this.children[0];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
memoLocal<T>(key: string, fn: () => T): T {
|
|
284
|
+
if (this._memoLocalCache.has(key)) {
|
|
285
|
+
return this._memoLocalCache.get(key) as T;
|
|
286
|
+
}
|
|
287
|
+
const data = fn();
|
|
288
|
+
this._memoLocalCache.set(key, data);
|
|
289
|
+
return data as T;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
memoGlobal<T>(key: string, fn: () => T): T {
|
|
293
|
+
if (this._memoGlobalCache.has(key)) {
|
|
294
|
+
return this._memoGlobalCache.get(key) as T;
|
|
295
|
+
}
|
|
296
|
+
const data = fn();
|
|
297
|
+
this._memoGlobalCache.set(key, data);
|
|
298
|
+
return data as T;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
clearMemoGlobal() {
|
|
302
|
+
this._memoGlobalCache.clear();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
clearMemoLocal() {
|
|
306
|
+
this._memoLocalCache.clear();
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
get childrenLength() {
|
|
310
|
+
return this.children.length;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
get collapsed(): boolean {
|
|
314
|
+
if (this.document.renderTree.isCollapsed(this)) return true;
|
|
315
|
+
return !!this.parent?.collapsed;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
set collapsed(collapsed) {
|
|
319
|
+
this.document.renderTree.setCollapsed(this, collapsed);
|
|
320
|
+
this.clearMemoGlobal();
|
|
321
|
+
this.clearMemoLocal();
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
get hidden(): boolean {
|
|
325
|
+
return this._hidden;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// 展开该节点
|
|
329
|
+
openInsideCollapsed() {
|
|
330
|
+
this.document.renderTree.openNodeInsideCollapsed(this);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* 可以重载
|
|
335
|
+
*/
|
|
336
|
+
getJSONData(): any {
|
|
337
|
+
return this.getExtInfo();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* 生成 JSON
|
|
342
|
+
* @param newId
|
|
343
|
+
*/
|
|
344
|
+
toJSON(): FlowNodeJSON {
|
|
345
|
+
return this.document.toNodeJSON(this);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
get isVertical(): boolean {
|
|
349
|
+
return this.document.layout.name === FlowLayoutDefault.VERTICAL_FIXED_LAYOUT;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* 修改节点扩展信息
|
|
354
|
+
* @param info
|
|
355
|
+
*/
|
|
356
|
+
updateExtInfo<T extends Record<string, any> = Record<string, any>>(
|
|
357
|
+
extInfo: T,
|
|
358
|
+
fullUpdate?: boolean
|
|
359
|
+
): void {
|
|
360
|
+
this.getData(FlowNodeRenderData).updateExtInfo(extInfo, fullUpdate);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* 获取节点扩展信息
|
|
365
|
+
*/
|
|
366
|
+
getExtInfo<T extends Record<string, any> = Record<string, any>>(): T {
|
|
367
|
+
return this.getData<FlowNodeRenderData>(FlowNodeRenderData).getExtInfo() as T;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
get onExtInfoChange(): Event<{ newInfo: any; oldInfo: any }> {
|
|
371
|
+
return this.renderData.onExtInfoChange;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* 获取渲染数据
|
|
376
|
+
*/
|
|
377
|
+
get renderData(): FlowNodeRenderData {
|
|
378
|
+
return this.getData(FlowNodeRenderData);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* 获取位置大小数据
|
|
383
|
+
*/
|
|
384
|
+
get transform(): FlowNodeTransformData {
|
|
385
|
+
return this.getData(FlowNodeTransformData);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* 获取节点的位置及大小矩形
|
|
390
|
+
*/
|
|
391
|
+
get bounds(): Rectangle {
|
|
392
|
+
return this.transform.bounds;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Check node extend type
|
|
397
|
+
*/
|
|
398
|
+
isExtend(parentType: FlowNodeType): boolean {
|
|
399
|
+
return this.document.isExtend(this.flowNodeType, parentType);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Check node type
|
|
404
|
+
* @param parentType
|
|
405
|
+
*/
|
|
406
|
+
isTypeOrExtendType(parentType: FlowNodeType): boolean {
|
|
407
|
+
return this.document.isTypeOrExtendType(this.flowNodeType, parentType);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export namespace FlowNodeEntity {
|
|
412
|
+
export function is(obj: Entity): obj is FlowNodeEntity {
|
|
413
|
+
return obj instanceof FlowNodeEntity;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { debounce } from 'lodash-es';
|
|
7
|
+
import { type Disposable } from '@flowgram-vue/utils';
|
|
8
|
+
import { ConfigEntity, type EntityOpts } from '@flowgram-vue/core';
|
|
9
|
+
|
|
10
|
+
import { LABEL_SIDE_TYPE } from '../typings';
|
|
11
|
+
import { type FlowNodeEntity } from './flow-node-entity';
|
|
12
|
+
|
|
13
|
+
interface FlowRendererStateEntityConfig extends EntityOpts {}
|
|
14
|
+
|
|
15
|
+
interface FlowRendererState {
|
|
16
|
+
nodeHoveredId?: string;
|
|
17
|
+
nodeDroppingId?: string;
|
|
18
|
+
nodeDragStartId?: string;
|
|
19
|
+
nodeDragIds?: string[]; // 框选批量拖拽
|
|
20
|
+
nodeDragIdsWithChildren?: string[]; // 批量拖拽(含子节点)
|
|
21
|
+
dragLabelSide?: LABEL_SIDE_TYPE;
|
|
22
|
+
dragging?: boolean;
|
|
23
|
+
isBranch?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 渲染相关的全局状态管理
|
|
27
|
+
*/
|
|
28
|
+
export class FlowRendererStateEntity extends ConfigEntity<
|
|
29
|
+
FlowRendererState,
|
|
30
|
+
FlowRendererStateEntityConfig
|
|
31
|
+
> {
|
|
32
|
+
static type = 'FlowRendererStateEntity';
|
|
33
|
+
|
|
34
|
+
getDefaultConfig() {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
constructor(conf: FlowRendererStateEntityConfig) {
|
|
39
|
+
super(conf);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getNodeHovered(): FlowNodeEntity | undefined {
|
|
43
|
+
return this.config.nodeHoveredId
|
|
44
|
+
? this.entityManager.getEntityById(this.config.nodeHoveredId)
|
|
45
|
+
: undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
setNodeHovered(node: FlowNodeEntity | undefined): void {
|
|
49
|
+
this.updateConfig({
|
|
50
|
+
nodeHoveredId: node?.id,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get dragging() {
|
|
55
|
+
return this.config.dragging;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
setDragging(dragging: boolean) {
|
|
59
|
+
this.updateConfig({
|
|
60
|
+
dragging,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get isBranch() {
|
|
65
|
+
return this.config.isBranch;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
setIsBranch(isBranch: boolean) {
|
|
69
|
+
this.updateConfig({
|
|
70
|
+
isBranch,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
getDragLabelSide(): LABEL_SIDE_TYPE | undefined {
|
|
75
|
+
return this.config.dragLabelSide;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
setDragLabelSide(dragLabelSide?: LABEL_SIDE_TYPE): void {
|
|
79
|
+
this.updateConfig({
|
|
80
|
+
dragLabelSide,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getNodeDroppingId(): string | undefined {
|
|
85
|
+
return this.config.nodeDroppingId;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
setNodeDroppingId(nodeDroppingId?: string): void {
|
|
89
|
+
this.updateConfig({
|
|
90
|
+
nodeDroppingId,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
getDragStartEntity(): FlowNodeEntity | undefined {
|
|
95
|
+
const { nodeDragStartId } = this.config;
|
|
96
|
+
return this.entityManager.getEntityById(nodeDragStartId!);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
setDragStartEntity(node?: FlowNodeEntity): void {
|
|
100
|
+
this.updateConfig({
|
|
101
|
+
nodeDragStartId: node?.id,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 拖拽多个节点时
|
|
106
|
+
getDragEntities(): FlowNodeEntity[] {
|
|
107
|
+
const { nodeDragIds } = this.config;
|
|
108
|
+
return (nodeDragIds || []).map((_id) => this.entityManager.getEntityById(_id)!);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 设置拖拽的节点
|
|
112
|
+
setDragEntities(nodes: FlowNodeEntity[]): void {
|
|
113
|
+
this.updateConfig({
|
|
114
|
+
nodeDragIds: nodes.map((_node) => _node.id),
|
|
115
|
+
nodeDragIdsWithChildren: nodes
|
|
116
|
+
.map((_node) => [_node.id, ..._node.allCollapsedChildren.map((_n) => _n.id)])
|
|
117
|
+
.flat(),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
onNodeHoveredChange(
|
|
122
|
+
fn: (hoveredNode: FlowNodeEntity | undefined) => void,
|
|
123
|
+
debounceTime = 100 // 延迟执行避免频繁 hover
|
|
124
|
+
): Disposable {
|
|
125
|
+
return this.onConfigChanged(debounce(() => fn(this.getNodeHovered()), debounceTime));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { inject, injectable, optional } from 'inversify';
|
|
7
|
+
import { Emitter } from '@flowgram-vue/utils';
|
|
8
|
+
|
|
9
|
+
export const FlowDocumentConfigDefaultData = Symbol('FlowDocumentConfigDefaultData');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 用于文档扩展配置
|
|
13
|
+
*/
|
|
14
|
+
@injectable()
|
|
15
|
+
export class FlowDocumentConfig {
|
|
16
|
+
private onDataChangeEmitter = new Emitter<string>();
|
|
17
|
+
|
|
18
|
+
readonly onChange = this.onDataChangeEmitter.event;
|
|
19
|
+
|
|
20
|
+
constructor(
|
|
21
|
+
@inject(FlowDocumentConfigDefaultData)
|
|
22
|
+
@optional()
|
|
23
|
+
private _data: Record<string, any> = {},
|
|
24
|
+
) {}
|
|
25
|
+
|
|
26
|
+
get(key: string): any {
|
|
27
|
+
return this._data[key];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
set(key: string, value: any): void {
|
|
31
|
+
if (this.get(key) !== value) {
|
|
32
|
+
this._data[key] = value;
|
|
33
|
+
this.onDataChangeEmitter.fire(key);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
registerConfigs(config: Record<string, any>) {
|
|
38
|
+
Object.keys(config).forEach(key => {
|
|
39
|
+
this.set(key, config[key]);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ContainerModule } from 'inversify';
|
|
7
|
+
|
|
8
|
+
import { FlowOperationBaseService } from './typings/flow-operation';
|
|
9
|
+
import { FlowDragService } from './services/flow-drag-service';
|
|
10
|
+
import { FlowGroupService, FlowOperationBaseServiceImpl } from './services';
|
|
11
|
+
import { HorizontalFixedLayout, VerticalFixedLayout } from './layout';
|
|
12
|
+
import { FlowDocumentContribution } from './flow-document-contribution';
|
|
13
|
+
import { FlowDocumentConfig } from './flow-document-config';
|
|
14
|
+
import { FlowDocument, FlowDocumentProvider } from './flow-document';
|
|
15
|
+
|
|
16
|
+
export const FlowDocumentContainerModule = new ContainerModule((bind) => {
|
|
17
|
+
bind(FlowDocument).toSelf().inSingletonScope();
|
|
18
|
+
bind(FlowDocumentProvider)
|
|
19
|
+
.toDynamicValue((ctx) => () => ctx.container.get(FlowDocument))
|
|
20
|
+
.inSingletonScope();
|
|
21
|
+
bind(FlowDocumentConfig).toSelf().inSingletonScope();
|
|
22
|
+
bind(VerticalFixedLayout).toSelf().inSingletonScope();
|
|
23
|
+
bind(HorizontalFixedLayout).toSelf().inSingletonScope();
|
|
24
|
+
bind(FlowDragService).toSelf().inSingletonScope();
|
|
25
|
+
bind(FlowOperationBaseService).to(FlowOperationBaseServiceImpl).inSingletonScope();
|
|
26
|
+
bind(FlowGroupService).toSelf().inSingletonScope();
|
|
27
|
+
bind(FlowDocumentContribution).toDynamicValue((ctx) => ({
|
|
28
|
+
registerDocument: (document: FlowDocument) => {
|
|
29
|
+
document.registerLayout(ctx.container.get(VerticalFixedLayout));
|
|
30
|
+
document.registerLayout(ctx.container.get(HorizontalFixedLayout));
|
|
31
|
+
},
|
|
32
|
+
}));
|
|
33
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type FlowDocument } from './flow-document';
|
|
7
|
+
|
|
8
|
+
export const FlowDocumentContribution = Symbol('FlowDocumentContribution');
|
|
9
|
+
|
|
10
|
+
export interface FlowDocumentContribution<T extends FlowDocument = FlowDocument> {
|
|
11
|
+
/**
|
|
12
|
+
* 注册
|
|
13
|
+
* @param document
|
|
14
|
+
*/
|
|
15
|
+
registerDocument?(document: T): void;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 加载数据
|
|
19
|
+
* @param document
|
|
20
|
+
*/
|
|
21
|
+
loadDocument?(document: T): Promise<void>;
|
|
22
|
+
}
|