@flowgram-vue/free-hover-plugin 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 +274 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +272 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
- package/src/create-free-hover-plugin.ts +14 -0
- package/src/env.d.ts +10 -0
- package/src/hover-layer.ts +323 -0
- package/src/index.ts +6 -0
- package/src/selection-utils.ts +25 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
4
|
+
Copyright (c) 2026 Crayon
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@flowgram-vue/core');
|
|
4
|
+
var inversify = require('inversify');
|
|
5
|
+
var renderer = require('@flowgram-vue/renderer');
|
|
6
|
+
var freeLayoutCore = require('@flowgram-vue/free-layout-core');
|
|
7
|
+
var document = require('@flowgram-vue/document');
|
|
8
|
+
var utils = require('@flowgram-vue/utils');
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
13
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
14
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
15
|
+
if (decorator = decorators[i])
|
|
16
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
17
|
+
if (kind && result) __defProp(target, key, result);
|
|
18
|
+
return result;
|
|
19
|
+
};
|
|
20
|
+
var BOUNDS_PADDING = 2;
|
|
21
|
+
function getSelectionBounds(selection, ignoreOneSelect = true) {
|
|
22
|
+
const selectedNodes = selection.filter((node) => node instanceof freeLayoutCore.WorkflowNodeEntity);
|
|
23
|
+
return selectedNodes.length > (ignoreOneSelect ? 1 : 0) ? utils.Rectangle.enlarge(selectedNodes.map((n) => n.getData(document.FlowNodeTransformData).bounds)).pad(
|
|
24
|
+
BOUNDS_PADDING
|
|
25
|
+
) : utils.Rectangle.EMPTY;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/hover-layer.ts
|
|
29
|
+
var PORT_BG_CLASS_NAME = "workflow-port-bg";
|
|
30
|
+
var HoverLayerOptions;
|
|
31
|
+
((HoverLayerOptions2) => {
|
|
32
|
+
HoverLayerOptions2.DEFAULT = {
|
|
33
|
+
canHovered: () => true
|
|
34
|
+
};
|
|
35
|
+
})(HoverLayerOptions || (HoverLayerOptions = {}));
|
|
36
|
+
var LINE_CLASS_NAME = ".gedit-flow-activity-line";
|
|
37
|
+
var NODE_CLASS_NAME = ".gedit-flow-activity-node";
|
|
38
|
+
var HoverLayer = class extends core.Layer {
|
|
39
|
+
constructor() {
|
|
40
|
+
super(...arguments);
|
|
41
|
+
/**
|
|
42
|
+
* 按选中排序
|
|
43
|
+
* @private
|
|
44
|
+
*/
|
|
45
|
+
this.nodeTransformsWithSort = [];
|
|
46
|
+
}
|
|
47
|
+
autorun() {
|
|
48
|
+
const { activatedNode } = this.selectionService;
|
|
49
|
+
this.nodeTransformsWithSort = this.nodeTransforms.filter((n) => n.entity.id !== "root" && n.entity.flowNodeType !== document.FlowNodeBaseType.GROUP).reverse().sort((n1) => n1.entity === activatedNode ? -1 : 0);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 是否正在调整线条
|
|
53
|
+
* @protected
|
|
54
|
+
*/
|
|
55
|
+
get isDrawing() {
|
|
56
|
+
return this.linesManager.isDrawing;
|
|
57
|
+
}
|
|
58
|
+
onReady() {
|
|
59
|
+
this.options = {
|
|
60
|
+
...HoverLayerOptions.DEFAULT,
|
|
61
|
+
...this.options
|
|
62
|
+
};
|
|
63
|
+
this.toDispose.pushAll([
|
|
64
|
+
// 监听主动触发的 hover 事件
|
|
65
|
+
this.hoverService.onUpdateHoverPosition((hoverPosition) => {
|
|
66
|
+
const { position, target } = hoverPosition;
|
|
67
|
+
const canvasPosition = this.config.getPosFromMouseEvent({
|
|
68
|
+
clientX: position.x,
|
|
69
|
+
clientY: position.y
|
|
70
|
+
});
|
|
71
|
+
this.updateHoveredState(canvasPosition, target);
|
|
72
|
+
}),
|
|
73
|
+
// 监听画布鼠标移动事件
|
|
74
|
+
this.listenPlaygroundEvent("mousemove", (e) => {
|
|
75
|
+
this.hoverService.hoveredPos = this.config.getPosFromMouseEvent(e);
|
|
76
|
+
if (!this.isEnabled()) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (!this.options.canHovered(e, this.hoverService)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const mousePos = this.config.getPosFromMouseEvent(e);
|
|
83
|
+
this.updateHoveredState(mousePos, e?.target);
|
|
84
|
+
}),
|
|
85
|
+
this.selectionService.onSelectionChanged(() => this.autorun()),
|
|
86
|
+
// 控制触控
|
|
87
|
+
this.listenPlaygroundEvent("touchstart", (e) => {
|
|
88
|
+
if (!this.isEnabled() || this.isDrawing) {
|
|
89
|
+
return void 0;
|
|
90
|
+
}
|
|
91
|
+
return this.handleDragLine(e);
|
|
92
|
+
}),
|
|
93
|
+
// 控制选中逻辑
|
|
94
|
+
this.listenPlaygroundEvent("mousedown", (e) => {
|
|
95
|
+
if (!this.isEnabled() || this.isDrawing) {
|
|
96
|
+
return void 0;
|
|
97
|
+
}
|
|
98
|
+
const { hoveredNode } = this.hoverService;
|
|
99
|
+
const lineDrag = this.handleDragLine(e);
|
|
100
|
+
if (lineDrag) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
const mousePos = this.config.getPosFromMouseEvent(e);
|
|
104
|
+
const selectionBounds = getSelectionBounds(
|
|
105
|
+
this.selectionService.selection,
|
|
106
|
+
// 这里只考虑多选模式,单选模式已经下沉到 use-node-render 中
|
|
107
|
+
true
|
|
108
|
+
);
|
|
109
|
+
if (selectionBounds.width > 0 && selectionBounds.contains(mousePos.x, mousePos.y)) {
|
|
110
|
+
this.dragService.startDragSelectedNodes(e)?.then((dragSuccess) => {
|
|
111
|
+
if (!dragSuccess) {
|
|
112
|
+
if (hoveredNode && hoveredNode instanceof freeLayoutCore.WorkflowNodeEntity) {
|
|
113
|
+
if (e.shiftKey) {
|
|
114
|
+
this.selectionService.toggleSelect(hoveredNode);
|
|
115
|
+
} else {
|
|
116
|
+
this.selectionService.selectNode(hoveredNode);
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
this.selectionService.clear();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
return true;
|
|
124
|
+
} else {
|
|
125
|
+
if (!hoveredNode) {
|
|
126
|
+
this.selectionService.clear();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return void 0;
|
|
130
|
+
})
|
|
131
|
+
]);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 更新 hoverd
|
|
135
|
+
* @param mousePos
|
|
136
|
+
*/
|
|
137
|
+
updateHoveredState(mousePos, target) {
|
|
138
|
+
const { hoverService } = this;
|
|
139
|
+
const nodeTransforms = this.nodeTransformsWithSort;
|
|
140
|
+
const outputPortHovered = this.linesManager.getPortFromMousePos(mousePos, "output");
|
|
141
|
+
const inputPortHovered = this.linesManager.getPortFromMousePos(mousePos, "input");
|
|
142
|
+
const portHovered = outputPortHovered || inputPortHovered;
|
|
143
|
+
const lineDomNodes = this.playgroundNode.querySelectorAll(LINE_CLASS_NAME);
|
|
144
|
+
const checkTargetFromLine = [...lineDomNodes].some(
|
|
145
|
+
(lineDom) => lineDom.contains(target)
|
|
146
|
+
);
|
|
147
|
+
if (portHovered) {
|
|
148
|
+
if (this.document.options.twoWayConnection) {
|
|
149
|
+
hoverService.updateHoveredKey(portHovered.id);
|
|
150
|
+
} else {
|
|
151
|
+
if (portHovered.portType === "output") {
|
|
152
|
+
hoverService.updateHoveredKey(portHovered.id);
|
|
153
|
+
} else if (checkTargetFromLine || target?.className?.includes?.(PORT_BG_CLASS_NAME)) {
|
|
154
|
+
const lineHovered2 = this.linesManager.getCloseInLineFromMousePos(mousePos);
|
|
155
|
+
if (lineHovered2) {
|
|
156
|
+
this.updateHoveredKey(lineHovered2.id);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (this.isDrawing) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const nodeHovered = nodeTransforms.find(
|
|
166
|
+
(trans) => trans.bounds.contains(mousePos.x, mousePos.y)
|
|
167
|
+
)?.entity;
|
|
168
|
+
const nodeDomNodes = this.playgroundNode.querySelectorAll(NODE_CLASS_NAME);
|
|
169
|
+
const checkTargetFromNode = [...nodeDomNodes].some(
|
|
170
|
+
(nodeDom) => nodeDom.contains(target)
|
|
171
|
+
);
|
|
172
|
+
if (nodeHovered || checkTargetFromNode) {
|
|
173
|
+
if (nodeHovered?.id) {
|
|
174
|
+
this.updateHoveredKey(nodeHovered.id);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const lineHovered = checkTargetFromLine ? this.linesManager.getCloseInLineFromMousePos(mousePos) : void 0;
|
|
178
|
+
if (nodeHovered && lineHovered) {
|
|
179
|
+
const nodeStackIndex = nodeHovered.renderData.stackIndex;
|
|
180
|
+
const lineStackIndex = lineHovered.stackIndex;
|
|
181
|
+
if (nodeStackIndex > lineStackIndex) {
|
|
182
|
+
return this.updateHoveredKey(nodeHovered.id);
|
|
183
|
+
} else {
|
|
184
|
+
return this.updateHoveredKey(lineHovered.id);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (nodeHovered) {
|
|
188
|
+
return this.updateHoveredKey(nodeHovered.id);
|
|
189
|
+
}
|
|
190
|
+
if (lineHovered) {
|
|
191
|
+
return this.updateHoveredKey(lineHovered.id);
|
|
192
|
+
}
|
|
193
|
+
hoverService.clearHovered();
|
|
194
|
+
const currentState = this.editorStateConfig.getCurrentState();
|
|
195
|
+
const isMouseFriendly = currentState === core.EditorState.STATE_MOUSE_FRIENDLY_SELECT;
|
|
196
|
+
if (isMouseFriendly && !this.editorStateConfig.isPressingShift) {
|
|
197
|
+
this.configEntity.updateCursor("grab");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
updateHoveredKey(key) {
|
|
201
|
+
this.configEntity.updateCursor("default");
|
|
202
|
+
this.hoverService.updateHoveredKey(key);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* 判断是否能够 hover
|
|
206
|
+
* @returns 是否能 hover
|
|
207
|
+
*/
|
|
208
|
+
isEnabled() {
|
|
209
|
+
const currentState = this.editorStateConfig.getCurrentState();
|
|
210
|
+
return (
|
|
211
|
+
// 鼠标友好模式下,也需要支持 hover 效果,不然线条选择不到
|
|
212
|
+
// Coze 中没有使用该插件,需要在 workflow/render 包相应位置改动
|
|
213
|
+
(currentState === core.EditorState.STATE_SELECT || currentState === core.EditorState.STATE_MOUSE_FRIENDLY_SELECT) && !this.selectorBoxConfigEntity.isStart && !this.dragService.isDragging
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
handleDragLine(e) {
|
|
217
|
+
const { someHovered } = this.hoverService;
|
|
218
|
+
if (someHovered && someHovered instanceof freeLayoutCore.WorkflowLineEntity) {
|
|
219
|
+
this.dragService.resetLine(someHovered, e);
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
if (someHovered && someHovered instanceof freeLayoutCore.WorkflowPortEntity && !someHovered.disabled && e.button !== 1) {
|
|
223
|
+
e.stopPropagation();
|
|
224
|
+
e.preventDefault();
|
|
225
|
+
this.dragService.startDrawingLine(someHovered, e);
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
HoverLayer.type = "HoverLayer";
|
|
231
|
+
__decorateClass([
|
|
232
|
+
inversify.inject(freeLayoutCore.WorkflowDocument)
|
|
233
|
+
], HoverLayer.prototype, "document", 2);
|
|
234
|
+
__decorateClass([
|
|
235
|
+
inversify.inject(freeLayoutCore.WorkflowSelectService)
|
|
236
|
+
], HoverLayer.prototype, "selectionService", 2);
|
|
237
|
+
__decorateClass([
|
|
238
|
+
inversify.inject(freeLayoutCore.WorkflowDragService)
|
|
239
|
+
], HoverLayer.prototype, "dragService", 2);
|
|
240
|
+
__decorateClass([
|
|
241
|
+
inversify.inject(freeLayoutCore.WorkflowHoverService)
|
|
242
|
+
], HoverLayer.prototype, "hoverService", 2);
|
|
243
|
+
__decorateClass([
|
|
244
|
+
inversify.inject(freeLayoutCore.WorkflowLinesManager)
|
|
245
|
+
], HoverLayer.prototype, "linesManager", 2);
|
|
246
|
+
__decorateClass([
|
|
247
|
+
core.observeEntity(core.EditorStateConfigEntity)
|
|
248
|
+
], HoverLayer.prototype, "editorStateConfig", 2);
|
|
249
|
+
__decorateClass([
|
|
250
|
+
core.observeEntity(renderer.SelectorBoxConfigEntity)
|
|
251
|
+
], HoverLayer.prototype, "selectorBoxConfigEntity", 2);
|
|
252
|
+
__decorateClass([
|
|
253
|
+
inversify.inject(core.PlaygroundConfigEntity)
|
|
254
|
+
], HoverLayer.prototype, "configEntity", 2);
|
|
255
|
+
__decorateClass([
|
|
256
|
+
core.observeEntityDatas(freeLayoutCore.WorkflowNodeEntity, document.FlowNodeTransformData)
|
|
257
|
+
], HoverLayer.prototype, "nodeTransforms", 2);
|
|
258
|
+
__decorateClass([
|
|
259
|
+
core.observeEntities(freeLayoutCore.WorkflowLineEntity)
|
|
260
|
+
], HoverLayer.prototype, "lines", 2);
|
|
261
|
+
HoverLayer = __decorateClass([
|
|
262
|
+
inversify.injectable()
|
|
263
|
+
], HoverLayer);
|
|
264
|
+
|
|
265
|
+
// src/create-free-hover-plugin.ts
|
|
266
|
+
var createFreeHoverPlugin = core.definePluginCreator({
|
|
267
|
+
onInit(ctx) {
|
|
268
|
+
ctx.playground.registerLayer(HoverLayer);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
exports.createFreeHoverPlugin = createFreeHoverPlugin;
|
|
273
|
+
//# sourceMappingURL=index.cjs.map
|
|
274
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/selection-utils.ts","../src/hover-layer.ts","../src/create-free-hover-plugin.ts"],"names":["WorkflowNodeEntity","Rectangle","FlowNodeTransformData","HoverLayerOptions","Layer","FlowNodeBaseType","lineHovered","EditorState","WorkflowLineEntity","WorkflowPortEntity","inject","WorkflowDocument","WorkflowSelectService","WorkflowDragService","WorkflowHoverService","WorkflowLinesManager","observeEntity","EditorStateConfigEntity","SelectorBoxConfigEntity","PlaygroundConfigEntity","observeEntityDatas","observeEntities","injectable","definePluginCreator"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { Rectangle } from '@flowgram-vue/utils';\nimport { WorkflowNodeEntity } from '@flowgram-vue/free-layout-core';\nimport { FlowNodeTransformData } from '@flowgram-vue/document';\nimport { type Entity } from '@flowgram-vue/core';\n\nconst BOUNDS_PADDING = 2;\n\nexport function getSelectionBounds(\n selection: Entity[],\n ignoreOneSelect: boolean = true // 忽略单选\n): Rectangle {\n const selectedNodes = selection.filter((node) => node instanceof WorkflowNodeEntity);\n\n // 选中单个的时候不显示\n return selectedNodes.length > (ignoreOneSelect ? 1 : 0)\n ? Rectangle.enlarge(selectedNodes.map((n) => n.getData(FlowNodeTransformData)!.bounds)).pad(\n BOUNDS_PADDING\n )\n : Rectangle.EMPTY;\n}\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\n/* eslint-disable complexity */\nimport { inject, injectable } from 'inversify';\nimport { type IPoint } from '@flowgram-vue/utils';\nimport { SelectorBoxConfigEntity } from '@flowgram-vue/renderer';\nimport {\n WorkflowDocument,\n WorkflowDragService,\n WorkflowHoverService,\n WorkflowLineEntity,\n WorkflowLinesManager,\n WorkflowNodeEntity,\n WorkflowSelectService,\n} from '@flowgram-vue/free-layout-core';\nimport { WorkflowPortEntity } from '@flowgram-vue/free-layout-core';\nimport { FlowNodeBaseType, FlowNodeTransformData } from '@flowgram-vue/document';\nimport {\n EditorState,\n EditorStateConfigEntity,\n Layer,\n PlaygroundConfigEntity,\n observeEntities,\n observeEntity,\n observeEntityDatas,\n type LayerOptions,\n} from '@flowgram-vue/core';\n\nimport { getSelectionBounds } from './selection-utils';\nconst PORT_BG_CLASS_NAME = 'workflow-port-bg';\n\nexport interface HoverLayerOptions extends LayerOptions {\n canHovered?: (e: MouseEvent, service: WorkflowHoverService) => boolean;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-namespace\nexport namespace HoverLayerOptions {\n export const DEFAULT: HoverLayerOptions = {\n canHovered: () => true,\n };\n}\n\nconst LINE_CLASS_NAME = '.gedit-flow-activity-line';\nconst NODE_CLASS_NAME = '.gedit-flow-activity-node';\n\n@injectable()\nexport class HoverLayer extends Layer<HoverLayerOptions> {\n static type = 'HoverLayer';\n\n @inject(WorkflowDocument) document: WorkflowDocument;\n\n @inject(WorkflowSelectService) selectionService: WorkflowSelectService;\n\n @inject(WorkflowDragService) dragService: WorkflowDragService;\n\n @inject(WorkflowHoverService) hoverService: WorkflowHoverService;\n\n @inject(WorkflowLinesManager)\n linesManager: WorkflowLinesManager;\n\n @observeEntity(EditorStateConfigEntity)\n protected editorStateConfig: EditorStateConfigEntity;\n\n @observeEntity(SelectorBoxConfigEntity)\n protected selectorBoxConfigEntity: SelectorBoxConfigEntity;\n\n @inject(PlaygroundConfigEntity) configEntity: PlaygroundConfigEntity;\n\n /**\n * 监听节点 transform\n */\n @observeEntityDatas(WorkflowNodeEntity, FlowNodeTransformData)\n protected readonly nodeTransforms: FlowNodeTransformData[];\n\n /**\n * 按选中排序\n * @private\n */\n protected nodeTransformsWithSort: FlowNodeTransformData[] = [];\n\n autorun(): void {\n const { activatedNode } = this.selectionService;\n this.nodeTransformsWithSort = this.nodeTransforms\n .filter((n) => n.entity.id !== 'root' && n.entity.flowNodeType !== FlowNodeBaseType.GROUP)\n .reverse() // 后创建的排在前面\n .sort((n1) => (n1.entity === activatedNode ? -1 : 0));\n }\n\n /**\n * 监听线条\n */\n @observeEntities(WorkflowLineEntity)\n protected readonly lines: WorkflowLineEntity[];\n\n /**\n * 是否正在调整线条\n * @protected\n */\n get isDrawing(): boolean {\n return this.linesManager.isDrawing;\n }\n\n onReady(): void {\n this.options = {\n ...HoverLayerOptions.DEFAULT,\n ...this.options,\n };\n this.toDispose.pushAll([\n // 监听主动触发的 hover 事件\n this.hoverService.onUpdateHoverPosition((hoverPosition) => {\n const { position, target } = hoverPosition;\n const canvasPosition = this.config.getPosFromMouseEvent({\n clientX: position.x,\n clientY: position.y,\n });\n this.updateHoveredState(canvasPosition, target);\n }),\n // 监听画布鼠标移动事件\n this.listenPlaygroundEvent('mousemove', (e: MouseEvent) => {\n this.hoverService.hoveredPos = this.config.getPosFromMouseEvent(e);\n if (!this.isEnabled()) {\n return;\n }\n if (!this.options.canHovered!(e, this.hoverService)) {\n return;\n }\n const mousePos = this.config.getPosFromMouseEvent(e);\n // 更新 hover 状态\n this.updateHoveredState(mousePos, e?.target as HTMLElement);\n }),\n this.selectionService.onSelectionChanged(() => this.autorun()),\n // 控制触控\n this.listenPlaygroundEvent('touchstart', (e: MouseEvent): boolean | undefined => {\n if (!this.isEnabled() || this.isDrawing) {\n return undefined;\n }\n return this.handleDragLine(e);\n }),\n // 控制选中逻辑\n this.listenPlaygroundEvent('mousedown', (e: MouseEvent): boolean | undefined => {\n if (!this.isEnabled() || this.isDrawing) {\n return undefined;\n }\n const { hoveredNode } = this.hoverService;\n const lineDrag = this.handleDragLine(e);\n if (lineDrag) {\n return true;\n }\n const mousePos = this.config.getPosFromMouseEvent(e);\n const selectionBounds = getSelectionBounds(\n this.selectionService.selection,\n // 这里只考虑多选模式,单选模式已经下沉到 use-node-render 中\n true\n );\n if (selectionBounds.width > 0 && selectionBounds.contains(mousePos.x, mousePos.y)) {\n /**\n * 拖拽选择框\n */\n this.dragService.startDragSelectedNodes(e)?.then((dragSuccess) => {\n if (!dragSuccess) {\n // 拖拽没有成功触发了点击\n if (hoveredNode && hoveredNode instanceof WorkflowNodeEntity) {\n // 追加选择\n if (e.shiftKey) {\n this.selectionService.toggleSelect(hoveredNode);\n } else {\n this.selectionService.selectNode(hoveredNode);\n }\n } else {\n this.selectionService.clear();\n }\n }\n });\n // 这里会组织触发 selector box\n return true;\n } else {\n if (!hoveredNode) {\n this.selectionService.clear();\n }\n }\n return undefined;\n }),\n ]);\n }\n\n /**\n * 更新 hoverd\n * @param mousePos\n */\n updateHoveredState(mousePos: IPoint, target?: HTMLElement): void {\n const { hoverService } = this;\n const nodeTransforms = this.nodeTransformsWithSort;\n const outputPortHovered = this.linesManager.getPortFromMousePos(mousePos, 'output');\n const inputPortHovered = this.linesManager.getPortFromMousePos(mousePos, 'input');\n // 在两个端口叠加情况,优先使用 outputPort\n const portHovered = outputPortHovered || inputPortHovered;\n\n const lineDomNodes = this.playgroundNode.querySelectorAll(LINE_CLASS_NAME);\n const checkTargetFromLine = [...lineDomNodes].some((lineDom) =>\n lineDom.contains(target as HTMLElement)\n );\n if (portHovered) {\n if (this.document.options.twoWayConnection) {\n hoverService.updateHoveredKey(portHovered.id);\n } else {\n // 默认 只有 output 点位可以 hover\n if (portHovered.portType === 'output') {\n hoverService.updateHoveredKey(portHovered.id);\n } else if (checkTargetFromLine || target?.className?.includes?.(PORT_BG_CLASS_NAME)) {\n // 输入点采用获取最接近的线条\n const lineHovered = this.linesManager.getCloseInLineFromMousePos(mousePos);\n if (lineHovered) {\n this.updateHoveredKey(lineHovered.id);\n }\n }\n }\n return;\n }\n\n // Drawing 情况,不能选中节点和线条\n if (this.isDrawing) {\n return;\n }\n\n const nodeHovered = nodeTransforms.find((trans: FlowNodeTransformData) =>\n trans.bounds.contains(mousePos.x, mousePos.y)\n )?.entity as WorkflowNodeEntity;\n\n // 判断当前鼠标位置所在元素是否在节点内部\n const nodeDomNodes = this.playgroundNode.querySelectorAll(NODE_CLASS_NAME);\n const checkTargetFromNode = [...nodeDomNodes].some((nodeDom) =>\n nodeDom.contains(target as HTMLElement)\n );\n\n if (nodeHovered || checkTargetFromNode) {\n if (nodeHovered?.id) {\n this.updateHoveredKey(nodeHovered.id);\n }\n }\n\n // 获取最接近的线条\n // 线条会相交需要获取最接近点位的线条,不能删除的线条不能被选中\n const lineHovered = checkTargetFromLine\n ? this.linesManager.getCloseInLineFromMousePos(mousePos)\n : undefined;\n\n if (nodeHovered && lineHovered) {\n const nodeStackIndex = nodeHovered.renderData.stackIndex;\n const lineStackIndex = lineHovered.stackIndex;\n if (nodeStackIndex > lineStackIndex) {\n return this.updateHoveredKey(nodeHovered.id);\n } else {\n return this.updateHoveredKey(lineHovered.id);\n }\n }\n\n // 判断节点是否 hover\n if (nodeHovered) {\n return this.updateHoveredKey(nodeHovered.id);\n }\n // 判断线条是否 hover\n if (lineHovered) {\n return this.updateHoveredKey(lineHovered.id);\n }\n\n // 上述逻辑都未命中 则清空 hovered\n hoverService.clearHovered();\n\n const currentState = this.editorStateConfig.getCurrentState();\n const isMouseFriendly = currentState === EditorState.STATE_MOUSE_FRIENDLY_SELECT;\n\n // 鼠标优先,并且不是按住 shift 键,更新为小手\n if (isMouseFriendly && !this.editorStateConfig.isPressingShift) {\n this.configEntity.updateCursor('grab');\n }\n }\n\n updateHoveredKey(key: string): void {\n // 鼠标优先交互模式,如果是 hover,需要将鼠标的小手去掉,还原鼠标原有样式\n this.configEntity.updateCursor('default');\n this.hoverService.updateHoveredKey(key);\n }\n\n /**\n * 判断是否能够 hover\n * @returns 是否能 hover\n */\n isEnabled(): boolean {\n const currentState = this.editorStateConfig.getCurrentState();\n // 选择框情况禁止 hover\n return (\n // 鼠标友好模式下,也需要支持 hover 效果,不然线条选择不到\n // Coze 中没有使用该插件,需要在 workflow/render 包相应位置改动\n (currentState === EditorState.STATE_SELECT ||\n currentState === EditorState.STATE_MOUSE_FRIENDLY_SELECT) &&\n !this.selectorBoxConfigEntity.isStart &&\n !this.dragService.isDragging\n );\n }\n\n private handleDragLine(e: MouseEvent): boolean | undefined {\n const { someHovered } = this.hoverService;\n // 重置线条\n if (someHovered && someHovered instanceof WorkflowLineEntity) {\n this.dragService.resetLine(someHovered, e);\n return true;\n }\n if (\n someHovered &&\n someHovered instanceof WorkflowPortEntity &&\n !someHovered.disabled &&\n e.button !== 1\n ) {\n e.stopPropagation();\n e.preventDefault();\n this.dragService.startDrawingLine(someHovered, e);\n return true;\n }\n }\n}\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { definePluginCreator } from '@flowgram-vue/core';\n\nimport { HoverLayer } from './hover-layer';\n\nexport const createFreeHoverPlugin = definePluginCreator({\n onInit(ctx): void {\n ctx.playground.registerLayer(HoverLayer);\n },\n});\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as _flowgram_vue_core from '@flowgram-vue/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
5
|
+
* SPDX-License-Identifier: MIT
|
|
6
|
+
*/
|
|
7
|
+
declare const createFreeHoverPlugin: _flowgram_vue_core.PluginCreator<unknown>;
|
|
8
|
+
|
|
9
|
+
export { createFreeHoverPlugin };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { observeEntity, EditorStateConfigEntity, PlaygroundConfigEntity, observeEntityDatas, observeEntities, definePluginCreator, Layer, EditorState } from '@flowgram-vue/core';
|
|
2
|
+
import { inject, injectable } from 'inversify';
|
|
3
|
+
import { SelectorBoxConfigEntity } from '@flowgram-vue/renderer';
|
|
4
|
+
import { WorkflowDocument, WorkflowSelectService, WorkflowDragService, WorkflowHoverService, WorkflowLinesManager, WorkflowNodeEntity, WorkflowLineEntity, WorkflowPortEntity } from '@flowgram-vue/free-layout-core';
|
|
5
|
+
import { FlowNodeTransformData, FlowNodeBaseType } from '@flowgram-vue/document';
|
|
6
|
+
import { Rectangle } from '@flowgram-vue/utils';
|
|
7
|
+
|
|
8
|
+
var __defProp = Object.defineProperty;
|
|
9
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
10
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
11
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
12
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
13
|
+
if (decorator = decorators[i])
|
|
14
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
15
|
+
if (kind && result) __defProp(target, key, result);
|
|
16
|
+
return result;
|
|
17
|
+
};
|
|
18
|
+
var BOUNDS_PADDING = 2;
|
|
19
|
+
function getSelectionBounds(selection, ignoreOneSelect = true) {
|
|
20
|
+
const selectedNodes = selection.filter((node) => node instanceof WorkflowNodeEntity);
|
|
21
|
+
return selectedNodes.length > (ignoreOneSelect ? 1 : 0) ? Rectangle.enlarge(selectedNodes.map((n) => n.getData(FlowNodeTransformData).bounds)).pad(
|
|
22
|
+
BOUNDS_PADDING
|
|
23
|
+
) : Rectangle.EMPTY;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/hover-layer.ts
|
|
27
|
+
var PORT_BG_CLASS_NAME = "workflow-port-bg";
|
|
28
|
+
var HoverLayerOptions;
|
|
29
|
+
((HoverLayerOptions2) => {
|
|
30
|
+
HoverLayerOptions2.DEFAULT = {
|
|
31
|
+
canHovered: () => true
|
|
32
|
+
};
|
|
33
|
+
})(HoverLayerOptions || (HoverLayerOptions = {}));
|
|
34
|
+
var LINE_CLASS_NAME = ".gedit-flow-activity-line";
|
|
35
|
+
var NODE_CLASS_NAME = ".gedit-flow-activity-node";
|
|
36
|
+
var HoverLayer = class extends Layer {
|
|
37
|
+
constructor() {
|
|
38
|
+
super(...arguments);
|
|
39
|
+
/**
|
|
40
|
+
* 按选中排序
|
|
41
|
+
* @private
|
|
42
|
+
*/
|
|
43
|
+
this.nodeTransformsWithSort = [];
|
|
44
|
+
}
|
|
45
|
+
autorun() {
|
|
46
|
+
const { activatedNode } = this.selectionService;
|
|
47
|
+
this.nodeTransformsWithSort = this.nodeTransforms.filter((n) => n.entity.id !== "root" && n.entity.flowNodeType !== FlowNodeBaseType.GROUP).reverse().sort((n1) => n1.entity === activatedNode ? -1 : 0);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 是否正在调整线条
|
|
51
|
+
* @protected
|
|
52
|
+
*/
|
|
53
|
+
get isDrawing() {
|
|
54
|
+
return this.linesManager.isDrawing;
|
|
55
|
+
}
|
|
56
|
+
onReady() {
|
|
57
|
+
this.options = {
|
|
58
|
+
...HoverLayerOptions.DEFAULT,
|
|
59
|
+
...this.options
|
|
60
|
+
};
|
|
61
|
+
this.toDispose.pushAll([
|
|
62
|
+
// 监听主动触发的 hover 事件
|
|
63
|
+
this.hoverService.onUpdateHoverPosition((hoverPosition) => {
|
|
64
|
+
const { position, target } = hoverPosition;
|
|
65
|
+
const canvasPosition = this.config.getPosFromMouseEvent({
|
|
66
|
+
clientX: position.x,
|
|
67
|
+
clientY: position.y
|
|
68
|
+
});
|
|
69
|
+
this.updateHoveredState(canvasPosition, target);
|
|
70
|
+
}),
|
|
71
|
+
// 监听画布鼠标移动事件
|
|
72
|
+
this.listenPlaygroundEvent("mousemove", (e) => {
|
|
73
|
+
this.hoverService.hoveredPos = this.config.getPosFromMouseEvent(e);
|
|
74
|
+
if (!this.isEnabled()) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (!this.options.canHovered(e, this.hoverService)) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const mousePos = this.config.getPosFromMouseEvent(e);
|
|
81
|
+
this.updateHoveredState(mousePos, e?.target);
|
|
82
|
+
}),
|
|
83
|
+
this.selectionService.onSelectionChanged(() => this.autorun()),
|
|
84
|
+
// 控制触控
|
|
85
|
+
this.listenPlaygroundEvent("touchstart", (e) => {
|
|
86
|
+
if (!this.isEnabled() || this.isDrawing) {
|
|
87
|
+
return void 0;
|
|
88
|
+
}
|
|
89
|
+
return this.handleDragLine(e);
|
|
90
|
+
}),
|
|
91
|
+
// 控制选中逻辑
|
|
92
|
+
this.listenPlaygroundEvent("mousedown", (e) => {
|
|
93
|
+
if (!this.isEnabled() || this.isDrawing) {
|
|
94
|
+
return void 0;
|
|
95
|
+
}
|
|
96
|
+
const { hoveredNode } = this.hoverService;
|
|
97
|
+
const lineDrag = this.handleDragLine(e);
|
|
98
|
+
if (lineDrag) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
const mousePos = this.config.getPosFromMouseEvent(e);
|
|
102
|
+
const selectionBounds = getSelectionBounds(
|
|
103
|
+
this.selectionService.selection,
|
|
104
|
+
// 这里只考虑多选模式,单选模式已经下沉到 use-node-render 中
|
|
105
|
+
true
|
|
106
|
+
);
|
|
107
|
+
if (selectionBounds.width > 0 && selectionBounds.contains(mousePos.x, mousePos.y)) {
|
|
108
|
+
this.dragService.startDragSelectedNodes(e)?.then((dragSuccess) => {
|
|
109
|
+
if (!dragSuccess) {
|
|
110
|
+
if (hoveredNode && hoveredNode instanceof WorkflowNodeEntity) {
|
|
111
|
+
if (e.shiftKey) {
|
|
112
|
+
this.selectionService.toggleSelect(hoveredNode);
|
|
113
|
+
} else {
|
|
114
|
+
this.selectionService.selectNode(hoveredNode);
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
this.selectionService.clear();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
return true;
|
|
122
|
+
} else {
|
|
123
|
+
if (!hoveredNode) {
|
|
124
|
+
this.selectionService.clear();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return void 0;
|
|
128
|
+
})
|
|
129
|
+
]);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* 更新 hoverd
|
|
133
|
+
* @param mousePos
|
|
134
|
+
*/
|
|
135
|
+
updateHoveredState(mousePos, target) {
|
|
136
|
+
const { hoverService } = this;
|
|
137
|
+
const nodeTransforms = this.nodeTransformsWithSort;
|
|
138
|
+
const outputPortHovered = this.linesManager.getPortFromMousePos(mousePos, "output");
|
|
139
|
+
const inputPortHovered = this.linesManager.getPortFromMousePos(mousePos, "input");
|
|
140
|
+
const portHovered = outputPortHovered || inputPortHovered;
|
|
141
|
+
const lineDomNodes = this.playgroundNode.querySelectorAll(LINE_CLASS_NAME);
|
|
142
|
+
const checkTargetFromLine = [...lineDomNodes].some(
|
|
143
|
+
(lineDom) => lineDom.contains(target)
|
|
144
|
+
);
|
|
145
|
+
if (portHovered) {
|
|
146
|
+
if (this.document.options.twoWayConnection) {
|
|
147
|
+
hoverService.updateHoveredKey(portHovered.id);
|
|
148
|
+
} else {
|
|
149
|
+
if (portHovered.portType === "output") {
|
|
150
|
+
hoverService.updateHoveredKey(portHovered.id);
|
|
151
|
+
} else if (checkTargetFromLine || target?.className?.includes?.(PORT_BG_CLASS_NAME)) {
|
|
152
|
+
const lineHovered2 = this.linesManager.getCloseInLineFromMousePos(mousePos);
|
|
153
|
+
if (lineHovered2) {
|
|
154
|
+
this.updateHoveredKey(lineHovered2.id);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (this.isDrawing) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const nodeHovered = nodeTransforms.find(
|
|
164
|
+
(trans) => trans.bounds.contains(mousePos.x, mousePos.y)
|
|
165
|
+
)?.entity;
|
|
166
|
+
const nodeDomNodes = this.playgroundNode.querySelectorAll(NODE_CLASS_NAME);
|
|
167
|
+
const checkTargetFromNode = [...nodeDomNodes].some(
|
|
168
|
+
(nodeDom) => nodeDom.contains(target)
|
|
169
|
+
);
|
|
170
|
+
if (nodeHovered || checkTargetFromNode) {
|
|
171
|
+
if (nodeHovered?.id) {
|
|
172
|
+
this.updateHoveredKey(nodeHovered.id);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const lineHovered = checkTargetFromLine ? this.linesManager.getCloseInLineFromMousePos(mousePos) : void 0;
|
|
176
|
+
if (nodeHovered && lineHovered) {
|
|
177
|
+
const nodeStackIndex = nodeHovered.renderData.stackIndex;
|
|
178
|
+
const lineStackIndex = lineHovered.stackIndex;
|
|
179
|
+
if (nodeStackIndex > lineStackIndex) {
|
|
180
|
+
return this.updateHoveredKey(nodeHovered.id);
|
|
181
|
+
} else {
|
|
182
|
+
return this.updateHoveredKey(lineHovered.id);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (nodeHovered) {
|
|
186
|
+
return this.updateHoveredKey(nodeHovered.id);
|
|
187
|
+
}
|
|
188
|
+
if (lineHovered) {
|
|
189
|
+
return this.updateHoveredKey(lineHovered.id);
|
|
190
|
+
}
|
|
191
|
+
hoverService.clearHovered();
|
|
192
|
+
const currentState = this.editorStateConfig.getCurrentState();
|
|
193
|
+
const isMouseFriendly = currentState === EditorState.STATE_MOUSE_FRIENDLY_SELECT;
|
|
194
|
+
if (isMouseFriendly && !this.editorStateConfig.isPressingShift) {
|
|
195
|
+
this.configEntity.updateCursor("grab");
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
updateHoveredKey(key) {
|
|
199
|
+
this.configEntity.updateCursor("default");
|
|
200
|
+
this.hoverService.updateHoveredKey(key);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* 判断是否能够 hover
|
|
204
|
+
* @returns 是否能 hover
|
|
205
|
+
*/
|
|
206
|
+
isEnabled() {
|
|
207
|
+
const currentState = this.editorStateConfig.getCurrentState();
|
|
208
|
+
return (
|
|
209
|
+
// 鼠标友好模式下,也需要支持 hover 效果,不然线条选择不到
|
|
210
|
+
// Coze 中没有使用该插件,需要在 workflow/render 包相应位置改动
|
|
211
|
+
(currentState === EditorState.STATE_SELECT || currentState === EditorState.STATE_MOUSE_FRIENDLY_SELECT) && !this.selectorBoxConfigEntity.isStart && !this.dragService.isDragging
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
handleDragLine(e) {
|
|
215
|
+
const { someHovered } = this.hoverService;
|
|
216
|
+
if (someHovered && someHovered instanceof WorkflowLineEntity) {
|
|
217
|
+
this.dragService.resetLine(someHovered, e);
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
if (someHovered && someHovered instanceof WorkflowPortEntity && !someHovered.disabled && e.button !== 1) {
|
|
221
|
+
e.stopPropagation();
|
|
222
|
+
e.preventDefault();
|
|
223
|
+
this.dragService.startDrawingLine(someHovered, e);
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
HoverLayer.type = "HoverLayer";
|
|
229
|
+
__decorateClass([
|
|
230
|
+
inject(WorkflowDocument)
|
|
231
|
+
], HoverLayer.prototype, "document", 2);
|
|
232
|
+
__decorateClass([
|
|
233
|
+
inject(WorkflowSelectService)
|
|
234
|
+
], HoverLayer.prototype, "selectionService", 2);
|
|
235
|
+
__decorateClass([
|
|
236
|
+
inject(WorkflowDragService)
|
|
237
|
+
], HoverLayer.prototype, "dragService", 2);
|
|
238
|
+
__decorateClass([
|
|
239
|
+
inject(WorkflowHoverService)
|
|
240
|
+
], HoverLayer.prototype, "hoverService", 2);
|
|
241
|
+
__decorateClass([
|
|
242
|
+
inject(WorkflowLinesManager)
|
|
243
|
+
], HoverLayer.prototype, "linesManager", 2);
|
|
244
|
+
__decorateClass([
|
|
245
|
+
observeEntity(EditorStateConfigEntity)
|
|
246
|
+
], HoverLayer.prototype, "editorStateConfig", 2);
|
|
247
|
+
__decorateClass([
|
|
248
|
+
observeEntity(SelectorBoxConfigEntity)
|
|
249
|
+
], HoverLayer.prototype, "selectorBoxConfigEntity", 2);
|
|
250
|
+
__decorateClass([
|
|
251
|
+
inject(PlaygroundConfigEntity)
|
|
252
|
+
], HoverLayer.prototype, "configEntity", 2);
|
|
253
|
+
__decorateClass([
|
|
254
|
+
observeEntityDatas(WorkflowNodeEntity, FlowNodeTransformData)
|
|
255
|
+
], HoverLayer.prototype, "nodeTransforms", 2);
|
|
256
|
+
__decorateClass([
|
|
257
|
+
observeEntities(WorkflowLineEntity)
|
|
258
|
+
], HoverLayer.prototype, "lines", 2);
|
|
259
|
+
HoverLayer = __decorateClass([
|
|
260
|
+
injectable()
|
|
261
|
+
], HoverLayer);
|
|
262
|
+
|
|
263
|
+
// src/create-free-hover-plugin.ts
|
|
264
|
+
var createFreeHoverPlugin = definePluginCreator({
|
|
265
|
+
onInit(ctx) {
|
|
266
|
+
ctx.playground.registerLayer(HoverLayer);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
export { createFreeHoverPlugin };
|
|
271
|
+
//# sourceMappingURL=index.js.map
|
|
272
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/selection-utils.ts","../src/hover-layer.ts","../src/create-free-hover-plugin.ts"],"names":["HoverLayerOptions","WorkflowNodeEntity","lineHovered","FlowNodeTransformData"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { Rectangle } from '@flowgram-vue/utils';\nimport { WorkflowNodeEntity } from '@flowgram-vue/free-layout-core';\nimport { FlowNodeTransformData } from '@flowgram-vue/document';\nimport { type Entity } from '@flowgram-vue/core';\n\nconst BOUNDS_PADDING = 2;\n\nexport function getSelectionBounds(\n selection: Entity[],\n ignoreOneSelect: boolean = true // 忽略单选\n): Rectangle {\n const selectedNodes = selection.filter((node) => node instanceof WorkflowNodeEntity);\n\n // 选中单个的时候不显示\n return selectedNodes.length > (ignoreOneSelect ? 1 : 0)\n ? Rectangle.enlarge(selectedNodes.map((n) => n.getData(FlowNodeTransformData)!.bounds)).pad(\n BOUNDS_PADDING\n )\n : Rectangle.EMPTY;\n}\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\n/* eslint-disable complexity */\nimport { inject, injectable } from 'inversify';\nimport { type IPoint } from '@flowgram-vue/utils';\nimport { SelectorBoxConfigEntity } from '@flowgram-vue/renderer';\nimport {\n WorkflowDocument,\n WorkflowDragService,\n WorkflowHoverService,\n WorkflowLineEntity,\n WorkflowLinesManager,\n WorkflowNodeEntity,\n WorkflowSelectService,\n} from '@flowgram-vue/free-layout-core';\nimport { WorkflowPortEntity } from '@flowgram-vue/free-layout-core';\nimport { FlowNodeBaseType, FlowNodeTransformData } from '@flowgram-vue/document';\nimport {\n EditorState,\n EditorStateConfigEntity,\n Layer,\n PlaygroundConfigEntity,\n observeEntities,\n observeEntity,\n observeEntityDatas,\n type LayerOptions,\n} from '@flowgram-vue/core';\n\nimport { getSelectionBounds } from './selection-utils';\nconst PORT_BG_CLASS_NAME = 'workflow-port-bg';\n\nexport interface HoverLayerOptions extends LayerOptions {\n canHovered?: (e: MouseEvent, service: WorkflowHoverService) => boolean;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-namespace\nexport namespace HoverLayerOptions {\n export const DEFAULT: HoverLayerOptions = {\n canHovered: () => true,\n };\n}\n\nconst LINE_CLASS_NAME = '.gedit-flow-activity-line';\nconst NODE_CLASS_NAME = '.gedit-flow-activity-node';\n\n@injectable()\nexport class HoverLayer extends Layer<HoverLayerOptions> {\n static type = 'HoverLayer';\n\n @inject(WorkflowDocument) document: WorkflowDocument;\n\n @inject(WorkflowSelectService) selectionService: WorkflowSelectService;\n\n @inject(WorkflowDragService) dragService: WorkflowDragService;\n\n @inject(WorkflowHoverService) hoverService: WorkflowHoverService;\n\n @inject(WorkflowLinesManager)\n linesManager: WorkflowLinesManager;\n\n @observeEntity(EditorStateConfigEntity)\n protected editorStateConfig: EditorStateConfigEntity;\n\n @observeEntity(SelectorBoxConfigEntity)\n protected selectorBoxConfigEntity: SelectorBoxConfigEntity;\n\n @inject(PlaygroundConfigEntity) configEntity: PlaygroundConfigEntity;\n\n /**\n * 监听节点 transform\n */\n @observeEntityDatas(WorkflowNodeEntity, FlowNodeTransformData)\n protected readonly nodeTransforms: FlowNodeTransformData[];\n\n /**\n * 按选中排序\n * @private\n */\n protected nodeTransformsWithSort: FlowNodeTransformData[] = [];\n\n autorun(): void {\n const { activatedNode } = this.selectionService;\n this.nodeTransformsWithSort = this.nodeTransforms\n .filter((n) => n.entity.id !== 'root' && n.entity.flowNodeType !== FlowNodeBaseType.GROUP)\n .reverse() // 后创建的排在前面\n .sort((n1) => (n1.entity === activatedNode ? -1 : 0));\n }\n\n /**\n * 监听线条\n */\n @observeEntities(WorkflowLineEntity)\n protected readonly lines: WorkflowLineEntity[];\n\n /**\n * 是否正在调整线条\n * @protected\n */\n get isDrawing(): boolean {\n return this.linesManager.isDrawing;\n }\n\n onReady(): void {\n this.options = {\n ...HoverLayerOptions.DEFAULT,\n ...this.options,\n };\n this.toDispose.pushAll([\n // 监听主动触发的 hover 事件\n this.hoverService.onUpdateHoverPosition((hoverPosition) => {\n const { position, target } = hoverPosition;\n const canvasPosition = this.config.getPosFromMouseEvent({\n clientX: position.x,\n clientY: position.y,\n });\n this.updateHoveredState(canvasPosition, target);\n }),\n // 监听画布鼠标移动事件\n this.listenPlaygroundEvent('mousemove', (e: MouseEvent) => {\n this.hoverService.hoveredPos = this.config.getPosFromMouseEvent(e);\n if (!this.isEnabled()) {\n return;\n }\n if (!this.options.canHovered!(e, this.hoverService)) {\n return;\n }\n const mousePos = this.config.getPosFromMouseEvent(e);\n // 更新 hover 状态\n this.updateHoveredState(mousePos, e?.target as HTMLElement);\n }),\n this.selectionService.onSelectionChanged(() => this.autorun()),\n // 控制触控\n this.listenPlaygroundEvent('touchstart', (e: MouseEvent): boolean | undefined => {\n if (!this.isEnabled() || this.isDrawing) {\n return undefined;\n }\n return this.handleDragLine(e);\n }),\n // 控制选中逻辑\n this.listenPlaygroundEvent('mousedown', (e: MouseEvent): boolean | undefined => {\n if (!this.isEnabled() || this.isDrawing) {\n return undefined;\n }\n const { hoveredNode } = this.hoverService;\n const lineDrag = this.handleDragLine(e);\n if (lineDrag) {\n return true;\n }\n const mousePos = this.config.getPosFromMouseEvent(e);\n const selectionBounds = getSelectionBounds(\n this.selectionService.selection,\n // 这里只考虑多选模式,单选模式已经下沉到 use-node-render 中\n true\n );\n if (selectionBounds.width > 0 && selectionBounds.contains(mousePos.x, mousePos.y)) {\n /**\n * 拖拽选择框\n */\n this.dragService.startDragSelectedNodes(e)?.then((dragSuccess) => {\n if (!dragSuccess) {\n // 拖拽没有成功触发了点击\n if (hoveredNode && hoveredNode instanceof WorkflowNodeEntity) {\n // 追加选择\n if (e.shiftKey) {\n this.selectionService.toggleSelect(hoveredNode);\n } else {\n this.selectionService.selectNode(hoveredNode);\n }\n } else {\n this.selectionService.clear();\n }\n }\n });\n // 这里会组织触发 selector box\n return true;\n } else {\n if (!hoveredNode) {\n this.selectionService.clear();\n }\n }\n return undefined;\n }),\n ]);\n }\n\n /**\n * 更新 hoverd\n * @param mousePos\n */\n updateHoveredState(mousePos: IPoint, target?: HTMLElement): void {\n const { hoverService } = this;\n const nodeTransforms = this.nodeTransformsWithSort;\n const outputPortHovered = this.linesManager.getPortFromMousePos(mousePos, 'output');\n const inputPortHovered = this.linesManager.getPortFromMousePos(mousePos, 'input');\n // 在两个端口叠加情况,优先使用 outputPort\n const portHovered = outputPortHovered || inputPortHovered;\n\n const lineDomNodes = this.playgroundNode.querySelectorAll(LINE_CLASS_NAME);\n const checkTargetFromLine = [...lineDomNodes].some((lineDom) =>\n lineDom.contains(target as HTMLElement)\n );\n if (portHovered) {\n if (this.document.options.twoWayConnection) {\n hoverService.updateHoveredKey(portHovered.id);\n } else {\n // 默认 只有 output 点位可以 hover\n if (portHovered.portType === 'output') {\n hoverService.updateHoveredKey(portHovered.id);\n } else if (checkTargetFromLine || target?.className?.includes?.(PORT_BG_CLASS_NAME)) {\n // 输入点采用获取最接近的线条\n const lineHovered = this.linesManager.getCloseInLineFromMousePos(mousePos);\n if (lineHovered) {\n this.updateHoveredKey(lineHovered.id);\n }\n }\n }\n return;\n }\n\n // Drawing 情况,不能选中节点和线条\n if (this.isDrawing) {\n return;\n }\n\n const nodeHovered = nodeTransforms.find((trans: FlowNodeTransformData) =>\n trans.bounds.contains(mousePos.x, mousePos.y)\n )?.entity as WorkflowNodeEntity;\n\n // 判断当前鼠标位置所在元素是否在节点内部\n const nodeDomNodes = this.playgroundNode.querySelectorAll(NODE_CLASS_NAME);\n const checkTargetFromNode = [...nodeDomNodes].some((nodeDom) =>\n nodeDom.contains(target as HTMLElement)\n );\n\n if (nodeHovered || checkTargetFromNode) {\n if (nodeHovered?.id) {\n this.updateHoveredKey(nodeHovered.id);\n }\n }\n\n // 获取最接近的线条\n // 线条会相交需要获取最接近点位的线条,不能删除的线条不能被选中\n const lineHovered = checkTargetFromLine\n ? this.linesManager.getCloseInLineFromMousePos(mousePos)\n : undefined;\n\n if (nodeHovered && lineHovered) {\n const nodeStackIndex = nodeHovered.renderData.stackIndex;\n const lineStackIndex = lineHovered.stackIndex;\n if (nodeStackIndex > lineStackIndex) {\n return this.updateHoveredKey(nodeHovered.id);\n } else {\n return this.updateHoveredKey(lineHovered.id);\n }\n }\n\n // 判断节点是否 hover\n if (nodeHovered) {\n return this.updateHoveredKey(nodeHovered.id);\n }\n // 判断线条是否 hover\n if (lineHovered) {\n return this.updateHoveredKey(lineHovered.id);\n }\n\n // 上述逻辑都未命中 则清空 hovered\n hoverService.clearHovered();\n\n const currentState = this.editorStateConfig.getCurrentState();\n const isMouseFriendly = currentState === EditorState.STATE_MOUSE_FRIENDLY_SELECT;\n\n // 鼠标优先,并且不是按住 shift 键,更新为小手\n if (isMouseFriendly && !this.editorStateConfig.isPressingShift) {\n this.configEntity.updateCursor('grab');\n }\n }\n\n updateHoveredKey(key: string): void {\n // 鼠标优先交互模式,如果是 hover,需要将鼠标的小手去掉,还原鼠标原有样式\n this.configEntity.updateCursor('default');\n this.hoverService.updateHoveredKey(key);\n }\n\n /**\n * 判断是否能够 hover\n * @returns 是否能 hover\n */\n isEnabled(): boolean {\n const currentState = this.editorStateConfig.getCurrentState();\n // 选择框情况禁止 hover\n return (\n // 鼠标友好模式下,也需要支持 hover 效果,不然线条选择不到\n // Coze 中没有使用该插件,需要在 workflow/render 包相应位置改动\n (currentState === EditorState.STATE_SELECT ||\n currentState === EditorState.STATE_MOUSE_FRIENDLY_SELECT) &&\n !this.selectorBoxConfigEntity.isStart &&\n !this.dragService.isDragging\n );\n }\n\n private handleDragLine(e: MouseEvent): boolean | undefined {\n const { someHovered } = this.hoverService;\n // 重置线条\n if (someHovered && someHovered instanceof WorkflowLineEntity) {\n this.dragService.resetLine(someHovered, e);\n return true;\n }\n if (\n someHovered &&\n someHovered instanceof WorkflowPortEntity &&\n !someHovered.disabled &&\n e.button !== 1\n ) {\n e.stopPropagation();\n e.preventDefault();\n this.dragService.startDrawingLine(someHovered, e);\n return true;\n }\n }\n}\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { definePluginCreator } from '@flowgram-vue/core';\n\nimport { HoverLayer } from './hover-layer';\n\nexport const createFreeHoverPlugin = definePluginCreator({\n onInit(ctx): void {\n ctx.playground.registerLayer(HoverLayer);\n },\n});\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flowgram-vue/free-hover-plugin",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://flowgram.ai/",
|
|
7
|
+
"repository": "https://github.com/Crayon-hua/flowgram-vue",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/index.cjs",
|
|
16
|
+
"module": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"files": [
|
|
19
|
+
"src",
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"inversify": "^6.0.1",
|
|
24
|
+
"reflect-metadata": "~0.2.2",
|
|
25
|
+
"@flowgram-vue/core": "0.2.0",
|
|
26
|
+
"@flowgram-vue/document": "0.2.0",
|
|
27
|
+
"@flowgram-vue/renderer": "0.2.0",
|
|
28
|
+
"@flowgram-vue/utils": "0.2.0",
|
|
29
|
+
"@flowgram-vue/free-layout-core": "0.2.0"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"vue": "^3.3.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/bezier-js": "4.1.3",
|
|
36
|
+
"@types/lodash-es": "^4.17.12",
|
|
37
|
+
"@types/node": "^20",
|
|
38
|
+
"eslint": "^9.0.0",
|
|
39
|
+
"typescript": "^5.8.3",
|
|
40
|
+
"vitest": "^3.2.4",
|
|
41
|
+
"vue": "^3.5.18",
|
|
42
|
+
"@flowgram-vue/eslint-config": "0.2.0",
|
|
43
|
+
"@flowgram-vue/ts-config": "0.2.0",
|
|
44
|
+
"@flowgram-vue/build-config": "0.2.0"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public",
|
|
48
|
+
"registry": "https://registry.npmjs.org/"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "flowgram-build",
|
|
52
|
+
"test": "exit 0",
|
|
53
|
+
"test:cov": "exit 0",
|
|
54
|
+
"lint": "eslint .",
|
|
55
|
+
"ts-check": "tsc --noEmit",
|
|
56
|
+
"build:fast": "flowgram-build --fast",
|
|
57
|
+
"build:watch": "flowgram-build --watch"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
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 { HoverLayer } from './hover-layer';
|
|
9
|
+
|
|
10
|
+
export const createFreeHoverPlugin = definePluginCreator({
|
|
11
|
+
onInit(ctx): void {
|
|
12
|
+
ctx.playground.registerLayer(HoverLayer);
|
|
13
|
+
},
|
|
14
|
+
});
|
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,323 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* eslint-disable complexity */
|
|
7
|
+
import { inject, injectable } from 'inversify';
|
|
8
|
+
import { type IPoint } from '@flowgram-vue/utils';
|
|
9
|
+
import { SelectorBoxConfigEntity } from '@flowgram-vue/renderer';
|
|
10
|
+
import {
|
|
11
|
+
WorkflowDocument,
|
|
12
|
+
WorkflowDragService,
|
|
13
|
+
WorkflowHoverService,
|
|
14
|
+
WorkflowLineEntity,
|
|
15
|
+
WorkflowLinesManager,
|
|
16
|
+
WorkflowNodeEntity,
|
|
17
|
+
WorkflowSelectService,
|
|
18
|
+
} from '@flowgram-vue/free-layout-core';
|
|
19
|
+
import { WorkflowPortEntity } from '@flowgram-vue/free-layout-core';
|
|
20
|
+
import { FlowNodeBaseType, FlowNodeTransformData } from '@flowgram-vue/document';
|
|
21
|
+
import {
|
|
22
|
+
EditorState,
|
|
23
|
+
EditorStateConfigEntity,
|
|
24
|
+
Layer,
|
|
25
|
+
PlaygroundConfigEntity,
|
|
26
|
+
observeEntities,
|
|
27
|
+
observeEntity,
|
|
28
|
+
observeEntityDatas,
|
|
29
|
+
type LayerOptions,
|
|
30
|
+
} from '@flowgram-vue/core';
|
|
31
|
+
|
|
32
|
+
import { getSelectionBounds } from './selection-utils';
|
|
33
|
+
const PORT_BG_CLASS_NAME = 'workflow-port-bg';
|
|
34
|
+
|
|
35
|
+
export interface HoverLayerOptions extends LayerOptions {
|
|
36
|
+
canHovered?: (e: MouseEvent, service: WorkflowHoverService) => boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
40
|
+
export namespace HoverLayerOptions {
|
|
41
|
+
export const DEFAULT: HoverLayerOptions = {
|
|
42
|
+
canHovered: () => true,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const LINE_CLASS_NAME = '.gedit-flow-activity-line';
|
|
47
|
+
const NODE_CLASS_NAME = '.gedit-flow-activity-node';
|
|
48
|
+
|
|
49
|
+
@injectable()
|
|
50
|
+
export class HoverLayer extends Layer<HoverLayerOptions> {
|
|
51
|
+
static type = 'HoverLayer';
|
|
52
|
+
|
|
53
|
+
@inject(WorkflowDocument) document: WorkflowDocument;
|
|
54
|
+
|
|
55
|
+
@inject(WorkflowSelectService) selectionService: WorkflowSelectService;
|
|
56
|
+
|
|
57
|
+
@inject(WorkflowDragService) dragService: WorkflowDragService;
|
|
58
|
+
|
|
59
|
+
@inject(WorkflowHoverService) hoverService: WorkflowHoverService;
|
|
60
|
+
|
|
61
|
+
@inject(WorkflowLinesManager)
|
|
62
|
+
linesManager: WorkflowLinesManager;
|
|
63
|
+
|
|
64
|
+
@observeEntity(EditorStateConfigEntity)
|
|
65
|
+
protected editorStateConfig: EditorStateConfigEntity;
|
|
66
|
+
|
|
67
|
+
@observeEntity(SelectorBoxConfigEntity)
|
|
68
|
+
protected selectorBoxConfigEntity: SelectorBoxConfigEntity;
|
|
69
|
+
|
|
70
|
+
@inject(PlaygroundConfigEntity) configEntity: PlaygroundConfigEntity;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 监听节点 transform
|
|
74
|
+
*/
|
|
75
|
+
@observeEntityDatas(WorkflowNodeEntity, FlowNodeTransformData)
|
|
76
|
+
protected readonly nodeTransforms: FlowNodeTransformData[];
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 按选中排序
|
|
80
|
+
* @private
|
|
81
|
+
*/
|
|
82
|
+
protected nodeTransformsWithSort: FlowNodeTransformData[] = [];
|
|
83
|
+
|
|
84
|
+
autorun(): void {
|
|
85
|
+
const { activatedNode } = this.selectionService;
|
|
86
|
+
this.nodeTransformsWithSort = this.nodeTransforms
|
|
87
|
+
.filter((n) => n.entity.id !== 'root' && n.entity.flowNodeType !== FlowNodeBaseType.GROUP)
|
|
88
|
+
.reverse() // 后创建的排在前面
|
|
89
|
+
.sort((n1) => (n1.entity === activatedNode ? -1 : 0));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 监听线条
|
|
94
|
+
*/
|
|
95
|
+
@observeEntities(WorkflowLineEntity)
|
|
96
|
+
protected readonly lines: WorkflowLineEntity[];
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 是否正在调整线条
|
|
100
|
+
* @protected
|
|
101
|
+
*/
|
|
102
|
+
get isDrawing(): boolean {
|
|
103
|
+
return this.linesManager.isDrawing;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
onReady(): void {
|
|
107
|
+
this.options = {
|
|
108
|
+
...HoverLayerOptions.DEFAULT,
|
|
109
|
+
...this.options,
|
|
110
|
+
};
|
|
111
|
+
this.toDispose.pushAll([
|
|
112
|
+
// 监听主动触发的 hover 事件
|
|
113
|
+
this.hoverService.onUpdateHoverPosition((hoverPosition) => {
|
|
114
|
+
const { position, target } = hoverPosition;
|
|
115
|
+
const canvasPosition = this.config.getPosFromMouseEvent({
|
|
116
|
+
clientX: position.x,
|
|
117
|
+
clientY: position.y,
|
|
118
|
+
});
|
|
119
|
+
this.updateHoveredState(canvasPosition, target);
|
|
120
|
+
}),
|
|
121
|
+
// 监听画布鼠标移动事件
|
|
122
|
+
this.listenPlaygroundEvent('mousemove', (e: MouseEvent) => {
|
|
123
|
+
this.hoverService.hoveredPos = this.config.getPosFromMouseEvent(e);
|
|
124
|
+
if (!this.isEnabled()) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (!this.options.canHovered!(e, this.hoverService)) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const mousePos = this.config.getPosFromMouseEvent(e);
|
|
131
|
+
// 更新 hover 状态
|
|
132
|
+
this.updateHoveredState(mousePos, e?.target as HTMLElement);
|
|
133
|
+
}),
|
|
134
|
+
this.selectionService.onSelectionChanged(() => this.autorun()),
|
|
135
|
+
// 控制触控
|
|
136
|
+
this.listenPlaygroundEvent('touchstart', (e: MouseEvent): boolean | undefined => {
|
|
137
|
+
if (!this.isEnabled() || this.isDrawing) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
return this.handleDragLine(e);
|
|
141
|
+
}),
|
|
142
|
+
// 控制选中逻辑
|
|
143
|
+
this.listenPlaygroundEvent('mousedown', (e: MouseEvent): boolean | undefined => {
|
|
144
|
+
if (!this.isEnabled() || this.isDrawing) {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
const { hoveredNode } = this.hoverService;
|
|
148
|
+
const lineDrag = this.handleDragLine(e);
|
|
149
|
+
if (lineDrag) {
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
const mousePos = this.config.getPosFromMouseEvent(e);
|
|
153
|
+
const selectionBounds = getSelectionBounds(
|
|
154
|
+
this.selectionService.selection,
|
|
155
|
+
// 这里只考虑多选模式,单选模式已经下沉到 use-node-render 中
|
|
156
|
+
true
|
|
157
|
+
);
|
|
158
|
+
if (selectionBounds.width > 0 && selectionBounds.contains(mousePos.x, mousePos.y)) {
|
|
159
|
+
/**
|
|
160
|
+
* 拖拽选择框
|
|
161
|
+
*/
|
|
162
|
+
this.dragService.startDragSelectedNodes(e)?.then((dragSuccess) => {
|
|
163
|
+
if (!dragSuccess) {
|
|
164
|
+
// 拖拽没有成功触发了点击
|
|
165
|
+
if (hoveredNode && hoveredNode instanceof WorkflowNodeEntity) {
|
|
166
|
+
// 追加选择
|
|
167
|
+
if (e.shiftKey) {
|
|
168
|
+
this.selectionService.toggleSelect(hoveredNode);
|
|
169
|
+
} else {
|
|
170
|
+
this.selectionService.selectNode(hoveredNode);
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
this.selectionService.clear();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
// 这里会组织触发 selector box
|
|
178
|
+
return true;
|
|
179
|
+
} else {
|
|
180
|
+
if (!hoveredNode) {
|
|
181
|
+
this.selectionService.clear();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return undefined;
|
|
185
|
+
}),
|
|
186
|
+
]);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* 更新 hoverd
|
|
191
|
+
* @param mousePos
|
|
192
|
+
*/
|
|
193
|
+
updateHoveredState(mousePos: IPoint, target?: HTMLElement): void {
|
|
194
|
+
const { hoverService } = this;
|
|
195
|
+
const nodeTransforms = this.nodeTransformsWithSort;
|
|
196
|
+
const outputPortHovered = this.linesManager.getPortFromMousePos(mousePos, 'output');
|
|
197
|
+
const inputPortHovered = this.linesManager.getPortFromMousePos(mousePos, 'input');
|
|
198
|
+
// 在两个端口叠加情况,优先使用 outputPort
|
|
199
|
+
const portHovered = outputPortHovered || inputPortHovered;
|
|
200
|
+
|
|
201
|
+
const lineDomNodes = this.playgroundNode.querySelectorAll(LINE_CLASS_NAME);
|
|
202
|
+
const checkTargetFromLine = [...lineDomNodes].some((lineDom) =>
|
|
203
|
+
lineDom.contains(target as HTMLElement)
|
|
204
|
+
);
|
|
205
|
+
if (portHovered) {
|
|
206
|
+
if (this.document.options.twoWayConnection) {
|
|
207
|
+
hoverService.updateHoveredKey(portHovered.id);
|
|
208
|
+
} else {
|
|
209
|
+
// 默认 只有 output 点位可以 hover
|
|
210
|
+
if (portHovered.portType === 'output') {
|
|
211
|
+
hoverService.updateHoveredKey(portHovered.id);
|
|
212
|
+
} else if (checkTargetFromLine || target?.className?.includes?.(PORT_BG_CLASS_NAME)) {
|
|
213
|
+
// 输入点采用获取最接近的线条
|
|
214
|
+
const lineHovered = this.linesManager.getCloseInLineFromMousePos(mousePos);
|
|
215
|
+
if (lineHovered) {
|
|
216
|
+
this.updateHoveredKey(lineHovered.id);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Drawing 情况,不能选中节点和线条
|
|
224
|
+
if (this.isDrawing) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const nodeHovered = nodeTransforms.find((trans: FlowNodeTransformData) =>
|
|
229
|
+
trans.bounds.contains(mousePos.x, mousePos.y)
|
|
230
|
+
)?.entity as WorkflowNodeEntity;
|
|
231
|
+
|
|
232
|
+
// 判断当前鼠标位置所在元素是否在节点内部
|
|
233
|
+
const nodeDomNodes = this.playgroundNode.querySelectorAll(NODE_CLASS_NAME);
|
|
234
|
+
const checkTargetFromNode = [...nodeDomNodes].some((nodeDom) =>
|
|
235
|
+
nodeDom.contains(target as HTMLElement)
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
if (nodeHovered || checkTargetFromNode) {
|
|
239
|
+
if (nodeHovered?.id) {
|
|
240
|
+
this.updateHoveredKey(nodeHovered.id);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// 获取最接近的线条
|
|
245
|
+
// 线条会相交需要获取最接近点位的线条,不能删除的线条不能被选中
|
|
246
|
+
const lineHovered = checkTargetFromLine
|
|
247
|
+
? this.linesManager.getCloseInLineFromMousePos(mousePos)
|
|
248
|
+
: undefined;
|
|
249
|
+
|
|
250
|
+
if (nodeHovered && lineHovered) {
|
|
251
|
+
const nodeStackIndex = nodeHovered.renderData.stackIndex;
|
|
252
|
+
const lineStackIndex = lineHovered.stackIndex;
|
|
253
|
+
if (nodeStackIndex > lineStackIndex) {
|
|
254
|
+
return this.updateHoveredKey(nodeHovered.id);
|
|
255
|
+
} else {
|
|
256
|
+
return this.updateHoveredKey(lineHovered.id);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// 判断节点是否 hover
|
|
261
|
+
if (nodeHovered) {
|
|
262
|
+
return this.updateHoveredKey(nodeHovered.id);
|
|
263
|
+
}
|
|
264
|
+
// 判断线条是否 hover
|
|
265
|
+
if (lineHovered) {
|
|
266
|
+
return this.updateHoveredKey(lineHovered.id);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// 上述逻辑都未命中 则清空 hovered
|
|
270
|
+
hoverService.clearHovered();
|
|
271
|
+
|
|
272
|
+
const currentState = this.editorStateConfig.getCurrentState();
|
|
273
|
+
const isMouseFriendly = currentState === EditorState.STATE_MOUSE_FRIENDLY_SELECT;
|
|
274
|
+
|
|
275
|
+
// 鼠标优先,并且不是按住 shift 键,更新为小手
|
|
276
|
+
if (isMouseFriendly && !this.editorStateConfig.isPressingShift) {
|
|
277
|
+
this.configEntity.updateCursor('grab');
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
updateHoveredKey(key: string): void {
|
|
282
|
+
// 鼠标优先交互模式,如果是 hover,需要将鼠标的小手去掉,还原鼠标原有样式
|
|
283
|
+
this.configEntity.updateCursor('default');
|
|
284
|
+
this.hoverService.updateHoveredKey(key);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* 判断是否能够 hover
|
|
289
|
+
* @returns 是否能 hover
|
|
290
|
+
*/
|
|
291
|
+
isEnabled(): boolean {
|
|
292
|
+
const currentState = this.editorStateConfig.getCurrentState();
|
|
293
|
+
// 选择框情况禁止 hover
|
|
294
|
+
return (
|
|
295
|
+
// 鼠标友好模式下,也需要支持 hover 效果,不然线条选择不到
|
|
296
|
+
// Coze 中没有使用该插件,需要在 workflow/render 包相应位置改动
|
|
297
|
+
(currentState === EditorState.STATE_SELECT ||
|
|
298
|
+
currentState === EditorState.STATE_MOUSE_FRIENDLY_SELECT) &&
|
|
299
|
+
!this.selectorBoxConfigEntity.isStart &&
|
|
300
|
+
!this.dragService.isDragging
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
private handleDragLine(e: MouseEvent): boolean | undefined {
|
|
305
|
+
const { someHovered } = this.hoverService;
|
|
306
|
+
// 重置线条
|
|
307
|
+
if (someHovered && someHovered instanceof WorkflowLineEntity) {
|
|
308
|
+
this.dragService.resetLine(someHovered, e);
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
if (
|
|
312
|
+
someHovered &&
|
|
313
|
+
someHovered instanceof WorkflowPortEntity &&
|
|
314
|
+
!someHovered.disabled &&
|
|
315
|
+
e.button !== 1
|
|
316
|
+
) {
|
|
317
|
+
e.stopPropagation();
|
|
318
|
+
e.preventDefault();
|
|
319
|
+
this.dragService.startDrawingLine(someHovered, e);
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Rectangle } from '@flowgram-vue/utils';
|
|
7
|
+
import { WorkflowNodeEntity } from '@flowgram-vue/free-layout-core';
|
|
8
|
+
import { FlowNodeTransformData } from '@flowgram-vue/document';
|
|
9
|
+
import { type Entity } from '@flowgram-vue/core';
|
|
10
|
+
|
|
11
|
+
const BOUNDS_PADDING = 2;
|
|
12
|
+
|
|
13
|
+
export function getSelectionBounds(
|
|
14
|
+
selection: Entity[],
|
|
15
|
+
ignoreOneSelect: boolean = true // 忽略单选
|
|
16
|
+
): Rectangle {
|
|
17
|
+
const selectedNodes = selection.filter((node) => node instanceof WorkflowNodeEntity);
|
|
18
|
+
|
|
19
|
+
// 选中单个的时候不显示
|
|
20
|
+
return selectedNodes.length > (ignoreOneSelect ? 1 : 0)
|
|
21
|
+
? Rectangle.enlarge(selectedNodes.map((n) => n.getData(FlowNodeTransformData)!.bounds)).pad(
|
|
22
|
+
BOUNDS_PADDING
|
|
23
|
+
)
|
|
24
|
+
: Rectangle.EMPTY;
|
|
25
|
+
}
|