@flowgram-vue/fixed-layout-editor 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 +0 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +0 -0
- package/dist/index.js.map +1 -0
- package/index.css +176 -0
- package/package.json +71 -0
- package/src/components/fixed-layout-editor-provider.vue +102 -0
- package/src/components/fixed-layout-editor.vue +54 -0
- package/src/components/index.ts +7 -0
- package/src/env.d.ts +10 -0
- package/src/hooks/use-client-context.ts +12 -0
- package/src/hooks/use-node-render.ts +222 -0
- package/src/hooks/use-playground-tools.ts +192 -0
- package/src/index.ts +23 -0
- package/src/plugins/create-operation-plugin.ts +23 -0
- package/src/preset/fixed-layout-preset.ts +184 -0
- package/src/preset/fixed-layout-props.ts +84 -0
- package/src/preset/index.ts +7 -0
- package/src/preset/node-serialize.ts +72 -0
- package/src/services/flow-operation-service.ts +59 -0
- package/src/services/history-operation-service.ts +125 -0
- package/src/types.ts +40 -0
- package/src/utils/compose.ts +18 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { interfaces } from 'inversify';
|
|
7
|
+
import { HistoryService } from '@flowgram-vue/history';
|
|
8
|
+
import {
|
|
9
|
+
FlowDocument,
|
|
10
|
+
createPluginContextDefault,
|
|
11
|
+
PlaygroundVueProvider,
|
|
12
|
+
ClipboardService,
|
|
13
|
+
SelectionService,
|
|
14
|
+
Playground,
|
|
15
|
+
} from '@flowgram-vue/editor';
|
|
16
|
+
import { ref } from 'vue';
|
|
17
|
+
|
|
18
|
+
import { FlowOperationService } from '../types';
|
|
19
|
+
import {
|
|
20
|
+
createFixedLayoutPreset,
|
|
21
|
+
type FixedLayoutPluginContext,
|
|
22
|
+
type FixedLayoutPluginTools,
|
|
23
|
+
type FixedLayoutProps,
|
|
24
|
+
} from '../preset';
|
|
25
|
+
|
|
26
|
+
defineOptions({ name: 'FixedLayoutEditorProvider' });
|
|
27
|
+
|
|
28
|
+
const props = defineProps<FixedLayoutProps>();
|
|
29
|
+
const providerRef = ref<FixedLayoutPluginContext | null>(null);
|
|
30
|
+
|
|
31
|
+
const preset = createFixedLayoutPreset(props);
|
|
32
|
+
|
|
33
|
+
const customPluginContext = (container: interfaces.Container) =>
|
|
34
|
+
({
|
|
35
|
+
...createPluginContextDefault(container),
|
|
36
|
+
get document(): FlowDocument {
|
|
37
|
+
return container.get<FlowDocument>(FlowDocument);
|
|
38
|
+
},
|
|
39
|
+
get operation(): FlowOperationService {
|
|
40
|
+
return container.get<FlowOperationService>(FlowOperationService);
|
|
41
|
+
},
|
|
42
|
+
get clipboard(): ClipboardService {
|
|
43
|
+
return container.get<ClipboardService>(ClipboardService);
|
|
44
|
+
},
|
|
45
|
+
get selection(): SelectionService {
|
|
46
|
+
return container.get<SelectionService>(SelectionService);
|
|
47
|
+
},
|
|
48
|
+
get history(): HistoryService {
|
|
49
|
+
return container.get<HistoryService>(HistoryService);
|
|
50
|
+
},
|
|
51
|
+
get tools(): FixedLayoutPluginTools {
|
|
52
|
+
return {
|
|
53
|
+
fitView: (easing?: boolean) => {
|
|
54
|
+
const playgroundConfig = container.get<Playground>(Playground).config;
|
|
55
|
+
const doc = container.get(FlowDocument);
|
|
56
|
+
return playgroundConfig.fitView(doc.root.bounds, easing, 30);
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
}) as FixedLayoutPluginContext;
|
|
61
|
+
|
|
62
|
+
defineExpose({
|
|
63
|
+
get playground() {
|
|
64
|
+
return providerRef.value?.playground as FixedLayoutPluginContext['playground'];
|
|
65
|
+
},
|
|
66
|
+
get container() {
|
|
67
|
+
return providerRef.value?.container as FixedLayoutPluginContext['container'];
|
|
68
|
+
},
|
|
69
|
+
get document() {
|
|
70
|
+
return providerRef.value?.document as FixedLayoutPluginContext['document'];
|
|
71
|
+
},
|
|
72
|
+
get operation() {
|
|
73
|
+
return providerRef.value?.operation as FixedLayoutPluginContext['operation'];
|
|
74
|
+
},
|
|
75
|
+
get clipboard() {
|
|
76
|
+
return providerRef.value?.clipboard as FixedLayoutPluginContext['clipboard'];
|
|
77
|
+
},
|
|
78
|
+
get selection() {
|
|
79
|
+
return providerRef.value?.selection as FixedLayoutPluginContext['selection'];
|
|
80
|
+
},
|
|
81
|
+
get history() {
|
|
82
|
+
return providerRef.value?.history as FixedLayoutPluginContext['history'];
|
|
83
|
+
},
|
|
84
|
+
get tools() {
|
|
85
|
+
return providerRef.value?.tools as FixedLayoutPluginContext['tools'];
|
|
86
|
+
},
|
|
87
|
+
get: ((identifier) => providerRef.value?.get(identifier)) as FixedLayoutPluginContext['get'],
|
|
88
|
+
getAll: ((identifier) =>
|
|
89
|
+
providerRef.value?.getAll(identifier) ?? []) as FixedLayoutPluginContext['getAll'],
|
|
90
|
+
});
|
|
91
|
+
</script>
|
|
92
|
+
|
|
93
|
+
<template>
|
|
94
|
+
<PlaygroundVueProvider
|
|
95
|
+
ref="providerRef"
|
|
96
|
+
:plugins="preset"
|
|
97
|
+
:custom-plugin-context="customPluginContext"
|
|
98
|
+
:parent-container="props.parentContainer"
|
|
99
|
+
>
|
|
100
|
+
<slot />
|
|
101
|
+
</PlaygroundVueProvider>
|
|
102
|
+
</template>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { EditorRenderer } from '@flowgram-vue/editor';
|
|
7
|
+
import { ref } from 'vue';
|
|
8
|
+
|
|
9
|
+
import { type FixedLayoutPluginContext, type FixedLayoutProps } from '../preset';
|
|
10
|
+
import FixedLayoutEditorProvider from './fixed-layout-editor-provider.vue';
|
|
11
|
+
|
|
12
|
+
defineOptions({ name: 'FixedLayoutEditor' });
|
|
13
|
+
|
|
14
|
+
const props = defineProps<FixedLayoutProps>();
|
|
15
|
+
const providerRef = ref<FixedLayoutPluginContext | null>(null);
|
|
16
|
+
|
|
17
|
+
defineExpose({
|
|
18
|
+
get playground() {
|
|
19
|
+
return providerRef.value?.playground as FixedLayoutPluginContext['playground'];
|
|
20
|
+
},
|
|
21
|
+
get container() {
|
|
22
|
+
return providerRef.value?.container as FixedLayoutPluginContext['container'];
|
|
23
|
+
},
|
|
24
|
+
get document() {
|
|
25
|
+
return providerRef.value?.document as FixedLayoutPluginContext['document'];
|
|
26
|
+
},
|
|
27
|
+
get operation() {
|
|
28
|
+
return providerRef.value?.operation as FixedLayoutPluginContext['operation'];
|
|
29
|
+
},
|
|
30
|
+
get clipboard() {
|
|
31
|
+
return providerRef.value?.clipboard as FixedLayoutPluginContext['clipboard'];
|
|
32
|
+
},
|
|
33
|
+
get selection() {
|
|
34
|
+
return providerRef.value?.selection as FixedLayoutPluginContext['selection'];
|
|
35
|
+
},
|
|
36
|
+
get history() {
|
|
37
|
+
return providerRef.value?.history as FixedLayoutPluginContext['history'];
|
|
38
|
+
},
|
|
39
|
+
get tools() {
|
|
40
|
+
return providerRef.value?.tools as FixedLayoutPluginContext['tools'];
|
|
41
|
+
},
|
|
42
|
+
get: ((identifier) => providerRef.value?.get(identifier)) as FixedLayoutPluginContext['get'],
|
|
43
|
+
getAll: ((identifier) =>
|
|
44
|
+
providerRef.value?.getAll(identifier) ?? []) as FixedLayoutPluginContext['getAll'],
|
|
45
|
+
});
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<template>
|
|
49
|
+
<FixedLayoutEditorProvider ref="providerRef" v-bind="props">
|
|
50
|
+
<EditorRenderer>
|
|
51
|
+
<slot />
|
|
52
|
+
</EditorRenderer>
|
|
53
|
+
</FixedLayoutEditorProvider>
|
|
54
|
+
</template>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { default as FixedLayoutEditorProvider } from './fixed-layout-editor-provider.vue';
|
|
7
|
+
export { default as FixedLayoutEditor } from './fixed-layout-editor.vue';
|
package/src/env.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
declare module '*.vue' {
|
|
7
|
+
import type { DefineComponent } from 'vue';
|
|
8
|
+
const component: DefineComponent<object, object, unknown>;
|
|
9
|
+
export default component;
|
|
10
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useService, PluginContext } from '@flowgram-vue/editor';
|
|
7
|
+
|
|
8
|
+
import { FixedLayoutPluginContext } from '../preset';
|
|
9
|
+
|
|
10
|
+
export function useClientContext(): FixedLayoutPluginContext {
|
|
11
|
+
return useService<FixedLayoutPluginContext>(PluginContext);
|
|
12
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { inject, onBeforeUnmount } from 'vue';
|
|
7
|
+
|
|
8
|
+
import { useObserve } from '@flowgram-vue/reactive';
|
|
9
|
+
import { useStartDragNode } from '@flowgram-vue/fixed-drag-plugin';
|
|
10
|
+
import {
|
|
11
|
+
usePlayground,
|
|
12
|
+
FlowNodeBaseType,
|
|
13
|
+
FlowNodeEntity,
|
|
14
|
+
FlowNodeRenderData,
|
|
15
|
+
useService,
|
|
16
|
+
type Disposable,
|
|
17
|
+
PlaygroundEntityContextKey,
|
|
18
|
+
type NodeFormProps,
|
|
19
|
+
getNodeForm,
|
|
20
|
+
} from '@flowgram-vue/editor';
|
|
21
|
+
|
|
22
|
+
import { FlowOperationService } from '../types';
|
|
23
|
+
|
|
24
|
+
export interface NodeRenderReturnType {
|
|
25
|
+
id: string;
|
|
26
|
+
type: string | number;
|
|
27
|
+
/**
|
|
28
|
+
* 节点 data 数据
|
|
29
|
+
*/
|
|
30
|
+
data: any;
|
|
31
|
+
/**
|
|
32
|
+
* 更新节点 data 数据
|
|
33
|
+
*/
|
|
34
|
+
updateData: (newData: any) => void;
|
|
35
|
+
/**
|
|
36
|
+
* BlockOrderIcon节点,一般用于分支的第一个占位节点
|
|
37
|
+
*/
|
|
38
|
+
isBlockOrderIcon: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* BlockIcon 节点,一般用于带有分支的占位节点
|
|
41
|
+
*/
|
|
42
|
+
isBlockIcon: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* 当前节点 (如果是 icon 则会返回它的父节点)
|
|
45
|
+
*/
|
|
46
|
+
node: FlowNodeEntity;
|
|
47
|
+
/**
|
|
48
|
+
* 是否在拖拽中
|
|
49
|
+
*/
|
|
50
|
+
dragging: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* 节点是否激活
|
|
53
|
+
*/
|
|
54
|
+
activated: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* 节点是否展开
|
|
57
|
+
*/
|
|
58
|
+
expanded: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* 触发拖拽
|
|
61
|
+
* @param e
|
|
62
|
+
*/
|
|
63
|
+
startDrag: (e: MouseEvent) => void;
|
|
64
|
+
/**
|
|
65
|
+
* 鼠标进入, 主要用于控制 activated 状态
|
|
66
|
+
*/
|
|
67
|
+
onMouseEnter: (e: MouseEvent) => void;
|
|
68
|
+
/**
|
|
69
|
+
* 鼠标离开, 主要用于控制 activated 状态
|
|
70
|
+
*/
|
|
71
|
+
onMouseLeave: (e: MouseEvent) => void;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 渲染表单,只有节点引擎开启才能使用
|
|
75
|
+
*/
|
|
76
|
+
form: NodeFormProps<any> | undefined;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 获取节点的扩展数据
|
|
80
|
+
*/
|
|
81
|
+
getExtInfo<T = any>(): T;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 更新节点的扩展数据
|
|
85
|
+
* @param extInfo
|
|
86
|
+
*/
|
|
87
|
+
updateExtInfo<T = any>(extInfo: T, fullUpdate?: boolean): void;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 展开/收起节点
|
|
91
|
+
* @param expanded
|
|
92
|
+
*/
|
|
93
|
+
toggleExpand(): void;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 删除节点
|
|
97
|
+
*/
|
|
98
|
+
deleteNode: () => void;
|
|
99
|
+
/**
|
|
100
|
+
* 全局 readonly 状态
|
|
101
|
+
*/
|
|
102
|
+
readonly: boolean;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Provides methods related to node rendering
|
|
107
|
+
* @param nodeFromProps
|
|
108
|
+
*/
|
|
109
|
+
export function useNodeRender(nodeFromProps?: FlowNodeEntity): NodeRenderReturnType {
|
|
110
|
+
const renderNode =
|
|
111
|
+
nodeFromProps || (inject(PlaygroundEntityContextKey, undefined) as FlowNodeEntity);
|
|
112
|
+
const nodeCache = { current: undefined as FlowNodeEntity | undefined };
|
|
113
|
+
const renderData = renderNode.getData<FlowNodeRenderData>(FlowNodeRenderData)!;
|
|
114
|
+
const { startDrag: startDragOrigin, dragOffset } = useStartDragNode();
|
|
115
|
+
const playground = usePlayground();
|
|
116
|
+
const isBlockOrderIcon = renderNode.flowNodeType === FlowNodeBaseType.BLOCK_ORDER_ICON;
|
|
117
|
+
const isBlockIcon = renderNode.flowNodeType === FlowNodeBaseType.BLOCK_ICON;
|
|
118
|
+
const formValueDependRef = { current: false };
|
|
119
|
+
formValueDependRef.current = false;
|
|
120
|
+
const node =
|
|
121
|
+
(isBlockOrderIcon || isBlockIcon ? renderNode.parent! : renderNode) || nodeCache.current!;
|
|
122
|
+
nodeCache.current = node;
|
|
123
|
+
const operationService = useService<FlowOperationService>(FlowOperationService);
|
|
124
|
+
const deleteNode = () => {
|
|
125
|
+
operationService.deleteNode(node);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const startDrag = (e: MouseEvent) => {
|
|
129
|
+
startDragOrigin(
|
|
130
|
+
e,
|
|
131
|
+
{ dragStartEntity: renderNode },
|
|
132
|
+
{ dragOffsetX: dragOffset.x, dragOffsetY: dragOffset.y }
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const onMouseEnter = (_e: MouseEvent) => {
|
|
137
|
+
renderData.toggleMouseEnter();
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const onMouseLeave = (_e: MouseEvent) => {
|
|
141
|
+
renderData.toggleMouseLeave();
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const toggleExpand = () => {
|
|
145
|
+
renderData.toggleExpand();
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const getExtInfo = () => node.getExtInfo() as any;
|
|
149
|
+
const updateExtInfo = (data: any, fullUpdate?: boolean) => {
|
|
150
|
+
node.updateExtInfo(data, fullUpdate);
|
|
151
|
+
};
|
|
152
|
+
const form = getNodeForm(node);
|
|
153
|
+
const formState = useObserve<any>(form?.state);
|
|
154
|
+
|
|
155
|
+
let extInfoDispose: Disposable | undefined;
|
|
156
|
+
if (isBlockIcon || isBlockOrderIcon) {
|
|
157
|
+
extInfoDispose = renderNode.parent!.onExtInfoChange(() => renderNode.renderData.fireChange());
|
|
158
|
+
}
|
|
159
|
+
const formValuesDispose = form?.onFormValuesChange(() => {
|
|
160
|
+
if (formValueDependRef.current) {
|
|
161
|
+
renderData.fireChange();
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
onBeforeUnmount(() => {
|
|
165
|
+
extInfoDispose?.dispose();
|
|
166
|
+
formValuesDispose?.dispose();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const readonly = playground.config.readonly;
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
id: node.id,
|
|
173
|
+
type: node.flowNodeType,
|
|
174
|
+
get data() {
|
|
175
|
+
if (form) {
|
|
176
|
+
formValueDependRef.current = true;
|
|
177
|
+
return form.values;
|
|
178
|
+
}
|
|
179
|
+
return getExtInfo();
|
|
180
|
+
},
|
|
181
|
+
updateData(values: any) {
|
|
182
|
+
if (form) {
|
|
183
|
+
form.updateFormValues(values);
|
|
184
|
+
} else {
|
|
185
|
+
updateExtInfo(values, true);
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
node,
|
|
189
|
+
isBlockOrderIcon,
|
|
190
|
+
isBlockIcon,
|
|
191
|
+
get activated() {
|
|
192
|
+
return renderData.activated;
|
|
193
|
+
},
|
|
194
|
+
readonly,
|
|
195
|
+
get expanded() {
|
|
196
|
+
return renderData.expanded;
|
|
197
|
+
},
|
|
198
|
+
get dragging() {
|
|
199
|
+
return renderData.dragging;
|
|
200
|
+
},
|
|
201
|
+
startDrag,
|
|
202
|
+
deleteNode,
|
|
203
|
+
onMouseEnter,
|
|
204
|
+
onMouseLeave,
|
|
205
|
+
getExtInfo,
|
|
206
|
+
updateExtInfo,
|
|
207
|
+
toggleExpand,
|
|
208
|
+
get form() {
|
|
209
|
+
if (!form) return undefined;
|
|
210
|
+
return {
|
|
211
|
+
...form,
|
|
212
|
+
get values() {
|
|
213
|
+
formValueDependRef.current = true;
|
|
214
|
+
return form.values!;
|
|
215
|
+
},
|
|
216
|
+
get state() {
|
|
217
|
+
return formState;
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { computed, onBeforeUnmount, reactive, ref, watch } from 'vue';
|
|
7
|
+
|
|
8
|
+
import { DisposableCollection } from '@flowgram-vue/utils';
|
|
9
|
+
import { HistoryService } from '@flowgram-vue/history';
|
|
10
|
+
import {
|
|
11
|
+
FlowDocument,
|
|
12
|
+
FlowLayoutDefault,
|
|
13
|
+
FlowNodeRenderData,
|
|
14
|
+
PlaygroundInteractiveType,
|
|
15
|
+
EditorState,
|
|
16
|
+
usePlayground,
|
|
17
|
+
usePlaygroundContainer,
|
|
18
|
+
useService,
|
|
19
|
+
} from '@flowgram-vue/editor';
|
|
20
|
+
|
|
21
|
+
export interface PlaygroundToolsPropsType {
|
|
22
|
+
/**
|
|
23
|
+
* 最大缩放比,默认 2
|
|
24
|
+
*/
|
|
25
|
+
maxZoom?: number;
|
|
26
|
+
/**
|
|
27
|
+
* 最小缩放比,默认 0.25
|
|
28
|
+
*/
|
|
29
|
+
minZoom?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface PlaygroundTools {
|
|
33
|
+
/**
|
|
34
|
+
* 缩放 zoom 大小比例
|
|
35
|
+
*/
|
|
36
|
+
zoom: number;
|
|
37
|
+
/**
|
|
38
|
+
* 放大
|
|
39
|
+
*/
|
|
40
|
+
zoomin: (easing?: boolean) => void;
|
|
41
|
+
/**
|
|
42
|
+
* 缩小
|
|
43
|
+
*/
|
|
44
|
+
zoomout: (easing?: boolean) => void;
|
|
45
|
+
/**
|
|
46
|
+
* 设置缩放比例
|
|
47
|
+
* @param zoom
|
|
48
|
+
*/
|
|
49
|
+
updateZoom: (newZoom: number, easing?: boolean, easingDuration?: number) => void;
|
|
50
|
+
/**
|
|
51
|
+
* 自适应视区
|
|
52
|
+
*/
|
|
53
|
+
fitView: (easing?: boolean, easingDuration?: number, padding?: number) => Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* 是否垂直布局
|
|
56
|
+
*/
|
|
57
|
+
isVertical: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* 切换布局, 如果不传入则直接切换
|
|
60
|
+
*/
|
|
61
|
+
changeLayout: (layout?: FlowLayoutDefault) => void;
|
|
62
|
+
|
|
63
|
+
/** 交互模式:鼠标 or 触控板 */
|
|
64
|
+
interactiveType: PlaygroundInteractiveType;
|
|
65
|
+
setInteractiveType: (type: PlaygroundInteractiveType) => void;
|
|
66
|
+
/**
|
|
67
|
+
* 是否可 redo
|
|
68
|
+
*/
|
|
69
|
+
canRedo: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* 是否可 undo
|
|
72
|
+
*/
|
|
73
|
+
canUndo: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* redo
|
|
76
|
+
*/
|
|
77
|
+
redo: () => void;
|
|
78
|
+
/**
|
|
79
|
+
* undo
|
|
80
|
+
*/
|
|
81
|
+
undo: () => void;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function usePlaygroundTools(props?: PlaygroundToolsPropsType): PlaygroundTools {
|
|
85
|
+
const { maxZoom, minZoom } = props || {};
|
|
86
|
+
const playground = usePlayground();
|
|
87
|
+
const container = usePlaygroundContainer();
|
|
88
|
+
const historyService = container.isBound(HistoryService)
|
|
89
|
+
? container.get(HistoryService)
|
|
90
|
+
: undefined;
|
|
91
|
+
const doc = useService<FlowDocument>(FlowDocument);
|
|
92
|
+
const interactiveType = ref<PlaygroundInteractiveType>('PAD');
|
|
93
|
+
const zoom = ref(1);
|
|
94
|
+
const currentLayout = ref(doc.layout);
|
|
95
|
+
const canUndo = ref(false);
|
|
96
|
+
const canRedo = ref(false);
|
|
97
|
+
|
|
98
|
+
const handleFitView = (easing?: boolean, easingDuration?: number, padding?: number) => {
|
|
99
|
+
padding = padding || 30;
|
|
100
|
+
return playground.config.fitView(doc.root.bounds, easing, padding, easingDuration);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const changeLayout = (newLayout?: FlowLayoutDefault) => {
|
|
104
|
+
const allNodes = doc.getAllNodes();
|
|
105
|
+
newLayout =
|
|
106
|
+
newLayout ||
|
|
107
|
+
(doc.layout.name === FlowLayoutDefault.HORIZONTAL_FIXED_LAYOUT
|
|
108
|
+
? FlowLayoutDefault.VERTICAL_FIXED_LAYOUT
|
|
109
|
+
: FlowLayoutDefault.HORIZONTAL_FIXED_LAYOUT);
|
|
110
|
+
allNodes.map((node) => {
|
|
111
|
+
const renderData = node.getData(FlowNodeRenderData);
|
|
112
|
+
renderData.node.classList.add('gedit-transition-ease');
|
|
113
|
+
});
|
|
114
|
+
setTimeout(() => {
|
|
115
|
+
handleFitView();
|
|
116
|
+
}, 10);
|
|
117
|
+
setTimeout(() => {
|
|
118
|
+
allNodes.map((node) => {
|
|
119
|
+
const renderData = node.getData(FlowNodeRenderData);
|
|
120
|
+
renderData.node.classList.remove('gedit-transition-ease');
|
|
121
|
+
});
|
|
122
|
+
}, 500);
|
|
123
|
+
doc.setLayout(newLayout);
|
|
124
|
+
currentLayout.value = doc.layout;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const handleZoomOut = (easing?: boolean) => {
|
|
128
|
+
playground?.config.zoomout(easing);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const handleZoomIn = (easing?: boolean) => {
|
|
132
|
+
playground?.config.zoomin(easing);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const handleUpdateZoom = (value: number, easing?: boolean, easingDuration?: number) => {
|
|
136
|
+
playground.config.updateZoom(value, easing, easingDuration);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const handleUndo = () => historyService?.undo();
|
|
140
|
+
const handleRedo = () => historyService?.redo();
|
|
141
|
+
|
|
142
|
+
function handleUpdateInteractiveType(nextType: PlaygroundInteractiveType) {
|
|
143
|
+
if (nextType === 'MOUSE') {
|
|
144
|
+
playground.editorState.changeState(EditorState.STATE_MOUSE_FRIENDLY_SELECT.id);
|
|
145
|
+
} else if (nextType === 'PAD') {
|
|
146
|
+
playground.editorState.changeState(EditorState.STATE_SELECT.id);
|
|
147
|
+
}
|
|
148
|
+
interactiveType.value = nextType;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const dispose = new DisposableCollection();
|
|
152
|
+
if (playground) {
|
|
153
|
+
dispose.push(playground.onZoom((z) => (zoom.value = z)));
|
|
154
|
+
}
|
|
155
|
+
if (historyService) {
|
|
156
|
+
dispose.push(
|
|
157
|
+
historyService.undoRedoService.onChange(() => {
|
|
158
|
+
canUndo.value = historyService.canUndo();
|
|
159
|
+
canRedo.value = historyService.canRedo();
|
|
160
|
+
})
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
onBeforeUnmount(() => dispose.dispose());
|
|
164
|
+
|
|
165
|
+
watch(
|
|
166
|
+
() => [maxZoom, minZoom],
|
|
167
|
+
() => {
|
|
168
|
+
const config = playground.config.config;
|
|
169
|
+
playground.config.updateConfig({
|
|
170
|
+
maxZoom: maxZoom !== undefined ? maxZoom : config.maxZoom,
|
|
171
|
+
minZoom: minZoom !== undefined ? minZoom : config.minZoom,
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
{ immediate: true }
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
return reactive({
|
|
178
|
+
zoomin: handleZoomIn,
|
|
179
|
+
zoomout: handleZoomOut,
|
|
180
|
+
fitView: handleFitView,
|
|
181
|
+
updateZoom: handleUpdateZoom,
|
|
182
|
+
zoom,
|
|
183
|
+
isVertical: computed(() => currentLayout.value.name === FlowLayoutDefault.VERTICAL_FIXED_LAYOUT),
|
|
184
|
+
changeLayout,
|
|
185
|
+
canRedo,
|
|
186
|
+
canUndo,
|
|
187
|
+
undo: handleUndo,
|
|
188
|
+
redo: handleRedo,
|
|
189
|
+
interactiveType,
|
|
190
|
+
setInteractiveType: handleUpdateInteractiveType,
|
|
191
|
+
}) as PlaygroundTools;
|
|
192
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import 'reflect-metadata';
|
|
7
|
+
|
|
8
|
+
/* 核心模块导出 */
|
|
9
|
+
export * from '@flowgram-vue/editor';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 固定布局模块导出
|
|
13
|
+
*/
|
|
14
|
+
export * from '@flowgram-vue/fixed-layout-core';
|
|
15
|
+
export { useStartDragNode } from '@flowgram-vue/fixed-drag-plugin';
|
|
16
|
+
export * from './preset';
|
|
17
|
+
export * from './components';
|
|
18
|
+
export * from '@flowgram-vue/fixed-history-plugin';
|
|
19
|
+
export * from './hooks/use-node-render';
|
|
20
|
+
export * from './hooks/use-playground-tools';
|
|
21
|
+
export { useClientContext } from './hooks/use-client-context';
|
|
22
|
+
export * from './types';
|
|
23
|
+
export { createOperationPlugin } from './plugins/create-operation-plugin';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { definePluginCreator } from '@flowgram-vue/core';
|
|
7
|
+
|
|
8
|
+
import { FlowOperationService } from '../types';
|
|
9
|
+
import { HistoryOperationServiceImpl } from '../services/history-operation-service';
|
|
10
|
+
import { FlowOperationServiceImpl } from '../services/flow-operation-service';
|
|
11
|
+
import { FixedLayoutProps } from '../preset';
|
|
12
|
+
|
|
13
|
+
export const createOperationPlugin = definePluginCreator<FixedLayoutProps>({
|
|
14
|
+
onBind: ({ bind }, opts) => {
|
|
15
|
+
bind(FlowOperationService)
|
|
16
|
+
.to(opts?.history?.enable ? HistoryOperationServiceImpl : FlowOperationServiceImpl)
|
|
17
|
+
.inSingletonScope();
|
|
18
|
+
},
|
|
19
|
+
onDispose: ctx => {
|
|
20
|
+
const flowOperationService = ctx.container.get<FlowOperationService>(FlowOperationService);
|
|
21
|
+
flowOperationService.dispose();
|
|
22
|
+
},
|
|
23
|
+
});
|