@flowgram.ai/free-container-plugin 0.1.0-alpha.10
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/dist/esm/index.js +840 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/index.d.mts +165 -0
- package/dist/index.d.ts +165 -0
- package/dist/index.js +869 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,840 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/node-into-container/constant.ts
|
|
13
|
+
var NodeIntoContainerType = /* @__PURE__ */ ((NodeIntoContainerType2) => {
|
|
14
|
+
NodeIntoContainerType2["In"] = "in";
|
|
15
|
+
NodeIntoContainerType2["Out"] = "out";
|
|
16
|
+
return NodeIntoContainerType2;
|
|
17
|
+
})(NodeIntoContainerType || {});
|
|
18
|
+
|
|
19
|
+
// src/node-into-container/service.ts
|
|
20
|
+
import { throttle } from "lodash";
|
|
21
|
+
import { inject, injectable } from "inversify";
|
|
22
|
+
import {
|
|
23
|
+
Rectangle,
|
|
24
|
+
DisposableCollection,
|
|
25
|
+
Emitter
|
|
26
|
+
} from "@flowgram.ai/utils";
|
|
27
|
+
import {
|
|
28
|
+
WorkflowDocument,
|
|
29
|
+
WorkflowDragService,
|
|
30
|
+
WorkflowLinesManager,
|
|
31
|
+
WorkflowOperationBaseService,
|
|
32
|
+
WorkflowSelectService
|
|
33
|
+
} from "@flowgram.ai/free-layout-core";
|
|
34
|
+
import { HistoryService } from "@flowgram.ai/free-history-plugin";
|
|
35
|
+
import { FlowNodeRenderData, FlowNodeBaseType } from "@flowgram.ai/document";
|
|
36
|
+
import { PlaygroundConfigEntity, TransformData } from "@flowgram.ai/core";
|
|
37
|
+
var NodeIntoContainerService = class {
|
|
38
|
+
constructor() {
|
|
39
|
+
this.emitter = new Emitter();
|
|
40
|
+
this.toDispose = new DisposableCollection();
|
|
41
|
+
this.on = this.emitter.event;
|
|
42
|
+
}
|
|
43
|
+
init() {
|
|
44
|
+
this.initState();
|
|
45
|
+
this.toDispose.push(this.emitter);
|
|
46
|
+
}
|
|
47
|
+
ready() {
|
|
48
|
+
this.toDispose.push(this.listenDragToContainer());
|
|
49
|
+
}
|
|
50
|
+
dispose() {
|
|
51
|
+
this.initState();
|
|
52
|
+
this.toDispose.dispose();
|
|
53
|
+
}
|
|
54
|
+
/** 将节点移出容器 */
|
|
55
|
+
async moveOutContainer(params) {
|
|
56
|
+
const { node } = params;
|
|
57
|
+
const parentNode = node.parent;
|
|
58
|
+
const containerNode = parentNode?.parent;
|
|
59
|
+
const nodeJSON = this.document.toNodeJSON(node);
|
|
60
|
+
if (!parentNode || !containerNode || !this.isContainer(parentNode) || !nodeJSON.meta?.position) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const parentTransform = parentNode.getData(TransformData);
|
|
64
|
+
this.operationService.moveNode(node, {
|
|
65
|
+
parent: containerNode
|
|
66
|
+
});
|
|
67
|
+
await this.nextFrame();
|
|
68
|
+
parentTransform.fireChange();
|
|
69
|
+
this.operationService.updateNodePosition(node, {
|
|
70
|
+
x: parentTransform.position.x + nodeJSON.meta.position.x,
|
|
71
|
+
y: parentTransform.position.y + nodeJSON.meta.position.y
|
|
72
|
+
});
|
|
73
|
+
this.emitter.fire({
|
|
74
|
+
type: "out" /* Out */,
|
|
75
|
+
node,
|
|
76
|
+
sourceContainer: parentNode,
|
|
77
|
+
targetContainer: containerNode
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/** 能否将节点移出容器 */
|
|
81
|
+
canMoveOutContainer(node) {
|
|
82
|
+
const parentNode = node.parent;
|
|
83
|
+
const containerNode = parentNode?.parent;
|
|
84
|
+
if (!parentNode || !containerNode || !this.isContainer(parentNode)) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const canDrop = this.dragService.canDropToNode({
|
|
88
|
+
dragNodeType: node.flowNodeType,
|
|
89
|
+
dragNode: node,
|
|
90
|
+
dropNodeType: containerNode?.flowNodeType,
|
|
91
|
+
dropNode: containerNode
|
|
92
|
+
});
|
|
93
|
+
if (!canDrop.allowDrop) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
/** 移除节点所有非法连线 */
|
|
99
|
+
async clearInvalidLines(params) {
|
|
100
|
+
const { dragNode, sourceParent } = params;
|
|
101
|
+
if (!dragNode) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (dragNode.parent === sourceParent) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (dragNode.parent?.flowNodeType === FlowNodeBaseType.GROUP || sourceParent?.flowNodeType === FlowNodeBaseType.GROUP) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
await this.removeNodeLines(dragNode);
|
|
111
|
+
}
|
|
112
|
+
/** 移除节点连线 */
|
|
113
|
+
async removeNodeLines(node) {
|
|
114
|
+
const lines = this.linesManager.getAllLines();
|
|
115
|
+
lines.forEach((line) => {
|
|
116
|
+
if (line.from.id !== node.id && line.to?.id !== node.id) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
line.dispose();
|
|
120
|
+
});
|
|
121
|
+
await this.nextFrame();
|
|
122
|
+
}
|
|
123
|
+
/** 初始化状态 */
|
|
124
|
+
initState() {
|
|
125
|
+
this.state = {
|
|
126
|
+
isDraggingNode: false,
|
|
127
|
+
isSkipEvent: false,
|
|
128
|
+
transforms: void 0,
|
|
129
|
+
dragNode: void 0,
|
|
130
|
+
dropNode: void 0,
|
|
131
|
+
sourceParent: void 0
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/** 监听节点拖拽 */
|
|
135
|
+
listenDragToContainer() {
|
|
136
|
+
const draggingNode = (e) => this.draggingNode(e);
|
|
137
|
+
const throttledDraggingNode = throttle(draggingNode, 200);
|
|
138
|
+
return this.dragService.onNodesDrag(async (event) => {
|
|
139
|
+
if (this.selectService.selectedNodes.length !== 1) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (event.type === "onDragStart") {
|
|
143
|
+
if (this.state.isSkipEvent) {
|
|
144
|
+
this.state.isSkipEvent = false;
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
this.historyService.startTransaction();
|
|
148
|
+
this.state.isDraggingNode = true;
|
|
149
|
+
this.state.transforms = this.getContainerTransforms();
|
|
150
|
+
this.state.dragNode = this.selectService.selectedNodes[0];
|
|
151
|
+
this.state.dropNode = void 0;
|
|
152
|
+
this.state.sourceParent = this.state.dragNode?.parent;
|
|
153
|
+
await this.dragOutContainer(event);
|
|
154
|
+
}
|
|
155
|
+
if (event.type === "onDragging") {
|
|
156
|
+
throttledDraggingNode(event);
|
|
157
|
+
}
|
|
158
|
+
if (event.type === "onDragEnd") {
|
|
159
|
+
if (this.state.isSkipEvent) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
throttledDraggingNode.cancel();
|
|
163
|
+
draggingNode(event);
|
|
164
|
+
await this.dropNodeToContainer();
|
|
165
|
+
await this.clearInvalidLines({
|
|
166
|
+
dragNode: this.state.dragNode,
|
|
167
|
+
sourceParent: this.state.sourceParent
|
|
168
|
+
});
|
|
169
|
+
this.setDropNode(void 0);
|
|
170
|
+
this.initState();
|
|
171
|
+
this.historyService.endTransaction();
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/** 监听节点拖拽出容器 */
|
|
176
|
+
async dragOutContainer(event) {
|
|
177
|
+
const { dragNode } = this.state;
|
|
178
|
+
const activated = event.triggerEvent.metaKey || event.triggerEvent.ctrlKey;
|
|
179
|
+
if (!activated || // 必须按住指定按键
|
|
180
|
+
!dragNode || // 必须有一个节点
|
|
181
|
+
!this.canMoveOutContainer(dragNode)) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
this.moveOutContainer({ node: dragNode });
|
|
185
|
+
this.state.isSkipEvent = true;
|
|
186
|
+
event.dragger.stop(event.dragEvent.clientX, event.dragEvent.clientY);
|
|
187
|
+
await this.nextFrame();
|
|
188
|
+
this.dragService.startDragSelectedNodes(event.triggerEvent);
|
|
189
|
+
}
|
|
190
|
+
/** 获取重叠位置 */
|
|
191
|
+
getCollisionTransform(params) {
|
|
192
|
+
const { targetRect, targetPoint, transforms, withPadding = false } = params;
|
|
193
|
+
const collisionTransform = transforms.find((transform) => {
|
|
194
|
+
const { bounds, entity } = transform;
|
|
195
|
+
const padding = withPadding ? this.document.layout.getPadding(entity) : { left: 0, right: 0 };
|
|
196
|
+
const transformRect = new Rectangle(
|
|
197
|
+
bounds.x + padding.left + padding.right,
|
|
198
|
+
bounds.y,
|
|
199
|
+
bounds.width,
|
|
200
|
+
bounds.height
|
|
201
|
+
);
|
|
202
|
+
if (targetRect) {
|
|
203
|
+
return this.isRectIntersects(targetRect, transformRect);
|
|
204
|
+
}
|
|
205
|
+
if (targetPoint) {
|
|
206
|
+
return this.isPointInRect(targetPoint, transformRect);
|
|
207
|
+
}
|
|
208
|
+
return false;
|
|
209
|
+
});
|
|
210
|
+
return collisionTransform;
|
|
211
|
+
}
|
|
212
|
+
/** 设置放置节点高亮 */
|
|
213
|
+
setDropNode(dropNode) {
|
|
214
|
+
if (this.state.dropNode === dropNode) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (this.state.dropNode) {
|
|
218
|
+
const renderData2 = this.state.dropNode.getData(FlowNodeRenderData);
|
|
219
|
+
const renderDom2 = renderData2.node?.children?.[0];
|
|
220
|
+
if (renderDom2) {
|
|
221
|
+
renderDom2.classList.remove("selected");
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
this.state.dropNode = dropNode;
|
|
225
|
+
if (!dropNode) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const renderData = dropNode.getData(FlowNodeRenderData);
|
|
229
|
+
const renderDom = renderData.node?.children?.[0];
|
|
230
|
+
if (renderDom) {
|
|
231
|
+
renderDom.classList.add("selected");
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/** 获取容器节点transforms */
|
|
235
|
+
getContainerTransforms() {
|
|
236
|
+
return this.document.getAllNodes().filter((node) => {
|
|
237
|
+
if (node.originParent) {
|
|
238
|
+
return node.getNodeMeta().selectable && node.originParent.getNodeMeta().selectable;
|
|
239
|
+
}
|
|
240
|
+
return node.getNodeMeta().selectable;
|
|
241
|
+
}).filter((node) => this.isContainer(node)).sort((a, b) => {
|
|
242
|
+
const aIndex = a.renderData.stackIndex;
|
|
243
|
+
const bIndex = b.renderData.stackIndex;
|
|
244
|
+
return bIndex - aIndex;
|
|
245
|
+
}).map((node) => node.transform);
|
|
246
|
+
}
|
|
247
|
+
/** 放置节点到容器 */
|
|
248
|
+
async dropNodeToContainer() {
|
|
249
|
+
const { dropNode, dragNode, isDraggingNode } = this.state;
|
|
250
|
+
if (!isDraggingNode || !dragNode || !dropNode) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
return await this.moveIntoContainer({
|
|
254
|
+
node: dragNode,
|
|
255
|
+
containerNode: dropNode
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
/** 拖拽节点 */
|
|
259
|
+
draggingNode(nodeDragEvent) {
|
|
260
|
+
const { dragNode, isDraggingNode, transforms = [] } = this.state;
|
|
261
|
+
if (!isDraggingNode || !dragNode || !transforms?.length) {
|
|
262
|
+
return this.setDropNode(void 0);
|
|
263
|
+
}
|
|
264
|
+
const mousePos = this.playgroundConfig.getPosFromMouseEvent(nodeDragEvent.dragEvent);
|
|
265
|
+
const availableTransforms = transforms.filter(
|
|
266
|
+
(transform) => transform.entity.id !== dragNode.id
|
|
267
|
+
);
|
|
268
|
+
const collisionTransform = this.getCollisionTransform({
|
|
269
|
+
targetPoint: mousePos,
|
|
270
|
+
transforms: availableTransforms
|
|
271
|
+
});
|
|
272
|
+
const dropNode = collisionTransform?.entity;
|
|
273
|
+
const canDrop = this.canDropToContainer({
|
|
274
|
+
dragNode,
|
|
275
|
+
dropNode
|
|
276
|
+
});
|
|
277
|
+
if (!canDrop) {
|
|
278
|
+
return this.setDropNode(void 0);
|
|
279
|
+
}
|
|
280
|
+
return this.setDropNode(dropNode);
|
|
281
|
+
}
|
|
282
|
+
/** 判断能否将节点拖入容器 */
|
|
283
|
+
canDropToContainer(params) {
|
|
284
|
+
const { dragNode, dropNode } = params;
|
|
285
|
+
const isDropContainer = dropNode?.getNodeMeta().isContainer;
|
|
286
|
+
if (!dropNode || !isDropContainer || this.isParent(dragNode, dropNode)) {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
if (dragNode.flowNodeType === FlowNodeBaseType.GROUP && dropNode.flowNodeType !== FlowNodeBaseType.GROUP) {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
const canDrop = this.dragService.canDropToNode({
|
|
293
|
+
dragNodeType: dragNode.flowNodeType,
|
|
294
|
+
dropNodeType: dropNode?.flowNodeType,
|
|
295
|
+
dragNode,
|
|
296
|
+
dropNode
|
|
297
|
+
});
|
|
298
|
+
if (!canDrop.allowDrop) {
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
/** 判断一个节点是否为另一个节点的父节点(向上查找直到根节点) */
|
|
304
|
+
isParent(node, parent) {
|
|
305
|
+
let current = node.parent;
|
|
306
|
+
while (current) {
|
|
307
|
+
if (current.id === parent.id) {
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
current = current.parent;
|
|
311
|
+
}
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
/** 将节点移入容器 */
|
|
315
|
+
async moveIntoContainer(params) {
|
|
316
|
+
const { node, containerNode } = params;
|
|
317
|
+
const parentNode = node.parent;
|
|
318
|
+
this.operationService.moveNode(node, {
|
|
319
|
+
parent: containerNode
|
|
320
|
+
});
|
|
321
|
+
this.operationService.updateNodePosition(node, this.adjustSubNodePosition(node, containerNode));
|
|
322
|
+
await this.nextFrame();
|
|
323
|
+
this.emitter.fire({
|
|
324
|
+
type: "in" /* In */,
|
|
325
|
+
node,
|
|
326
|
+
sourceContainer: parentNode,
|
|
327
|
+
targetContainer: containerNode
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* 如果存在容器节点,且传入鼠标坐标,需要用容器的坐标减去传入的鼠标坐标
|
|
332
|
+
*/
|
|
333
|
+
adjustSubNodePosition(targetNode, containerNode) {
|
|
334
|
+
if (containerNode.flowNodeType === FlowNodeBaseType.ROOT) {
|
|
335
|
+
return targetNode.transform.position;
|
|
336
|
+
}
|
|
337
|
+
const nodeWorldTransform = targetNode.transform.transform.worldTransform;
|
|
338
|
+
const containerWorldTransform = containerNode.transform.transform.worldTransform;
|
|
339
|
+
const nodePosition = {
|
|
340
|
+
x: nodeWorldTransform.tx,
|
|
341
|
+
y: nodeWorldTransform.ty
|
|
342
|
+
};
|
|
343
|
+
const isParentEmpty = !containerNode.children || containerNode.children.length === 0;
|
|
344
|
+
const containerPadding = this.document.layout.getPadding(containerNode);
|
|
345
|
+
if (isParentEmpty) {
|
|
346
|
+
return {
|
|
347
|
+
x: 0,
|
|
348
|
+
y: containerPadding.top
|
|
349
|
+
};
|
|
350
|
+
} else {
|
|
351
|
+
return {
|
|
352
|
+
x: nodePosition.x - containerWorldTransform.tx,
|
|
353
|
+
y: nodePosition.y - containerWorldTransform.ty
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
isContainer(node) {
|
|
358
|
+
return node?.getNodeMeta().isContainer ?? false;
|
|
359
|
+
}
|
|
360
|
+
/** 判断点是否在矩形内 */
|
|
361
|
+
isPointInRect(point, rect) {
|
|
362
|
+
return point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.bottom;
|
|
363
|
+
}
|
|
364
|
+
/** 判断两个矩形是否相交 */
|
|
365
|
+
isRectIntersects(rectA, rectB) {
|
|
366
|
+
const hasHorizontalOverlap = rectA.right > rectB.left && rectA.left < rectB.right;
|
|
367
|
+
const hasVerticalOverlap = rectA.bottom > rectB.top && rectA.top < rectB.bottom;
|
|
368
|
+
return hasHorizontalOverlap && hasVerticalOverlap;
|
|
369
|
+
}
|
|
370
|
+
async nextFrame() {
|
|
371
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
__decorateClass([
|
|
375
|
+
inject(WorkflowDragService)
|
|
376
|
+
], NodeIntoContainerService.prototype, "dragService", 2);
|
|
377
|
+
__decorateClass([
|
|
378
|
+
inject(WorkflowDocument)
|
|
379
|
+
], NodeIntoContainerService.prototype, "document", 2);
|
|
380
|
+
__decorateClass([
|
|
381
|
+
inject(PlaygroundConfigEntity)
|
|
382
|
+
], NodeIntoContainerService.prototype, "playgroundConfig", 2);
|
|
383
|
+
__decorateClass([
|
|
384
|
+
inject(WorkflowOperationBaseService)
|
|
385
|
+
], NodeIntoContainerService.prototype, "operationService", 2);
|
|
386
|
+
__decorateClass([
|
|
387
|
+
inject(WorkflowLinesManager)
|
|
388
|
+
], NodeIntoContainerService.prototype, "linesManager", 2);
|
|
389
|
+
__decorateClass([
|
|
390
|
+
inject(HistoryService)
|
|
391
|
+
], NodeIntoContainerService.prototype, "historyService", 2);
|
|
392
|
+
__decorateClass([
|
|
393
|
+
inject(WorkflowSelectService)
|
|
394
|
+
], NodeIntoContainerService.prototype, "selectService", 2);
|
|
395
|
+
NodeIntoContainerService = __decorateClass([
|
|
396
|
+
injectable()
|
|
397
|
+
], NodeIntoContainerService);
|
|
398
|
+
|
|
399
|
+
// src/node-into-container/plugin.tsx
|
|
400
|
+
import { definePluginCreator } from "@flowgram.ai/core";
|
|
401
|
+
var createContainerNodePlugin = definePluginCreator({
|
|
402
|
+
onBind: ({ bind }) => {
|
|
403
|
+
bind(NodeIntoContainerService).toSelf().inSingletonScope();
|
|
404
|
+
},
|
|
405
|
+
onInit(ctx, options) {
|
|
406
|
+
ctx.get(NodeIntoContainerService).init();
|
|
407
|
+
},
|
|
408
|
+
onReady(ctx, options) {
|
|
409
|
+
if (options.disableNodeIntoContainer !== true) {
|
|
410
|
+
ctx.get(NodeIntoContainerService).ready();
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
onDispose(ctx) {
|
|
414
|
+
ctx.get(NodeIntoContainerService).dispose();
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// src/sub-canvas/hooks/use-node-size.ts
|
|
419
|
+
import { useState, useEffect } from "react";
|
|
420
|
+
import {
|
|
421
|
+
useCurrentEntity,
|
|
422
|
+
WorkflowNodePortsData
|
|
423
|
+
} from "@flowgram.ai/free-layout-core";
|
|
424
|
+
import { FlowNodeTransformData as FlowNodeTransformData2 } from "@flowgram.ai/document";
|
|
425
|
+
var useNodeSize = () => {
|
|
426
|
+
const node = useCurrentEntity();
|
|
427
|
+
const nodeMeta = node.getNodeMeta();
|
|
428
|
+
const { size = { width: 300, height: 200 }, isContainer } = nodeMeta;
|
|
429
|
+
const transform = node.getData(FlowNodeTransformData2);
|
|
430
|
+
const [width, setWidth] = useState(size.width);
|
|
431
|
+
const [height, setHeight] = useState(size.height);
|
|
432
|
+
const updatePorts = () => {
|
|
433
|
+
const portsData = node.getData(WorkflowNodePortsData);
|
|
434
|
+
portsData.updateDynamicPorts();
|
|
435
|
+
};
|
|
436
|
+
const updateSize = () => {
|
|
437
|
+
if (node.blocks.length === 0) {
|
|
438
|
+
setWidth(size.width);
|
|
439
|
+
setHeight(size.height);
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
setWidth(transform.bounds.width);
|
|
443
|
+
setHeight(transform.bounds.height);
|
|
444
|
+
};
|
|
445
|
+
useEffect(() => {
|
|
446
|
+
const dispose = transform.onDataChange(() => {
|
|
447
|
+
updateSize();
|
|
448
|
+
updatePorts();
|
|
449
|
+
});
|
|
450
|
+
return () => dispose.dispose();
|
|
451
|
+
}, [transform, width, height]);
|
|
452
|
+
useEffect(() => {
|
|
453
|
+
updateSize();
|
|
454
|
+
}, []);
|
|
455
|
+
if (!isContainer) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
return {
|
|
459
|
+
width,
|
|
460
|
+
height
|
|
461
|
+
};
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
// src/sub-canvas/hooks/use-sync-node-render-size.ts
|
|
465
|
+
import { useLayoutEffect } from "react";
|
|
466
|
+
import { useCurrentEntity as useCurrentEntity2 } from "@flowgram.ai/free-layout-core";
|
|
467
|
+
var useSyncNodeRenderSize = (nodeSize) => {
|
|
468
|
+
const node = useCurrentEntity2();
|
|
469
|
+
useLayoutEffect(() => {
|
|
470
|
+
if (!nodeSize) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
node.renderData.node.style.width = nodeSize.width + "px";
|
|
474
|
+
node.renderData.node.style.height = nodeSize.height + "px";
|
|
475
|
+
}, [nodeSize?.width, nodeSize?.height]);
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
// src/sub-canvas/components/background/index.tsx
|
|
479
|
+
import React from "react";
|
|
480
|
+
import { useCurrentEntity as useCurrentEntity3 } from "@flowgram.ai/free-layout-core";
|
|
481
|
+
import { useService } from "@flowgram.ai/core";
|
|
482
|
+
import { BackgroundConfig } from "@flowgram.ai/background-plugin";
|
|
483
|
+
|
|
484
|
+
// src/sub-canvas/components/background/style.ts
|
|
485
|
+
import styled from "styled-components";
|
|
486
|
+
var SubCanvasBackgroundStyle = styled.div`
|
|
487
|
+
width: 100%;
|
|
488
|
+
height: 100%;
|
|
489
|
+
inset: 56px 18px 18px;
|
|
490
|
+
/* 背景色现在通过 style 属性动态设置 */
|
|
491
|
+
`;
|
|
492
|
+
|
|
493
|
+
// src/sub-canvas/components/background/index.tsx
|
|
494
|
+
var SubCanvasBackground = () => {
|
|
495
|
+
const node = useCurrentEntity3();
|
|
496
|
+
let backgroundConfig = {};
|
|
497
|
+
try {
|
|
498
|
+
backgroundConfig = useService(BackgroundConfig);
|
|
499
|
+
} catch (error) {
|
|
500
|
+
}
|
|
501
|
+
const gridSize = backgroundConfig.gridSize ?? 20;
|
|
502
|
+
const dotSize = backgroundConfig.dotSize ?? 1;
|
|
503
|
+
const dotColor = backgroundConfig.dotColor ?? "#eceeef";
|
|
504
|
+
const dotOpacity = backgroundConfig.dotOpacity ?? 0.5;
|
|
505
|
+
const backgroundColor = backgroundConfig.backgroundColor ?? "#f2f3f5";
|
|
506
|
+
const dotFillColor = backgroundConfig.dotFillColor === dotColor ? "" : backgroundConfig.dotFillColor;
|
|
507
|
+
const patternId = `sub-canvas-dot-pattern-${node.id}`;
|
|
508
|
+
return /* @__PURE__ */ React.createElement(
|
|
509
|
+
SubCanvasBackgroundStyle,
|
|
510
|
+
{
|
|
511
|
+
className: "sub-canvas-background",
|
|
512
|
+
"data-flow-editor-selectable": "true",
|
|
513
|
+
style: { backgroundColor }
|
|
514
|
+
},
|
|
515
|
+
/* @__PURE__ */ React.createElement("svg", { width: "100%", height: "100%" }, /* @__PURE__ */ React.createElement("pattern", { id: patternId, width: gridSize, height: gridSize, patternUnits: "userSpaceOnUse" }, /* @__PURE__ */ React.createElement(
|
|
516
|
+
"circle",
|
|
517
|
+
{
|
|
518
|
+
cx: dotSize,
|
|
519
|
+
cy: dotSize,
|
|
520
|
+
r: dotSize,
|
|
521
|
+
stroke: dotColor,
|
|
522
|
+
fill: dotFillColor,
|
|
523
|
+
fillOpacity: dotOpacity
|
|
524
|
+
}
|
|
525
|
+
)), /* @__PURE__ */ React.createElement(
|
|
526
|
+
"rect",
|
|
527
|
+
{
|
|
528
|
+
width: "100%",
|
|
529
|
+
height: "100%",
|
|
530
|
+
fill: `url(#${patternId})`,
|
|
531
|
+
"data-node-panel-container": node.id
|
|
532
|
+
}
|
|
533
|
+
))
|
|
534
|
+
);
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
// src/sub-canvas/components/border/index.tsx
|
|
538
|
+
import React2 from "react";
|
|
539
|
+
|
|
540
|
+
// src/sub-canvas/components/border/style.ts
|
|
541
|
+
import styled2 from "styled-components";
|
|
542
|
+
var SubCanvasBorderStyle = styled2.div`
|
|
543
|
+
pointer-events: none;
|
|
544
|
+
|
|
545
|
+
position: relative;
|
|
546
|
+
|
|
547
|
+
display: flex;
|
|
548
|
+
align-items: center;
|
|
549
|
+
|
|
550
|
+
width: 100%;
|
|
551
|
+
height: 100%;
|
|
552
|
+
|
|
553
|
+
background-color: transparent;
|
|
554
|
+
border: 1px solid var(--coz-stroke-plus, rgba(6, 7, 9, 15%));
|
|
555
|
+
border-color: var(--coz-bg-plus, rgb(249, 249, 249));
|
|
556
|
+
border-style: solid;
|
|
557
|
+
border-width: 8px;
|
|
558
|
+
border-radius: 8px;
|
|
559
|
+
|
|
560
|
+
&::before {
|
|
561
|
+
content: '';
|
|
562
|
+
|
|
563
|
+
position: absolute;
|
|
564
|
+
z-index: 0;
|
|
565
|
+
inset: -4px;
|
|
566
|
+
|
|
567
|
+
background-color: transparent;
|
|
568
|
+
border-color: var(--coz-bg-plus, rgb(249, 249, 249));
|
|
569
|
+
border-style: solid;
|
|
570
|
+
border-width: 4px;
|
|
571
|
+
border-radius: 8px;
|
|
572
|
+
}
|
|
573
|
+
`;
|
|
574
|
+
|
|
575
|
+
// src/sub-canvas/components/border/index.tsx
|
|
576
|
+
var SubCanvasBorder = ({ style, children }) => /* @__PURE__ */ React2.createElement(
|
|
577
|
+
SubCanvasBorderStyle,
|
|
578
|
+
{
|
|
579
|
+
className: "sub-canvas-border",
|
|
580
|
+
style: {
|
|
581
|
+
...style
|
|
582
|
+
}
|
|
583
|
+
},
|
|
584
|
+
children
|
|
585
|
+
);
|
|
586
|
+
|
|
587
|
+
// src/sub-canvas/components/render/index.tsx
|
|
588
|
+
import React5 from "react";
|
|
589
|
+
|
|
590
|
+
// src/sub-canvas/components/render/style.ts
|
|
591
|
+
import styled3 from "styled-components";
|
|
592
|
+
var SubCanvasRenderStyle = styled3.div`
|
|
593
|
+
width: 100%;
|
|
594
|
+
height: 100%;
|
|
595
|
+
`;
|
|
596
|
+
|
|
597
|
+
// src/sub-canvas/components/tips/index.tsx
|
|
598
|
+
import React4 from "react";
|
|
599
|
+
import { I18n } from "@flowgram.ai/i18n";
|
|
600
|
+
|
|
601
|
+
// src/sub-canvas/components/tips/use-control.ts
|
|
602
|
+
import { useCallback, useEffect as useEffect2, useState as useState2 } from "react";
|
|
603
|
+
import { useCurrentEntity as useCurrentEntity4 } from "@flowgram.ai/free-layout-core";
|
|
604
|
+
import { useService as useService2 } from "@flowgram.ai/core";
|
|
605
|
+
|
|
606
|
+
// src/sub-canvas/components/tips/global-store.ts
|
|
607
|
+
var STORAGE_KEY = "workflow-move-into-sub-canvas-tip-visible";
|
|
608
|
+
var STORAGE_VALUE = "false";
|
|
609
|
+
var TipsGlobalStore = class _TipsGlobalStore {
|
|
610
|
+
constructor() {
|
|
611
|
+
this.closed = false;
|
|
612
|
+
}
|
|
613
|
+
static get instance() {
|
|
614
|
+
if (!this._instance) {
|
|
615
|
+
this._instance = new _TipsGlobalStore();
|
|
616
|
+
}
|
|
617
|
+
return this._instance;
|
|
618
|
+
}
|
|
619
|
+
isClosed() {
|
|
620
|
+
return this.isCloseForever() || this.closed;
|
|
621
|
+
}
|
|
622
|
+
close() {
|
|
623
|
+
this.closed = true;
|
|
624
|
+
}
|
|
625
|
+
isCloseForever() {
|
|
626
|
+
return localStorage.getItem(STORAGE_KEY) === STORAGE_VALUE;
|
|
627
|
+
}
|
|
628
|
+
closeForever() {
|
|
629
|
+
localStorage.setItem(STORAGE_KEY, STORAGE_VALUE);
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
// src/sub-canvas/components/tips/use-control.ts
|
|
634
|
+
var useControlTips = () => {
|
|
635
|
+
const node = useCurrentEntity4();
|
|
636
|
+
const [visible, setVisible] = useState2(false);
|
|
637
|
+
const globalStore = TipsGlobalStore.instance;
|
|
638
|
+
const nodeIntoContainerService = useService2(NodeIntoContainerService);
|
|
639
|
+
const show = useCallback(() => {
|
|
640
|
+
if (globalStore.isClosed()) {
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
setVisible(true);
|
|
644
|
+
}, [globalStore]);
|
|
645
|
+
const close = useCallback(() => {
|
|
646
|
+
globalStore.close();
|
|
647
|
+
setVisible(false);
|
|
648
|
+
}, [globalStore]);
|
|
649
|
+
const closeForever = useCallback(() => {
|
|
650
|
+
globalStore.closeForever();
|
|
651
|
+
close();
|
|
652
|
+
}, [close, globalStore]);
|
|
653
|
+
useEffect2(() => {
|
|
654
|
+
const inDisposer = nodeIntoContainerService.on((e) => {
|
|
655
|
+
if (e.type !== "in" /* In */) {
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
if (e.targetContainer === node) {
|
|
659
|
+
show();
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
const outDisposer = nodeIntoContainerService.on((e) => {
|
|
663
|
+
if (e.type !== "out" /* Out */) {
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
if (e.sourceContainer === node && !node.blocks.length) {
|
|
667
|
+
setVisible(false);
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
return () => {
|
|
671
|
+
inDisposer.dispose();
|
|
672
|
+
outDisposer.dispose();
|
|
673
|
+
};
|
|
674
|
+
}, [nodeIntoContainerService, node, show, close, visible]);
|
|
675
|
+
return {
|
|
676
|
+
visible,
|
|
677
|
+
close,
|
|
678
|
+
closeForever
|
|
679
|
+
};
|
|
680
|
+
};
|
|
681
|
+
|
|
682
|
+
// src/sub-canvas/components/tips/style.ts
|
|
683
|
+
import styled4 from "styled-components";
|
|
684
|
+
var SubCanvasTipsStyle = styled4.div`
|
|
685
|
+
pointer-events: auto;
|
|
686
|
+
position: absolute;
|
|
687
|
+
top: 0;
|
|
688
|
+
|
|
689
|
+
width: 100%;
|
|
690
|
+
height: 28px;
|
|
691
|
+
|
|
692
|
+
.container {
|
|
693
|
+
height: 100%;
|
|
694
|
+
background-color: #e4e6f5;
|
|
695
|
+
border-radius: 4px 4px 0 0;
|
|
696
|
+
|
|
697
|
+
.content {
|
|
698
|
+
overflow: hidden;
|
|
699
|
+
display: inline-flex;
|
|
700
|
+
align-items: center;
|
|
701
|
+
justify-content: center;
|
|
702
|
+
|
|
703
|
+
width: 100%;
|
|
704
|
+
height: 100%;
|
|
705
|
+
|
|
706
|
+
.text {
|
|
707
|
+
font-size: 14px;
|
|
708
|
+
font-weight: 400;
|
|
709
|
+
font-style: normal;
|
|
710
|
+
line-height: 20px;
|
|
711
|
+
color: rgba(15, 21, 40, 82%);
|
|
712
|
+
text-overflow: ellipsis;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
.custom-content {
|
|
716
|
+
display: flex;
|
|
717
|
+
align-items: center;
|
|
718
|
+
justify-content: center;
|
|
719
|
+
width: 100%;
|
|
720
|
+
height: 100%;
|
|
721
|
+
|
|
722
|
+
/* 为自定义内容提供默认样式,但允许覆盖 */
|
|
723
|
+
font-size: 14px;
|
|
724
|
+
font-weight: 400;
|
|
725
|
+
line-height: 20px;
|
|
726
|
+
color: rgba(15, 21, 40, 82%);
|
|
727
|
+
|
|
728
|
+
/* 确保自定义内容不会超出容器 */
|
|
729
|
+
overflow: hidden;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
.space {
|
|
733
|
+
width: 128px;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
.actions {
|
|
738
|
+
position: absolute;
|
|
739
|
+
top: 0;
|
|
740
|
+
right: 0;
|
|
741
|
+
|
|
742
|
+
display: flex;
|
|
743
|
+
gap: 8px;
|
|
744
|
+
align-items: center;
|
|
745
|
+
|
|
746
|
+
height: 28px;
|
|
747
|
+
padding: 0 16px;
|
|
748
|
+
|
|
749
|
+
.close-forever {
|
|
750
|
+
cursor: pointer;
|
|
751
|
+
|
|
752
|
+
padding: 0 3px;
|
|
753
|
+
|
|
754
|
+
font-size: 12px;
|
|
755
|
+
font-weight: 400;
|
|
756
|
+
font-style: normal;
|
|
757
|
+
line-height: 12px;
|
|
758
|
+
color: rgba(32, 41, 69, 62%);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
.close {
|
|
762
|
+
display: flex;
|
|
763
|
+
cursor: pointer;
|
|
764
|
+
height: 100%;
|
|
765
|
+
align-items: center;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
`;
|
|
770
|
+
|
|
771
|
+
// src/sub-canvas/components/tips/is-mac-os.ts
|
|
772
|
+
var isMacOS = /(Macintosh|MacIntel|MacPPC|Mac68K|iPad)/.test(navigator.userAgent);
|
|
773
|
+
|
|
774
|
+
// src/sub-canvas/components/tips/icon-close.tsx
|
|
775
|
+
import React3 from "react";
|
|
776
|
+
var IconClose = () => /* @__PURE__ */ React3.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", fill: "none", viewBox: "0 0 16 16" }, /* @__PURE__ */ React3.createElement(
|
|
777
|
+
"path",
|
|
778
|
+
{
|
|
779
|
+
fill: "#060709",
|
|
780
|
+
fillOpacity: "0.5",
|
|
781
|
+
d: "M12.13 12.128a.5.5 0 0 0 .001-.706L8.71 8l3.422-3.423a.5.5 0 0 0-.001-.705.5.5 0 0 0-.706-.002L8.002 7.293 4.579 3.87a.5.5 0 0 0-.705.002.5.5 0 0 0-.002.705L7.295 8l-3.423 3.422a.5.5 0 0 0 .002.706c.195.195.51.197.705.001l3.423-3.422 3.422 3.422c.196.196.51.194.706-.001"
|
|
782
|
+
}
|
|
783
|
+
));
|
|
784
|
+
|
|
785
|
+
// src/sub-canvas/components/tips/index.tsx
|
|
786
|
+
var SubCanvasTips = ({ tipText, neverRemindText }) => {
|
|
787
|
+
const { visible, close, closeForever } = useControlTips();
|
|
788
|
+
const displayContent = tipText || I18n.t("Hold {{key}} to drag node out", { key: isMacOS ? "Cmd \u2318" : "Ctrl" });
|
|
789
|
+
if (!visible) {
|
|
790
|
+
return null;
|
|
791
|
+
}
|
|
792
|
+
return /* @__PURE__ */ React4.createElement(SubCanvasTipsStyle, { className: "sub-canvas-tips" }, /* @__PURE__ */ React4.createElement("div", { className: "container" }, /* @__PURE__ */ React4.createElement("div", { className: "content" }, typeof displayContent === "string" ? /* @__PURE__ */ React4.createElement("p", { className: "text" }, displayContent) : /* @__PURE__ */ React4.createElement("div", { className: "custom-content" }, displayContent), /* @__PURE__ */ React4.createElement(
|
|
793
|
+
"div",
|
|
794
|
+
{
|
|
795
|
+
className: "space",
|
|
796
|
+
style: {
|
|
797
|
+
width: 0
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
)), /* @__PURE__ */ React4.createElement("div", { className: "actions" }, /* @__PURE__ */ React4.createElement("p", { className: "close-forever", onClick: closeForever }, neverRemindText || I18n.t("Never Remind")), /* @__PURE__ */ React4.createElement("div", { className: "close", onClick: close }, /* @__PURE__ */ React4.createElement(IconClose, null)))));
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
// src/sub-canvas/components/render/index.tsx
|
|
804
|
+
var SubCanvasRender = ({
|
|
805
|
+
className,
|
|
806
|
+
style,
|
|
807
|
+
offsetY = 0,
|
|
808
|
+
tipText
|
|
809
|
+
}) => {
|
|
810
|
+
const nodeSize = useNodeSize();
|
|
811
|
+
const nodeHeight = nodeSize?.height ?? 0;
|
|
812
|
+
useSyncNodeRenderSize(nodeSize);
|
|
813
|
+
return /* @__PURE__ */ React5.createElement(
|
|
814
|
+
SubCanvasRenderStyle,
|
|
815
|
+
{
|
|
816
|
+
className: `sub-canvas-render ${className ?? ""}`,
|
|
817
|
+
style: {
|
|
818
|
+
height: nodeHeight + offsetY,
|
|
819
|
+
...style
|
|
820
|
+
},
|
|
821
|
+
"data-flow-editor-selectable": "true",
|
|
822
|
+
onDragStart: (e) => {
|
|
823
|
+
e.stopPropagation();
|
|
824
|
+
}
|
|
825
|
+
},
|
|
826
|
+
/* @__PURE__ */ React5.createElement(SubCanvasBorder, null, /* @__PURE__ */ React5.createElement(SubCanvasBackground, null), /* @__PURE__ */ React5.createElement(SubCanvasTips, { tipText }))
|
|
827
|
+
);
|
|
828
|
+
};
|
|
829
|
+
export {
|
|
830
|
+
NodeIntoContainerService,
|
|
831
|
+
NodeIntoContainerType,
|
|
832
|
+
SubCanvasBackground,
|
|
833
|
+
SubCanvasBorder,
|
|
834
|
+
SubCanvasRender,
|
|
835
|
+
SubCanvasTips,
|
|
836
|
+
createContainerNodePlugin,
|
|
837
|
+
useNodeSize,
|
|
838
|
+
useSyncNodeRenderSize
|
|
839
|
+
};
|
|
840
|
+
//# sourceMappingURL=index.js.map
|