@flowgram.ai/free-node-panel-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.
@@ -0,0 +1,614 @@
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/create-plugin.ts
13
+ import { definePluginCreator } from "@flowgram.ai/core";
14
+
15
+ // src/service.ts
16
+ import { inject, injectable } from "inversify";
17
+ import { DisposableCollection } from "@flowgram.ai/utils";
18
+ import {
19
+ WorkflowDocument,
20
+ WorkflowDragService,
21
+ WorkflowLinesManager as WorkflowLinesManager2
22
+ } from "@flowgram.ai/free-layout-core";
23
+ import { WorkflowSelectService } from "@flowgram.ai/free-layout-core";
24
+ import { HistoryService as HistoryService2 } from "@flowgram.ai/free-history-plugin";
25
+ import { PlaygroundConfigEntity } from "@flowgram.ai/core";
26
+
27
+ // src/utils/wait-node-render.ts
28
+ import { delay } from "@flowgram.ai/free-layout-core";
29
+ var waitNodeRender = async () => {
30
+ await delay(20);
31
+ };
32
+
33
+ // src/utils/update-sub-nodes-position.ts
34
+ import { FreeOperationType } from "@flowgram.ai/free-history-plugin";
35
+ import { TransformData } from "@flowgram.ai/core";
36
+
37
+ // src/utils/get-port-box.ts
38
+ import { Rectangle } from "@flowgram.ai/utils";
39
+ import { FlowNodeTransformData } from "@flowgram.ai/document";
40
+
41
+ // src/utils/is-container.ts
42
+ var isContainer = (node) => node?.getNodeMeta().isContainer ?? false;
43
+
44
+ // src/utils/get-port-box.ts
45
+ var getPortBox = (port, offset = { x: 0, y: 0 }) => {
46
+ const node = port.node;
47
+ if (isContainer(node)) {
48
+ const { point } = port;
49
+ if (port.portType === "input") {
50
+ return new Rectangle(point.x + offset.x, point.y - 50 + offset.y, 300, 100);
51
+ }
52
+ return new Rectangle(point.x - 300, point.y - 50, 300, 100);
53
+ }
54
+ const box = node.getData(FlowNodeTransformData).bounds;
55
+ return box;
56
+ };
57
+
58
+ // src/utils/update-sub-nodes-position.ts
59
+ var updateSubSequentNodesPosition = (params) => {
60
+ const {
61
+ node,
62
+ subsequentNodes,
63
+ fromPort,
64
+ toPort,
65
+ containerNode,
66
+ offset,
67
+ historyService,
68
+ dragService
69
+ } = params;
70
+ if (!offset || !toPort) {
71
+ return;
72
+ }
73
+ const subsequentNodesPositions = subsequentNodes.map((node2) => {
74
+ const nodeTrans2 = node2.getData(TransformData);
75
+ return {
76
+ x: nodeTrans2.position.x,
77
+ y: nodeTrans2.position.y
78
+ };
79
+ });
80
+ historyService.pushOperation({
81
+ type: FreeOperationType.dragNodes,
82
+ value: {
83
+ ids: subsequentNodes.map((node2) => node2.id),
84
+ value: subsequentNodesPositions.map((position) => ({
85
+ x: position.x + offset.x,
86
+ y: position.y + offset.y
87
+ })),
88
+ oldValue: subsequentNodesPositions
89
+ }
90
+ });
91
+ const fromBox = getPortBox(fromPort);
92
+ const toBox = getPortBox(toPort, offset);
93
+ const nodeTrans = node.getData(TransformData);
94
+ let nodePos = {
95
+ x: (fromBox.center.x + toBox.center.x) / 2,
96
+ y: (fromBox.y + toBox.y) / 2
97
+ };
98
+ if (containerNode) {
99
+ nodePos = dragService.adjustSubNodePosition(
100
+ node.flowNodeType,
101
+ containerNode,
102
+ nodePos
103
+ );
104
+ }
105
+ historyService.pushOperation({
106
+ type: FreeOperationType.dragNodes,
107
+ value: {
108
+ ids: [node.id],
109
+ value: [nodePos],
110
+ oldValue: [
111
+ {
112
+ x: nodeTrans.position.x,
113
+ y: nodeTrans.position.y
114
+ }
115
+ ]
116
+ }
117
+ });
118
+ };
119
+
120
+ // src/utils/sub-position-offset.ts
121
+ import { Rectangle as Rectangle3 } from "@flowgram.ai/utils";
122
+ import { FlowNodeTransformData as FlowNodeTransformData2 } from "@flowgram.ai/document";
123
+
124
+ // src/utils/rect-distance.ts
125
+ import { Rectangle as Rectangle2 } from "@flowgram.ai/utils";
126
+ var rectDistance = (rectA, rectB) => {
127
+ const distanceX = Math.abs(Math.min(rectA.right, rectB.right) - Math.max(rectA.left, rectB.left));
128
+ const distanceY = Math.abs(Math.min(rectA.bottom, rectB.bottom) - Math.max(rectA.top, rectB.top));
129
+ if (Rectangle2.intersects(rectA, rectB)) {
130
+ return {
131
+ x: -distanceX,
132
+ y: -distanceY
133
+ };
134
+ }
135
+ return {
136
+ x: distanceX,
137
+ y: distanceY
138
+ };
139
+ };
140
+
141
+ // src/utils/greater-or-less.ts
142
+ var isGreaterThan = (a, b) => {
143
+ if (a === void 0 || b === void 0) {
144
+ return false;
145
+ }
146
+ const EPSILON = 1e-5;
147
+ return a - b > EPSILON;
148
+ };
149
+ var isLessThan = (a, b) => {
150
+ if (a === void 0 || b === void 0) {
151
+ return false;
152
+ }
153
+ const EPSILON = 1e-5;
154
+ return b - a > EPSILON;
155
+ };
156
+
157
+ // src/utils/sub-position-offset.ts
158
+ var subPositionOffset = (params) => {
159
+ const { node, fromPort, toPort, padding } = params;
160
+ const fromBox = getPortBox(fromPort);
161
+ const toBox = getPortBox(toPort);
162
+ const nodeTrans = node.getData(FlowNodeTransformData2);
163
+ const nodeSize = node.getNodeMeta()?.size ?? {
164
+ width: nodeTrans.bounds.width,
165
+ height: nodeTrans.bounds.height
166
+ };
167
+ const minDistance = {
168
+ x: nodeSize.width + padding.x,
169
+ y: nodeSize.height + padding.y
170
+ };
171
+ const boxDistance = rectDistance(fromBox, toBox);
172
+ const neededOffset = {
173
+ x: isGreaterThan(boxDistance.x, minDistance.x) ? 0 : minDistance.x - boxDistance.x,
174
+ y: isGreaterThan(boxDistance.y, minDistance.y) ? 0 : minDistance.y - boxDistance.y
175
+ };
176
+ if (neededOffset.x === 0 || neededOffset.y === 0) {
177
+ return;
178
+ }
179
+ const intersection = {
180
+ // 这里没有写反,Rectangle内置的算法是反的
181
+ vertical: Rectangle3.intersects(fromBox, toBox, "horizontal"),
182
+ horizontal: Rectangle3.intersects(fromBox, toBox, "vertical")
183
+ };
184
+ let offsetX = 0;
185
+ let offsetY = 0;
186
+ if (!intersection.horizontal) {
187
+ if (isGreaterThan(toBox.center.y, fromBox.center.y)) {
188
+ offsetY = neededOffset.y;
189
+ } else if (isLessThan(toBox.center.y, fromBox.center.y)) {
190
+ offsetY = -neededOffset.y;
191
+ }
192
+ }
193
+ if (!intersection.vertical) {
194
+ if (isGreaterThan(toBox.center.x, fromBox.center.x)) {
195
+ offsetX = neededOffset.x;
196
+ } else if (isLessThan(toBox.center.x, fromBox.center.x)) {
197
+ offsetX = -neededOffset.x;
198
+ }
199
+ }
200
+ return {
201
+ x: offsetX,
202
+ y: offsetY
203
+ };
204
+ };
205
+
206
+ // src/utils/get-sub-nodes.ts
207
+ var getSubsequentNodes = (params) => {
208
+ const { node, linesManager } = params;
209
+ if (isContainer(node)) {
210
+ return [];
211
+ }
212
+ const brothers = node.parent?.blocks ?? [];
213
+ const linkedBrothers = /* @__PURE__ */ new Set();
214
+ const linesMap = /* @__PURE__ */ new Map();
215
+ linesManager.getAllLines().forEach((line) => {
216
+ if (!linesMap.has(line.from.id)) {
217
+ linesMap.set(line.from.id, []);
218
+ }
219
+ if (!line.to?.id || isContainer(line.to)) {
220
+ return;
221
+ }
222
+ linesMap.get(line.from.id)?.push(line.to.id);
223
+ });
224
+ const bfs = (nodeId) => {
225
+ if (linkedBrothers.has(nodeId)) {
226
+ return;
227
+ }
228
+ linkedBrothers.add(nodeId);
229
+ const nextNodes = linesMap.get(nodeId) ?? [];
230
+ nextNodes.forEach(bfs);
231
+ };
232
+ bfs(node.id);
233
+ const subsequentNodes = brothers.filter((node2) => linkedBrothers.has(node2.id));
234
+ return subsequentNodes;
235
+ };
236
+
237
+ // src/utils/sub-nodes-auto-offset.ts
238
+ var subNodesAutoOffset = (params) => {
239
+ const {
240
+ node,
241
+ fromPort,
242
+ toPort,
243
+ linesManager,
244
+ historyService,
245
+ dragService,
246
+ containerNode,
247
+ padding = {
248
+ x: 100,
249
+ y: 100
250
+ }
251
+ } = params;
252
+ const subOffset = subPositionOffset({
253
+ node,
254
+ fromPort,
255
+ toPort,
256
+ padding
257
+ });
258
+ const subsequentNodes = getSubsequentNodes({
259
+ node: toPort.node,
260
+ linesManager
261
+ });
262
+ updateSubSequentNodesPosition({
263
+ node,
264
+ subsequentNodes,
265
+ fromPort,
266
+ toPort,
267
+ containerNode,
268
+ offset: subOffset,
269
+ historyService,
270
+ dragService
271
+ });
272
+ };
273
+
274
+ // src/utils/get-container-node.ts
275
+ var getContainerNode = (params) => {
276
+ const { fromPort, containerNode } = params;
277
+ if (containerNode) {
278
+ return containerNode;
279
+ }
280
+ const fromNode = fromPort?.node;
281
+ const fromContainer = fromNode?.parent;
282
+ if (isContainer(fromNode)) {
283
+ return fromNode;
284
+ }
285
+ return fromContainer;
286
+ };
287
+
288
+ // src/utils/build-line.ts
289
+ import {
290
+ WorkflowNodePortsData
291
+ } from "@flowgram.ai/free-layout-core";
292
+ var buildLine = (params) => {
293
+ const { fromPort, node, toPort, linesManager } = params;
294
+ const portsData = node.getData(WorkflowNodePortsData);
295
+ if (!portsData) {
296
+ return;
297
+ }
298
+ const shouldBuildFromLine = portsData.inputPorts?.length > 0;
299
+ if (fromPort && shouldBuildFromLine) {
300
+ const toTargetPort = portsData.inputPorts[0];
301
+ linesManager.createLine({
302
+ from: fromPort.node.id,
303
+ fromPort: fromPort.portID,
304
+ to: node.id,
305
+ toPort: toTargetPort.portID
306
+ });
307
+ }
308
+ const shouldBuildToLine = portsData.outputPorts?.length > 0;
309
+ if (toPort && shouldBuildToLine) {
310
+ const fromTargetPort = portsData.outputPorts[0];
311
+ linesManager.createLine({
312
+ from: node.id,
313
+ fromPort: fromTargetPort.portID,
314
+ to: toPort.node.id,
315
+ toPort: toPort.portID
316
+ });
317
+ }
318
+ };
319
+
320
+ // src/utils/adjust-node-position.ts
321
+ var adjustNodePosition = (params) => {
322
+ const { nodeType, position, fromPort, toPort, containerNode, document, dragService } = params;
323
+ const register = document.getNodeRegistry(nodeType);
324
+ const size = register?.meta?.size;
325
+ let adjustedPosition = position;
326
+ if (!size) {
327
+ adjustedPosition = position;
328
+ } else if (fromPort && toPort) {
329
+ adjustedPosition = {
330
+ x: position.x,
331
+ y: position.y - size.height / 2
332
+ };
333
+ } else if (fromPort && !toPort) {
334
+ adjustedPosition = {
335
+ x: position.x + size.width / 2,
336
+ y: position.y - size.height / 2
337
+ };
338
+ } else if (!fromPort && toPort) {
339
+ adjustedPosition = {
340
+ x: position.x - size.width / 2,
341
+ y: position.y - size.height / 2
342
+ };
343
+ } else {
344
+ adjustedPosition = position;
345
+ }
346
+ return dragService.adjustSubNodePosition(nodeType, containerNode, adjustedPosition);
347
+ };
348
+
349
+ // src/utils/index.ts
350
+ var WorkflowNodePanelUtils = {
351
+ adjustNodePosition,
352
+ buildLine,
353
+ getPortBox,
354
+ getSubsequentNodes,
355
+ getContainerNode,
356
+ rectDistance,
357
+ subNodesAutoOffset,
358
+ subPositionOffset,
359
+ updateSubSequentNodesPosition,
360
+ waitNodeRender
361
+ };
362
+
363
+ // src/service.ts
364
+ var WorkflowNodePanelService = class {
365
+ constructor() {
366
+ this.toDispose = new DisposableCollection();
367
+ this.callNodePanel = async () => void 0;
368
+ }
369
+ /** 销毁 */
370
+ dispose() {
371
+ this.toDispose.dispose();
372
+ }
373
+ setCallNodePanel(callNodePanel) {
374
+ this.callNodePanel = callNodePanel;
375
+ }
376
+ /** 唤起节点面板 */
377
+ async call(callParams) {
378
+ const {
379
+ panelPosition,
380
+ fromPort,
381
+ enableMultiAdd = false,
382
+ panelProps = {},
383
+ containerNode,
384
+ afterAddNode
385
+ } = callParams;
386
+ if (!panelPosition || this.playgroundConfig.readonly) {
387
+ return;
388
+ }
389
+ const nodes = [];
390
+ return new Promise((resolve) => {
391
+ this.callNodePanel({
392
+ position: panelPosition,
393
+ enableMultiAdd,
394
+ panelProps,
395
+ containerNode: WorkflowNodePanelUtils.getContainerNode({
396
+ fromPort,
397
+ containerNode
398
+ }),
399
+ onSelect: async (panelParams) => {
400
+ const node = await this.addNode(callParams, panelParams);
401
+ afterAddNode?.(node);
402
+ if (!enableMultiAdd) {
403
+ resolve(node);
404
+ } else if (node) {
405
+ nodes.push(node);
406
+ }
407
+ },
408
+ onClose: () => {
409
+ resolve(enableMultiAdd ? nodes : void 0);
410
+ }
411
+ });
412
+ });
413
+ }
414
+ /**
415
+ * 唤起单选面板
416
+ */
417
+ async singleSelectNodePanel(params) {
418
+ return new Promise((resolve) => {
419
+ this.callNodePanel({
420
+ ...params,
421
+ enableMultiAdd: false,
422
+ onSelect: async (panelParams) => {
423
+ resolve(panelParams);
424
+ },
425
+ onClose: () => {
426
+ resolve(void 0);
427
+ }
428
+ });
429
+ });
430
+ }
431
+ /** 添加节点 */
432
+ async addNode(callParams, panelParams) {
433
+ const {
434
+ panelPosition,
435
+ fromPort,
436
+ toPort,
437
+ canAddNode,
438
+ autoOffsetPadding = {
439
+ x: 100,
440
+ y: 100
441
+ },
442
+ enableBuildLine = false,
443
+ enableSelectPosition = false,
444
+ enableAutoOffset = false,
445
+ enableDragNode = false
446
+ } = callParams;
447
+ if (!panelPosition || !panelParams) {
448
+ return;
449
+ }
450
+ const { nodeType, selectEvent, nodeJSON } = panelParams;
451
+ const containerNode = WorkflowNodePanelUtils.getContainerNode({
452
+ fromPort,
453
+ containerNode: callParams.containerNode
454
+ });
455
+ if (canAddNode) {
456
+ const canAdd = canAddNode({ nodeType, containerNode });
457
+ if (!canAdd) {
458
+ return;
459
+ }
460
+ }
461
+ const selectPosition = this.playgroundConfig.getPosFromMouseEvent(selectEvent);
462
+ const nodePosition = callParams.customPosition ? callParams.customPosition({ nodeType, selectPosition }) : WorkflowNodePanelUtils.adjustNodePosition({
463
+ nodeType,
464
+ position: enableSelectPosition ? selectPosition : panelPosition,
465
+ fromPort,
466
+ toPort,
467
+ containerNode,
468
+ document: this.document,
469
+ dragService: this.dragService
470
+ });
471
+ const node = this.document.createWorkflowNodeByType(
472
+ nodeType,
473
+ nodePosition,
474
+ nodeJSON ?? {},
475
+ containerNode?.id
476
+ );
477
+ if (!node) {
478
+ return;
479
+ }
480
+ if (enableAutoOffset && fromPort && toPort) {
481
+ WorkflowNodePanelUtils.subNodesAutoOffset({
482
+ node,
483
+ fromPort,
484
+ toPort,
485
+ padding: autoOffsetPadding,
486
+ containerNode,
487
+ historyService: this.historyService,
488
+ dragService: this.dragService,
489
+ linesManager: this.linesManager
490
+ });
491
+ }
492
+ if (!enableBuildLine && !enableDragNode) {
493
+ return node;
494
+ }
495
+ await WorkflowNodePanelUtils.waitNodeRender();
496
+ if (enableBuildLine) {
497
+ WorkflowNodePanelUtils.buildLine({
498
+ fromPort,
499
+ node,
500
+ toPort,
501
+ linesManager: this.linesManager
502
+ });
503
+ }
504
+ if (enableDragNode) {
505
+ this.selectService.selectNode(node);
506
+ this.dragService.startDragSelectedNodes(selectEvent);
507
+ }
508
+ return node;
509
+ }
510
+ };
511
+ __decorateClass([
512
+ inject(WorkflowDocument)
513
+ ], WorkflowNodePanelService.prototype, "document", 2);
514
+ __decorateClass([
515
+ inject(WorkflowDragService)
516
+ ], WorkflowNodePanelService.prototype, "dragService", 2);
517
+ __decorateClass([
518
+ inject(WorkflowSelectService)
519
+ ], WorkflowNodePanelService.prototype, "selectService", 2);
520
+ __decorateClass([
521
+ inject(WorkflowLinesManager2)
522
+ ], WorkflowNodePanelService.prototype, "linesManager", 2);
523
+ __decorateClass([
524
+ inject(PlaygroundConfigEntity)
525
+ ], WorkflowNodePanelService.prototype, "playgroundConfig", 2);
526
+ __decorateClass([
527
+ inject(HistoryService2)
528
+ ], WorkflowNodePanelService.prototype, "historyService", 2);
529
+ WorkflowNodePanelService = __decorateClass([
530
+ injectable()
531
+ ], WorkflowNodePanelService);
532
+
533
+ // src/layer.tsx
534
+ import React from "react";
535
+ import { inject as inject2 } from "inversify";
536
+ import { domUtils } from "@flowgram.ai/utils";
537
+ import { nanoid } from "@flowgram.ai/free-layout-core";
538
+ import { Layer } from "@flowgram.ai/core";
539
+ var WorkflowNodePanelLayer = class extends Layer {
540
+ constructor() {
541
+ super();
542
+ this.node = domUtils.createDivWithClass("gedit-playground-layer gedit-node-panel-layer");
543
+ this.node.style.zIndex = "9999";
544
+ this.renderList = /* @__PURE__ */ new Map();
545
+ }
546
+ onReady() {
547
+ this.service.setCallNodePanel(this.call.bind(this));
548
+ }
549
+ onZoom(zoom) {
550
+ this.node.style.transform = `scale(${zoom})`;
551
+ }
552
+ render() {
553
+ const NodePanelRender = this.options.renderer;
554
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, Array.from(this.renderList.keys()).map((taskId) => {
555
+ const renderProps = this.renderList.get(taskId);
556
+ return /* @__PURE__ */ React.createElement(NodePanelRender, { key: taskId, ...renderProps });
557
+ }));
558
+ }
559
+ async call(params) {
560
+ const taskId = nanoid();
561
+ const { onSelect, onClose, enableMultiAdd = false, panelProps = {} } = params;
562
+ return new Promise((resolve) => {
563
+ const unmount = () => {
564
+ this.renderList.delete(taskId);
565
+ this.render();
566
+ resolve();
567
+ };
568
+ const handleClose = () => {
569
+ unmount();
570
+ onClose();
571
+ };
572
+ const handleSelect = (params2) => {
573
+ onSelect(params2);
574
+ if (!enableMultiAdd) {
575
+ unmount();
576
+ }
577
+ };
578
+ const renderProps = {
579
+ ...params,
580
+ panelProps,
581
+ onSelect: handleSelect,
582
+ onClose: handleClose
583
+ };
584
+ this.renderList.set(taskId, renderProps);
585
+ this.render();
586
+ });
587
+ }
588
+ };
589
+ WorkflowNodePanelLayer.type = "WorkflowNodePanelLayer";
590
+ __decorateClass([
591
+ inject2(WorkflowNodePanelService)
592
+ ], WorkflowNodePanelLayer.prototype, "service", 2);
593
+
594
+ // src/create-plugin.ts
595
+ var createFreeNodePanelPlugin = definePluginCreator({
596
+ onBind({ bind }) {
597
+ bind(WorkflowNodePanelService).toSelf().inSingletonScope();
598
+ },
599
+ onInit: (ctx, opts) => {
600
+ ctx.playground.registerLayer(WorkflowNodePanelLayer, {
601
+ renderer: opts.renderer
602
+ });
603
+ },
604
+ onDispose: (ctx) => {
605
+ const nodePanelService = ctx.get(WorkflowNodePanelService);
606
+ nodePanelService.dispose();
607
+ }
608
+ });
609
+ export {
610
+ WorkflowNodePanelService,
611
+ WorkflowNodePanelUtils,
612
+ createFreeNodePanelPlugin
613
+ };
614
+ //# sourceMappingURL=index.js.map