@flowgram-vue/renderer 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 +2933 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +721 -0
- package/dist/index.js +2918 -0
- package/dist/index.js.map +1 -0
- package/index.module.less +167 -0
- package/package.json +64 -0
- package/src/components/Adder.ts +107 -0
- package/src/components/BranchDraggableRenderer.ts +77 -0
- package/src/components/Collapse.ts +87 -0
- package/src/components/CollapseAdder.ts +94 -0
- package/src/components/CustomLine.ts +35 -0
- package/src/components/LabelsRenderer.ts +162 -0
- package/src/components/LinesRenderer.ts +99 -0
- package/src/components/MarkerActivatedArrow.ts +44 -0
- package/src/components/MarkerArrow.ts +44 -0
- package/src/components/RoundedTurningLine.ts +165 -0
- package/src/components/StraightLine.ts +31 -0
- package/src/components/utils.ts +295 -0
- package/src/entities/README.md +3 -0
- package/src/entities/flow-drag-entity.ts +267 -0
- package/src/entities/flow-select-config-entity.ts +114 -0
- package/src/entities/index.ts +8 -0
- package/src/entities/selector-box-config-entity.ts +88 -0
- package/src/env.d.ts +10 -0
- package/src/flow-renderer-container-module.ts +14 -0
- package/src/flow-renderer-contribution.ts +12 -0
- package/src/flow-renderer-registry.ts +155 -0
- package/src/flow-renderer-resize-observer.ts +56 -0
- package/src/hooks/use-base-color.ts +26 -0
- package/src/index.ts +16 -0
- package/src/layer-vue-provide.ts +44 -0
- package/src/layers/flow-context-menu-layer.ts +153 -0
- package/src/layers/flow-debug-layer.ts +227 -0
- package/src/layers/flow-drag-layer.ts +413 -0
- package/src/layers/flow-labels-layer.ts +100 -0
- package/src/layers/flow-lines-layer.ts +111 -0
- package/src/layers/flow-nodes-content-layer.ts +155 -0
- package/src/layers/flow-nodes-transform-layer.ts +151 -0
- package/src/layers/flow-scroll-bar-layer.ts +401 -0
- package/src/layers/flow-scroll-limit-layer.ts +36 -0
- package/src/layers/flow-selector-bounds-layer.ts +191 -0
- package/src/layers/flow-selector-box-layer.ts +244 -0
- package/src/layers/index.ts +16 -0
- package/src/utils/element.ts +36 -0
- package/src/utils/find-selected-nodes.ts +80 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/scroll-bar-events.ts +13 -0
- package/src/utils/scroll-limit.ts +58 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Fragment, h, type VNode } from 'vue';
|
|
7
|
+
import { throttle } from 'lodash-es';
|
|
8
|
+
import { inject, injectable, optional } from 'inversify';
|
|
9
|
+
import { domUtils } from '@flowgram-vue/utils';
|
|
10
|
+
import {
|
|
11
|
+
FlowDocument,
|
|
12
|
+
FlowDocumentOptions,
|
|
13
|
+
FlowDocumentTransformerEntity,
|
|
14
|
+
FlowNodeEntity,
|
|
15
|
+
FlowNodeTransitionData,
|
|
16
|
+
FlowRendererStateEntity,
|
|
17
|
+
} from '@flowgram-vue/document';
|
|
18
|
+
import { Layer, observeEntity, observeEntityDatas, PlaygroundContainerFactory } from '@flowgram-vue/core';
|
|
19
|
+
|
|
20
|
+
import { wrapLayerRender } from '../layer-vue-provide';
|
|
21
|
+
import { getBaseColor } from '../hooks/use-base-color';
|
|
22
|
+
import { FlowRendererRegistry } from '../flow-renderer-registry';
|
|
23
|
+
import { createLabels } from '../components/LabelsRenderer';
|
|
24
|
+
|
|
25
|
+
@injectable()
|
|
26
|
+
export class FlowLabelsLayer extends Layer {
|
|
27
|
+
@inject(FlowDocument) readonly document: FlowDocument;
|
|
28
|
+
|
|
29
|
+
@inject(FlowRendererRegistry) readonly rendererRegistry: FlowRendererRegistry;
|
|
30
|
+
|
|
31
|
+
@inject(PlaygroundContainerFactory) readonly playgroundContainer: PlaygroundContainerFactory;
|
|
32
|
+
|
|
33
|
+
@optional()
|
|
34
|
+
@inject(FlowDocumentOptions)
|
|
35
|
+
readonly documentOptions?: FlowDocumentOptions;
|
|
36
|
+
|
|
37
|
+
node = domUtils.createDivWithClass('gedit-flow-labels-layer');
|
|
38
|
+
|
|
39
|
+
@observeEntity(FlowDocumentTransformerEntity)
|
|
40
|
+
readonly documentTransformer: FlowDocumentTransformerEntity;
|
|
41
|
+
|
|
42
|
+
@observeEntity(FlowRendererStateEntity)
|
|
43
|
+
readonly flowRenderState: FlowRendererStateEntity;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 监听 transition 变化
|
|
47
|
+
*/
|
|
48
|
+
@observeEntityDatas(FlowNodeEntity, FlowNodeTransitionData)
|
|
49
|
+
_transitions: FlowNodeTransitionData[];
|
|
50
|
+
|
|
51
|
+
get transitions(): FlowNodeTransitionData[] {
|
|
52
|
+
return this.document.getRenderDatas<FlowNodeTransitionData>(FlowNodeTransitionData);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 监听缩放,目前采用整体缩放
|
|
57
|
+
* @param scale
|
|
58
|
+
*/
|
|
59
|
+
onZoom(scale: number) {
|
|
60
|
+
this.node!.style.transform = `scale(${scale})`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 可视区域变化
|
|
65
|
+
*/
|
|
66
|
+
onViewportChange: ReturnType<typeof throttle> = throttle(() => {
|
|
67
|
+
this.render();
|
|
68
|
+
}, 100);
|
|
69
|
+
|
|
70
|
+
onReady() {
|
|
71
|
+
// 图层顺序调整:节点 > label > 线条
|
|
72
|
+
// 节点 z-index: 10
|
|
73
|
+
this.node.style.zIndex = '9';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 监听readonly和 disabled 状态 并刷新layer, 并刷新
|
|
78
|
+
*/
|
|
79
|
+
onReadonlyOrDisabledChange() {
|
|
80
|
+
this.render();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
render() {
|
|
84
|
+
const labels: VNode[] = [];
|
|
85
|
+
if (this.documentTransformer?.loading) return null;
|
|
86
|
+
this.documentTransformer?.refresh?.();
|
|
87
|
+
const { baseActivatedColor, baseColor } = getBaseColor(this.documentOptions);
|
|
88
|
+
const isViewportVisible = this.config.isViewportVisible.bind(this.config);
|
|
89
|
+
this.transitions.forEach((transition) => {
|
|
90
|
+
createLabels({
|
|
91
|
+
data: transition,
|
|
92
|
+
rendererRegistry: this.rendererRegistry,
|
|
93
|
+
isViewportVisible,
|
|
94
|
+
labelsSave: labels,
|
|
95
|
+
getLabelColor: (activated) => (activated ? baseActivatedColor : baseColor),
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
return wrapLayerRender(this.playgroundContainer, h(Fragment, null, labels));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { h, type VNode } from 'vue';
|
|
7
|
+
import { groupBy, throttle } from 'lodash-es';
|
|
8
|
+
import { inject, injectable } from 'inversify';
|
|
9
|
+
import { domUtils } from '@flowgram-vue/utils';
|
|
10
|
+
import {
|
|
11
|
+
FlowDocument,
|
|
12
|
+
FlowDocumentTransformerEntity,
|
|
13
|
+
FlowNodeEntity,
|
|
14
|
+
FlowNodeTransitionData,
|
|
15
|
+
FlowRendererStateEntity,
|
|
16
|
+
FlowDragService,
|
|
17
|
+
} from '@flowgram-vue/document';
|
|
18
|
+
import { Layer, observeEntity, observeEntityDatas, PlaygroundContainerFactory } from '@flowgram-vue/core';
|
|
19
|
+
|
|
20
|
+
import { wrapLayerRender } from '../layer-vue-provide';
|
|
21
|
+
import { FlowRendererRegistry } from '../flow-renderer-registry';
|
|
22
|
+
import { createLines } from '../components/LinesRenderer';
|
|
23
|
+
|
|
24
|
+
@injectable()
|
|
25
|
+
export class FlowLinesLayer extends Layer {
|
|
26
|
+
@inject(FlowDocument) readonly document: FlowDocument;
|
|
27
|
+
|
|
28
|
+
@inject(FlowDragService)
|
|
29
|
+
protected readonly dragService: FlowDragService;
|
|
30
|
+
|
|
31
|
+
@inject(FlowRendererRegistry) readonly rendererRegistry: FlowRendererRegistry;
|
|
32
|
+
|
|
33
|
+
@inject(PlaygroundContainerFactory) readonly playgroundContainer: PlaygroundContainerFactory;
|
|
34
|
+
|
|
35
|
+
node = domUtils.createDivWithClass('gedit-flow-lines-layer');
|
|
36
|
+
|
|
37
|
+
@observeEntity(FlowDocumentTransformerEntity)
|
|
38
|
+
readonly documentTransformer: FlowDocumentTransformerEntity;
|
|
39
|
+
|
|
40
|
+
@observeEntity(FlowRendererStateEntity)
|
|
41
|
+
readonly flowRenderState: FlowRendererStateEntity;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 监听 transition 变化
|
|
45
|
+
*/
|
|
46
|
+
@observeEntityDatas(FlowNodeEntity, FlowNodeTransitionData)
|
|
47
|
+
_transitions: FlowNodeTransitionData[];
|
|
48
|
+
|
|
49
|
+
get transitions(): FlowNodeTransitionData[] {
|
|
50
|
+
return this.document.getRenderDatas<FlowNodeTransitionData>(FlowNodeTransitionData);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 可视区域变化
|
|
55
|
+
*/
|
|
56
|
+
onViewportChange: ReturnType<typeof throttle> = throttle(() => {
|
|
57
|
+
this.render();
|
|
58
|
+
}, 100);
|
|
59
|
+
|
|
60
|
+
onZoom() {
|
|
61
|
+
const svgContainer = this.node!.querySelector('svg.flow-lines-container')!;
|
|
62
|
+
svgContainer?.setAttribute?.('viewBox', this.viewBox);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
onReady() {
|
|
66
|
+
this.node.style.zIndex = '1';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get viewBox(): string {
|
|
70
|
+
const ratio = 1000 / this.config.finalScale;
|
|
71
|
+
return `0 0 ${ratio} ${ratio}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
render() {
|
|
75
|
+
const allLines: VNode[] = [];
|
|
76
|
+
const isViewportVisible = this.config.isViewportVisible.bind(this.config);
|
|
77
|
+
if (this.documentTransformer.loading) return null;
|
|
78
|
+
this.documentTransformer.refresh();
|
|
79
|
+
|
|
80
|
+
this.transitions.forEach((transition) => {
|
|
81
|
+
createLines({
|
|
82
|
+
data: transition,
|
|
83
|
+
rendererRegistry: this.rendererRegistry,
|
|
84
|
+
isViewportVisible,
|
|
85
|
+
linesSave: allLines,
|
|
86
|
+
dragService: this.dragService,
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const { activateLines = [], normalLines = [] } = groupBy(allLines, (line) =>
|
|
91
|
+
line.props?.activated ? 'activateLines' : 'normalLines'
|
|
92
|
+
);
|
|
93
|
+
const resultLines = [...normalLines, ...activateLines];
|
|
94
|
+
|
|
95
|
+
return wrapLayerRender(
|
|
96
|
+
this.playgroundContainer,
|
|
97
|
+
h(
|
|
98
|
+
'svg',
|
|
99
|
+
{
|
|
100
|
+
class: 'flow-lines-container',
|
|
101
|
+
width: '1000',
|
|
102
|
+
height: '1000',
|
|
103
|
+
overflow: 'visible',
|
|
104
|
+
viewBox: this.viewBox,
|
|
105
|
+
xmlns: 'http://www.w3.org/2000/svg',
|
|
106
|
+
},
|
|
107
|
+
resultLines
|
|
108
|
+
)
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { defineComponent, Fragment, h, onMounted, provide, Teleport, type Component } from 'vue';
|
|
7
|
+
import { inject, injectable } from 'inversify';
|
|
8
|
+
import { Cache, type CacheOriginItem, domUtils } from '@flowgram-vue/utils';
|
|
9
|
+
import {
|
|
10
|
+
FlowDocument,
|
|
11
|
+
FlowDocumentTransformerEntity,
|
|
12
|
+
FlowNodeEntity,
|
|
13
|
+
FlowNodeRenderData,
|
|
14
|
+
FlowNodeTransformData,
|
|
15
|
+
} from '@flowgram-vue/document';
|
|
16
|
+
import {
|
|
17
|
+
Layer,
|
|
18
|
+
observeEntity,
|
|
19
|
+
observeEntityDatas,
|
|
20
|
+
PlaygroundContainerFactory,
|
|
21
|
+
PlaygroundEntityContextKey,
|
|
22
|
+
} from '@flowgram-vue/core';
|
|
23
|
+
|
|
24
|
+
import { wrapLayerRender } from '../layer-vue-provide';
|
|
25
|
+
import { FlowRendererKey, FlowRendererRegistry } from '../flow-renderer-registry';
|
|
26
|
+
|
|
27
|
+
interface NodePortal extends CacheOriginItem {
|
|
28
|
+
id: string;
|
|
29
|
+
Portal: Component;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 渲染节点内容
|
|
34
|
+
*/
|
|
35
|
+
@injectable()
|
|
36
|
+
export class FlowNodesContentLayer extends Layer {
|
|
37
|
+
@inject(FlowDocument) readonly document: FlowDocument;
|
|
38
|
+
|
|
39
|
+
@inject(FlowRendererRegistry) readonly rendererRegistry: FlowRendererRegistry;
|
|
40
|
+
|
|
41
|
+
@inject(PlaygroundContainerFactory) readonly playgroundContainer: PlaygroundContainerFactory;
|
|
42
|
+
|
|
43
|
+
@observeEntity(FlowDocumentTransformerEntity)
|
|
44
|
+
readonly documentTransformer: FlowDocumentTransformerEntity;
|
|
45
|
+
|
|
46
|
+
@observeEntityDatas(FlowNodeEntity, FlowNodeRenderData)
|
|
47
|
+
_renderStates: FlowNodeRenderData[];
|
|
48
|
+
|
|
49
|
+
get renderStatesVisible(): FlowNodeRenderData[] {
|
|
50
|
+
return this.document.getRenderDatas<FlowNodeRenderData>(FlowNodeRenderData, false);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private renderMemoCache = new WeakMap<any, Component>();
|
|
54
|
+
|
|
55
|
+
node = domUtils.createDivWithClass('gedit-flow-nodes-layer');
|
|
56
|
+
|
|
57
|
+
getPortalRenderer(data: FlowNodeRenderData): Component {
|
|
58
|
+
const meta = data.entity.getNodeMeta();
|
|
59
|
+
const renderer = this.rendererRegistry.getRendererComponent(
|
|
60
|
+
(meta.renderKey as FlowRendererKey) || FlowRendererKey.NODE_RENDER
|
|
61
|
+
);
|
|
62
|
+
const vueRenderer = renderer.renderer as Component;
|
|
63
|
+
let memoCache = this.renderMemoCache.get(vueRenderer);
|
|
64
|
+
if (!memoCache) {
|
|
65
|
+
memoCache = vueRenderer;
|
|
66
|
+
this.renderMemoCache.set(vueRenderer, memoCache);
|
|
67
|
+
}
|
|
68
|
+
return memoCache;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 监听缩放,目前采用整体缩放
|
|
73
|
+
* @param scale
|
|
74
|
+
*/
|
|
75
|
+
onZoom(scale: number) {
|
|
76
|
+
this.node!.style.transform = `scale(${scale})`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
dispose(): void {
|
|
80
|
+
this.vuePortals.dispose();
|
|
81
|
+
super.dispose();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
protected vuePortals = Cache.create<NodePortal, FlowNodeRenderData>(
|
|
85
|
+
(data?: FlowNodeRenderData) => {
|
|
86
|
+
const { node, entity } = data!;
|
|
87
|
+
const { config } = this;
|
|
88
|
+
const PortalRenderer = this.getPortalRenderer(data!);
|
|
89
|
+
|
|
90
|
+
const Portal = defineComponent({
|
|
91
|
+
name: 'FlowNodePortal',
|
|
92
|
+
setup() {
|
|
93
|
+
provide(PlaygroundEntityContextKey, entity);
|
|
94
|
+
onMounted(() => {
|
|
95
|
+
if (!entity.getNodeMeta().autoResizeDisable && node.clientWidth && node.clientHeight) {
|
|
96
|
+
const transform = entity.getData<FlowNodeTransformData>(FlowNodeTransformData);
|
|
97
|
+
if (transform)
|
|
98
|
+
transform.size = {
|
|
99
|
+
width: node.clientWidth,
|
|
100
|
+
height: node.clientHeight,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
return () =>
|
|
105
|
+
h(Teleport, { to: node }, [
|
|
106
|
+
h(PortalRenderer, {
|
|
107
|
+
node: entity,
|
|
108
|
+
version: data?.version,
|
|
109
|
+
activated: data?.activated,
|
|
110
|
+
readonly: config.readonly,
|
|
111
|
+
disabled: config.disabled,
|
|
112
|
+
}),
|
|
113
|
+
]);
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
id: node.id || entity.id,
|
|
119
|
+
dispose: () => {
|
|
120
|
+
// TODO, 删除逻辑由 node 去控制了
|
|
121
|
+
},
|
|
122
|
+
Portal,
|
|
123
|
+
} as NodePortal;
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
onReady() {
|
|
128
|
+
this.node!.style.zIndex = '10';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 监听readonly和 disabled 状态 并刷新layer, 并刷新节点
|
|
133
|
+
*/
|
|
134
|
+
onReadonlyOrDisabledChange() {
|
|
135
|
+
this.render();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
getPortals(): NodePortal[] {
|
|
139
|
+
return this.vuePortals.getMoreByItems(this.renderStatesVisible);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
render() {
|
|
143
|
+
if (this.documentTransformer.loading) return null;
|
|
144
|
+
this.documentTransformer.refresh();
|
|
145
|
+
|
|
146
|
+
return wrapLayerRender(
|
|
147
|
+
this.playgroundContainer,
|
|
148
|
+
h(
|
|
149
|
+
Fragment,
|
|
150
|
+
null,
|
|
151
|
+
this.getPortals().map((portal) => h(portal.Portal, { key: portal.id }))
|
|
152
|
+
)
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { inject, injectable } from 'inversify';
|
|
7
|
+
import { Cache, type Disposable, domUtils } from '@flowgram-vue/utils';
|
|
8
|
+
import {
|
|
9
|
+
FlowDocument,
|
|
10
|
+
FlowDocumentTransformerEntity,
|
|
11
|
+
FlowNodeEntity,
|
|
12
|
+
FlowNodeTransformData,
|
|
13
|
+
} from '@flowgram-vue/document';
|
|
14
|
+
import { Layer, observeEntity, observeEntityDatas } from '@flowgram-vue/core';
|
|
15
|
+
// import { throttle } from 'lodash-es'
|
|
16
|
+
|
|
17
|
+
import { FlowRendererResizeObserver } from '../flow-renderer-resize-observer';
|
|
18
|
+
|
|
19
|
+
interface TransformRenderCache {
|
|
20
|
+
updateBounds(): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface FlowNodesTransformLayerOptions {
|
|
24
|
+
renderElement?: HTMLElement | (() => HTMLElement | undefined);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 渲染节点位置
|
|
29
|
+
*/
|
|
30
|
+
@injectable()
|
|
31
|
+
export class FlowNodesTransformLayer extends Layer<FlowNodesTransformLayerOptions> {
|
|
32
|
+
@inject(FlowDocument) readonly document: FlowDocument;
|
|
33
|
+
|
|
34
|
+
@inject(FlowRendererResizeObserver)
|
|
35
|
+
readonly resizeObserver: FlowRendererResizeObserver;
|
|
36
|
+
|
|
37
|
+
@observeEntity(FlowDocumentTransformerEntity)
|
|
38
|
+
readonly documentTransformer: FlowDocumentTransformerEntity;
|
|
39
|
+
|
|
40
|
+
@observeEntityDatas(FlowNodeEntity, FlowNodeTransformData)
|
|
41
|
+
_transforms: FlowNodeTransformData[];
|
|
42
|
+
|
|
43
|
+
node = domUtils.createDivWithClass('gedit-flow-nodes-layer');
|
|
44
|
+
|
|
45
|
+
get transformVisibles(): FlowNodeTransformData[] {
|
|
46
|
+
return this.document.getRenderDatas<FlowNodeTransformData>(FlowNodeTransformData, false);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 监听缩放,目前采用整体缩放
|
|
51
|
+
* @param scale
|
|
52
|
+
*/
|
|
53
|
+
onZoom(scale: number) {
|
|
54
|
+
this.node!.style.transform = `scale(${scale})`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
dispose(): void {
|
|
58
|
+
this.renderCache.dispose();
|
|
59
|
+
super.dispose();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// onViewportChange() {
|
|
63
|
+
// this.throttleUpdate()
|
|
64
|
+
// }
|
|
65
|
+
|
|
66
|
+
// throttleUpdate = throttle(() => {
|
|
67
|
+
// this.renderCache.getFromCache().forEach((cache) => cache.updateBounds())
|
|
68
|
+
// }, 100)
|
|
69
|
+
|
|
70
|
+
protected renderCache = Cache.create<TransformRenderCache, FlowNodeTransformData>(
|
|
71
|
+
(transform?: FlowNodeTransformData) => {
|
|
72
|
+
const { renderState } = transform!;
|
|
73
|
+
const { node } = renderState;
|
|
74
|
+
const { entity } = transform!;
|
|
75
|
+
node.id = entity.id;
|
|
76
|
+
let resizeDispose: Disposable | undefined;
|
|
77
|
+
const append = () => {
|
|
78
|
+
if (resizeDispose) return;
|
|
79
|
+
// 监听 dom 节点的大小变化
|
|
80
|
+
this.renderElement.appendChild(node);
|
|
81
|
+
if (!entity.getNodeMeta().autoResizeDisable) {
|
|
82
|
+
resizeDispose = this.resizeObserver.observe(node, transform!);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const dispose = () => {
|
|
86
|
+
if (!resizeDispose) return;
|
|
87
|
+
// 脱离文档流,但是 react 组件会保留
|
|
88
|
+
if (node.parentElement) {
|
|
89
|
+
this.renderElement.removeChild(node);
|
|
90
|
+
}
|
|
91
|
+
resizeDispose.dispose();
|
|
92
|
+
resizeDispose = undefined;
|
|
93
|
+
};
|
|
94
|
+
append();
|
|
95
|
+
return {
|
|
96
|
+
dispose,
|
|
97
|
+
updateBounds: () => {
|
|
98
|
+
const { bounds } = transform!;
|
|
99
|
+
// 保留2位小数
|
|
100
|
+
const rawX: number = parseFloat(node.style.left);
|
|
101
|
+
const rawY: number = parseFloat(node.style.top);
|
|
102
|
+
if (!this.isCoordEqual(rawX, bounds.x) || !this.isCoordEqual(rawY, bounds.y)) {
|
|
103
|
+
node.style.left = `${bounds.x}px`;
|
|
104
|
+
node.style.top = `${bounds.y}px`;
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
private isCoordEqual(a: number, b: number) {
|
|
112
|
+
const browserCoordEpsilon = 0.05; // 浏览器处理坐标的精度误差: 两位小数四舍五入
|
|
113
|
+
return Math.abs(a - b) < browserCoordEpsilon;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
onReady() {
|
|
117
|
+
this.node!.style.zIndex = '10';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
get visibeBounds() {
|
|
121
|
+
return this.transformVisibles.map((transform) => transform.bounds);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 更新节点的 bounds 数据
|
|
126
|
+
*/
|
|
127
|
+
updateNodesBounds() {
|
|
128
|
+
this.renderCache
|
|
129
|
+
.getMoreByItems(this.transformVisibles)
|
|
130
|
+
.forEach((render) => render.updateBounds());
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
autorun() {
|
|
134
|
+
// 更新节点偏移数据 O(n) TODO 这个更新会从 render 里移除改成自动触发
|
|
135
|
+
if (this.documentTransformer.loading) return;
|
|
136
|
+
this.documentTransformer.refresh();
|
|
137
|
+
this.updateNodesBounds();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private get renderElement(): HTMLElement {
|
|
141
|
+
if (typeof this.options.renderElement === 'function') {
|
|
142
|
+
const element = this.options.renderElement();
|
|
143
|
+
if (element) {
|
|
144
|
+
return element;
|
|
145
|
+
}
|
|
146
|
+
} else if (typeof this.options.renderElement !== 'undefined') {
|
|
147
|
+
return this.options.renderElement as HTMLElement;
|
|
148
|
+
}
|
|
149
|
+
return this.node;
|
|
150
|
+
}
|
|
151
|
+
}
|