@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
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flowgram-vue/document",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "automation flow engine",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"flow",
|
|
7
|
+
"engine"
|
|
8
|
+
],
|
|
9
|
+
"homepage": "https://flowgram.ai/",
|
|
10
|
+
"repository": "https://github.com/Crayon-hua/flowgram-vue",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"require": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"main": "./dist/index.cjs",
|
|
21
|
+
"module": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"src",
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"inversify": "^6.0.1",
|
|
29
|
+
"lodash-es": "^4.17.21",
|
|
30
|
+
"nanoid": "^5.0.9",
|
|
31
|
+
"reflect-metadata": "~0.2.2",
|
|
32
|
+
"@flowgram-vue/core": "0.2.0",
|
|
33
|
+
"@flowgram-vue/utils": "0.2.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/lodash-es": "^4.17.12",
|
|
37
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
38
|
+
"eslint": "^9.0.0",
|
|
39
|
+
"tsup": "^8.0.1",
|
|
40
|
+
"typescript": "^5.8.3",
|
|
41
|
+
"vitest": "^3.2.4",
|
|
42
|
+
"@flowgram-vue/build-config": "0.2.0",
|
|
43
|
+
"@flowgram-vue/ts-config": "0.2.0",
|
|
44
|
+
"@flowgram-vue/eslint-config": "0.2.0"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public",
|
|
48
|
+
"registry": "https://registry.npmjs.org/"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "flowgram-build",
|
|
52
|
+
"build:fast": "flowgram-build --fast",
|
|
53
|
+
"build:watch": "flowgram-build --watch",
|
|
54
|
+
"clean": "rimraf dist",
|
|
55
|
+
"test": "vitest run",
|
|
56
|
+
"test:cov": "vitest run --coverage",
|
|
57
|
+
"test:update": "vitest run --update",
|
|
58
|
+
"ts-check": "tsc --noEmit",
|
|
59
|
+
"watch": "npm run build:fast -- --dts-resolve --watch --ignore-watch dist"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Compare, Disposable, domUtils, Emitter } from '@flowgram-vue/utils';
|
|
7
|
+
import { EntityData } from '@flowgram-vue/core';
|
|
8
|
+
|
|
9
|
+
import { FlowNodeBaseType } from '../typings';
|
|
10
|
+
import type { FlowNodeEntity } from '../entities';
|
|
11
|
+
import { FlowNodeTransformData } from './index';
|
|
12
|
+
|
|
13
|
+
export interface FlowNodeRenderSchema {
|
|
14
|
+
addable: boolean; // 是否可添加节点
|
|
15
|
+
expandable: boolean; // 是否可展开
|
|
16
|
+
collapsed?: boolean; // 复合节点是否收起
|
|
17
|
+
expanded: boolean;
|
|
18
|
+
activated: boolean; // 是否高亮节点
|
|
19
|
+
hovered: boolean; // 是否悬浮在节点上
|
|
20
|
+
dragging: boolean; // 是否正在拖拽
|
|
21
|
+
stackIndex: number; // 渲染层级
|
|
22
|
+
extInfo?: Record<string, any>; // 扩展渲染状态字段
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 节点渲染状态相关数据
|
|
27
|
+
*/
|
|
28
|
+
export class FlowNodeRenderData extends EntityData<FlowNodeRenderSchema> {
|
|
29
|
+
static type = 'FlowNodeRenderData';
|
|
30
|
+
|
|
31
|
+
declare entity: FlowNodeEntity;
|
|
32
|
+
|
|
33
|
+
private _node?: HTMLDivElement;
|
|
34
|
+
|
|
35
|
+
protected onExtInfoChangeEmitter = new Emitter<{ newInfo: any; oldInfo: any }>();
|
|
36
|
+
|
|
37
|
+
readonly onExtInfoChange = this.onExtInfoChangeEmitter.event;
|
|
38
|
+
|
|
39
|
+
get key(): string {
|
|
40
|
+
return this.entity.id;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getDefaultData(): FlowNodeRenderSchema {
|
|
44
|
+
const { addable, expandable, defaultExpanded } = this.entity.getNodeMeta();
|
|
45
|
+
return {
|
|
46
|
+
addable,
|
|
47
|
+
expandable,
|
|
48
|
+
expanded: defaultExpanded || false,
|
|
49
|
+
activated: false,
|
|
50
|
+
hovered: false,
|
|
51
|
+
dragging: false,
|
|
52
|
+
stackIndex: 0,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
updateExtInfo(info: Record<string, any>, fullUpdate?: boolean) {
|
|
57
|
+
const oldInfo = this.data.extInfo;
|
|
58
|
+
const newInfo = fullUpdate ? info : { ...oldInfo, ...info };
|
|
59
|
+
if (Compare.isChanged(oldInfo, newInfo)) {
|
|
60
|
+
this.update({
|
|
61
|
+
extInfo: newInfo,
|
|
62
|
+
});
|
|
63
|
+
this.onExtInfoChangeEmitter.fire({ oldInfo, newInfo });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getExtInfo(): Record<string, any> | undefined {
|
|
68
|
+
return this.data.extInfo;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
constructor(entity: FlowNodeEntity) {
|
|
72
|
+
super(entity);
|
|
73
|
+
this.toDispose.push(
|
|
74
|
+
Disposable.create(() => {
|
|
75
|
+
if (this._node) this._node.remove();
|
|
76
|
+
})
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
get addable(): boolean {
|
|
81
|
+
return this.data.addable;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
get expandable(): boolean {
|
|
85
|
+
return this.data.expandable;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
get draggable(): boolean {
|
|
89
|
+
const { draggable } = this.entity.getNodeMeta();
|
|
90
|
+
|
|
91
|
+
if (typeof draggable === 'function') {
|
|
92
|
+
return draggable(this.entity);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return draggable;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get expanded(): boolean {
|
|
99
|
+
return this.data.expanded;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
set expanded(expanded: boolean) {
|
|
103
|
+
if (this.expandable && this.data.expanded !== expanded) {
|
|
104
|
+
this.data.expanded = expanded;
|
|
105
|
+
this.fireChange();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
toggleExpand() {
|
|
110
|
+
this.expanded = !this.expanded;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
mouseLeaveTimeout?: ReturnType<typeof setTimeout>;
|
|
114
|
+
|
|
115
|
+
toggleMouseEnter(silent = false) {
|
|
116
|
+
this.entity.document.renderState.setNodeHovered(this.entity);
|
|
117
|
+
if (silent) return;
|
|
118
|
+
const transform = this.entity.getData(FlowNodeTransformData)!;
|
|
119
|
+
if (transform.renderState.hidden) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (this.mouseLeaveTimeout) {
|
|
123
|
+
clearTimeout(this.mouseLeaveTimeout);
|
|
124
|
+
this.mouseLeaveTimeout = undefined;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
transform.renderState.hovered = true;
|
|
128
|
+
|
|
129
|
+
if (this.entity.isFirst && this.entity.parent?.id !== 'root') {
|
|
130
|
+
// 分支中第一个节点 hover,parent activated 设置为 true
|
|
131
|
+
transform.parent!.renderState.activated = true;
|
|
132
|
+
} else {
|
|
133
|
+
transform.renderState.activated = true;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
toggleMouseLeave(silent = false) {
|
|
138
|
+
this.entity.document.renderState.setNodeHovered(undefined);
|
|
139
|
+
if (silent) return;
|
|
140
|
+
const transform = this.entity.getData(FlowNodeTransformData)!;
|
|
141
|
+
this.mouseLeaveTimeout = setTimeout(() => {
|
|
142
|
+
transform.renderState.hovered = false;
|
|
143
|
+
|
|
144
|
+
if (this.entity.isFirst && this.entity.parent?.id !== 'root') {
|
|
145
|
+
transform.parent!.renderState.activated = false;
|
|
146
|
+
}
|
|
147
|
+
transform.renderState.activated = false;
|
|
148
|
+
}, 200);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
get hidden(): boolean {
|
|
152
|
+
return this.entity.hidden;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
set hovered(hovered: boolean) {
|
|
156
|
+
this.data.hovered = hovered;
|
|
157
|
+
this.fireChange();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
get hovered() {
|
|
161
|
+
return this.data.hovered;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
get dragging(): boolean {
|
|
165
|
+
return this.data.dragging;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
set dragging(dragging: boolean) {
|
|
169
|
+
if (this.data.dragging !== dragging) {
|
|
170
|
+
this.data.dragging = dragging;
|
|
171
|
+
this.fireChange();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
set activated(activated: boolean) {
|
|
176
|
+
if (this.entity.flowNodeType === FlowNodeBaseType.BLOCK_ICON && this.entity.parent) {
|
|
177
|
+
this.entity.parent.getData<FlowNodeRenderData>(FlowNodeRenderData)!.activated = activated;
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (this.data.activated !== activated) {
|
|
181
|
+
this.data.activated = activated;
|
|
182
|
+
this.fireChange();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
get activated() {
|
|
187
|
+
const { entity } = this;
|
|
188
|
+
if (entity.parent && entity.parent.getData<FlowNodeRenderData>(FlowNodeRenderData)!.activated) {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
return this.data.activated;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
get stackIndex(): number {
|
|
195
|
+
return this.data.stackIndex;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
set stackIndex(index: number) {
|
|
199
|
+
this.data.stackIndex = index;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
get lineActivated() {
|
|
203
|
+
const { activated } = this;
|
|
204
|
+
if (!activated) return false;
|
|
205
|
+
// 只有 parent 高亮的情况才高亮下面的线条,否则只高亮 node
|
|
206
|
+
// inlineBlock 仅看自身
|
|
207
|
+
// 圈选情况下个节点被高量,则也跟着高量
|
|
208
|
+
return Boolean(
|
|
209
|
+
this.entity.parent?.getData(FlowNodeRenderData)?.activated ||
|
|
210
|
+
this.entity.isInlineBlock ||
|
|
211
|
+
this.entity.next?.getData(FlowNodeRenderData)!.activated
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
get node(): HTMLDivElement {
|
|
216
|
+
if (this._node) return this._node;
|
|
217
|
+
this._node = domUtils.createDivWithClass('gedit-flow-activity-node');
|
|
218
|
+
this._node.dataset.testid = 'sdk.workflow.canvas.node';
|
|
219
|
+
this._node.dataset.nodeId = this.entity.id;
|
|
220
|
+
return this._node;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
dispose() {
|
|
224
|
+
super.dispose();
|
|
225
|
+
this.onExtInfoChangeEmitter.dispose();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Disposable, type IPoint, Rectangle } from '@flowgram-vue/utils';
|
|
7
|
+
import {
|
|
8
|
+
Bounds,
|
|
9
|
+
EntityData,
|
|
10
|
+
PositionSchema,
|
|
11
|
+
type SizeSchema,
|
|
12
|
+
TransformData,
|
|
13
|
+
} from '@flowgram-vue/core';
|
|
14
|
+
|
|
15
|
+
import type { FlowNodeEntity } from '../entities';
|
|
16
|
+
import { FlowNodeRenderData } from './flow-node-render-data';
|
|
17
|
+
|
|
18
|
+
export interface FlowNodeTransformSchema {
|
|
19
|
+
size: SizeSchema; // 当前节点大小
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class FlowNodeTransformData extends EntityData<FlowNodeTransformSchema> {
|
|
23
|
+
static type = 'FlowNodeTransformData';
|
|
24
|
+
|
|
25
|
+
// 这里加 declare 原因:覆盖了 EntityData 默认的 EntityType,如果不声明会报 TS2612 错误
|
|
26
|
+
declare entity: FlowNodeEntity;
|
|
27
|
+
|
|
28
|
+
transform: TransformData;
|
|
29
|
+
|
|
30
|
+
renderState: FlowNodeRenderData;
|
|
31
|
+
|
|
32
|
+
localDirty = true;
|
|
33
|
+
|
|
34
|
+
get origin() {
|
|
35
|
+
return this.transform.origin;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get key(): string {
|
|
39
|
+
return this.entity.id;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getDefaultData(): FlowNodeTransformSchema {
|
|
43
|
+
const { size, hidden } = this.entity.getNodeMeta();
|
|
44
|
+
// 更新默认 size 大小
|
|
45
|
+
return {
|
|
46
|
+
size: !hidden ? { ...size } : { width: 0, height: 0 },
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
constructor(entity: FlowNodeEntity) {
|
|
51
|
+
super(entity);
|
|
52
|
+
const { origin } = this.entity.getNodeMeta();
|
|
53
|
+
this.transform = this.entity.addData<TransformData>(TransformData);
|
|
54
|
+
this.transform.changeLocked = true;
|
|
55
|
+
this.transform.update({ origin: { ...origin } });
|
|
56
|
+
this.transform.changeLocked = false;
|
|
57
|
+
this.renderState = this.entity.addData<FlowNodeRenderData>(FlowNodeRenderData);
|
|
58
|
+
this.bindChange(this.transform);
|
|
59
|
+
// 删除节点要让下一个节点或者父节点变成 dirty
|
|
60
|
+
this.toDispose.push(
|
|
61
|
+
Disposable.create(() => {
|
|
62
|
+
const { next, parent } = this;
|
|
63
|
+
if (next) next.localDirty = true;
|
|
64
|
+
if (parent) parent.localDirty = true;
|
|
65
|
+
})
|
|
66
|
+
);
|
|
67
|
+
// this.bindChange(this.renderState)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 获取节点是否展开
|
|
72
|
+
*/
|
|
73
|
+
get collapsed(): boolean {
|
|
74
|
+
return this.entity.collapsed;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
set collapsed(collapsed: boolean) {
|
|
78
|
+
this.entity.collapsed = collapsed;
|
|
79
|
+
this.localDirty = true;
|
|
80
|
+
|
|
81
|
+
// 第一个子节点也设置为 dirty
|
|
82
|
+
if (this.firstChild) this.firstChild.localDirty = true;
|
|
83
|
+
|
|
84
|
+
this.fireChange();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 获取节点的大小
|
|
89
|
+
*/
|
|
90
|
+
get size(): SizeSchema {
|
|
91
|
+
return this.entity.memoGlobal<SizeSchema>('size', () => {
|
|
92
|
+
if (this.isContainer) return this.transform.localSize;
|
|
93
|
+
return this.data.size;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
get position(): PositionSchema {
|
|
98
|
+
const { position } = this.transform;
|
|
99
|
+
return {
|
|
100
|
+
x: position.x,
|
|
101
|
+
y: position.y,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
set position(position: PositionSchema) {
|
|
106
|
+
this.transform.update({
|
|
107
|
+
position,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
set size(size: SizeSchema) {
|
|
112
|
+
const { width, height } = this.data.size;
|
|
113
|
+
// Container size 由子节点决定
|
|
114
|
+
if (this.isContainer) return;
|
|
115
|
+
if (size.width !== width || size.height !== height) {
|
|
116
|
+
this._data.size = { ...size };
|
|
117
|
+
this.localDirty = true;
|
|
118
|
+
this.fireChange();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
get inputPoint() {
|
|
123
|
+
return this.entity.memoGlobal<IPoint>('inputPoint', () => {
|
|
124
|
+
const { getInputPoint } = this.entity.getNodeRegistry();
|
|
125
|
+
return getInputPoint
|
|
126
|
+
? getInputPoint(this, this.entity.document.layout)
|
|
127
|
+
: this.defaultInputPoint;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
get defaultInputPoint() {
|
|
132
|
+
return this.entity.memoGlobal<IPoint>('defaultInputPoint', () =>
|
|
133
|
+
this.entity.document.layout.getDefaultInputPoint(this.entity)
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
get defaultOutputPoint() {
|
|
138
|
+
return this.entity.memoGlobal<IPoint>('defaultOutputPoint', () =>
|
|
139
|
+
this.entity.document.layout.getDefaultOutputPoint(this.entity)
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
get outputPoint() {
|
|
144
|
+
return this.entity.memoGlobal<IPoint>('outputPoint', () => {
|
|
145
|
+
const { getOutputPoint } = this.entity.getNodeRegistry();
|
|
146
|
+
return getOutputPoint
|
|
147
|
+
? getOutputPoint(this, this.entity.document.layout)
|
|
148
|
+
: this.defaultOutputPoint;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 原点的最左偏移
|
|
154
|
+
*/
|
|
155
|
+
get originDeltaX(): number {
|
|
156
|
+
return this.entity.memoLocal<number>('originDeltaX', () => {
|
|
157
|
+
const { children } = this;
|
|
158
|
+
const { getOriginDeltaX } = this.entity.getNodeRegistry();
|
|
159
|
+
if (getOriginDeltaX) return getOriginDeltaX(this, this.entity.document.layout);
|
|
160
|
+
// 没有子节点,则采用自身的原点偏移
|
|
161
|
+
if (children.length === 0) {
|
|
162
|
+
return -this.size.width * this.origin.x;
|
|
163
|
+
}
|
|
164
|
+
// 采用子节点的最左偏移量来计算
|
|
165
|
+
if (children.length === 1) return children[0].originDeltaX;
|
|
166
|
+
// 这里代表水平偏移,则采用第一个节点加上自身偏移
|
|
167
|
+
if (this.entity.isInlineBlocks && children.length > 1) {
|
|
168
|
+
return children[0].originDeltaX + this.transform.position.x;
|
|
169
|
+
}
|
|
170
|
+
return children.reduce((res: undefined | number, child) => {
|
|
171
|
+
const deltaX = child.originDeltaX;
|
|
172
|
+
return res === undefined || deltaX < res ? deltaX : (res as number);
|
|
173
|
+
}, undefined) as number;
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 原点 y 轴偏移
|
|
179
|
+
*/
|
|
180
|
+
get originDeltaY(): number {
|
|
181
|
+
return this.entity.memoLocal<number>('originDeltaY', () => {
|
|
182
|
+
const { children } = this;
|
|
183
|
+
const { getOriginDeltaY } = this.entity.getNodeRegistry();
|
|
184
|
+
if (getOriginDeltaY) return getOriginDeltaY(this, this.entity.document.layout);
|
|
185
|
+
// 没有子节点,则采用自身的原点偏移
|
|
186
|
+
if (children.length === 0) {
|
|
187
|
+
return -this.size.height * this.origin.y;
|
|
188
|
+
}
|
|
189
|
+
// 采用子节点的最上偏移量来计算
|
|
190
|
+
if (children.length === 1) return children[0].originDeltaY;
|
|
191
|
+
// 这里代表水平偏移,则采用第一个节点加上自身偏移
|
|
192
|
+
if (this.entity.isInlineBlocks && children.length > 1) {
|
|
193
|
+
return children[0].originDeltaY + this.transform.position.y;
|
|
194
|
+
}
|
|
195
|
+
return children.reduce((res: undefined | number, child) => {
|
|
196
|
+
const deltaY = child.originDeltaY;
|
|
197
|
+
return res === undefined || deltaY < res ? deltaY : (res as number);
|
|
198
|
+
}, undefined) as number;
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* 绝对坐标 bbox, 不包含自身的 spacing(marginBottom), 但是包含 inlineSpacing 和 子节点的 spacing
|
|
204
|
+
*/
|
|
205
|
+
get bounds(): Rectangle {
|
|
206
|
+
return this.entity.memoGlobal<Rectangle>('bounds', () => {
|
|
207
|
+
const { transform } = this;
|
|
208
|
+
|
|
209
|
+
if (this.isContainer) {
|
|
210
|
+
const childrenRects = transform.children.map(
|
|
211
|
+
(c) => c.entity.getData<FlowNodeTransformData>(FlowNodeTransformData)!.boundsWithPadding
|
|
212
|
+
);
|
|
213
|
+
// Container 要加上 inlineSpacing
|
|
214
|
+
return Rectangle.enlarge(childrenRects).withPadding(this.padding);
|
|
215
|
+
}
|
|
216
|
+
return transform.bounds; // 单个节点取默认的 bounds
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
get boundsWithPadding(): Rectangle {
|
|
221
|
+
return this.entity.memoGlobal<Rectangle>('boundsWithPadding', () => {
|
|
222
|
+
const { transform } = this;
|
|
223
|
+
|
|
224
|
+
if (this.isContainer) {
|
|
225
|
+
const childrenRects = transform.children.map(
|
|
226
|
+
(c) => c.entity.getData<FlowNodeTransformData>(FlowNodeTransformData)!.boundsWithPadding
|
|
227
|
+
);
|
|
228
|
+
return Rectangle.enlarge(childrenRects).withPadding(this.padding);
|
|
229
|
+
}
|
|
230
|
+
return transform.bounds.clone().withPadding(this.padding);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
get isContainer(): boolean {
|
|
235
|
+
return this.transform.isContainer;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* 相对坐标 bbox, 这里的 localBounds 会加入 padding 一起算
|
|
240
|
+
*/
|
|
241
|
+
get localBounds(): Rectangle {
|
|
242
|
+
return this.entity.memoLocal<Rectangle>('localBounds', () => {
|
|
243
|
+
const { transform } = this;
|
|
244
|
+
|
|
245
|
+
if (this.isContainer) {
|
|
246
|
+
const childrenRects = transform.children.map(
|
|
247
|
+
(c) => c.entity.getData<FlowNodeTransformData>(FlowNodeTransformData)!.localBounds
|
|
248
|
+
);
|
|
249
|
+
const childrenBounds = Rectangle.enlarge(childrenRects).withPadding(this.padding);
|
|
250
|
+
return Bounds.applyMatrix(childrenBounds, transform.localTransform);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return transform.localBounds.clone().withPadding(this.padding);
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
get padding() {
|
|
258
|
+
return this.entity.document.layout.getPadding(this.entity);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
setParentTransform(transform?: FlowNodeTransformData): void {
|
|
262
|
+
// 拖拽父元素变化需要重新布局
|
|
263
|
+
if (this.transform.parent !== transform?.transform) {
|
|
264
|
+
this.localDirty = true;
|
|
265
|
+
}
|
|
266
|
+
this.transform.setParent(transform?.transform);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
get spacing(): number {
|
|
270
|
+
const { spacing } = this.entity.getNodeMeta();
|
|
271
|
+
return typeof spacing === 'function' ? spacing(this) : spacing;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
get inlineSpacingPre(): number {
|
|
275
|
+
const { inlineSpacingPre } = this.entity.getNodeMeta();
|
|
276
|
+
return typeof inlineSpacingPre === 'function' ? inlineSpacingPre(this) : inlineSpacingPre;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
get inlineSpacingAfter(): number {
|
|
280
|
+
const { inlineSpacingAfter } = this.entity.getNodeMeta();
|
|
281
|
+
return typeof inlineSpacingAfter === 'function' ? inlineSpacingAfter(this) : inlineSpacingAfter;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
get minInlineBlockSpacing(): number {
|
|
285
|
+
const { minInlineBlockSpacing } = this.entity.getNodeMeta();
|
|
286
|
+
return typeof minInlineBlockSpacing === 'function'
|
|
287
|
+
? minInlineBlockSpacing(this)
|
|
288
|
+
: minInlineBlockSpacing;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
get children(): FlowNodeTransformData[] {
|
|
292
|
+
return this.entity.children.map(
|
|
293
|
+
(child) => child.getData<FlowNodeTransformData>(FlowNodeTransformData)!
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* 上一个节点的 transform 数据
|
|
299
|
+
*/
|
|
300
|
+
get pre(): FlowNodeTransformData | undefined {
|
|
301
|
+
return this.entity.pre?.getData<FlowNodeTransformData>(FlowNodeTransformData);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
get originParent(): FlowNodeTransformData | undefined {
|
|
305
|
+
return this.entity.originParent?.getData<FlowNodeTransformData>(FlowNodeTransformData);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
get isFirst(): boolean {
|
|
309
|
+
return this.entity.isFirst;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
get isLast(): boolean {
|
|
313
|
+
return this.entity.isLast;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
get lastChild(): FlowNodeTransformData | undefined {
|
|
317
|
+
return this.entity.lastChild?.getData<FlowNodeTransformData>(FlowNodeTransformData);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
get firstChild(): FlowNodeTransformData | undefined {
|
|
321
|
+
return this.entity.firstChild?.getData<FlowNodeTransformData>(FlowNodeTransformData);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* 下一个节点的 transform 数据
|
|
326
|
+
*/
|
|
327
|
+
get next(): FlowNodeTransformData | undefined {
|
|
328
|
+
return this.entity.next?.getData<FlowNodeTransformData>(FlowNodeTransformData);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* parent 节点的 transform 数据
|
|
333
|
+
*/
|
|
334
|
+
get parent(): FlowNodeTransformData | undefined {
|
|
335
|
+
return this.entity.parent?.getData<FlowNodeTransformData>(FlowNodeTransformData);
|
|
336
|
+
}
|
|
337
|
+
}
|