@flowgram.ai/free-snap-plugin 0.1.0-alpha.3 → 0.1.0-alpha.31
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 +51 -37
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +54 -40
- package/dist/index.js.map +1 -1
- package/package.json +15 -17
package/dist/esm/index.js
CHANGED
|
@@ -14,12 +14,12 @@ import { definePluginCreator } from "@flowgram.ai/core";
|
|
|
14
14
|
|
|
15
15
|
// src/service.ts
|
|
16
16
|
import { inject, injectable } from "inversify";
|
|
17
|
+
import { Emitter, Rectangle } from "@flowgram.ai/utils";
|
|
18
|
+
import { WorkflowNodeEntity, WorkflowDocument } from "@flowgram.ai/free-layout-core";
|
|
19
|
+
import { WorkflowDragService } from "@flowgram.ai/free-layout-core";
|
|
17
20
|
import { FlowNodeTransformData } from "@flowgram.ai/document";
|
|
18
21
|
import { FlowNodeBaseType } from "@flowgram.ai/document";
|
|
19
22
|
import { EntityManager, PlaygroundConfigEntity, TransformData } from "@flowgram.ai/core";
|
|
20
|
-
import { WorkflowNodeEntity, WorkflowDocument } from "@flowgram.ai/free-layout-core";
|
|
21
|
-
import { WorkflowDragService } from "@flowgram.ai/free-layout-core";
|
|
22
|
-
import { Emitter, Rectangle } from "@flowgram.ai/utils";
|
|
23
23
|
|
|
24
24
|
// src/constant.ts
|
|
25
25
|
var SnapDefaultOptions = {
|
|
@@ -27,7 +27,7 @@ var SnapDefaultOptions = {
|
|
|
27
27
|
edgeThreshold: 7,
|
|
28
28
|
enableGridSnapping: false,
|
|
29
29
|
gridSize: 20,
|
|
30
|
-
enableMultiSnapping:
|
|
30
|
+
enableMultiSnapping: true,
|
|
31
31
|
enableOnlyViewportSnapping: true,
|
|
32
32
|
edgeColor: "#4E40E5",
|
|
33
33
|
alignColor: "#4E40E5",
|
|
@@ -65,6 +65,7 @@ var WorkflowSnapService = class {
|
|
|
65
65
|
this.disposers = [];
|
|
66
66
|
this.snapEmitter = new Emitter();
|
|
67
67
|
this.onSnap = this.snapEmitter.event;
|
|
68
|
+
this._disabled = false;
|
|
68
69
|
}
|
|
69
70
|
init(params = {}) {
|
|
70
71
|
this.options = {
|
|
@@ -76,15 +77,40 @@ var WorkflowSnapService = class {
|
|
|
76
77
|
dispose() {
|
|
77
78
|
this.disposers.forEach((disposer) => disposer.dispose());
|
|
78
79
|
}
|
|
80
|
+
get disabled() {
|
|
81
|
+
return this._disabled;
|
|
82
|
+
}
|
|
83
|
+
disable() {
|
|
84
|
+
if (this._disabled) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
this._disabled = true;
|
|
88
|
+
this.clear();
|
|
89
|
+
}
|
|
90
|
+
enable() {
|
|
91
|
+
if (!this._disabled) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
this._disabled = false;
|
|
95
|
+
this.clear();
|
|
96
|
+
}
|
|
79
97
|
mountListener() {
|
|
80
|
-
const dragAdjusterDisposer = this.dragService.registerPosAdjuster(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
98
|
+
const dragAdjusterDisposer = this.dragService.registerPosAdjuster((params) => {
|
|
99
|
+
const { selectedNodes: targetNodes, position } = params;
|
|
100
|
+
const isMultiSnapping = this.options.enableMultiSnapping ? false : targetNodes.length !== 1;
|
|
101
|
+
if (this._disabled || !this.options.enableEdgeSnapping || isMultiSnapping) {
|
|
102
|
+
return {
|
|
103
|
+
x: 0,
|
|
104
|
+
y: 0
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return this.snapping({
|
|
108
|
+
targetNodes,
|
|
109
|
+
position
|
|
110
|
+
});
|
|
111
|
+
});
|
|
86
112
|
const dragEndDisposer = this.dragService.onNodesDrag((event) => {
|
|
87
|
-
if (event.type !== "onDragEnd") {
|
|
113
|
+
if (event.type !== "onDragEnd" || this._disabled) {
|
|
88
114
|
return;
|
|
89
115
|
}
|
|
90
116
|
if (this.options.enableGridSnapping) {
|
|
@@ -101,19 +127,12 @@ var WorkflowSnapService = class {
|
|
|
101
127
|
}
|
|
102
128
|
snapping(params) {
|
|
103
129
|
const { targetNodes, position } = params;
|
|
104
|
-
const
|
|
105
|
-
if (!this.options.enableEdgeSnapping || isMultiSnapping) {
|
|
106
|
-
return {
|
|
107
|
-
x: 0,
|
|
108
|
-
y: 0
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
const selectedBounds = this.getBounds(targetNodes);
|
|
130
|
+
const targetBounds = this.getBounds(targetNodes);
|
|
112
131
|
const targetRect = new Rectangle(
|
|
113
132
|
position.x,
|
|
114
133
|
position.y,
|
|
115
|
-
|
|
116
|
-
|
|
134
|
+
targetBounds.width,
|
|
135
|
+
targetBounds.height
|
|
117
136
|
);
|
|
118
137
|
const snapNodeRects = this.getSnapNodeRects({
|
|
119
138
|
targetNodes,
|
|
@@ -321,15 +340,10 @@ var WorkflowSnapService = class {
|
|
|
321
340
|
x: transform.position.x + offset.x,
|
|
322
341
|
y: transform.position.y + offset.y
|
|
323
342
|
};
|
|
324
|
-
if (node.collapsedChildren?.length > 0) {
|
|
325
|
-
node.collapsedChildren.forEach((childNode) => {
|
|
326
|
-
const childNodeTransformData = childNode.getData(FlowNodeTransformData);
|
|
327
|
-
childNodeTransformData.fireChange();
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
343
|
transform.update({
|
|
331
344
|
position: positionWithOffset
|
|
332
345
|
});
|
|
346
|
+
this.document.layout.updateAffectedTransform(node);
|
|
333
347
|
}
|
|
334
348
|
calcAlignOffset(params) {
|
|
335
349
|
const { snapNodeRects, targetRect, alignThreshold } = params;
|
|
@@ -580,10 +594,10 @@ WorkflowSnapService = __decorateClass([
|
|
|
580
594
|
// src/layer.tsx
|
|
581
595
|
import React from "react";
|
|
582
596
|
import { inject as inject2, injectable as injectable2 } from "inversify";
|
|
597
|
+
import { domUtils } from "@flowgram.ai/utils";
|
|
598
|
+
import { WorkflowDocument as WorkflowDocument2 } from "@flowgram.ai/free-layout-core";
|
|
583
599
|
import { FlowNodeTransformData as FlowNodeTransformData2 } from "@flowgram.ai/document";
|
|
584
600
|
import { Layer } from "@flowgram.ai/core";
|
|
585
|
-
import { WorkflowDocument as WorkflowDocument2 } from "@flowgram.ai/free-layout-core";
|
|
586
|
-
import { domUtils } from "@flowgram.ai/utils";
|
|
587
601
|
var WorkflowSnapLayer = class extends Layer {
|
|
588
602
|
constructor() {
|
|
589
603
|
super(...arguments);
|
|
@@ -621,7 +635,7 @@ var WorkflowSnapLayer = class extends Layer {
|
|
|
621
635
|
className: `workflow-snap-edge-line ${className}`,
|
|
622
636
|
"data-testid": "sdk.workflow.canvas.snap.edgeLine",
|
|
623
637
|
"data-snap-line-id": id,
|
|
624
|
-
"data-snap-line-
|
|
638
|
+
"data-snap-line-source-node": sourceNode,
|
|
625
639
|
key: id,
|
|
626
640
|
style: {
|
|
627
641
|
top,
|
|
@@ -650,7 +664,7 @@ var WorkflowSnapLayer = class extends Layer {
|
|
|
650
664
|
className: `workflow-snap-align-line ${renderLine.className}`,
|
|
651
665
|
"data-testid": "sdk.workflow.canvas.snap.alignLine",
|
|
652
666
|
"data-snap-line-id": id,
|
|
653
|
-
"data-snap-line-
|
|
667
|
+
"data-snap-line-source-node": renderLine.sourceNode,
|
|
654
668
|
key: id,
|
|
655
669
|
style: {
|
|
656
670
|
position: "absolute"
|
|
@@ -739,7 +753,7 @@ var WorkflowSnapLayer = class extends Layer {
|
|
|
739
753
|
edgeLines.push({
|
|
740
754
|
className: "edge-full-top-right",
|
|
741
755
|
sourceNode: topFullAlign.sourceNodeId,
|
|
742
|
-
left: snapRect.right,
|
|
756
|
+
left: snapRect.right - 1,
|
|
743
757
|
...lineData
|
|
744
758
|
});
|
|
745
759
|
edgeLines.push({
|
|
@@ -768,7 +782,7 @@ var WorkflowSnapLayer = class extends Layer {
|
|
|
768
782
|
edgeLines.push({
|
|
769
783
|
className: "edge-full-bottom-right",
|
|
770
784
|
sourceNode: bottomFullAlign.sourceNodeId,
|
|
771
|
-
left: snapRect.right,
|
|
785
|
+
left: snapRect.right - 1,
|
|
772
786
|
...lineData
|
|
773
787
|
});
|
|
774
788
|
edgeLines.push({
|
|
@@ -797,7 +811,7 @@ var WorkflowSnapLayer = class extends Layer {
|
|
|
797
811
|
edgeLines.push({
|
|
798
812
|
className: "edge-full-left-bottom",
|
|
799
813
|
sourceNode: leftFullAlign.sourceNodeId,
|
|
800
|
-
top: snapRect.bottom,
|
|
814
|
+
top: snapRect.bottom - 1,
|
|
801
815
|
...lineData
|
|
802
816
|
});
|
|
803
817
|
edgeLines.push({
|
|
@@ -826,7 +840,7 @@ var WorkflowSnapLayer = class extends Layer {
|
|
|
826
840
|
edgeLines.push({
|
|
827
841
|
className: "edge-full-right-bottom",
|
|
828
842
|
sourceNode: rightFullAlign.sourceNodeId,
|
|
829
|
-
top: snapRect.bottom,
|
|
843
|
+
top: snapRect.bottom - 1,
|
|
830
844
|
...lineData
|
|
831
845
|
});
|
|
832
846
|
edgeLines.push({
|
|
@@ -850,7 +864,7 @@ var WorkflowSnapLayer = class extends Layer {
|
|
|
850
864
|
const top = Math.min(nodeRect.top, snapRect.top);
|
|
851
865
|
const bottom = Math.max(nodeRect.bottom, snapRect.bottom);
|
|
852
866
|
const height = bottom - top;
|
|
853
|
-
const left = snapLine.x;
|
|
867
|
+
const left = direction === "right" ? snapLine.x - 1 : snapLine.x;
|
|
854
868
|
const width = this.options.edgeLineWidth;
|
|
855
869
|
const isMidX = direction === "midVertical";
|
|
856
870
|
const lineData = {
|
|
@@ -874,7 +888,7 @@ var WorkflowSnapLayer = class extends Layer {
|
|
|
874
888
|
const left = Math.min(nodeRect.left, snapRect.left);
|
|
875
889
|
const right = Math.max(nodeRect.right, snapRect.right);
|
|
876
890
|
const width = right - left;
|
|
877
|
-
const top = snapLine.y;
|
|
891
|
+
const top = direction === "bottom" ? snapLine.y - 1 : snapLine.y;
|
|
878
892
|
const height = this.options.edgeLineWidth;
|
|
879
893
|
const isMidY = direction === "midHorizontal";
|
|
880
894
|
const lineData = {
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/create-plugin.ts","../../src/service.ts","../../src/constant.ts","../../src/utils.ts","../../src/layer.tsx"],"sourcesContent":["import { definePluginCreator } from '@flowgram.ai/core';\n\nimport {\n FreeSnapPluginOptions,\n WorkflowSnapLayerOptions,\n WorkflowSnapServiceOptions,\n} from './type';\nimport { WorkflowSnapService } from './service';\nimport { WorkflowSnapLayer } from './layer';\nimport { SnapDefaultOptions } from './constant';\n\nexport const createFreeSnapPlugin = definePluginCreator<FreeSnapPluginOptions>({\n onBind({ bind }) {\n bind(WorkflowSnapService).toSelf().inSingletonScope();\n },\n onInit(ctx, opts) {\n const options: WorkflowSnapServiceOptions & WorkflowSnapLayerOptions = {\n ...SnapDefaultOptions,\n ...opts,\n };\n ctx.playground.registerLayer(WorkflowSnapLayer, options);\n const snapService = ctx.get<WorkflowSnapService>(WorkflowSnapService);\n snapService.init(options);\n },\n onDispose(ctx) {\n const snapService = ctx.get<WorkflowSnapService>(WorkflowSnapService);\n snapService.dispose();\n },\n});\n","import { inject, injectable } from 'inversify';\nimport { FlowNodeTransformData } from '@flowgram.ai/document';\nimport { FlowNodeBaseType } from '@flowgram.ai/document';\nimport { EntityManager, PlaygroundConfigEntity, TransformData } from '@flowgram.ai/core';\nimport { WorkflowNodeEntity, WorkflowDocument } from '@flowgram.ai/free-layout-core';\nimport { WorkflowDragService } from '@flowgram.ai/free-layout-core';\nimport { Disposable, Emitter, Rectangle } from '@flowgram.ai/utils';\nimport { IPoint } from '@flowgram.ai/utils';\n\nimport { isEqual, isGreaterThan, isLessThan, isLessThanOrEqual, isNumber } from './utils';\nimport type {\n SnapEvent,\n SnapHorizontalLine,\n SnapLines,\n SnapMidHorizontalLine,\n SnapMidVerticalLine,\n SnapVerticalLine,\n WorkflowSnapServiceOptions,\n AlignRects,\n AlignRect,\n AlignSpacing,\n SnapNodeRect,\n SnapEdgeLines,\n} from './type';\nimport { SnapDefaultOptions } from './constant';\n\n@injectable()\nexport class WorkflowSnapService {\n @inject(WorkflowDocument) private readonly document: WorkflowDocument;\n\n @inject(EntityManager) private readonly entityManager: EntityManager;\n\n @inject(WorkflowDragService)\n private readonly dragService: WorkflowDragService;\n\n @inject(PlaygroundConfigEntity)\n private readonly playgroundConfig: PlaygroundConfigEntity;\n\n private disposers: Disposable[] = [];\n\n private options: WorkflowSnapServiceOptions;\n\n private snapEmitter = new Emitter<SnapEvent>();\n\n public readonly onSnap = this.snapEmitter.event;\n\n public init(params: Partial<WorkflowSnapServiceOptions> = {}): void {\n this.options = {\n ...SnapDefaultOptions,\n ...params,\n };\n this.mountListener();\n }\n\n public dispose(): void {\n this.disposers.forEach(disposer => disposer.dispose());\n }\n\n private mountListener(): void {\n const dragAdjusterDisposer = this.dragService.registerPosAdjuster(params =>\n this.snapping({\n targetNodes: params.selectedNodes,\n position: params.position,\n }),\n );\n const dragEndDisposer = this.dragService.onNodesDrag(event => {\n if (event.type !== 'onDragEnd') {\n return;\n }\n if (this.options.enableGridSnapping) {\n this.gridSnapping({\n targetNodes: event.nodes,\n gridSize: this.options.gridSize,\n });\n }\n if (this.options.enableEdgeSnapping) {\n this.clear();\n }\n });\n this.disposers.push(dragAdjusterDisposer, dragEndDisposer);\n }\n\n private snapping(params: { targetNodes: WorkflowNodeEntity[]; position: IPoint }): IPoint {\n const { targetNodes: targetNodes, position } = params;\n\n const isMultiSnapping = this.options.enableMultiSnapping ? false : targetNodes.length !== 1;\n\n if (!this.options.enableEdgeSnapping || isMultiSnapping) {\n return {\n x: 0,\n y: 0,\n };\n }\n\n const selectedBounds = this.getBounds(targetNodes);\n\n const targetRect = new Rectangle(\n position.x,\n position.y,\n selectedBounds.width,\n selectedBounds.height,\n );\n\n const snapNodeRects = this.getSnapNodeRects({\n targetNodes,\n targetRect,\n });\n\n const { alignOffset, alignRects, alignSpacing } = this.calcAlignOffset({\n targetRect,\n alignThreshold: this.options.edgeThreshold,\n snapNodeRects,\n });\n\n const { snapOffset, snapEdgeLines } = this.calcSnapOffset({\n targetRect,\n edgeThreshold: this.options.edgeThreshold,\n snapNodeRects,\n });\n\n const offset: IPoint = {\n x: snapOffset.x || alignOffset.x,\n y: snapOffset.y || alignOffset.y,\n };\n\n const snapRect = new Rectangle(\n position.x + offset.x,\n position.y + offset.y,\n targetRect.width,\n targetRect.height,\n );\n\n this.snapEmitter.fire({\n snapRect,\n snapEdgeLines,\n alignRects,\n alignSpacing,\n });\n\n return offset;\n }\n\n private calcSnapOffset(params: {\n snapNodeRects: SnapNodeRect[];\n targetRect: Rectangle;\n edgeThreshold: number;\n }): {\n snapOffset: IPoint;\n snapEdgeLines: SnapEdgeLines;\n } {\n const { snapNodeRects, edgeThreshold, targetRect } = params;\n\n const snapLines = this.getSnapLines({\n snapNodeRects,\n });\n\n // 找到最近的线条\n const topYClosestLine = snapLines.horizontal.find(line =>\n isLessThanOrEqual(Math.abs(line.y - targetRect.top), edgeThreshold),\n );\n const bottomYClosestLine = snapLines.horizontal.find(line =>\n isLessThanOrEqual(Math.abs(line.y - targetRect.bottom), edgeThreshold),\n );\n const leftXClosestLine = snapLines.vertical.find(line =>\n isLessThanOrEqual(Math.abs(line.x - targetRect.left), edgeThreshold),\n );\n const rightXClosestLine = snapLines.vertical.find(line =>\n isLessThanOrEqual(Math.abs(line.x - targetRect.right), edgeThreshold),\n );\n const midYClosestLine = snapLines.midHorizontal.find(line =>\n isLessThanOrEqual(Math.abs(line.y - targetRect.center.y), edgeThreshold),\n );\n const midXClosestLine = snapLines.midVertical.find(line =>\n isLessThanOrEqual(Math.abs(line.x - targetRect.center.x), edgeThreshold),\n );\n\n // 计算最近坐标\n const topYClosest = topYClosestLine?.y;\n const bottomYClosest = isNumber(bottomYClosestLine?.y)\n ? bottomYClosestLine!.y - targetRect.height\n : undefined;\n const leftXClosest = leftXClosestLine?.x;\n const rightXClosest = isNumber(rightXClosestLine?.x)\n ? rightXClosestLine!.x - targetRect.width\n : undefined;\n const midYClosest = isNumber(midYClosestLine?.y)\n ? midYClosestLine!.y - targetRect.height / 2\n : undefined;\n const midXClosest = isNumber(midXClosestLine?.x)\n ? midXClosestLine!.x - targetRect.width / 2\n : undefined;\n\n // 吸附后坐标,按优先级取值\n const snappingPosition = {\n x: midXClosest ?? leftXClosest ?? rightXClosest ?? targetRect.x,\n y: midYClosest ?? topYClosest ?? bottomYClosest ?? targetRect.y,\n };\n\n // 吸附修正偏移量\n const snapOffset: IPoint = {\n x: snappingPosition.x - targetRect.x,\n y: snappingPosition.y - targetRect.y,\n };\n\n // 生效的吸附线条\n const snapEdgeLines: SnapEdgeLines = {\n top: isEqual(topYClosest, snappingPosition.y) ? topYClosestLine : undefined,\n bottom: isEqual(bottomYClosest, snappingPosition.y) ? bottomYClosestLine : undefined,\n left: isEqual(leftXClosest, snappingPosition.x) ? leftXClosestLine : undefined,\n right: isEqual(rightXClosest, snappingPosition.x) ? rightXClosestLine : undefined,\n midVertical: isEqual(midXClosest, snappingPosition.x) ? midXClosestLine : undefined,\n midHorizontal: isEqual(midYClosest, snappingPosition.y) ? midYClosestLine : undefined,\n };\n\n return { snapOffset, snapEdgeLines };\n }\n\n private gridSnapping(params: { gridSize: number; targetNodes: WorkflowNodeEntity[] }): void {\n const { gridSize, targetNodes } = params;\n const rect = this.getBounds(targetNodes);\n const snap = (value: number) => Math.round(value / gridSize) * gridSize;\n const snappedPosition: IPoint = {\n x: snap(rect.x),\n y: snap(rect.y),\n };\n const offset: IPoint = {\n x: snappedPosition.x - rect.x,\n y: snappedPosition.y - rect.y,\n };\n targetNodes.forEach(node =>\n this.updateNodePositionWithOffset({\n node,\n offset,\n }),\n );\n }\n\n private clear() {\n this.snapEmitter.fire({\n snapEdgeLines: {},\n snapRect: Rectangle.EMPTY,\n alignRects: {\n top: [],\n bottom: [],\n left: [],\n right: [],\n },\n alignSpacing: {},\n });\n }\n\n private getSnapLines(params: { snapNodeRects: SnapNodeRect[] }): SnapLines {\n const { snapNodeRects } = params;\n const horizontalLines: SnapHorizontalLine[] = [];\n const verticalLines: SnapVerticalLine[] = [];\n const midHorizontalLines: SnapMidHorizontalLine[] = [];\n const midVerticalLines: SnapMidVerticalLine[] = [];\n\n snapNodeRects.forEach(snapNodeRect => {\n const nodeBounds = snapNodeRect.rect;\n const nodeCenter = nodeBounds.center;\n // 边缘横线\n const top: SnapHorizontalLine = {\n y: nodeBounds.top,\n sourceNodeId: snapNodeRect.id,\n };\n const bottom: SnapHorizontalLine = {\n y: nodeBounds.bottom,\n sourceNodeId: snapNodeRect.id,\n };\n // 边缘竖线\n const left: SnapVerticalLine = {\n x: nodeBounds.left,\n sourceNodeId: snapNodeRect.id,\n };\n const right: SnapVerticalLine = {\n x: nodeBounds.right,\n sourceNodeId: snapNodeRect.id,\n };\n // 中间横线\n const midHorizontal: SnapMidHorizontalLine = {\n y: nodeCenter.y,\n sourceNodeId: snapNodeRect.id,\n };\n // 中间竖线\n const midVertical: SnapMidVerticalLine = {\n x: nodeCenter.x,\n sourceNodeId: snapNodeRect.id,\n };\n horizontalLines.push(top, bottom);\n verticalLines.push(left, right);\n midHorizontalLines.push(midHorizontal);\n midVerticalLines.push(midVertical);\n });\n\n return {\n horizontal: horizontalLines,\n vertical: verticalLines,\n midHorizontal: midHorizontalLines,\n midVertical: midVerticalLines,\n };\n }\n\n private getAvailableNodes(params: {\n targetNodes: WorkflowNodeEntity[];\n targetRect: Rectangle;\n }): WorkflowNodeEntity[] {\n const { targetNodes, targetRect } = params;\n\n const targetCenter = targetRect.center;\n const targetContainerId = targetNodes[0].parent?.id ?? this.document.root.id;\n\n const disabledNodeIds = targetNodes.map(n => n.id);\n disabledNodeIds.push(FlowNodeBaseType.ROOT);\n const availableNodes = this.nodes\n .filter(n => n.parent?.id === targetContainerId)\n .filter(n => !disabledNodeIds.includes(n.id))\n .sort((nodeA, nodeB) => {\n const nodeCenterA = nodeA.getData(FlowNodeTransformData)!.bounds.center;\n const nodeCenterB = nodeB.getData(FlowNodeTransformData)!.bounds.center;\n // 距离越近优先级越高\n const distanceA =\n Math.abs(nodeCenterA.x - targetCenter.x) + Math.abs(nodeCenterA.y - targetCenter.y);\n const distanceB =\n Math.abs(nodeCenterB.x - targetCenter.x) + Math.abs(nodeCenterB.y - targetCenter.y);\n return distanceA - distanceB;\n });\n return availableNodes;\n }\n\n private viewRect(): Rectangle {\n const { width, height, scrollX, scrollY, zoom } = this.playgroundConfig.config;\n return new Rectangle(scrollX / zoom, scrollY / zoom, width / zoom, height / zoom);\n }\n\n private getSnapNodeRects(params: {\n targetNodes: WorkflowNodeEntity[];\n targetRect: Rectangle;\n }): SnapNodeRect[] {\n const availableNodes = this.getAvailableNodes(params);\n const viewRect = this.viewRect();\n return availableNodes\n .map(node => {\n const snapNodeRect: SnapNodeRect = {\n id: node.id,\n rect: node.getData(FlowNodeTransformData).bounds,\n entity: node,\n };\n if (\n this.options.enableOnlyViewportSnapping &&\n node.parent?.flowNodeType === FlowNodeBaseType.ROOT &&\n !Rectangle.intersects(viewRect, snapNodeRect.rect)\n ) {\n // 最外层节点仅包含当前可见节点\n return;\n }\n return snapNodeRect;\n })\n .filter(Boolean) as SnapNodeRect[];\n }\n\n private get nodes(): WorkflowNodeEntity[] {\n return this.entityManager.getEntities<WorkflowNodeEntity>(WorkflowNodeEntity);\n }\n\n private getBounds(nodes: WorkflowNodeEntity[]): Rectangle {\n if (nodes.length === 0) {\n return Rectangle.EMPTY;\n }\n return Rectangle.enlarge(nodes.map(n => n.getData(FlowNodeTransformData)!.bounds));\n }\n\n private updateNodePositionWithOffset(params: { node: WorkflowNodeEntity; offset: IPoint }): void {\n const { node, offset } = params;\n const transform = node.getData(TransformData);\n const positionWithOffset: IPoint = {\n x: transform.position.x + offset.x,\n y: transform.position.y + offset.y,\n };\n if (node.collapsedChildren?.length > 0) {\n // 嵌套情况下需将子节点 transform 设为 dirty\n node.collapsedChildren.forEach(childNode => {\n const childNodeTransformData =\n childNode.getData<FlowNodeTransformData>(FlowNodeTransformData);\n childNodeTransformData.fireChange();\n });\n }\n transform.update({\n position: positionWithOffset,\n });\n }\n\n private calcAlignOffset(params: {\n snapNodeRects: SnapNodeRect[];\n targetRect: Rectangle;\n alignThreshold: number;\n }): {\n alignOffset: IPoint;\n alignRects: AlignRects;\n alignSpacing: AlignSpacing;\n } {\n const { snapNodeRects, targetRect, alignThreshold } = params;\n\n const alignRects = this.getAlignRects({\n targetRect,\n snapNodeRects,\n });\n\n const alignSpacing = this.calcAlignSpacing({\n targetRect,\n alignRects,\n });\n\n let topY: number | undefined;\n let bottomY: number | undefined;\n let leftX: number | undefined;\n let rightX: number | undefined;\n let midY: number | undefined;\n let midX: number | undefined;\n\n if (alignSpacing.top) {\n const topAlignY = alignRects.top[0].rect.bottom + alignSpacing.top;\n const isAlignTop = isLessThanOrEqual(Math.abs(targetRect.top - topAlignY), alignThreshold);\n if (isAlignTop) {\n // 生效\n topY = topAlignY;\n } else {\n // 失效\n alignSpacing.top = undefined;\n }\n }\n if (alignSpacing.bottom) {\n const bottomAlignY = alignRects.bottom[0].rect.top - alignSpacing.bottom;\n const isAlignBottom = isLessThan(Math.abs(targetRect.bottom - bottomAlignY), alignThreshold);\n if (isAlignBottom) {\n bottomY = bottomAlignY - targetRect.height;\n } else {\n alignSpacing.bottom = undefined;\n }\n }\n if (alignSpacing.left) {\n const leftAlignX = alignRects.left[0].rect.right + alignSpacing.left;\n const isAlignLeft = isLessThanOrEqual(Math.abs(targetRect.left - leftAlignX), alignThreshold);\n if (isAlignLeft) {\n leftX = leftAlignX;\n } else {\n alignSpacing.left = undefined;\n }\n }\n if (alignSpacing.right) {\n const rightAlignX = alignRects.right[0].rect.left - alignSpacing.right;\n const isAlignRight = isLessThanOrEqual(\n Math.abs(targetRect.right - rightAlignX),\n alignThreshold,\n );\n if (isAlignRight) {\n rightX = rightAlignX - targetRect.width;\n } else {\n alignSpacing.right = undefined;\n }\n }\n if (alignSpacing.midHorizontal) {\n const leftAlignX = alignRects.left[0].rect.right + alignSpacing.midHorizontal;\n const isAlignMidHorizontal = isLessThanOrEqual(\n Math.abs(targetRect.left - leftAlignX),\n alignThreshold,\n );\n if (isAlignMidHorizontal) {\n midX = leftAlignX;\n } else {\n alignSpacing.midHorizontal = undefined;\n }\n }\n if (alignSpacing.midVertical) {\n const topAlignY = alignRects.top[0].rect.bottom + alignSpacing.midVertical;\n const isAlignMidVertical = isLessThanOrEqual(\n Math.abs(targetRect.top - topAlignY),\n alignThreshold,\n );\n if (isAlignMidVertical) {\n midY = topAlignY;\n } else {\n alignSpacing.midVertical = undefined;\n }\n }\n\n const alignPosition: IPoint = {\n x: midX ?? leftX ?? rightX ?? targetRect.x,\n y: midY ?? topY ?? bottomY ?? targetRect.y,\n };\n\n const alignOffset: IPoint = {\n x: alignPosition.x - targetRect.x,\n y: alignPosition.y - targetRect.y,\n };\n\n return { alignOffset, alignRects, alignSpacing };\n }\n\n private calcAlignSpacing(params: {\n targetRect: Rectangle;\n alignRects: AlignRects;\n }): AlignSpacing {\n const { targetRect, alignRects } = params;\n\n const topSpacing = this.getDirectionAlignSpacing({\n rects: alignRects.top,\n isHorizontal: false,\n });\n const bottomSpacing = this.getDirectionAlignSpacing({\n rects: alignRects.bottom,\n isHorizontal: false,\n });\n const leftSpacing = this.getDirectionAlignSpacing({\n rects: alignRects.left,\n isHorizontal: true,\n });\n const rightSpacing = this.getDirectionAlignSpacing({\n rects: alignRects.right,\n isHorizontal: true,\n });\n const midHorizontalSpacing = this.getMidAlignSpacing({\n rectA: alignRects.left[0]?.rect,\n rectB: alignRects.right[0]?.rect,\n targetRect,\n isHorizontal: true,\n });\n const midVerticalSpacing = this.getMidAlignSpacing({\n rectA: alignRects.top[0]?.rect,\n rectB: alignRects.bottom[0]?.rect,\n targetRect,\n isHorizontal: false,\n });\n return {\n top: topSpacing,\n bottom: bottomSpacing,\n left: leftSpacing,\n right: rightSpacing,\n midHorizontal: midHorizontalSpacing,\n midVertical: midVerticalSpacing,\n };\n }\n\n private getAlignRects(params: {\n targetRect: Rectangle;\n snapNodeRects: SnapNodeRect[];\n }): AlignRects {\n const { targetRect, snapNodeRects } = params;\n\n const topVerticalRects: AlignRect[] = [];\n const bottomVerticalRects: AlignRect[] = [];\n const leftHorizontalRects: AlignRect[] = [];\n const rightHorizontalRects: AlignRect[] = [];\n\n snapNodeRects.forEach(snapNodeRect => {\n const nodeRect = snapNodeRect.rect;\n const { isVerticalIntersection, isHorizontalIntersection, isIntersection } =\n this.intersection(nodeRect, targetRect);\n if (isIntersection) {\n // 忽略重叠的节点\n return;\n } else if (isVerticalIntersection) {\n // 垂直重合\n if (isGreaterThan(nodeRect.center.y, targetRect.center.y)) {\n // 下方\n bottomVerticalRects.push({\n rect: nodeRect,\n sourceNodeId: snapNodeRect.id,\n });\n } else {\n // 上方\n topVerticalRects.push({\n rect: nodeRect,\n sourceNodeId: snapNodeRect.id,\n });\n }\n } else if (isHorizontalIntersection) {\n // 水平重合\n if (isGreaterThan(nodeRect.center.x, targetRect.center.x)) {\n // 右方\n rightHorizontalRects.push({\n rect: nodeRect,\n sourceNodeId: snapNodeRect.id,\n });\n } else {\n // 左方\n leftHorizontalRects.push({\n rect: nodeRect,\n sourceNodeId: snapNodeRect.id,\n });\n }\n }\n });\n\n return {\n top: topVerticalRects,\n bottom: bottomVerticalRects,\n left: leftHorizontalRects,\n right: rightHorizontalRects,\n };\n }\n\n private getMidAlignSpacing(params: {\n rectA?: Rectangle;\n rectB?: Rectangle;\n targetRect: Rectangle;\n isHorizontal: boolean;\n }): number | undefined {\n const { rectA, rectB, targetRect, isHorizontal } = params;\n if (!rectA || !rectB) {\n return;\n }\n const { isVerticalIntersection, isHorizontalIntersection, isIntersection } = this.intersection(\n rectA,\n rectB,\n );\n if (isIntersection) {\n return;\n }\n if (isHorizontal && isHorizontalIntersection && !isVerticalIntersection) {\n const betweenSpacing = Math.min(\n Math.abs(rectA.left - rectB.right),\n Math.abs(rectA.right - rectB.left),\n );\n return (betweenSpacing - targetRect.width) / 2;\n } else if (!isHorizontal && isVerticalIntersection && !isHorizontalIntersection) {\n const betweenSpacing = Math.min(\n Math.abs(rectA.top - rectB.bottom),\n Math.abs(rectA.bottom - rectB.top),\n );\n return (betweenSpacing - targetRect.height) / 2;\n }\n }\n\n private getDirectionAlignSpacing(params: {\n rects: AlignRect[];\n isHorizontal: boolean;\n }): number | undefined {\n const { rects, isHorizontal } = params;\n if (rects.length < 2) {\n // 非法情况\n return;\n }\n const rectA = rects[0].rect;\n const rectB = rects[1].rect;\n\n const { isVerticalIntersection, isHorizontalIntersection, isIntersection } = this.intersection(\n rectA,\n rectB,\n );\n\n if (isIntersection) {\n // 非法情况:重叠\n return;\n }\n if (isHorizontal && isHorizontalIntersection && !isVerticalIntersection) {\n return Math.min(Math.abs(rectA.left - rectB.right), Math.abs(rectA.right - rectB.left));\n } else if (!isHorizontal && isVerticalIntersection && !isHorizontalIntersection) {\n return Math.min(Math.abs(rectA.top - rectB.bottom), Math.abs(rectA.bottom - rectB.top));\n }\n return;\n }\n\n private intersection(\n rectA: Rectangle,\n rectB: Rectangle,\n ): {\n isHorizontalIntersection: boolean;\n isVerticalIntersection: boolean;\n isIntersection: boolean;\n } {\n const isVerticalIntersection =\n isLessThan(rectA.left, rectB.right) && isGreaterThan(rectA.right, rectB.left);\n const isHorizontalIntersection =\n isLessThan(rectA.top, rectB.bottom) && isGreaterThan(rectA.bottom, rectB.top);\n const isIntersection = isHorizontalIntersection && isVerticalIntersection;\n\n return {\n isHorizontalIntersection,\n isVerticalIntersection,\n isIntersection,\n };\n }\n}\n","import type { WorkflowSnapLayerOptions, WorkflowSnapServiceOptions } from './type';\n\nexport const SnapDefaultOptions: WorkflowSnapServiceOptions & WorkflowSnapLayerOptions = {\n enableEdgeSnapping: true,\n edgeThreshold: 7,\n enableGridSnapping: false,\n gridSize: 20,\n enableMultiSnapping: false,\n enableOnlyViewportSnapping: true,\n edgeColor: '#4E40E5',\n alignColor: '#4E40E5',\n edgeLineWidth: 2,\n alignLineWidth: 2,\n alignCrossWidth: 16,\n};\n\nexport const Epsilon = 0.00001;\n","import { Epsilon } from './constant';\n\n/** 检查浮点数 a 是否等于 b */\nexport const isEqual = (a: number | undefined, b: number | undefined): boolean => {\n if (a === undefined || b === undefined) {\n return false;\n }\n // 检查 a 和 b 的差的绝对值是否小于 Epsilon\n return Math.abs(a - b) < Epsilon;\n};\n\n/** 检查浮点数 a 是否小于 b */\nexport const isLessThan = (a: number | undefined, b: number | undefined): boolean => {\n if (a === undefined || b === undefined) {\n return false;\n }\n // 检查 a 是否显著小于 b\n return b - a > Epsilon;\n};\n\n/** 检查浮点数 a 是否大于 b */\nexport const isGreaterThan = (a: number | undefined, b: number | undefined): boolean => {\n if (a === undefined || b === undefined) {\n return false;\n }\n return a - b > Epsilon;\n};\n\n/** 检查浮点数 a 是否小于等于 b */\nexport const isLessThanOrEqual = (a: number | undefined, b: number | undefined): boolean =>\n isEqual(a, b) || isLessThan(a, b);\n\n/** 检查浮点数 a 是否大于等于 b */\nexport const isGreaterThanOrEqual = (a: number | undefined, b: number | undefined): boolean =>\n isEqual(a, b) || isGreaterThan(a, b);\n\n/** 检查值是否是数字类型 */\nexport const isNumber = (value: unknown): value is number =>\n typeof value === 'number' && !isNaN(value);\n","import React from 'react';\n\nimport { inject, injectable } from 'inversify';\nimport { FlowNodeTransformData } from '@flowgram.ai/document';\nimport { Layer } from '@flowgram.ai/core';\nimport { WorkflowDocument } from '@flowgram.ai/free-layout-core';\nimport { domUtils } from '@flowgram.ai/utils';\nimport { Rectangle } from '@flowgram.ai/utils';\n\nimport { isEqual, isGreaterThan, isNumber } from './utils';\nimport { AlignRect, SnapEvent, WorkflowSnapLayerOptions } from './type';\nimport { WorkflowSnapService } from './service';\n\ninterface SnapRenderLine {\n className: string;\n sourceNode: string;\n top: number;\n left: number;\n width: number;\n height: number;\n dashed?: boolean;\n}\n\n@injectable()\nexport class WorkflowSnapLayer extends Layer<WorkflowSnapLayerOptions> {\n public static type = 'WorkflowSnapLayer';\n\n @inject(WorkflowDocument) private readonly document: WorkflowDocument;\n\n @inject(WorkflowSnapService) private readonly service: WorkflowSnapService;\n\n public readonly node = domUtils.createDivWithClass(\n 'gedit-playground-layer gedit-flow-snap-layer',\n );\n\n private edgeLines: SnapRenderLine[] = [];\n\n private alignLines: SnapRenderLine[] = [];\n\n public onReady(): void {\n this.node.style.zIndex = '9999';\n this.toDispose.pushAll([\n this.service.onSnap((event: SnapEvent) => {\n this.edgeLines = this.calcEdgeLines(event);\n this.alignLines = this.calcAlignLines(event);\n this.render();\n }),\n ]);\n }\n\n public render(): JSX.Element {\n return (\n <>\n {this.alignLines.length > 0 && (\n <div className=\"workflow-snap-align-lines\">{this.renderAlignLines()}</div>\n )}\n {this.edgeLines.length > 0 && (\n <div className=\"workflow-snap-edge-lines\">{this.renderEdgeLines()}</div>\n )}\n </>\n );\n }\n\n public onZoom(scale: number): void {\n this.node.style.transform = `scale(${scale})`;\n }\n\n private renderEdgeLines(): JSX.Element[] {\n return this.edgeLines.map((renderLine: SnapRenderLine) => {\n const { className, sourceNode, top, left, width, height, dashed } = renderLine;\n const id = `${className}-${sourceNode}-${top}-${left}-${width}-${height}`;\n const isHorizontal = width < height;\n const border = `${this.options.edgeLineWidth}px ${dashed ? 'dashed' : 'solid'} ${\n this.options.edgeColor\n }`;\n return (\n <div\n className={`workflow-snap-edge-line ${className}`}\n data-testid=\"sdk.workflow.canvas.snap.edgeLine\"\n data-snap-line-id={id}\n data-snap-line-sourceNode={sourceNode}\n key={id}\n style={{\n top,\n left,\n width,\n height,\n position: 'absolute',\n borderLeft: isHorizontal ? border : 'none',\n borderTop: !isHorizontal ? border : 'none',\n }}\n />\n );\n });\n }\n\n private renderAlignLines(): JSX.Element[] {\n return this.alignLines.map((renderLine: SnapRenderLine) => {\n const id = `${renderLine.className}-${renderLine.sourceNode}-${renderLine.top}-${renderLine.left}-${renderLine.width}-${renderLine.height}`;\n const isHorizontal = isGreaterThan(renderLine.width, renderLine.height);\n const alignLineWidth = this.options.alignLineWidth; // 整体线条粗细\n const alignCrossWidth = this.options.alignCrossWidth; // 工字形横线的长度\n\n // 调整渲染位置以保持居中\n const adjustedTop = isHorizontal ? renderLine.top - alignLineWidth / 2 : renderLine.top;\n const adjustedLeft = isHorizontal ? renderLine.left : renderLine.left - alignLineWidth / 2;\n\n return (\n <div\n className={`workflow-snap-align-line ${renderLine.className}`}\n data-testid=\"sdk.workflow.canvas.snap.alignLine\"\n data-snap-line-id={id}\n data-snap-line-sourceNode={renderLine.sourceNode}\n key={id}\n style={{\n position: 'absolute',\n }}\n >\n {/* 主线 */}\n <div\n style={{\n position: 'absolute',\n top: adjustedTop,\n left: adjustedLeft,\n width: isHorizontal ? renderLine.width : alignLineWidth,\n height: isHorizontal ? alignLineWidth : renderLine.height,\n backgroundColor: this.options.alignColor,\n }}\n />\n {/* 左端或上端横线 */}\n <div\n style={{\n position: 'absolute',\n top: isHorizontal\n ? adjustedTop - (alignCrossWidth - alignLineWidth) / 2\n : adjustedTop,\n left: isHorizontal\n ? adjustedLeft\n : adjustedLeft - (alignCrossWidth - alignLineWidth) / 2,\n width: isHorizontal ? alignLineWidth : alignCrossWidth,\n height: isHorizontal ? alignCrossWidth : alignLineWidth,\n backgroundColor: this.options.alignColor,\n }}\n />\n {/* 右端或下端横线 */}\n <div\n style={{\n position: 'absolute',\n top: isHorizontal\n ? adjustedTop - (alignCrossWidth - alignLineWidth) / 2\n : adjustedTop + renderLine.height - alignLineWidth,\n left: isHorizontal\n ? adjustedLeft + renderLine.width - alignLineWidth\n : adjustedLeft - (alignCrossWidth - alignLineWidth) / 2,\n width: isHorizontal ? alignLineWidth : alignCrossWidth,\n height: isHorizontal ? alignCrossWidth : alignLineWidth,\n backgroundColor: this.options.alignColor,\n }}\n />\n </div>\n );\n });\n }\n\n private calcEdgeLines(event: SnapEvent): SnapRenderLine[] {\n const { alignRects, snapRect, snapEdgeLines } = event;\n const edgeLines: SnapRenderLine[] = [];\n\n const topFullAlign = this.directionFullAlign({\n alignRects: alignRects.top,\n targetRect: snapRect,\n isVertical: true,\n });\n const bottomFullAlign = this.directionFullAlign({\n alignRects: alignRects.bottom,\n targetRect: snapRect,\n isVertical: true,\n });\n const leftFullAlign = this.directionFullAlign({\n alignRects: alignRects.left,\n targetRect: snapRect,\n isVertical: false,\n });\n const rightFullAlign = this.directionFullAlign({\n alignRects: alignRects.right,\n targetRect: snapRect,\n isVertical: false,\n });\n\n // 处理顶部对齐\n if (topFullAlign) {\n const top = topFullAlign.rect.top;\n const height = bottomFullAlign\n ? snapRect.bottom - snapRect.height / 2 - top\n : snapRect.bottom - top;\n const width = this.options.edgeLineWidth;\n const lineData = {\n top,\n width,\n height,\n };\n edgeLines.push({\n className: 'edge-full-top-left',\n sourceNode: topFullAlign.sourceNodeId,\n left: snapRect.left,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-top-right',\n sourceNode: topFullAlign.sourceNodeId,\n left: snapRect.right,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-top-mid',\n sourceNode: topFullAlign.sourceNodeId,\n left: snapRect.left + snapRect.width / 2,\n dashed: true,\n ...lineData,\n });\n }\n\n // 处理底部对齐\n if (bottomFullAlign) {\n const top = topFullAlign ? snapRect.top + snapRect.height / 2 : snapRect.top;\n const height = bottomFullAlign.rect.bottom - top;\n const width = this.options.edgeLineWidth;\n const lineData = {\n top,\n width,\n height,\n };\n edgeLines.push({\n className: 'edge-full-bottom-left',\n sourceNode: bottomFullAlign.sourceNodeId,\n left: snapRect.left,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-bottom-right',\n sourceNode: bottomFullAlign.sourceNodeId,\n left: snapRect.right,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-bottom-mid',\n sourceNode: bottomFullAlign.sourceNodeId,\n left: snapRect.left + snapRect.width / 2,\n dashed: true,\n ...lineData,\n });\n }\n\n // 处理左侧对齐\n if (leftFullAlign) {\n const left = leftFullAlign.rect.left;\n const width = rightFullAlign\n ? snapRect.right - snapRect.width / 2 - left\n : snapRect.right - left;\n const height = this.options.edgeLineWidth;\n const lineData = {\n left,\n width,\n height,\n };\n edgeLines.push({\n className: 'edge-full-left-top',\n sourceNode: leftFullAlign.sourceNodeId,\n top: snapRect.top,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-left-bottom',\n sourceNode: leftFullAlign.sourceNodeId,\n top: snapRect.bottom,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-left-mid',\n sourceNode: leftFullAlign.sourceNodeId,\n top: snapRect.top + snapRect.height / 2,\n dashed: true,\n ...lineData,\n });\n }\n\n // 处理右侧对齐\n if (rightFullAlign) {\n const left = leftFullAlign ? snapRect.left + snapRect.width / 2 : snapRect.left;\n const width = rightFullAlign.rect.right - left;\n const height = this.options.edgeLineWidth;\n const lineData = {\n left,\n width,\n height,\n };\n edgeLines.push({\n className: 'edge-full-right-top',\n sourceNode: rightFullAlign.sourceNodeId,\n top: snapRect.top,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-right-bottom',\n sourceNode: rightFullAlign.sourceNodeId,\n top: snapRect.bottom,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-right-mid',\n sourceNode: rightFullAlign.sourceNodeId,\n top: snapRect.top + snapRect.height / 2,\n dashed: true,\n ...lineData,\n });\n }\n\n const snappedEdgeLines = Object.entries(snapEdgeLines)\n .map(([direction, snapLine]) => {\n if (!snapLine) {\n return;\n }\n const sourceNode = this.document.getNode(snapLine.sourceNodeId);\n if (!sourceNode) {\n return;\n }\n const nodeRect = sourceNode.getData(FlowNodeTransformData).bounds;\n if (isNumber(snapLine.x)) {\n // 垂直\n const top = Math.min(nodeRect.top, snapRect.top);\n const bottom = Math.max(nodeRect.bottom, snapRect.bottom);\n const height = bottom - top;\n const left = snapLine.x;\n const width = this.options.edgeLineWidth;\n const isMidX = direction === 'midVertical';\n const lineData: SnapRenderLine = {\n className: `edge-snapped-${direction}`,\n sourceNode: snapLine.sourceNodeId,\n top,\n left,\n width,\n height,\n dashed: isMidX,\n };\n const onTop = top === nodeRect.top;\n if (onTop && topFullAlign) {\n return;\n }\n if (!onTop && bottomFullAlign) {\n return;\n }\n return lineData;\n } else if (isNumber(snapLine.y)) {\n // 水平\n const left = Math.min(nodeRect.left, snapRect.left);\n const right = Math.max(nodeRect.right, snapRect.right);\n const width = right - left;\n const top = snapLine.y;\n const height = this.options.edgeLineWidth;\n const isMidY = direction === 'midHorizontal';\n const lineData: SnapRenderLine = {\n className: `edge-snapped-${direction}`,\n sourceNode: snapLine.sourceNodeId,\n top,\n left,\n width,\n height,\n dashed: isMidY,\n };\n const onLeft = left === nodeRect.left;\n if (onLeft && leftFullAlign) {\n return;\n }\n if (!onLeft && rightFullAlign) {\n return;\n }\n return lineData;\n }\n })\n .filter(Boolean) as SnapRenderLine[];\n\n edgeLines.push(...snappedEdgeLines);\n\n return edgeLines;\n }\n\n private directionFullAlign(params: {\n alignRects: AlignRect[];\n targetRect: Rectangle;\n isVertical: boolean;\n }): AlignRect | undefined {\n const { alignRects, targetRect, isVertical } = params;\n let fullAlignIndex = -1;\n for (let i = 0; i < alignRects.length; i++) {\n const alignRect = alignRects[i];\n const prevRect = alignRects[i - 1]?.rect ?? targetRect;\n const isFullAlign = this.rectFullAlign(alignRect.rect, prevRect, isVertical);\n if (!isFullAlign) {\n // 未对齐则中断\n break; // 用 for 循环 + break 反而比 Array.findIndex 实现可读性更好\n }\n fullAlignIndex = i;\n }\n const fullAlignRect = alignRects[fullAlignIndex];\n return fullAlignRect;\n }\n\n private rectFullAlign(rectA: Rectangle, rectB: Rectangle, isVertical: boolean): boolean {\n if (isVertical) {\n return isEqual(rectA.left, rectB.left) && isEqual(rectA.right, rectB.right);\n } else {\n return isEqual(rectA.top, rectB.top) && isEqual(rectA.bottom, rectB.bottom);\n }\n }\n\n private calcAlignLines(event: SnapEvent): SnapRenderLine[] {\n const { alignRects, alignSpacing, snapRect } = event;\n\n const topAlignLines = this.calcDirectionAlignLines({\n alignRects: alignRects.top,\n targetRect: snapRect,\n isVertical: true,\n spacing: alignSpacing.midVertical ?? alignSpacing.top,\n });\n\n const bottomAlignLines = this.calcDirectionAlignLines({\n alignRects: alignRects.bottom,\n targetRect: snapRect,\n isVertical: true,\n spacing: alignSpacing.midVertical ?? alignSpacing.bottom,\n });\n\n const leftAlignLines = this.calcDirectionAlignLines({\n alignRects: alignRects.left,\n targetRect: snapRect,\n isVertical: false,\n spacing: alignSpacing.midHorizontal ?? alignSpacing.left,\n });\n\n const rightAlignLines = this.calcDirectionAlignLines({\n alignRects: alignRects.right,\n targetRect: snapRect,\n isVertical: false,\n spacing: alignSpacing.midHorizontal ?? alignSpacing.right,\n });\n\n return [...topAlignLines, ...bottomAlignLines, ...leftAlignLines, ...rightAlignLines];\n }\n\n private calcDirectionAlignLines(params: {\n alignRects: AlignRect[];\n targetRect: Rectangle;\n isVertical: boolean;\n spacing?: number;\n }) {\n const { alignRects, targetRect, isVertical, spacing } = params;\n const alignLines: SnapRenderLine[] = [];\n if (!spacing) {\n return alignLines;\n }\n for (let i = 0; i < alignRects.length; i++) {\n const alignRect = alignRects[i];\n const rect = alignRect.rect;\n const prevRect = alignRects[i - 1]?.rect ?? targetRect;\n\n const betweenSpacing = isVertical\n ? Math.min(Math.abs(prevRect.top - rect.bottom), Math.abs(prevRect.bottom - rect.top))\n : Math.min(Math.abs(prevRect.left - rect.right), Math.abs(prevRect.right - rect.left));\n if (!isEqual(betweenSpacing, spacing)) {\n // 不连续,需要中断\n break; // 因为要用到 break,所以不能用 Array.map()\n }\n if (isVertical) {\n const centerX = this.calcHorizontalIntersectionCenter(rect, targetRect);\n alignLines.push({\n className: 'align-vertical',\n sourceNode: alignRect.sourceNodeId,\n top: Math.min(rect.bottom, prevRect.bottom),\n left: centerX,\n width: 1,\n height: spacing,\n });\n } else {\n const centerY = this.calcVerticalIntersectionCenter(rect, targetRect);\n alignLines.push({\n className: 'align-horizontal',\n sourceNode: alignRect.sourceNodeId,\n top: centerY,\n left: Math.min(rect.right, prevRect.right),\n width: spacing,\n height: 1,\n });\n }\n }\n return alignLines;\n }\n\n private calcVerticalIntersectionCenter(rectA: Rectangle, rectB: Rectangle): number {\n const top = Math.max(rectA.top, rectB.top);\n const bottom = Math.min(rectA.bottom, rectB.bottom);\n return (top + bottom) / 2;\n }\n\n private calcHorizontalIntersectionCenter(rectA: Rectangle, rectB: Rectangle): number {\n const left = Math.max(rectA.left, rectB.left);\n const right = Math.min(rectA.right, rectB.right);\n return (left + right) / 2;\n }\n}\n"],"mappings":"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","names":["inject","injectable","FlowNodeTransformData","WorkflowDocument","FlowNodeTransformData","inject","WorkflowDocument","injectable"]}
|
|
1
|
+
{"version":3,"sources":["../../src/create-plugin.ts","../../src/service.ts","../../src/constant.ts","../../src/utils.ts","../../src/layer.tsx"],"sourcesContent":["/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { definePluginCreator } from '@flowgram.ai/core';\n\nimport {\n FreeSnapPluginOptions,\n WorkflowSnapLayerOptions,\n WorkflowSnapServiceOptions,\n} from './type';\nimport { WorkflowSnapService } from './service';\nimport { WorkflowSnapLayer } from './layer';\nimport { SnapDefaultOptions } from './constant';\n\nexport const createFreeSnapPlugin = definePluginCreator<FreeSnapPluginOptions>({\n onBind({ bind }) {\n bind(WorkflowSnapService).toSelf().inSingletonScope();\n },\n onInit(ctx, opts) {\n const options: WorkflowSnapServiceOptions & WorkflowSnapLayerOptions = {\n ...SnapDefaultOptions,\n ...opts,\n };\n ctx.playground.registerLayer(WorkflowSnapLayer, options);\n const snapService = ctx.get<WorkflowSnapService>(WorkflowSnapService);\n snapService.init(options);\n },\n onDispose(ctx) {\n const snapService = ctx.get<WorkflowSnapService>(WorkflowSnapService);\n snapService.dispose();\n },\n});\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { inject, injectable } from 'inversify';\nimport { Disposable, Emitter, Rectangle } from '@flowgram.ai/utils';\nimport { IPoint } from '@flowgram.ai/utils';\nimport { WorkflowNodeEntity, WorkflowDocument } from '@flowgram.ai/free-layout-core';\nimport { WorkflowDragService } from '@flowgram.ai/free-layout-core';\nimport { FlowNodeTransformData } from '@flowgram.ai/document';\nimport { FlowNodeBaseType } from '@flowgram.ai/document';\nimport { EntityManager, PlaygroundConfigEntity, TransformData } from '@flowgram.ai/core';\n\nimport { isEqual, isGreaterThan, isLessThan, isLessThanOrEqual, isNumber } from './utils';\nimport type {\n SnapEvent,\n SnapHorizontalLine,\n SnapLines,\n SnapMidHorizontalLine,\n SnapMidVerticalLine,\n SnapVerticalLine,\n WorkflowSnapServiceOptions,\n AlignRects,\n AlignRect,\n AlignSpacing,\n SnapNodeRect,\n SnapEdgeLines,\n} from './type';\nimport { SnapDefaultOptions } from './constant';\n\n@injectable()\nexport class WorkflowSnapService {\n @inject(WorkflowDocument) private readonly document: WorkflowDocument;\n\n @inject(EntityManager) private readonly entityManager: EntityManager;\n\n @inject(WorkflowDragService)\n private readonly dragService: WorkflowDragService;\n\n @inject(PlaygroundConfigEntity)\n private readonly playgroundConfig: PlaygroundConfigEntity;\n\n private disposers: Disposable[] = [];\n\n private options: WorkflowSnapServiceOptions;\n\n private snapEmitter = new Emitter<SnapEvent>();\n\n public readonly onSnap = this.snapEmitter.event;\n\n private _disabled = false;\n\n public init(params: Partial<WorkflowSnapServiceOptions> = {}): void {\n this.options = {\n ...SnapDefaultOptions,\n ...params,\n };\n this.mountListener();\n }\n\n public dispose(): void {\n this.disposers.forEach((disposer) => disposer.dispose());\n }\n\n public get disabled(): boolean {\n return this._disabled;\n }\n\n public disable(): void {\n if (this._disabled) {\n return;\n }\n this._disabled = true;\n this.clear();\n }\n\n public enable(): void {\n if (!this._disabled) {\n return;\n }\n this._disabled = false;\n this.clear();\n }\n\n private mountListener(): void {\n const dragAdjusterDisposer = this.dragService.registerPosAdjuster((params) => {\n const { selectedNodes: targetNodes, position } = params;\n const isMultiSnapping = this.options.enableMultiSnapping ? false : targetNodes.length !== 1;\n if (this._disabled || !this.options.enableEdgeSnapping || isMultiSnapping) {\n return {\n x: 0,\n y: 0,\n };\n }\n return this.snapping({\n targetNodes,\n position,\n });\n });\n const dragEndDisposer = this.dragService.onNodesDrag((event) => {\n if (event.type !== 'onDragEnd' || this._disabled) {\n return;\n }\n if (this.options.enableGridSnapping) {\n this.gridSnapping({\n targetNodes: event.nodes,\n gridSize: this.options.gridSize,\n });\n }\n if (this.options.enableEdgeSnapping) {\n this.clear();\n }\n });\n this.disposers.push(dragAdjusterDisposer, dragEndDisposer);\n }\n\n private snapping(params: { targetNodes: WorkflowNodeEntity[]; position: IPoint }): IPoint {\n const { targetNodes, position } = params;\n\n const targetBounds = this.getBounds(targetNodes);\n\n const targetRect = new Rectangle(\n position.x,\n position.y,\n targetBounds.width,\n targetBounds.height\n );\n\n const snapNodeRects = this.getSnapNodeRects({\n targetNodes,\n targetRect,\n });\n\n const { alignOffset, alignRects, alignSpacing } = this.calcAlignOffset({\n targetRect,\n alignThreshold: this.options.edgeThreshold,\n snapNodeRects,\n });\n\n const { snapOffset, snapEdgeLines } = this.calcSnapOffset({\n targetRect,\n edgeThreshold: this.options.edgeThreshold,\n snapNodeRects,\n });\n\n const offset: IPoint = {\n x: snapOffset.x || alignOffset.x,\n y: snapOffset.y || alignOffset.y,\n };\n\n const snapRect = new Rectangle(\n position.x + offset.x,\n position.y + offset.y,\n targetRect.width,\n targetRect.height\n );\n\n this.snapEmitter.fire({\n snapRect,\n snapEdgeLines,\n alignRects,\n alignSpacing,\n });\n\n return offset;\n }\n\n private calcSnapOffset(params: {\n snapNodeRects: SnapNodeRect[];\n targetRect: Rectangle;\n edgeThreshold: number;\n }): {\n snapOffset: IPoint;\n snapEdgeLines: SnapEdgeLines;\n } {\n const { snapNodeRects, edgeThreshold, targetRect } = params;\n\n const snapLines = this.getSnapLines({\n snapNodeRects,\n });\n\n // 找到最近的线条\n const topYClosestLine = snapLines.horizontal.find((line) =>\n isLessThanOrEqual(Math.abs(line.y - targetRect.top), edgeThreshold)\n );\n const bottomYClosestLine = snapLines.horizontal.find((line) =>\n isLessThanOrEqual(Math.abs(line.y - targetRect.bottom), edgeThreshold)\n );\n const leftXClosestLine = snapLines.vertical.find((line) =>\n isLessThanOrEqual(Math.abs(line.x - targetRect.left), edgeThreshold)\n );\n const rightXClosestLine = snapLines.vertical.find((line) =>\n isLessThanOrEqual(Math.abs(line.x - targetRect.right), edgeThreshold)\n );\n const midYClosestLine = snapLines.midHorizontal.find((line) =>\n isLessThanOrEqual(Math.abs(line.y - targetRect.center.y), edgeThreshold)\n );\n const midXClosestLine = snapLines.midVertical.find((line) =>\n isLessThanOrEqual(Math.abs(line.x - targetRect.center.x), edgeThreshold)\n );\n\n // 计算最近坐标\n const topYClosest = topYClosestLine?.y;\n const bottomYClosest = isNumber(bottomYClosestLine?.y)\n ? bottomYClosestLine!.y - targetRect.height\n : undefined;\n const leftXClosest = leftXClosestLine?.x;\n const rightXClosest = isNumber(rightXClosestLine?.x)\n ? rightXClosestLine!.x - targetRect.width\n : undefined;\n const midYClosest = isNumber(midYClosestLine?.y)\n ? midYClosestLine!.y - targetRect.height / 2\n : undefined;\n const midXClosest = isNumber(midXClosestLine?.x)\n ? midXClosestLine!.x - targetRect.width / 2\n : undefined;\n\n // 吸附后坐标,按优先级取值\n const snappingPosition = {\n x: midXClosest ?? leftXClosest ?? rightXClosest ?? targetRect.x,\n y: midYClosest ?? topYClosest ?? bottomYClosest ?? targetRect.y,\n };\n\n // 吸附修正偏移量\n const snapOffset: IPoint = {\n x: snappingPosition.x - targetRect.x,\n y: snappingPosition.y - targetRect.y,\n };\n\n // 生效的吸附线条\n const snapEdgeLines: SnapEdgeLines = {\n top: isEqual(topYClosest, snappingPosition.y) ? topYClosestLine : undefined,\n bottom: isEqual(bottomYClosest, snappingPosition.y) ? bottomYClosestLine : undefined,\n left: isEqual(leftXClosest, snappingPosition.x) ? leftXClosestLine : undefined,\n right: isEqual(rightXClosest, snappingPosition.x) ? rightXClosestLine : undefined,\n midVertical: isEqual(midXClosest, snappingPosition.x) ? midXClosestLine : undefined,\n midHorizontal: isEqual(midYClosest, snappingPosition.y) ? midYClosestLine : undefined,\n };\n\n return { snapOffset, snapEdgeLines };\n }\n\n private gridSnapping(params: { gridSize: number; targetNodes: WorkflowNodeEntity[] }): void {\n const { gridSize, targetNodes } = params;\n const rect = this.getBounds(targetNodes);\n const snap = (value: number) => Math.round(value / gridSize) * gridSize;\n const snappedPosition: IPoint = {\n x: snap(rect.x),\n y: snap(rect.y),\n };\n const offset: IPoint = {\n x: snappedPosition.x - rect.x,\n y: snappedPosition.y - rect.y,\n };\n targetNodes.forEach((node) =>\n this.updateNodePositionWithOffset({\n node,\n offset,\n })\n );\n }\n\n private clear() {\n this.snapEmitter.fire({\n snapEdgeLines: {},\n snapRect: Rectangle.EMPTY,\n alignRects: {\n top: [],\n bottom: [],\n left: [],\n right: [],\n },\n alignSpacing: {},\n });\n }\n\n private getSnapLines(params: { snapNodeRects: SnapNodeRect[] }): SnapLines {\n const { snapNodeRects } = params;\n const horizontalLines: SnapHorizontalLine[] = [];\n const verticalLines: SnapVerticalLine[] = [];\n const midHorizontalLines: SnapMidHorizontalLine[] = [];\n const midVerticalLines: SnapMidVerticalLine[] = [];\n\n snapNodeRects.forEach((snapNodeRect) => {\n const nodeBounds = snapNodeRect.rect;\n const nodeCenter = nodeBounds.center;\n // 边缘横线\n const top: SnapHorizontalLine = {\n y: nodeBounds.top,\n sourceNodeId: snapNodeRect.id,\n };\n const bottom: SnapHorizontalLine = {\n y: nodeBounds.bottom,\n sourceNodeId: snapNodeRect.id,\n };\n // 边缘竖线\n const left: SnapVerticalLine = {\n x: nodeBounds.left,\n sourceNodeId: snapNodeRect.id,\n };\n const right: SnapVerticalLine = {\n x: nodeBounds.right,\n sourceNodeId: snapNodeRect.id,\n };\n // 中间横线\n const midHorizontal: SnapMidHorizontalLine = {\n y: nodeCenter.y,\n sourceNodeId: snapNodeRect.id,\n };\n // 中间竖线\n const midVertical: SnapMidVerticalLine = {\n x: nodeCenter.x,\n sourceNodeId: snapNodeRect.id,\n };\n horizontalLines.push(top, bottom);\n verticalLines.push(left, right);\n midHorizontalLines.push(midHorizontal);\n midVerticalLines.push(midVertical);\n });\n\n return {\n horizontal: horizontalLines,\n vertical: verticalLines,\n midHorizontal: midHorizontalLines,\n midVertical: midVerticalLines,\n };\n }\n\n private getAvailableNodes(params: {\n targetNodes: WorkflowNodeEntity[];\n targetRect: Rectangle;\n }): WorkflowNodeEntity[] {\n const { targetNodes, targetRect } = params;\n\n const targetCenter = targetRect.center;\n const targetContainerId = targetNodes[0].parent?.id ?? this.document.root.id;\n\n const disabledNodeIds = targetNodes.map((n) => n.id);\n disabledNodeIds.push(FlowNodeBaseType.ROOT);\n const availableNodes = this.nodes\n .filter((n) => n.parent?.id === targetContainerId)\n .filter((n) => !disabledNodeIds.includes(n.id))\n .sort((nodeA, nodeB) => {\n const nodeCenterA = nodeA.getData(FlowNodeTransformData)!.bounds.center;\n const nodeCenterB = nodeB.getData(FlowNodeTransformData)!.bounds.center;\n // 距离越近优先级越高\n const distanceA =\n Math.abs(nodeCenterA.x - targetCenter.x) + Math.abs(nodeCenterA.y - targetCenter.y);\n const distanceB =\n Math.abs(nodeCenterB.x - targetCenter.x) + Math.abs(nodeCenterB.y - targetCenter.y);\n return distanceA - distanceB;\n });\n return availableNodes;\n }\n\n private viewRect(): Rectangle {\n const { width, height, scrollX, scrollY, zoom } = this.playgroundConfig.config;\n return new Rectangle(scrollX / zoom, scrollY / zoom, width / zoom, height / zoom);\n }\n\n private getSnapNodeRects(params: {\n targetNodes: WorkflowNodeEntity[];\n targetRect: Rectangle;\n }): SnapNodeRect[] {\n const availableNodes = this.getAvailableNodes(params);\n const viewRect = this.viewRect();\n return availableNodes\n .map((node) => {\n const snapNodeRect: SnapNodeRect = {\n id: node.id,\n rect: node.getData(FlowNodeTransformData).bounds,\n entity: node,\n };\n if (\n this.options.enableOnlyViewportSnapping &&\n node.parent?.flowNodeType === FlowNodeBaseType.ROOT &&\n !Rectangle.intersects(viewRect, snapNodeRect.rect)\n ) {\n // 最外层节点仅包含当前可见节点\n return;\n }\n return snapNodeRect;\n })\n .filter(Boolean) as SnapNodeRect[];\n }\n\n private get nodes(): WorkflowNodeEntity[] {\n return this.entityManager.getEntities<WorkflowNodeEntity>(WorkflowNodeEntity);\n }\n\n private getBounds(nodes: WorkflowNodeEntity[]): Rectangle {\n if (nodes.length === 0) {\n return Rectangle.EMPTY;\n }\n return Rectangle.enlarge(nodes.map((n) => n.getData(FlowNodeTransformData)!.bounds));\n }\n\n private updateNodePositionWithOffset(params: { node: WorkflowNodeEntity; offset: IPoint }): void {\n const { node, offset } = params;\n const transform = node.getData(TransformData);\n const positionWithOffset: IPoint = {\n x: transform.position.x + offset.x,\n y: transform.position.y + offset.y,\n };\n transform.update({\n position: positionWithOffset,\n });\n this.document.layout.updateAffectedTransform(node);\n }\n\n private calcAlignOffset(params: {\n snapNodeRects: SnapNodeRect[];\n targetRect: Rectangle;\n alignThreshold: number;\n }): {\n alignOffset: IPoint;\n alignRects: AlignRects;\n alignSpacing: AlignSpacing;\n } {\n const { snapNodeRects, targetRect, alignThreshold } = params;\n\n const alignRects = this.getAlignRects({\n targetRect,\n snapNodeRects,\n });\n\n const alignSpacing = this.calcAlignSpacing({\n targetRect,\n alignRects,\n });\n\n let topY: number | undefined;\n let bottomY: number | undefined;\n let leftX: number | undefined;\n let rightX: number | undefined;\n let midY: number | undefined;\n let midX: number | undefined;\n\n if (alignSpacing.top) {\n const topAlignY = alignRects.top[0].rect.bottom + alignSpacing.top;\n const isAlignTop = isLessThanOrEqual(Math.abs(targetRect.top - topAlignY), alignThreshold);\n if (isAlignTop) {\n // 生效\n topY = topAlignY;\n } else {\n // 失效\n alignSpacing.top = undefined;\n }\n }\n if (alignSpacing.bottom) {\n const bottomAlignY = alignRects.bottom[0].rect.top - alignSpacing.bottom;\n const isAlignBottom = isLessThan(Math.abs(targetRect.bottom - bottomAlignY), alignThreshold);\n if (isAlignBottom) {\n bottomY = bottomAlignY - targetRect.height;\n } else {\n alignSpacing.bottom = undefined;\n }\n }\n if (alignSpacing.left) {\n const leftAlignX = alignRects.left[0].rect.right + alignSpacing.left;\n const isAlignLeft = isLessThanOrEqual(Math.abs(targetRect.left - leftAlignX), alignThreshold);\n if (isAlignLeft) {\n leftX = leftAlignX;\n } else {\n alignSpacing.left = undefined;\n }\n }\n if (alignSpacing.right) {\n const rightAlignX = alignRects.right[0].rect.left - alignSpacing.right;\n const isAlignRight = isLessThanOrEqual(\n Math.abs(targetRect.right - rightAlignX),\n alignThreshold\n );\n if (isAlignRight) {\n rightX = rightAlignX - targetRect.width;\n } else {\n alignSpacing.right = undefined;\n }\n }\n if (alignSpacing.midHorizontal) {\n const leftAlignX = alignRects.left[0].rect.right + alignSpacing.midHorizontal;\n const isAlignMidHorizontal = isLessThanOrEqual(\n Math.abs(targetRect.left - leftAlignX),\n alignThreshold\n );\n if (isAlignMidHorizontal) {\n midX = leftAlignX;\n } else {\n alignSpacing.midHorizontal = undefined;\n }\n }\n if (alignSpacing.midVertical) {\n const topAlignY = alignRects.top[0].rect.bottom + alignSpacing.midVertical;\n const isAlignMidVertical = isLessThanOrEqual(\n Math.abs(targetRect.top - topAlignY),\n alignThreshold\n );\n if (isAlignMidVertical) {\n midY = topAlignY;\n } else {\n alignSpacing.midVertical = undefined;\n }\n }\n\n const alignPosition: IPoint = {\n x: midX ?? leftX ?? rightX ?? targetRect.x,\n y: midY ?? topY ?? bottomY ?? targetRect.y,\n };\n\n const alignOffset: IPoint = {\n x: alignPosition.x - targetRect.x,\n y: alignPosition.y - targetRect.y,\n };\n\n return { alignOffset, alignRects, alignSpacing };\n }\n\n private calcAlignSpacing(params: {\n targetRect: Rectangle;\n alignRects: AlignRects;\n }): AlignSpacing {\n const { targetRect, alignRects } = params;\n\n const topSpacing = this.getDirectionAlignSpacing({\n rects: alignRects.top,\n isHorizontal: false,\n });\n const bottomSpacing = this.getDirectionAlignSpacing({\n rects: alignRects.bottom,\n isHorizontal: false,\n });\n const leftSpacing = this.getDirectionAlignSpacing({\n rects: alignRects.left,\n isHorizontal: true,\n });\n const rightSpacing = this.getDirectionAlignSpacing({\n rects: alignRects.right,\n isHorizontal: true,\n });\n const midHorizontalSpacing = this.getMidAlignSpacing({\n rectA: alignRects.left[0]?.rect,\n rectB: alignRects.right[0]?.rect,\n targetRect,\n isHorizontal: true,\n });\n const midVerticalSpacing = this.getMidAlignSpacing({\n rectA: alignRects.top[0]?.rect,\n rectB: alignRects.bottom[0]?.rect,\n targetRect,\n isHorizontal: false,\n });\n return {\n top: topSpacing,\n bottom: bottomSpacing,\n left: leftSpacing,\n right: rightSpacing,\n midHorizontal: midHorizontalSpacing,\n midVertical: midVerticalSpacing,\n };\n }\n\n private getAlignRects(params: {\n targetRect: Rectangle;\n snapNodeRects: SnapNodeRect[];\n }): AlignRects {\n const { targetRect, snapNodeRects } = params;\n\n const topVerticalRects: AlignRect[] = [];\n const bottomVerticalRects: AlignRect[] = [];\n const leftHorizontalRects: AlignRect[] = [];\n const rightHorizontalRects: AlignRect[] = [];\n\n snapNodeRects.forEach((snapNodeRect) => {\n const nodeRect = snapNodeRect.rect;\n const { isVerticalIntersection, isHorizontalIntersection, isIntersection } =\n this.intersection(nodeRect, targetRect);\n if (isIntersection) {\n // 忽略重叠的节点\n return;\n } else if (isVerticalIntersection) {\n // 垂直重合\n if (isGreaterThan(nodeRect.center.y, targetRect.center.y)) {\n // 下方\n bottomVerticalRects.push({\n rect: nodeRect,\n sourceNodeId: snapNodeRect.id,\n });\n } else {\n // 上方\n topVerticalRects.push({\n rect: nodeRect,\n sourceNodeId: snapNodeRect.id,\n });\n }\n } else if (isHorizontalIntersection) {\n // 水平重合\n if (isGreaterThan(nodeRect.center.x, targetRect.center.x)) {\n // 右方\n rightHorizontalRects.push({\n rect: nodeRect,\n sourceNodeId: snapNodeRect.id,\n });\n } else {\n // 左方\n leftHorizontalRects.push({\n rect: nodeRect,\n sourceNodeId: snapNodeRect.id,\n });\n }\n }\n });\n\n return {\n top: topVerticalRects,\n bottom: bottomVerticalRects,\n left: leftHorizontalRects,\n right: rightHorizontalRects,\n };\n }\n\n private getMidAlignSpacing(params: {\n rectA?: Rectangle;\n rectB?: Rectangle;\n targetRect: Rectangle;\n isHorizontal: boolean;\n }): number | undefined {\n const { rectA, rectB, targetRect, isHorizontal } = params;\n if (!rectA || !rectB) {\n return;\n }\n const { isVerticalIntersection, isHorizontalIntersection, isIntersection } = this.intersection(\n rectA,\n rectB\n );\n if (isIntersection) {\n return;\n }\n if (isHorizontal && isHorizontalIntersection && !isVerticalIntersection) {\n const betweenSpacing = Math.min(\n Math.abs(rectA.left - rectB.right),\n Math.abs(rectA.right - rectB.left)\n );\n return (betweenSpacing - targetRect.width) / 2;\n } else if (!isHorizontal && isVerticalIntersection && !isHorizontalIntersection) {\n const betweenSpacing = Math.min(\n Math.abs(rectA.top - rectB.bottom),\n Math.abs(rectA.bottom - rectB.top)\n );\n return (betweenSpacing - targetRect.height) / 2;\n }\n }\n\n private getDirectionAlignSpacing(params: {\n rects: AlignRect[];\n isHorizontal: boolean;\n }): number | undefined {\n const { rects, isHorizontal } = params;\n if (rects.length < 2) {\n // 非法情况\n return;\n }\n const rectA = rects[0].rect;\n const rectB = rects[1].rect;\n\n const { isVerticalIntersection, isHorizontalIntersection, isIntersection } = this.intersection(\n rectA,\n rectB\n );\n\n if (isIntersection) {\n // 非法情况:重叠\n return;\n }\n if (isHorizontal && isHorizontalIntersection && !isVerticalIntersection) {\n return Math.min(Math.abs(rectA.left - rectB.right), Math.abs(rectA.right - rectB.left));\n } else if (!isHorizontal && isVerticalIntersection && !isHorizontalIntersection) {\n return Math.min(Math.abs(rectA.top - rectB.bottom), Math.abs(rectA.bottom - rectB.top));\n }\n return;\n }\n\n private intersection(\n rectA: Rectangle,\n rectB: Rectangle\n ): {\n isHorizontalIntersection: boolean;\n isVerticalIntersection: boolean;\n isIntersection: boolean;\n } {\n const isVerticalIntersection =\n isLessThan(rectA.left, rectB.right) && isGreaterThan(rectA.right, rectB.left);\n const isHorizontalIntersection =\n isLessThan(rectA.top, rectB.bottom) && isGreaterThan(rectA.bottom, rectB.top);\n const isIntersection = isHorizontalIntersection && isVerticalIntersection;\n\n return {\n isHorizontalIntersection,\n isVerticalIntersection,\n isIntersection,\n };\n }\n}\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport type { WorkflowSnapLayerOptions, WorkflowSnapServiceOptions } from './type';\n\nexport const SnapDefaultOptions: WorkflowSnapServiceOptions & WorkflowSnapLayerOptions = {\n enableEdgeSnapping: true,\n edgeThreshold: 7,\n enableGridSnapping: false,\n gridSize: 20,\n enableMultiSnapping: true,\n enableOnlyViewportSnapping: true,\n edgeColor: '#4E40E5',\n alignColor: '#4E40E5',\n edgeLineWidth: 2,\n alignLineWidth: 2,\n alignCrossWidth: 16,\n};\n\nexport const Epsilon = 0.00001;\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { Epsilon } from './constant';\n\n/** 检查浮点数 a 是否等于 b */\nexport const isEqual = (a: number | undefined, b: number | undefined): boolean => {\n if (a === undefined || b === undefined) {\n return false;\n }\n // 检查 a 和 b 的差的绝对值是否小于 Epsilon\n return Math.abs(a - b) < Epsilon;\n};\n\n/** 检查浮点数 a 是否小于 b */\nexport const isLessThan = (a: number | undefined, b: number | undefined): boolean => {\n if (a === undefined || b === undefined) {\n return false;\n }\n // 检查 a 是否显著小于 b\n return b - a > Epsilon;\n};\n\n/** 检查浮点数 a 是否大于 b */\nexport const isGreaterThan = (a: number | undefined, b: number | undefined): boolean => {\n if (a === undefined || b === undefined) {\n return false;\n }\n return a - b > Epsilon;\n};\n\n/** 检查浮点数 a 是否小于等于 b */\nexport const isLessThanOrEqual = (a: number | undefined, b: number | undefined): boolean =>\n isEqual(a, b) || isLessThan(a, b);\n\n/** 检查浮点数 a 是否大于等于 b */\nexport const isGreaterThanOrEqual = (a: number | undefined, b: number | undefined): boolean =>\n isEqual(a, b) || isGreaterThan(a, b);\n\n/** 检查值是否是数字类型 */\nexport const isNumber = (value: unknown): value is number =>\n typeof value === 'number' && !isNaN(value);\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport React from 'react';\n\nimport { inject, injectable } from 'inversify';\nimport { domUtils } from '@flowgram.ai/utils';\nimport { Rectangle } from '@flowgram.ai/utils';\nimport { WorkflowDocument } from '@flowgram.ai/free-layout-core';\nimport { FlowNodeTransformData } from '@flowgram.ai/document';\nimport { Layer } from '@flowgram.ai/core';\n\nimport { isEqual, isGreaterThan, isNumber } from './utils';\nimport { AlignRect, SnapEvent, WorkflowSnapLayerOptions } from './type';\nimport { WorkflowSnapService } from './service';\n\ninterface SnapRenderLine {\n className: string;\n sourceNode: string;\n top: number;\n left: number;\n width: number;\n height: number;\n dashed?: boolean;\n}\n\n@injectable()\nexport class WorkflowSnapLayer extends Layer<WorkflowSnapLayerOptions> {\n public static type = 'WorkflowSnapLayer';\n\n @inject(WorkflowDocument) private readonly document: WorkflowDocument;\n\n @inject(WorkflowSnapService) private readonly service: WorkflowSnapService;\n\n public readonly node = domUtils.createDivWithClass(\n 'gedit-playground-layer gedit-flow-snap-layer'\n );\n\n private edgeLines: SnapRenderLine[] = [];\n\n private alignLines: SnapRenderLine[] = [];\n\n public onReady(): void {\n this.node.style.zIndex = '9999';\n this.toDispose.pushAll([\n this.service.onSnap((event: SnapEvent) => {\n this.edgeLines = this.calcEdgeLines(event);\n this.alignLines = this.calcAlignLines(event);\n this.render();\n }),\n ]);\n }\n\n public render(): JSX.Element {\n return (\n <>\n {this.alignLines.length > 0 && (\n <div className=\"workflow-snap-align-lines\">{this.renderAlignLines()}</div>\n )}\n {this.edgeLines.length > 0 && (\n <div className=\"workflow-snap-edge-lines\">{this.renderEdgeLines()}</div>\n )}\n </>\n );\n }\n\n public onZoom(scale: number): void {\n this.node.style.transform = `scale(${scale})`;\n }\n\n private renderEdgeLines(): JSX.Element[] {\n return this.edgeLines.map((renderLine: SnapRenderLine) => {\n const { className, sourceNode, top, left, width, height, dashed } = renderLine;\n const id = `${className}-${sourceNode}-${top}-${left}-${width}-${height}`;\n const isHorizontal = width < height;\n const border = `${this.options.edgeLineWidth}px ${dashed ? 'dashed' : 'solid'} ${\n this.options.edgeColor\n }`;\n return (\n <div\n className={`workflow-snap-edge-line ${className}`}\n data-testid=\"sdk.workflow.canvas.snap.edgeLine\"\n data-snap-line-id={id}\n data-snap-line-source-node={sourceNode}\n key={id}\n style={{\n top,\n left,\n width,\n height,\n position: 'absolute',\n borderLeft: isHorizontal ? border : 'none',\n borderTop: !isHorizontal ? border : 'none',\n }}\n />\n );\n });\n }\n\n private renderAlignLines(): JSX.Element[] {\n return this.alignLines.map((renderLine: SnapRenderLine) => {\n const id = `${renderLine.className}-${renderLine.sourceNode}-${renderLine.top}-${renderLine.left}-${renderLine.width}-${renderLine.height}`;\n const isHorizontal = isGreaterThan(renderLine.width, renderLine.height);\n const alignLineWidth = this.options.alignLineWidth; // 整体线条粗细\n const alignCrossWidth = this.options.alignCrossWidth; // 工字形横线的长度\n\n // 调整渲染位置以保持居中\n const adjustedTop = isHorizontal ? renderLine.top - alignLineWidth / 2 : renderLine.top;\n const adjustedLeft = isHorizontal ? renderLine.left : renderLine.left - alignLineWidth / 2;\n\n return (\n <div\n className={`workflow-snap-align-line ${renderLine.className}`}\n data-testid=\"sdk.workflow.canvas.snap.alignLine\"\n data-snap-line-id={id}\n data-snap-line-source-node={renderLine.sourceNode}\n key={id}\n style={{\n position: 'absolute',\n }}\n >\n {/* 主线 */}\n <div\n style={{\n position: 'absolute',\n top: adjustedTop,\n left: adjustedLeft,\n width: isHorizontal ? renderLine.width : alignLineWidth,\n height: isHorizontal ? alignLineWidth : renderLine.height,\n backgroundColor: this.options.alignColor,\n }}\n />\n {/* 左端或上端横线 */}\n <div\n style={{\n position: 'absolute',\n top: isHorizontal\n ? adjustedTop - (alignCrossWidth - alignLineWidth) / 2\n : adjustedTop,\n left: isHorizontal\n ? adjustedLeft\n : adjustedLeft - (alignCrossWidth - alignLineWidth) / 2,\n width: isHorizontal ? alignLineWidth : alignCrossWidth,\n height: isHorizontal ? alignCrossWidth : alignLineWidth,\n backgroundColor: this.options.alignColor,\n }}\n />\n {/* 右端或下端横线 */}\n <div\n style={{\n position: 'absolute',\n top: isHorizontal\n ? adjustedTop - (alignCrossWidth - alignLineWidth) / 2\n : adjustedTop + renderLine.height - alignLineWidth,\n left: isHorizontal\n ? adjustedLeft + renderLine.width - alignLineWidth\n : adjustedLeft - (alignCrossWidth - alignLineWidth) / 2,\n width: isHorizontal ? alignLineWidth : alignCrossWidth,\n height: isHorizontal ? alignCrossWidth : alignLineWidth,\n backgroundColor: this.options.alignColor,\n }}\n />\n </div>\n );\n });\n }\n\n private calcEdgeLines(event: SnapEvent): SnapRenderLine[] {\n const { alignRects, snapRect, snapEdgeLines } = event;\n const edgeLines: SnapRenderLine[] = [];\n\n const topFullAlign = this.directionFullAlign({\n alignRects: alignRects.top,\n targetRect: snapRect,\n isVertical: true,\n });\n const bottomFullAlign = this.directionFullAlign({\n alignRects: alignRects.bottom,\n targetRect: snapRect,\n isVertical: true,\n });\n const leftFullAlign = this.directionFullAlign({\n alignRects: alignRects.left,\n targetRect: snapRect,\n isVertical: false,\n });\n const rightFullAlign = this.directionFullAlign({\n alignRects: alignRects.right,\n targetRect: snapRect,\n isVertical: false,\n });\n\n // 处理顶部对齐\n if (topFullAlign) {\n const top = topFullAlign.rect.top;\n const height = bottomFullAlign\n ? snapRect.bottom - snapRect.height / 2 - top\n : snapRect.bottom - top;\n const width = this.options.edgeLineWidth;\n const lineData = {\n top,\n width,\n height,\n };\n edgeLines.push({\n className: 'edge-full-top-left',\n sourceNode: topFullAlign.sourceNodeId,\n left: snapRect.left,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-top-right',\n sourceNode: topFullAlign.sourceNodeId,\n left: snapRect.right - 1,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-top-mid',\n sourceNode: topFullAlign.sourceNodeId,\n left: snapRect.left + snapRect.width / 2,\n dashed: true,\n ...lineData,\n });\n }\n\n // 处理底部对齐\n if (bottomFullAlign) {\n const top = topFullAlign ? snapRect.top + snapRect.height / 2 : snapRect.top;\n const height = bottomFullAlign.rect.bottom - top;\n const width = this.options.edgeLineWidth;\n const lineData = {\n top,\n width,\n height,\n };\n edgeLines.push({\n className: 'edge-full-bottom-left',\n sourceNode: bottomFullAlign.sourceNodeId,\n left: snapRect.left,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-bottom-right',\n sourceNode: bottomFullAlign.sourceNodeId,\n left: snapRect.right - 1,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-bottom-mid',\n sourceNode: bottomFullAlign.sourceNodeId,\n left: snapRect.left + snapRect.width / 2,\n dashed: true,\n ...lineData,\n });\n }\n\n // 处理左侧对齐\n if (leftFullAlign) {\n const left = leftFullAlign.rect.left;\n const width = rightFullAlign\n ? snapRect.right - snapRect.width / 2 - left\n : snapRect.right - left;\n const height = this.options.edgeLineWidth;\n const lineData = {\n left,\n width,\n height,\n };\n edgeLines.push({\n className: 'edge-full-left-top',\n sourceNode: leftFullAlign.sourceNodeId,\n top: snapRect.top,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-left-bottom',\n sourceNode: leftFullAlign.sourceNodeId,\n top: snapRect.bottom - 1,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-left-mid',\n sourceNode: leftFullAlign.sourceNodeId,\n top: snapRect.top + snapRect.height / 2,\n dashed: true,\n ...lineData,\n });\n }\n\n // 处理右侧对齐\n if (rightFullAlign) {\n const left = leftFullAlign ? snapRect.left + snapRect.width / 2 : snapRect.left;\n const width = rightFullAlign.rect.right - left;\n const height = this.options.edgeLineWidth;\n const lineData = {\n left,\n width,\n height,\n };\n edgeLines.push({\n className: 'edge-full-right-top',\n sourceNode: rightFullAlign.sourceNodeId,\n top: snapRect.top,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-right-bottom',\n sourceNode: rightFullAlign.sourceNodeId,\n top: snapRect.bottom - 1,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-right-mid',\n sourceNode: rightFullAlign.sourceNodeId,\n top: snapRect.top + snapRect.height / 2,\n dashed: true,\n ...lineData,\n });\n }\n\n const snappedEdgeLines = Object.entries(snapEdgeLines)\n .map(([direction, snapLine]) => {\n if (!snapLine) {\n return;\n }\n const sourceNode = this.document.getNode(snapLine.sourceNodeId);\n if (!sourceNode) {\n return;\n }\n const nodeRect = sourceNode.getData(FlowNodeTransformData).bounds;\n if (isNumber(snapLine.x)) {\n // 垂直\n const top = Math.min(nodeRect.top, snapRect.top);\n const bottom = Math.max(nodeRect.bottom, snapRect.bottom);\n const height = bottom - top;\n const left = direction === 'right' ? snapLine.x - 1 : snapLine.x;\n const width = this.options.edgeLineWidth;\n const isMidX = direction === 'midVertical';\n const lineData: SnapRenderLine = {\n className: `edge-snapped-${direction}`,\n sourceNode: snapLine.sourceNodeId,\n top,\n left,\n width,\n height,\n dashed: isMidX,\n };\n const onTop = top === nodeRect.top;\n if (onTop && topFullAlign) {\n return;\n }\n if (!onTop && bottomFullAlign) {\n return;\n }\n return lineData;\n } else if (isNumber(snapLine.y)) {\n // 水平\n const left = Math.min(nodeRect.left, snapRect.left);\n const right = Math.max(nodeRect.right, snapRect.right);\n const width = right - left;\n const top = direction === 'bottom' ? snapLine.y - 1 : snapLine.y;\n const height = this.options.edgeLineWidth;\n const isMidY = direction === 'midHorizontal';\n const lineData: SnapRenderLine = {\n className: `edge-snapped-${direction}`,\n sourceNode: snapLine.sourceNodeId,\n top,\n left,\n width,\n height,\n dashed: isMidY,\n };\n const onLeft = left === nodeRect.left;\n if (onLeft && leftFullAlign) {\n return;\n }\n if (!onLeft && rightFullAlign) {\n return;\n }\n return lineData;\n }\n })\n .filter(Boolean) as SnapRenderLine[];\n\n edgeLines.push(...snappedEdgeLines);\n\n return edgeLines;\n }\n\n private directionFullAlign(params: {\n alignRects: AlignRect[];\n targetRect: Rectangle;\n isVertical: boolean;\n }): AlignRect | undefined {\n const { alignRects, targetRect, isVertical } = params;\n let fullAlignIndex = -1;\n for (let i = 0; i < alignRects.length; i++) {\n const alignRect = alignRects[i];\n const prevRect = alignRects[i - 1]?.rect ?? targetRect;\n const isFullAlign = this.rectFullAlign(alignRect.rect, prevRect, isVertical);\n if (!isFullAlign) {\n // 未对齐则中断\n break; // 用 for 循环 + break 反而比 Array.findIndex 实现可读性更好\n }\n fullAlignIndex = i;\n }\n const fullAlignRect = alignRects[fullAlignIndex];\n return fullAlignRect;\n }\n\n private rectFullAlign(rectA: Rectangle, rectB: Rectangle, isVertical: boolean): boolean {\n if (isVertical) {\n return isEqual(rectA.left, rectB.left) && isEqual(rectA.right, rectB.right);\n } else {\n return isEqual(rectA.top, rectB.top) && isEqual(rectA.bottom, rectB.bottom);\n }\n }\n\n private calcAlignLines(event: SnapEvent): SnapRenderLine[] {\n const { alignRects, alignSpacing, snapRect } = event;\n\n const topAlignLines = this.calcDirectionAlignLines({\n alignRects: alignRects.top,\n targetRect: snapRect,\n isVertical: true,\n spacing: alignSpacing.midVertical ?? alignSpacing.top,\n });\n\n const bottomAlignLines = this.calcDirectionAlignLines({\n alignRects: alignRects.bottom,\n targetRect: snapRect,\n isVertical: true,\n spacing: alignSpacing.midVertical ?? alignSpacing.bottom,\n });\n\n const leftAlignLines = this.calcDirectionAlignLines({\n alignRects: alignRects.left,\n targetRect: snapRect,\n isVertical: false,\n spacing: alignSpacing.midHorizontal ?? alignSpacing.left,\n });\n\n const rightAlignLines = this.calcDirectionAlignLines({\n alignRects: alignRects.right,\n targetRect: snapRect,\n isVertical: false,\n spacing: alignSpacing.midHorizontal ?? alignSpacing.right,\n });\n\n return [...topAlignLines, ...bottomAlignLines, ...leftAlignLines, ...rightAlignLines];\n }\n\n private calcDirectionAlignLines(params: {\n alignRects: AlignRect[];\n targetRect: Rectangle;\n isVertical: boolean;\n spacing?: number;\n }) {\n const { alignRects, targetRect, isVertical, spacing } = params;\n const alignLines: SnapRenderLine[] = [];\n if (!spacing) {\n return alignLines;\n }\n for (let i = 0; i < alignRects.length; i++) {\n const alignRect = alignRects[i];\n const rect = alignRect.rect;\n const prevRect = alignRects[i - 1]?.rect ?? targetRect;\n\n const betweenSpacing = isVertical\n ? Math.min(Math.abs(prevRect.top - rect.bottom), Math.abs(prevRect.bottom - rect.top))\n : Math.min(Math.abs(prevRect.left - rect.right), Math.abs(prevRect.right - rect.left));\n if (!isEqual(betweenSpacing, spacing)) {\n // 不连续,需要中断\n break; // 因为要用到 break,所以不能用 Array.map()\n }\n if (isVertical) {\n const centerX = this.calcHorizontalIntersectionCenter(rect, targetRect);\n alignLines.push({\n className: 'align-vertical',\n sourceNode: alignRect.sourceNodeId,\n top: Math.min(rect.bottom, prevRect.bottom),\n left: centerX,\n width: 1,\n height: spacing,\n });\n } else {\n const centerY = this.calcVerticalIntersectionCenter(rect, targetRect);\n alignLines.push({\n className: 'align-horizontal',\n sourceNode: alignRect.sourceNodeId,\n top: centerY,\n left: Math.min(rect.right, prevRect.right),\n width: spacing,\n height: 1,\n });\n }\n }\n return alignLines;\n }\n\n private calcVerticalIntersectionCenter(rectA: Rectangle, rectB: Rectangle): number {\n const top = Math.max(rectA.top, rectB.top);\n const bottom = Math.min(rectA.bottom, rectB.bottom);\n return (top + bottom) / 2;\n }\n\n private calcHorizontalIntersectionCenter(rectA: Rectangle, rectB: Rectangle): number {\n const left = Math.max(rectA.left, rectB.left);\n const right = Math.min(rectA.right, rectB.right);\n return (left + right) / 2;\n }\n}\n"],"mappings":"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","names":["inject","injectable","WorkflowDocument","FlowNodeTransformData","FlowNodeTransformData","inject","WorkflowDocument","injectable"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -2,6 +2,11 @@ import * as _flowgram_ai_core from '@flowgram.ai/core';
|
|
|
2
2
|
import * as _flowgram_ai_utils from '@flowgram.ai/utils';
|
|
3
3
|
import { Rectangle } from '@flowgram.ai/utils';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
*/
|
|
9
|
+
|
|
5
10
|
interface SnapLine {
|
|
6
11
|
x?: number;
|
|
7
12
|
y?: number;
|
|
@@ -78,8 +83,12 @@ declare class WorkflowSnapService {
|
|
|
78
83
|
private options;
|
|
79
84
|
private snapEmitter;
|
|
80
85
|
readonly onSnap: _flowgram_ai_utils.Event<SnapEvent>;
|
|
86
|
+
private _disabled;
|
|
81
87
|
init(params?: Partial<WorkflowSnapServiceOptions>): void;
|
|
82
88
|
dispose(): void;
|
|
89
|
+
get disabled(): boolean;
|
|
90
|
+
disable(): void;
|
|
91
|
+
enable(): void;
|
|
83
92
|
private mountListener;
|
|
84
93
|
private snapping;
|
|
85
94
|
private calcSnapOffset;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,11 @@ import * as _flowgram_ai_core from '@flowgram.ai/core';
|
|
|
2
2
|
import * as _flowgram_ai_utils from '@flowgram.ai/utils';
|
|
3
3
|
import { Rectangle } from '@flowgram.ai/utils';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
*/
|
|
9
|
+
|
|
5
10
|
interface SnapLine {
|
|
6
11
|
x?: number;
|
|
7
12
|
y?: number;
|
|
@@ -78,8 +83,12 @@ declare class WorkflowSnapService {
|
|
|
78
83
|
private options;
|
|
79
84
|
private snapEmitter;
|
|
80
85
|
readonly onSnap: _flowgram_ai_utils.Event<SnapEvent>;
|
|
86
|
+
private _disabled;
|
|
81
87
|
init(params?: Partial<WorkflowSnapServiceOptions>): void;
|
|
82
88
|
dispose(): void;
|
|
89
|
+
get disabled(): boolean;
|
|
90
|
+
disable(): void;
|
|
91
|
+
enable(): void;
|
|
83
92
|
private mountListener;
|
|
84
93
|
private snapping;
|
|
85
94
|
private calcSnapOffset;
|
package/dist/index.js
CHANGED
|
@@ -36,24 +36,24 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
// src/index.ts
|
|
39
|
-
var
|
|
40
|
-
__export(
|
|
39
|
+
var index_exports = {};
|
|
40
|
+
__export(index_exports, {
|
|
41
41
|
WorkflowSnapService: () => WorkflowSnapService,
|
|
42
42
|
createFreeSnapPlugin: () => createFreeSnapPlugin
|
|
43
43
|
});
|
|
44
|
-
module.exports = __toCommonJS(
|
|
44
|
+
module.exports = __toCommonJS(index_exports);
|
|
45
45
|
|
|
46
46
|
// src/create-plugin.ts
|
|
47
47
|
var import_core3 = require("@flowgram.ai/core");
|
|
48
48
|
|
|
49
49
|
// src/service.ts
|
|
50
50
|
var import_inversify = require("inversify");
|
|
51
|
+
var import_utils = require("@flowgram.ai/utils");
|
|
52
|
+
var import_free_layout_core = require("@flowgram.ai/free-layout-core");
|
|
53
|
+
var import_free_layout_core2 = require("@flowgram.ai/free-layout-core");
|
|
51
54
|
var import_document = require("@flowgram.ai/document");
|
|
52
55
|
var import_document2 = require("@flowgram.ai/document");
|
|
53
56
|
var import_core = require("@flowgram.ai/core");
|
|
54
|
-
var import_free_layout_core = require("@flowgram.ai/free-layout-core");
|
|
55
|
-
var import_free_layout_core2 = require("@flowgram.ai/free-layout-core");
|
|
56
|
-
var import_utils = require("@flowgram.ai/utils");
|
|
57
57
|
|
|
58
58
|
// src/constant.ts
|
|
59
59
|
var SnapDefaultOptions = {
|
|
@@ -61,7 +61,7 @@ var SnapDefaultOptions = {
|
|
|
61
61
|
edgeThreshold: 7,
|
|
62
62
|
enableGridSnapping: false,
|
|
63
63
|
gridSize: 20,
|
|
64
|
-
enableMultiSnapping:
|
|
64
|
+
enableMultiSnapping: true,
|
|
65
65
|
enableOnlyViewportSnapping: true,
|
|
66
66
|
edgeColor: "#4E40E5",
|
|
67
67
|
alignColor: "#4E40E5",
|
|
@@ -99,6 +99,7 @@ var WorkflowSnapService = class {
|
|
|
99
99
|
this.disposers = [];
|
|
100
100
|
this.snapEmitter = new import_utils.Emitter();
|
|
101
101
|
this.onSnap = this.snapEmitter.event;
|
|
102
|
+
this._disabled = false;
|
|
102
103
|
}
|
|
103
104
|
init(params = {}) {
|
|
104
105
|
this.options = {
|
|
@@ -110,15 +111,40 @@ var WorkflowSnapService = class {
|
|
|
110
111
|
dispose() {
|
|
111
112
|
this.disposers.forEach((disposer) => disposer.dispose());
|
|
112
113
|
}
|
|
114
|
+
get disabled() {
|
|
115
|
+
return this._disabled;
|
|
116
|
+
}
|
|
117
|
+
disable() {
|
|
118
|
+
if (this._disabled) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
this._disabled = true;
|
|
122
|
+
this.clear();
|
|
123
|
+
}
|
|
124
|
+
enable() {
|
|
125
|
+
if (!this._disabled) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
this._disabled = false;
|
|
129
|
+
this.clear();
|
|
130
|
+
}
|
|
113
131
|
mountListener() {
|
|
114
|
-
const dragAdjusterDisposer = this.dragService.registerPosAdjuster(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
132
|
+
const dragAdjusterDisposer = this.dragService.registerPosAdjuster((params) => {
|
|
133
|
+
const { selectedNodes: targetNodes, position } = params;
|
|
134
|
+
const isMultiSnapping = this.options.enableMultiSnapping ? false : targetNodes.length !== 1;
|
|
135
|
+
if (this._disabled || !this.options.enableEdgeSnapping || isMultiSnapping) {
|
|
136
|
+
return {
|
|
137
|
+
x: 0,
|
|
138
|
+
y: 0
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return this.snapping({
|
|
142
|
+
targetNodes,
|
|
143
|
+
position
|
|
144
|
+
});
|
|
145
|
+
});
|
|
120
146
|
const dragEndDisposer = this.dragService.onNodesDrag((event) => {
|
|
121
|
-
if (event.type !== "onDragEnd") {
|
|
147
|
+
if (event.type !== "onDragEnd" || this._disabled) {
|
|
122
148
|
return;
|
|
123
149
|
}
|
|
124
150
|
if (this.options.enableGridSnapping) {
|
|
@@ -135,19 +161,12 @@ var WorkflowSnapService = class {
|
|
|
135
161
|
}
|
|
136
162
|
snapping(params) {
|
|
137
163
|
const { targetNodes, position } = params;
|
|
138
|
-
const
|
|
139
|
-
if (!this.options.enableEdgeSnapping || isMultiSnapping) {
|
|
140
|
-
return {
|
|
141
|
-
x: 0,
|
|
142
|
-
y: 0
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
const selectedBounds = this.getBounds(targetNodes);
|
|
164
|
+
const targetBounds = this.getBounds(targetNodes);
|
|
146
165
|
const targetRect = new import_utils.Rectangle(
|
|
147
166
|
position.x,
|
|
148
167
|
position.y,
|
|
149
|
-
|
|
150
|
-
|
|
168
|
+
targetBounds.width,
|
|
169
|
+
targetBounds.height
|
|
151
170
|
);
|
|
152
171
|
const snapNodeRects = this.getSnapNodeRects({
|
|
153
172
|
targetNodes,
|
|
@@ -355,15 +374,10 @@ var WorkflowSnapService = class {
|
|
|
355
374
|
x: transform.position.x + offset.x,
|
|
356
375
|
y: transform.position.y + offset.y
|
|
357
376
|
};
|
|
358
|
-
if (node.collapsedChildren?.length > 0) {
|
|
359
|
-
node.collapsedChildren.forEach((childNode) => {
|
|
360
|
-
const childNodeTransformData = childNode.getData(import_document.FlowNodeTransformData);
|
|
361
|
-
childNodeTransformData.fireChange();
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
377
|
transform.update({
|
|
365
378
|
position: positionWithOffset
|
|
366
379
|
});
|
|
380
|
+
this.document.layout.updateAffectedTransform(node);
|
|
367
381
|
}
|
|
368
382
|
calcAlignOffset(params) {
|
|
369
383
|
const { snapNodeRects, targetRect, alignThreshold } = params;
|
|
@@ -614,10 +628,10 @@ WorkflowSnapService = __decorateClass([
|
|
|
614
628
|
// src/layer.tsx
|
|
615
629
|
var import_react = __toESM(require("react"));
|
|
616
630
|
var import_inversify2 = require("inversify");
|
|
631
|
+
var import_utils3 = require("@flowgram.ai/utils");
|
|
632
|
+
var import_free_layout_core3 = require("@flowgram.ai/free-layout-core");
|
|
617
633
|
var import_document3 = require("@flowgram.ai/document");
|
|
618
634
|
var import_core2 = require("@flowgram.ai/core");
|
|
619
|
-
var import_free_layout_core3 = require("@flowgram.ai/free-layout-core");
|
|
620
|
-
var import_utils3 = require("@flowgram.ai/utils");
|
|
621
635
|
var WorkflowSnapLayer = class extends import_core2.Layer {
|
|
622
636
|
constructor() {
|
|
623
637
|
super(...arguments);
|
|
@@ -655,7 +669,7 @@ var WorkflowSnapLayer = class extends import_core2.Layer {
|
|
|
655
669
|
className: `workflow-snap-edge-line ${className}`,
|
|
656
670
|
"data-testid": "sdk.workflow.canvas.snap.edgeLine",
|
|
657
671
|
"data-snap-line-id": id,
|
|
658
|
-
"data-snap-line-
|
|
672
|
+
"data-snap-line-source-node": sourceNode,
|
|
659
673
|
key: id,
|
|
660
674
|
style: {
|
|
661
675
|
top,
|
|
@@ -684,7 +698,7 @@ var WorkflowSnapLayer = class extends import_core2.Layer {
|
|
|
684
698
|
className: `workflow-snap-align-line ${renderLine.className}`,
|
|
685
699
|
"data-testid": "sdk.workflow.canvas.snap.alignLine",
|
|
686
700
|
"data-snap-line-id": id,
|
|
687
|
-
"data-snap-line-
|
|
701
|
+
"data-snap-line-source-node": renderLine.sourceNode,
|
|
688
702
|
key: id,
|
|
689
703
|
style: {
|
|
690
704
|
position: "absolute"
|
|
@@ -773,7 +787,7 @@ var WorkflowSnapLayer = class extends import_core2.Layer {
|
|
|
773
787
|
edgeLines.push({
|
|
774
788
|
className: "edge-full-top-right",
|
|
775
789
|
sourceNode: topFullAlign.sourceNodeId,
|
|
776
|
-
left: snapRect.right,
|
|
790
|
+
left: snapRect.right - 1,
|
|
777
791
|
...lineData
|
|
778
792
|
});
|
|
779
793
|
edgeLines.push({
|
|
@@ -802,7 +816,7 @@ var WorkflowSnapLayer = class extends import_core2.Layer {
|
|
|
802
816
|
edgeLines.push({
|
|
803
817
|
className: "edge-full-bottom-right",
|
|
804
818
|
sourceNode: bottomFullAlign.sourceNodeId,
|
|
805
|
-
left: snapRect.right,
|
|
819
|
+
left: snapRect.right - 1,
|
|
806
820
|
...lineData
|
|
807
821
|
});
|
|
808
822
|
edgeLines.push({
|
|
@@ -831,7 +845,7 @@ var WorkflowSnapLayer = class extends import_core2.Layer {
|
|
|
831
845
|
edgeLines.push({
|
|
832
846
|
className: "edge-full-left-bottom",
|
|
833
847
|
sourceNode: leftFullAlign.sourceNodeId,
|
|
834
|
-
top: snapRect.bottom,
|
|
848
|
+
top: snapRect.bottom - 1,
|
|
835
849
|
...lineData
|
|
836
850
|
});
|
|
837
851
|
edgeLines.push({
|
|
@@ -860,7 +874,7 @@ var WorkflowSnapLayer = class extends import_core2.Layer {
|
|
|
860
874
|
edgeLines.push({
|
|
861
875
|
className: "edge-full-right-bottom",
|
|
862
876
|
sourceNode: rightFullAlign.sourceNodeId,
|
|
863
|
-
top: snapRect.bottom,
|
|
877
|
+
top: snapRect.bottom - 1,
|
|
864
878
|
...lineData
|
|
865
879
|
});
|
|
866
880
|
edgeLines.push({
|
|
@@ -884,7 +898,7 @@ var WorkflowSnapLayer = class extends import_core2.Layer {
|
|
|
884
898
|
const top = Math.min(nodeRect.top, snapRect.top);
|
|
885
899
|
const bottom = Math.max(nodeRect.bottom, snapRect.bottom);
|
|
886
900
|
const height = bottom - top;
|
|
887
|
-
const left = snapLine.x;
|
|
901
|
+
const left = direction === "right" ? snapLine.x - 1 : snapLine.x;
|
|
888
902
|
const width = this.options.edgeLineWidth;
|
|
889
903
|
const isMidX = direction === "midVertical";
|
|
890
904
|
const lineData = {
|
|
@@ -908,7 +922,7 @@ var WorkflowSnapLayer = class extends import_core2.Layer {
|
|
|
908
922
|
const left = Math.min(nodeRect.left, snapRect.left);
|
|
909
923
|
const right = Math.max(nodeRect.right, snapRect.right);
|
|
910
924
|
const width = right - left;
|
|
911
|
-
const top = snapLine.y;
|
|
925
|
+
const top = direction === "bottom" ? snapLine.y - 1 : snapLine.y;
|
|
912
926
|
const height = this.options.edgeLineWidth;
|
|
913
927
|
const isMidY = direction === "midHorizontal";
|
|
914
928
|
const lineData = {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/create-plugin.ts","../src/service.ts","../src/constant.ts","../src/utils.ts","../src/layer.tsx"],"sourcesContent":["export { createFreeSnapPlugin } from './create-plugin';\nexport { WorkflowSnapService } from './service';\n","import { definePluginCreator } from '@flowgram.ai/core';\n\nimport {\n FreeSnapPluginOptions,\n WorkflowSnapLayerOptions,\n WorkflowSnapServiceOptions,\n} from './type';\nimport { WorkflowSnapService } from './service';\nimport { WorkflowSnapLayer } from './layer';\nimport { SnapDefaultOptions } from './constant';\n\nexport const createFreeSnapPlugin = definePluginCreator<FreeSnapPluginOptions>({\n onBind({ bind }) {\n bind(WorkflowSnapService).toSelf().inSingletonScope();\n },\n onInit(ctx, opts) {\n const options: WorkflowSnapServiceOptions & WorkflowSnapLayerOptions = {\n ...SnapDefaultOptions,\n ...opts,\n };\n ctx.playground.registerLayer(WorkflowSnapLayer, options);\n const snapService = ctx.get<WorkflowSnapService>(WorkflowSnapService);\n snapService.init(options);\n },\n onDispose(ctx) {\n const snapService = ctx.get<WorkflowSnapService>(WorkflowSnapService);\n snapService.dispose();\n },\n});\n","import { inject, injectable } from 'inversify';\nimport { FlowNodeTransformData } from '@flowgram.ai/document';\nimport { FlowNodeBaseType } from '@flowgram.ai/document';\nimport { EntityManager, PlaygroundConfigEntity, TransformData } from '@flowgram.ai/core';\nimport { WorkflowNodeEntity, WorkflowDocument } from '@flowgram.ai/free-layout-core';\nimport { WorkflowDragService } from '@flowgram.ai/free-layout-core';\nimport { Disposable, Emitter, Rectangle } from '@flowgram.ai/utils';\nimport { IPoint } from '@flowgram.ai/utils';\n\nimport { isEqual, isGreaterThan, isLessThan, isLessThanOrEqual, isNumber } from './utils';\nimport type {\n SnapEvent,\n SnapHorizontalLine,\n SnapLines,\n SnapMidHorizontalLine,\n SnapMidVerticalLine,\n SnapVerticalLine,\n WorkflowSnapServiceOptions,\n AlignRects,\n AlignRect,\n AlignSpacing,\n SnapNodeRect,\n SnapEdgeLines,\n} from './type';\nimport { SnapDefaultOptions } from './constant';\n\n@injectable()\nexport class WorkflowSnapService {\n @inject(WorkflowDocument) private readonly document: WorkflowDocument;\n\n @inject(EntityManager) private readonly entityManager: EntityManager;\n\n @inject(WorkflowDragService)\n private readonly dragService: WorkflowDragService;\n\n @inject(PlaygroundConfigEntity)\n private readonly playgroundConfig: PlaygroundConfigEntity;\n\n private disposers: Disposable[] = [];\n\n private options: WorkflowSnapServiceOptions;\n\n private snapEmitter = new Emitter<SnapEvent>();\n\n public readonly onSnap = this.snapEmitter.event;\n\n public init(params: Partial<WorkflowSnapServiceOptions> = {}): void {\n this.options = {\n ...SnapDefaultOptions,\n ...params,\n };\n this.mountListener();\n }\n\n public dispose(): void {\n this.disposers.forEach(disposer => disposer.dispose());\n }\n\n private mountListener(): void {\n const dragAdjusterDisposer = this.dragService.registerPosAdjuster(params =>\n this.snapping({\n targetNodes: params.selectedNodes,\n position: params.position,\n }),\n );\n const dragEndDisposer = this.dragService.onNodesDrag(event => {\n if (event.type !== 'onDragEnd') {\n return;\n }\n if (this.options.enableGridSnapping) {\n this.gridSnapping({\n targetNodes: event.nodes,\n gridSize: this.options.gridSize,\n });\n }\n if (this.options.enableEdgeSnapping) {\n this.clear();\n }\n });\n this.disposers.push(dragAdjusterDisposer, dragEndDisposer);\n }\n\n private snapping(params: { targetNodes: WorkflowNodeEntity[]; position: IPoint }): IPoint {\n const { targetNodes: targetNodes, position } = params;\n\n const isMultiSnapping = this.options.enableMultiSnapping ? false : targetNodes.length !== 1;\n\n if (!this.options.enableEdgeSnapping || isMultiSnapping) {\n return {\n x: 0,\n y: 0,\n };\n }\n\n const selectedBounds = this.getBounds(targetNodes);\n\n const targetRect = new Rectangle(\n position.x,\n position.y,\n selectedBounds.width,\n selectedBounds.height,\n );\n\n const snapNodeRects = this.getSnapNodeRects({\n targetNodes,\n targetRect,\n });\n\n const { alignOffset, alignRects, alignSpacing } = this.calcAlignOffset({\n targetRect,\n alignThreshold: this.options.edgeThreshold,\n snapNodeRects,\n });\n\n const { snapOffset, snapEdgeLines } = this.calcSnapOffset({\n targetRect,\n edgeThreshold: this.options.edgeThreshold,\n snapNodeRects,\n });\n\n const offset: IPoint = {\n x: snapOffset.x || alignOffset.x,\n y: snapOffset.y || alignOffset.y,\n };\n\n const snapRect = new Rectangle(\n position.x + offset.x,\n position.y + offset.y,\n targetRect.width,\n targetRect.height,\n );\n\n this.snapEmitter.fire({\n snapRect,\n snapEdgeLines,\n alignRects,\n alignSpacing,\n });\n\n return offset;\n }\n\n private calcSnapOffset(params: {\n snapNodeRects: SnapNodeRect[];\n targetRect: Rectangle;\n edgeThreshold: number;\n }): {\n snapOffset: IPoint;\n snapEdgeLines: SnapEdgeLines;\n } {\n const { snapNodeRects, edgeThreshold, targetRect } = params;\n\n const snapLines = this.getSnapLines({\n snapNodeRects,\n });\n\n // 找到最近的线条\n const topYClosestLine = snapLines.horizontal.find(line =>\n isLessThanOrEqual(Math.abs(line.y - targetRect.top), edgeThreshold),\n );\n const bottomYClosestLine = snapLines.horizontal.find(line =>\n isLessThanOrEqual(Math.abs(line.y - targetRect.bottom), edgeThreshold),\n );\n const leftXClosestLine = snapLines.vertical.find(line =>\n isLessThanOrEqual(Math.abs(line.x - targetRect.left), edgeThreshold),\n );\n const rightXClosestLine = snapLines.vertical.find(line =>\n isLessThanOrEqual(Math.abs(line.x - targetRect.right), edgeThreshold),\n );\n const midYClosestLine = snapLines.midHorizontal.find(line =>\n isLessThanOrEqual(Math.abs(line.y - targetRect.center.y), edgeThreshold),\n );\n const midXClosestLine = snapLines.midVertical.find(line =>\n isLessThanOrEqual(Math.abs(line.x - targetRect.center.x), edgeThreshold),\n );\n\n // 计算最近坐标\n const topYClosest = topYClosestLine?.y;\n const bottomYClosest = isNumber(bottomYClosestLine?.y)\n ? bottomYClosestLine!.y - targetRect.height\n : undefined;\n const leftXClosest = leftXClosestLine?.x;\n const rightXClosest = isNumber(rightXClosestLine?.x)\n ? rightXClosestLine!.x - targetRect.width\n : undefined;\n const midYClosest = isNumber(midYClosestLine?.y)\n ? midYClosestLine!.y - targetRect.height / 2\n : undefined;\n const midXClosest = isNumber(midXClosestLine?.x)\n ? midXClosestLine!.x - targetRect.width / 2\n : undefined;\n\n // 吸附后坐标,按优先级取值\n const snappingPosition = {\n x: midXClosest ?? leftXClosest ?? rightXClosest ?? targetRect.x,\n y: midYClosest ?? topYClosest ?? bottomYClosest ?? targetRect.y,\n };\n\n // 吸附修正偏移量\n const snapOffset: IPoint = {\n x: snappingPosition.x - targetRect.x,\n y: snappingPosition.y - targetRect.y,\n };\n\n // 生效的吸附线条\n const snapEdgeLines: SnapEdgeLines = {\n top: isEqual(topYClosest, snappingPosition.y) ? topYClosestLine : undefined,\n bottom: isEqual(bottomYClosest, snappingPosition.y) ? bottomYClosestLine : undefined,\n left: isEqual(leftXClosest, snappingPosition.x) ? leftXClosestLine : undefined,\n right: isEqual(rightXClosest, snappingPosition.x) ? rightXClosestLine : undefined,\n midVertical: isEqual(midXClosest, snappingPosition.x) ? midXClosestLine : undefined,\n midHorizontal: isEqual(midYClosest, snappingPosition.y) ? midYClosestLine : undefined,\n };\n\n return { snapOffset, snapEdgeLines };\n }\n\n private gridSnapping(params: { gridSize: number; targetNodes: WorkflowNodeEntity[] }): void {\n const { gridSize, targetNodes } = params;\n const rect = this.getBounds(targetNodes);\n const snap = (value: number) => Math.round(value / gridSize) * gridSize;\n const snappedPosition: IPoint = {\n x: snap(rect.x),\n y: snap(rect.y),\n };\n const offset: IPoint = {\n x: snappedPosition.x - rect.x,\n y: snappedPosition.y - rect.y,\n };\n targetNodes.forEach(node =>\n this.updateNodePositionWithOffset({\n node,\n offset,\n }),\n );\n }\n\n private clear() {\n this.snapEmitter.fire({\n snapEdgeLines: {},\n snapRect: Rectangle.EMPTY,\n alignRects: {\n top: [],\n bottom: [],\n left: [],\n right: [],\n },\n alignSpacing: {},\n });\n }\n\n private getSnapLines(params: { snapNodeRects: SnapNodeRect[] }): SnapLines {\n const { snapNodeRects } = params;\n const horizontalLines: SnapHorizontalLine[] = [];\n const verticalLines: SnapVerticalLine[] = [];\n const midHorizontalLines: SnapMidHorizontalLine[] = [];\n const midVerticalLines: SnapMidVerticalLine[] = [];\n\n snapNodeRects.forEach(snapNodeRect => {\n const nodeBounds = snapNodeRect.rect;\n const nodeCenter = nodeBounds.center;\n // 边缘横线\n const top: SnapHorizontalLine = {\n y: nodeBounds.top,\n sourceNodeId: snapNodeRect.id,\n };\n const bottom: SnapHorizontalLine = {\n y: nodeBounds.bottom,\n sourceNodeId: snapNodeRect.id,\n };\n // 边缘竖线\n const left: SnapVerticalLine = {\n x: nodeBounds.left,\n sourceNodeId: snapNodeRect.id,\n };\n const right: SnapVerticalLine = {\n x: nodeBounds.right,\n sourceNodeId: snapNodeRect.id,\n };\n // 中间横线\n const midHorizontal: SnapMidHorizontalLine = {\n y: nodeCenter.y,\n sourceNodeId: snapNodeRect.id,\n };\n // 中间竖线\n const midVertical: SnapMidVerticalLine = {\n x: nodeCenter.x,\n sourceNodeId: snapNodeRect.id,\n };\n horizontalLines.push(top, bottom);\n verticalLines.push(left, right);\n midHorizontalLines.push(midHorizontal);\n midVerticalLines.push(midVertical);\n });\n\n return {\n horizontal: horizontalLines,\n vertical: verticalLines,\n midHorizontal: midHorizontalLines,\n midVertical: midVerticalLines,\n };\n }\n\n private getAvailableNodes(params: {\n targetNodes: WorkflowNodeEntity[];\n targetRect: Rectangle;\n }): WorkflowNodeEntity[] {\n const { targetNodes, targetRect } = params;\n\n const targetCenter = targetRect.center;\n const targetContainerId = targetNodes[0].parent?.id ?? this.document.root.id;\n\n const disabledNodeIds = targetNodes.map(n => n.id);\n disabledNodeIds.push(FlowNodeBaseType.ROOT);\n const availableNodes = this.nodes\n .filter(n => n.parent?.id === targetContainerId)\n .filter(n => !disabledNodeIds.includes(n.id))\n .sort((nodeA, nodeB) => {\n const nodeCenterA = nodeA.getData(FlowNodeTransformData)!.bounds.center;\n const nodeCenterB = nodeB.getData(FlowNodeTransformData)!.bounds.center;\n // 距离越近优先级越高\n const distanceA =\n Math.abs(nodeCenterA.x - targetCenter.x) + Math.abs(nodeCenterA.y - targetCenter.y);\n const distanceB =\n Math.abs(nodeCenterB.x - targetCenter.x) + Math.abs(nodeCenterB.y - targetCenter.y);\n return distanceA - distanceB;\n });\n return availableNodes;\n }\n\n private viewRect(): Rectangle {\n const { width, height, scrollX, scrollY, zoom } = this.playgroundConfig.config;\n return new Rectangle(scrollX / zoom, scrollY / zoom, width / zoom, height / zoom);\n }\n\n private getSnapNodeRects(params: {\n targetNodes: WorkflowNodeEntity[];\n targetRect: Rectangle;\n }): SnapNodeRect[] {\n const availableNodes = this.getAvailableNodes(params);\n const viewRect = this.viewRect();\n return availableNodes\n .map(node => {\n const snapNodeRect: SnapNodeRect = {\n id: node.id,\n rect: node.getData(FlowNodeTransformData).bounds,\n entity: node,\n };\n if (\n this.options.enableOnlyViewportSnapping &&\n node.parent?.flowNodeType === FlowNodeBaseType.ROOT &&\n !Rectangle.intersects(viewRect, snapNodeRect.rect)\n ) {\n // 最外层节点仅包含当前可见节点\n return;\n }\n return snapNodeRect;\n })\n .filter(Boolean) as SnapNodeRect[];\n }\n\n private get nodes(): WorkflowNodeEntity[] {\n return this.entityManager.getEntities<WorkflowNodeEntity>(WorkflowNodeEntity);\n }\n\n private getBounds(nodes: WorkflowNodeEntity[]): Rectangle {\n if (nodes.length === 0) {\n return Rectangle.EMPTY;\n }\n return Rectangle.enlarge(nodes.map(n => n.getData(FlowNodeTransformData)!.bounds));\n }\n\n private updateNodePositionWithOffset(params: { node: WorkflowNodeEntity; offset: IPoint }): void {\n const { node, offset } = params;\n const transform = node.getData(TransformData);\n const positionWithOffset: IPoint = {\n x: transform.position.x + offset.x,\n y: transform.position.y + offset.y,\n };\n if (node.collapsedChildren?.length > 0) {\n // 嵌套情况下需将子节点 transform 设为 dirty\n node.collapsedChildren.forEach(childNode => {\n const childNodeTransformData =\n childNode.getData<FlowNodeTransformData>(FlowNodeTransformData);\n childNodeTransformData.fireChange();\n });\n }\n transform.update({\n position: positionWithOffset,\n });\n }\n\n private calcAlignOffset(params: {\n snapNodeRects: SnapNodeRect[];\n targetRect: Rectangle;\n alignThreshold: number;\n }): {\n alignOffset: IPoint;\n alignRects: AlignRects;\n alignSpacing: AlignSpacing;\n } {\n const { snapNodeRects, targetRect, alignThreshold } = params;\n\n const alignRects = this.getAlignRects({\n targetRect,\n snapNodeRects,\n });\n\n const alignSpacing = this.calcAlignSpacing({\n targetRect,\n alignRects,\n });\n\n let topY: number | undefined;\n let bottomY: number | undefined;\n let leftX: number | undefined;\n let rightX: number | undefined;\n let midY: number | undefined;\n let midX: number | undefined;\n\n if (alignSpacing.top) {\n const topAlignY = alignRects.top[0].rect.bottom + alignSpacing.top;\n const isAlignTop = isLessThanOrEqual(Math.abs(targetRect.top - topAlignY), alignThreshold);\n if (isAlignTop) {\n // 生效\n topY = topAlignY;\n } else {\n // 失效\n alignSpacing.top = undefined;\n }\n }\n if (alignSpacing.bottom) {\n const bottomAlignY = alignRects.bottom[0].rect.top - alignSpacing.bottom;\n const isAlignBottom = isLessThan(Math.abs(targetRect.bottom - bottomAlignY), alignThreshold);\n if (isAlignBottom) {\n bottomY = bottomAlignY - targetRect.height;\n } else {\n alignSpacing.bottom = undefined;\n }\n }\n if (alignSpacing.left) {\n const leftAlignX = alignRects.left[0].rect.right + alignSpacing.left;\n const isAlignLeft = isLessThanOrEqual(Math.abs(targetRect.left - leftAlignX), alignThreshold);\n if (isAlignLeft) {\n leftX = leftAlignX;\n } else {\n alignSpacing.left = undefined;\n }\n }\n if (alignSpacing.right) {\n const rightAlignX = alignRects.right[0].rect.left - alignSpacing.right;\n const isAlignRight = isLessThanOrEqual(\n Math.abs(targetRect.right - rightAlignX),\n alignThreshold,\n );\n if (isAlignRight) {\n rightX = rightAlignX - targetRect.width;\n } else {\n alignSpacing.right = undefined;\n }\n }\n if (alignSpacing.midHorizontal) {\n const leftAlignX = alignRects.left[0].rect.right + alignSpacing.midHorizontal;\n const isAlignMidHorizontal = isLessThanOrEqual(\n Math.abs(targetRect.left - leftAlignX),\n alignThreshold,\n );\n if (isAlignMidHorizontal) {\n midX = leftAlignX;\n } else {\n alignSpacing.midHorizontal = undefined;\n }\n }\n if (alignSpacing.midVertical) {\n const topAlignY = alignRects.top[0].rect.bottom + alignSpacing.midVertical;\n const isAlignMidVertical = isLessThanOrEqual(\n Math.abs(targetRect.top - topAlignY),\n alignThreshold,\n );\n if (isAlignMidVertical) {\n midY = topAlignY;\n } else {\n alignSpacing.midVertical = undefined;\n }\n }\n\n const alignPosition: IPoint = {\n x: midX ?? leftX ?? rightX ?? targetRect.x,\n y: midY ?? topY ?? bottomY ?? targetRect.y,\n };\n\n const alignOffset: IPoint = {\n x: alignPosition.x - targetRect.x,\n y: alignPosition.y - targetRect.y,\n };\n\n return { alignOffset, alignRects, alignSpacing };\n }\n\n private calcAlignSpacing(params: {\n targetRect: Rectangle;\n alignRects: AlignRects;\n }): AlignSpacing {\n const { targetRect, alignRects } = params;\n\n const topSpacing = this.getDirectionAlignSpacing({\n rects: alignRects.top,\n isHorizontal: false,\n });\n const bottomSpacing = this.getDirectionAlignSpacing({\n rects: alignRects.bottom,\n isHorizontal: false,\n });\n const leftSpacing = this.getDirectionAlignSpacing({\n rects: alignRects.left,\n isHorizontal: true,\n });\n const rightSpacing = this.getDirectionAlignSpacing({\n rects: alignRects.right,\n isHorizontal: true,\n });\n const midHorizontalSpacing = this.getMidAlignSpacing({\n rectA: alignRects.left[0]?.rect,\n rectB: alignRects.right[0]?.rect,\n targetRect,\n isHorizontal: true,\n });\n const midVerticalSpacing = this.getMidAlignSpacing({\n rectA: alignRects.top[0]?.rect,\n rectB: alignRects.bottom[0]?.rect,\n targetRect,\n isHorizontal: false,\n });\n return {\n top: topSpacing,\n bottom: bottomSpacing,\n left: leftSpacing,\n right: rightSpacing,\n midHorizontal: midHorizontalSpacing,\n midVertical: midVerticalSpacing,\n };\n }\n\n private getAlignRects(params: {\n targetRect: Rectangle;\n snapNodeRects: SnapNodeRect[];\n }): AlignRects {\n const { targetRect, snapNodeRects } = params;\n\n const topVerticalRects: AlignRect[] = [];\n const bottomVerticalRects: AlignRect[] = [];\n const leftHorizontalRects: AlignRect[] = [];\n const rightHorizontalRects: AlignRect[] = [];\n\n snapNodeRects.forEach(snapNodeRect => {\n const nodeRect = snapNodeRect.rect;\n const { isVerticalIntersection, isHorizontalIntersection, isIntersection } =\n this.intersection(nodeRect, targetRect);\n if (isIntersection) {\n // 忽略重叠的节点\n return;\n } else if (isVerticalIntersection) {\n // 垂直重合\n if (isGreaterThan(nodeRect.center.y, targetRect.center.y)) {\n // 下方\n bottomVerticalRects.push({\n rect: nodeRect,\n sourceNodeId: snapNodeRect.id,\n });\n } else {\n // 上方\n topVerticalRects.push({\n rect: nodeRect,\n sourceNodeId: snapNodeRect.id,\n });\n }\n } else if (isHorizontalIntersection) {\n // 水平重合\n if (isGreaterThan(nodeRect.center.x, targetRect.center.x)) {\n // 右方\n rightHorizontalRects.push({\n rect: nodeRect,\n sourceNodeId: snapNodeRect.id,\n });\n } else {\n // 左方\n leftHorizontalRects.push({\n rect: nodeRect,\n sourceNodeId: snapNodeRect.id,\n });\n }\n }\n });\n\n return {\n top: topVerticalRects,\n bottom: bottomVerticalRects,\n left: leftHorizontalRects,\n right: rightHorizontalRects,\n };\n }\n\n private getMidAlignSpacing(params: {\n rectA?: Rectangle;\n rectB?: Rectangle;\n targetRect: Rectangle;\n isHorizontal: boolean;\n }): number | undefined {\n const { rectA, rectB, targetRect, isHorizontal } = params;\n if (!rectA || !rectB) {\n return;\n }\n const { isVerticalIntersection, isHorizontalIntersection, isIntersection } = this.intersection(\n rectA,\n rectB,\n );\n if (isIntersection) {\n return;\n }\n if (isHorizontal && isHorizontalIntersection && !isVerticalIntersection) {\n const betweenSpacing = Math.min(\n Math.abs(rectA.left - rectB.right),\n Math.abs(rectA.right - rectB.left),\n );\n return (betweenSpacing - targetRect.width) / 2;\n } else if (!isHorizontal && isVerticalIntersection && !isHorizontalIntersection) {\n const betweenSpacing = Math.min(\n Math.abs(rectA.top - rectB.bottom),\n Math.abs(rectA.bottom - rectB.top),\n );\n return (betweenSpacing - targetRect.height) / 2;\n }\n }\n\n private getDirectionAlignSpacing(params: {\n rects: AlignRect[];\n isHorizontal: boolean;\n }): number | undefined {\n const { rects, isHorizontal } = params;\n if (rects.length < 2) {\n // 非法情况\n return;\n }\n const rectA = rects[0].rect;\n const rectB = rects[1].rect;\n\n const { isVerticalIntersection, isHorizontalIntersection, isIntersection } = this.intersection(\n rectA,\n rectB,\n );\n\n if (isIntersection) {\n // 非法情况:重叠\n return;\n }\n if (isHorizontal && isHorizontalIntersection && !isVerticalIntersection) {\n return Math.min(Math.abs(rectA.left - rectB.right), Math.abs(rectA.right - rectB.left));\n } else if (!isHorizontal && isVerticalIntersection && !isHorizontalIntersection) {\n return Math.min(Math.abs(rectA.top - rectB.bottom), Math.abs(rectA.bottom - rectB.top));\n }\n return;\n }\n\n private intersection(\n rectA: Rectangle,\n rectB: Rectangle,\n ): {\n isHorizontalIntersection: boolean;\n isVerticalIntersection: boolean;\n isIntersection: boolean;\n } {\n const isVerticalIntersection =\n isLessThan(rectA.left, rectB.right) && isGreaterThan(rectA.right, rectB.left);\n const isHorizontalIntersection =\n isLessThan(rectA.top, rectB.bottom) && isGreaterThan(rectA.bottom, rectB.top);\n const isIntersection = isHorizontalIntersection && isVerticalIntersection;\n\n return {\n isHorizontalIntersection,\n isVerticalIntersection,\n isIntersection,\n };\n }\n}\n","import type { WorkflowSnapLayerOptions, WorkflowSnapServiceOptions } from './type';\n\nexport const SnapDefaultOptions: WorkflowSnapServiceOptions & WorkflowSnapLayerOptions = {\n enableEdgeSnapping: true,\n edgeThreshold: 7,\n enableGridSnapping: false,\n gridSize: 20,\n enableMultiSnapping: false,\n enableOnlyViewportSnapping: true,\n edgeColor: '#4E40E5',\n alignColor: '#4E40E5',\n edgeLineWidth: 2,\n alignLineWidth: 2,\n alignCrossWidth: 16,\n};\n\nexport const Epsilon = 0.00001;\n","import { Epsilon } from './constant';\n\n/** 检查浮点数 a 是否等于 b */\nexport const isEqual = (a: number | undefined, b: number | undefined): boolean => {\n if (a === undefined || b === undefined) {\n return false;\n }\n // 检查 a 和 b 的差的绝对值是否小于 Epsilon\n return Math.abs(a - b) < Epsilon;\n};\n\n/** 检查浮点数 a 是否小于 b */\nexport const isLessThan = (a: number | undefined, b: number | undefined): boolean => {\n if (a === undefined || b === undefined) {\n return false;\n }\n // 检查 a 是否显著小于 b\n return b - a > Epsilon;\n};\n\n/** 检查浮点数 a 是否大于 b */\nexport const isGreaterThan = (a: number | undefined, b: number | undefined): boolean => {\n if (a === undefined || b === undefined) {\n return false;\n }\n return a - b > Epsilon;\n};\n\n/** 检查浮点数 a 是否小于等于 b */\nexport const isLessThanOrEqual = (a: number | undefined, b: number | undefined): boolean =>\n isEqual(a, b) || isLessThan(a, b);\n\n/** 检查浮点数 a 是否大于等于 b */\nexport const isGreaterThanOrEqual = (a: number | undefined, b: number | undefined): boolean =>\n isEqual(a, b) || isGreaterThan(a, b);\n\n/** 检查值是否是数字类型 */\nexport const isNumber = (value: unknown): value is number =>\n typeof value === 'number' && !isNaN(value);\n","import React from 'react';\n\nimport { inject, injectable } from 'inversify';\nimport { FlowNodeTransformData } from '@flowgram.ai/document';\nimport { Layer } from '@flowgram.ai/core';\nimport { WorkflowDocument } from '@flowgram.ai/free-layout-core';\nimport { domUtils } from '@flowgram.ai/utils';\nimport { Rectangle } from '@flowgram.ai/utils';\n\nimport { isEqual, isGreaterThan, isNumber } from './utils';\nimport { AlignRect, SnapEvent, WorkflowSnapLayerOptions } from './type';\nimport { WorkflowSnapService } from './service';\n\ninterface SnapRenderLine {\n className: string;\n sourceNode: string;\n top: number;\n left: number;\n width: number;\n height: number;\n dashed?: boolean;\n}\n\n@injectable()\nexport class WorkflowSnapLayer extends Layer<WorkflowSnapLayerOptions> {\n public static type = 'WorkflowSnapLayer';\n\n @inject(WorkflowDocument) private readonly document: WorkflowDocument;\n\n @inject(WorkflowSnapService) private readonly service: WorkflowSnapService;\n\n public readonly node = domUtils.createDivWithClass(\n 'gedit-playground-layer gedit-flow-snap-layer',\n );\n\n private edgeLines: SnapRenderLine[] = [];\n\n private alignLines: SnapRenderLine[] = [];\n\n public onReady(): void {\n this.node.style.zIndex = '9999';\n this.toDispose.pushAll([\n this.service.onSnap((event: SnapEvent) => {\n this.edgeLines = this.calcEdgeLines(event);\n this.alignLines = this.calcAlignLines(event);\n this.render();\n }),\n ]);\n }\n\n public render(): JSX.Element {\n return (\n <>\n {this.alignLines.length > 0 && (\n <div className=\"workflow-snap-align-lines\">{this.renderAlignLines()}</div>\n )}\n {this.edgeLines.length > 0 && (\n <div className=\"workflow-snap-edge-lines\">{this.renderEdgeLines()}</div>\n )}\n </>\n );\n }\n\n public onZoom(scale: number): void {\n this.node.style.transform = `scale(${scale})`;\n }\n\n private renderEdgeLines(): JSX.Element[] {\n return this.edgeLines.map((renderLine: SnapRenderLine) => {\n const { className, sourceNode, top, left, width, height, dashed } = renderLine;\n const id = `${className}-${sourceNode}-${top}-${left}-${width}-${height}`;\n const isHorizontal = width < height;\n const border = `${this.options.edgeLineWidth}px ${dashed ? 'dashed' : 'solid'} ${\n this.options.edgeColor\n }`;\n return (\n <div\n className={`workflow-snap-edge-line ${className}`}\n data-testid=\"sdk.workflow.canvas.snap.edgeLine\"\n data-snap-line-id={id}\n data-snap-line-sourceNode={sourceNode}\n key={id}\n style={{\n top,\n left,\n width,\n height,\n position: 'absolute',\n borderLeft: isHorizontal ? border : 'none',\n borderTop: !isHorizontal ? border : 'none',\n }}\n />\n );\n });\n }\n\n private renderAlignLines(): JSX.Element[] {\n return this.alignLines.map((renderLine: SnapRenderLine) => {\n const id = `${renderLine.className}-${renderLine.sourceNode}-${renderLine.top}-${renderLine.left}-${renderLine.width}-${renderLine.height}`;\n const isHorizontal = isGreaterThan(renderLine.width, renderLine.height);\n const alignLineWidth = this.options.alignLineWidth; // 整体线条粗细\n const alignCrossWidth = this.options.alignCrossWidth; // 工字形横线的长度\n\n // 调整渲染位置以保持居中\n const adjustedTop = isHorizontal ? renderLine.top - alignLineWidth / 2 : renderLine.top;\n const adjustedLeft = isHorizontal ? renderLine.left : renderLine.left - alignLineWidth / 2;\n\n return (\n <div\n className={`workflow-snap-align-line ${renderLine.className}`}\n data-testid=\"sdk.workflow.canvas.snap.alignLine\"\n data-snap-line-id={id}\n data-snap-line-sourceNode={renderLine.sourceNode}\n key={id}\n style={{\n position: 'absolute',\n }}\n >\n {/* 主线 */}\n <div\n style={{\n position: 'absolute',\n top: adjustedTop,\n left: adjustedLeft,\n width: isHorizontal ? renderLine.width : alignLineWidth,\n height: isHorizontal ? alignLineWidth : renderLine.height,\n backgroundColor: this.options.alignColor,\n }}\n />\n {/* 左端或上端横线 */}\n <div\n style={{\n position: 'absolute',\n top: isHorizontal\n ? adjustedTop - (alignCrossWidth - alignLineWidth) / 2\n : adjustedTop,\n left: isHorizontal\n ? adjustedLeft\n : adjustedLeft - (alignCrossWidth - alignLineWidth) / 2,\n width: isHorizontal ? alignLineWidth : alignCrossWidth,\n height: isHorizontal ? alignCrossWidth : alignLineWidth,\n backgroundColor: this.options.alignColor,\n }}\n />\n {/* 右端或下端横线 */}\n <div\n style={{\n position: 'absolute',\n top: isHorizontal\n ? adjustedTop - (alignCrossWidth - alignLineWidth) / 2\n : adjustedTop + renderLine.height - alignLineWidth,\n left: isHorizontal\n ? adjustedLeft + renderLine.width - alignLineWidth\n : adjustedLeft - (alignCrossWidth - alignLineWidth) / 2,\n width: isHorizontal ? alignLineWidth : alignCrossWidth,\n height: isHorizontal ? alignCrossWidth : alignLineWidth,\n backgroundColor: this.options.alignColor,\n }}\n />\n </div>\n );\n });\n }\n\n private calcEdgeLines(event: SnapEvent): SnapRenderLine[] {\n const { alignRects, snapRect, snapEdgeLines } = event;\n const edgeLines: SnapRenderLine[] = [];\n\n const topFullAlign = this.directionFullAlign({\n alignRects: alignRects.top,\n targetRect: snapRect,\n isVertical: true,\n });\n const bottomFullAlign = this.directionFullAlign({\n alignRects: alignRects.bottom,\n targetRect: snapRect,\n isVertical: true,\n });\n const leftFullAlign = this.directionFullAlign({\n alignRects: alignRects.left,\n targetRect: snapRect,\n isVertical: false,\n });\n const rightFullAlign = this.directionFullAlign({\n alignRects: alignRects.right,\n targetRect: snapRect,\n isVertical: false,\n });\n\n // 处理顶部对齐\n if (topFullAlign) {\n const top = topFullAlign.rect.top;\n const height = bottomFullAlign\n ? snapRect.bottom - snapRect.height / 2 - top\n : snapRect.bottom - top;\n const width = this.options.edgeLineWidth;\n const lineData = {\n top,\n width,\n height,\n };\n edgeLines.push({\n className: 'edge-full-top-left',\n sourceNode: topFullAlign.sourceNodeId,\n left: snapRect.left,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-top-right',\n sourceNode: topFullAlign.sourceNodeId,\n left: snapRect.right,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-top-mid',\n sourceNode: topFullAlign.sourceNodeId,\n left: snapRect.left + snapRect.width / 2,\n dashed: true,\n ...lineData,\n });\n }\n\n // 处理底部对齐\n if (bottomFullAlign) {\n const top = topFullAlign ? snapRect.top + snapRect.height / 2 : snapRect.top;\n const height = bottomFullAlign.rect.bottom - top;\n const width = this.options.edgeLineWidth;\n const lineData = {\n top,\n width,\n height,\n };\n edgeLines.push({\n className: 'edge-full-bottom-left',\n sourceNode: bottomFullAlign.sourceNodeId,\n left: snapRect.left,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-bottom-right',\n sourceNode: bottomFullAlign.sourceNodeId,\n left: snapRect.right,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-bottom-mid',\n sourceNode: bottomFullAlign.sourceNodeId,\n left: snapRect.left + snapRect.width / 2,\n dashed: true,\n ...lineData,\n });\n }\n\n // 处理左侧对齐\n if (leftFullAlign) {\n const left = leftFullAlign.rect.left;\n const width = rightFullAlign\n ? snapRect.right - snapRect.width / 2 - left\n : snapRect.right - left;\n const height = this.options.edgeLineWidth;\n const lineData = {\n left,\n width,\n height,\n };\n edgeLines.push({\n className: 'edge-full-left-top',\n sourceNode: leftFullAlign.sourceNodeId,\n top: snapRect.top,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-left-bottom',\n sourceNode: leftFullAlign.sourceNodeId,\n top: snapRect.bottom,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-left-mid',\n sourceNode: leftFullAlign.sourceNodeId,\n top: snapRect.top + snapRect.height / 2,\n dashed: true,\n ...lineData,\n });\n }\n\n // 处理右侧对齐\n if (rightFullAlign) {\n const left = leftFullAlign ? snapRect.left + snapRect.width / 2 : snapRect.left;\n const width = rightFullAlign.rect.right - left;\n const height = this.options.edgeLineWidth;\n const lineData = {\n left,\n width,\n height,\n };\n edgeLines.push({\n className: 'edge-full-right-top',\n sourceNode: rightFullAlign.sourceNodeId,\n top: snapRect.top,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-right-bottom',\n sourceNode: rightFullAlign.sourceNodeId,\n top: snapRect.bottom,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-right-mid',\n sourceNode: rightFullAlign.sourceNodeId,\n top: snapRect.top + snapRect.height / 2,\n dashed: true,\n ...lineData,\n });\n }\n\n const snappedEdgeLines = Object.entries(snapEdgeLines)\n .map(([direction, snapLine]) => {\n if (!snapLine) {\n return;\n }\n const sourceNode = this.document.getNode(snapLine.sourceNodeId);\n if (!sourceNode) {\n return;\n }\n const nodeRect = sourceNode.getData(FlowNodeTransformData).bounds;\n if (isNumber(snapLine.x)) {\n // 垂直\n const top = Math.min(nodeRect.top, snapRect.top);\n const bottom = Math.max(nodeRect.bottom, snapRect.bottom);\n const height = bottom - top;\n const left = snapLine.x;\n const width = this.options.edgeLineWidth;\n const isMidX = direction === 'midVertical';\n const lineData: SnapRenderLine = {\n className: `edge-snapped-${direction}`,\n sourceNode: snapLine.sourceNodeId,\n top,\n left,\n width,\n height,\n dashed: isMidX,\n };\n const onTop = top === nodeRect.top;\n if (onTop && topFullAlign) {\n return;\n }\n if (!onTop && bottomFullAlign) {\n return;\n }\n return lineData;\n } else if (isNumber(snapLine.y)) {\n // 水平\n const left = Math.min(nodeRect.left, snapRect.left);\n const right = Math.max(nodeRect.right, snapRect.right);\n const width = right - left;\n const top = snapLine.y;\n const height = this.options.edgeLineWidth;\n const isMidY = direction === 'midHorizontal';\n const lineData: SnapRenderLine = {\n className: `edge-snapped-${direction}`,\n sourceNode: snapLine.sourceNodeId,\n top,\n left,\n width,\n height,\n dashed: isMidY,\n };\n const onLeft = left === nodeRect.left;\n if (onLeft && leftFullAlign) {\n return;\n }\n if (!onLeft && rightFullAlign) {\n return;\n }\n return lineData;\n }\n })\n .filter(Boolean) as SnapRenderLine[];\n\n edgeLines.push(...snappedEdgeLines);\n\n return edgeLines;\n }\n\n private directionFullAlign(params: {\n alignRects: AlignRect[];\n targetRect: Rectangle;\n isVertical: boolean;\n }): AlignRect | undefined {\n const { alignRects, targetRect, isVertical } = params;\n let fullAlignIndex = -1;\n for (let i = 0; i < alignRects.length; i++) {\n const alignRect = alignRects[i];\n const prevRect = alignRects[i - 1]?.rect ?? targetRect;\n const isFullAlign = this.rectFullAlign(alignRect.rect, prevRect, isVertical);\n if (!isFullAlign) {\n // 未对齐则中断\n break; // 用 for 循环 + break 反而比 Array.findIndex 实现可读性更好\n }\n fullAlignIndex = i;\n }\n const fullAlignRect = alignRects[fullAlignIndex];\n return fullAlignRect;\n }\n\n private rectFullAlign(rectA: Rectangle, rectB: Rectangle, isVertical: boolean): boolean {\n if (isVertical) {\n return isEqual(rectA.left, rectB.left) && isEqual(rectA.right, rectB.right);\n } else {\n return isEqual(rectA.top, rectB.top) && isEqual(rectA.bottom, rectB.bottom);\n }\n }\n\n private calcAlignLines(event: SnapEvent): SnapRenderLine[] {\n const { alignRects, alignSpacing, snapRect } = event;\n\n const topAlignLines = this.calcDirectionAlignLines({\n alignRects: alignRects.top,\n targetRect: snapRect,\n isVertical: true,\n spacing: alignSpacing.midVertical ?? alignSpacing.top,\n });\n\n const bottomAlignLines = this.calcDirectionAlignLines({\n alignRects: alignRects.bottom,\n targetRect: snapRect,\n isVertical: true,\n spacing: alignSpacing.midVertical ?? alignSpacing.bottom,\n });\n\n const leftAlignLines = this.calcDirectionAlignLines({\n alignRects: alignRects.left,\n targetRect: snapRect,\n isVertical: false,\n spacing: alignSpacing.midHorizontal ?? alignSpacing.left,\n });\n\n const rightAlignLines = this.calcDirectionAlignLines({\n alignRects: alignRects.right,\n targetRect: snapRect,\n isVertical: false,\n spacing: alignSpacing.midHorizontal ?? alignSpacing.right,\n });\n\n return [...topAlignLines, ...bottomAlignLines, ...leftAlignLines, ...rightAlignLines];\n }\n\n private calcDirectionAlignLines(params: {\n alignRects: AlignRect[];\n targetRect: Rectangle;\n isVertical: boolean;\n spacing?: number;\n }) {\n const { alignRects, targetRect, isVertical, spacing } = params;\n const alignLines: SnapRenderLine[] = [];\n if (!spacing) {\n return alignLines;\n }\n for (let i = 0; i < alignRects.length; i++) {\n const alignRect = alignRects[i];\n const rect = alignRect.rect;\n const prevRect = alignRects[i - 1]?.rect ?? targetRect;\n\n const betweenSpacing = isVertical\n ? Math.min(Math.abs(prevRect.top - rect.bottom), Math.abs(prevRect.bottom - rect.top))\n : Math.min(Math.abs(prevRect.left - rect.right), Math.abs(prevRect.right - rect.left));\n if (!isEqual(betweenSpacing, spacing)) {\n // 不连续,需要中断\n break; // 因为要用到 break,所以不能用 Array.map()\n }\n if (isVertical) {\n const centerX = this.calcHorizontalIntersectionCenter(rect, targetRect);\n alignLines.push({\n className: 'align-vertical',\n sourceNode: alignRect.sourceNodeId,\n top: Math.min(rect.bottom, prevRect.bottom),\n left: centerX,\n width: 1,\n height: spacing,\n });\n } else {\n const centerY = this.calcVerticalIntersectionCenter(rect, targetRect);\n alignLines.push({\n className: 'align-horizontal',\n sourceNode: alignRect.sourceNodeId,\n top: centerY,\n left: Math.min(rect.right, prevRect.right),\n width: spacing,\n height: 1,\n });\n }\n }\n return alignLines;\n }\n\n private calcVerticalIntersectionCenter(rectA: Rectangle, rectB: Rectangle): number {\n const top = Math.max(rectA.top, rectB.top);\n const bottom = Math.min(rectA.bottom, rectB.bottom);\n return (top + bottom) / 2;\n }\n\n private calcHorizontalIntersectionCenter(rectA: Rectangle, rectB: Rectangle): number {\n const left = Math.max(rectA.left, rectB.left);\n const right = Math.min(rectA.right, rectB.right);\n return (left + right) / 2;\n }\n}\n"],"mappings":"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","names":["import_core","import_document","import_free_layout_core","import_inversify","import_document","import_core","import_free_layout_core","import_utils","React"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/create-plugin.ts","../src/service.ts","../src/constant.ts","../src/utils.ts","../src/layer.tsx"],"sourcesContent":["/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nexport { createFreeSnapPlugin } from './create-plugin';\nexport { WorkflowSnapService } from './service';\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { definePluginCreator } from '@flowgram.ai/core';\n\nimport {\n FreeSnapPluginOptions,\n WorkflowSnapLayerOptions,\n WorkflowSnapServiceOptions,\n} from './type';\nimport { WorkflowSnapService } from './service';\nimport { WorkflowSnapLayer } from './layer';\nimport { SnapDefaultOptions } from './constant';\n\nexport const createFreeSnapPlugin = definePluginCreator<FreeSnapPluginOptions>({\n onBind({ bind }) {\n bind(WorkflowSnapService).toSelf().inSingletonScope();\n },\n onInit(ctx, opts) {\n const options: WorkflowSnapServiceOptions & WorkflowSnapLayerOptions = {\n ...SnapDefaultOptions,\n ...opts,\n };\n ctx.playground.registerLayer(WorkflowSnapLayer, options);\n const snapService = ctx.get<WorkflowSnapService>(WorkflowSnapService);\n snapService.init(options);\n },\n onDispose(ctx) {\n const snapService = ctx.get<WorkflowSnapService>(WorkflowSnapService);\n snapService.dispose();\n },\n});\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { inject, injectable } from 'inversify';\nimport { Disposable, Emitter, Rectangle } from '@flowgram.ai/utils';\nimport { IPoint } from '@flowgram.ai/utils';\nimport { WorkflowNodeEntity, WorkflowDocument } from '@flowgram.ai/free-layout-core';\nimport { WorkflowDragService } from '@flowgram.ai/free-layout-core';\nimport { FlowNodeTransformData } from '@flowgram.ai/document';\nimport { FlowNodeBaseType } from '@flowgram.ai/document';\nimport { EntityManager, PlaygroundConfigEntity, TransformData } from '@flowgram.ai/core';\n\nimport { isEqual, isGreaterThan, isLessThan, isLessThanOrEqual, isNumber } from './utils';\nimport type {\n SnapEvent,\n SnapHorizontalLine,\n SnapLines,\n SnapMidHorizontalLine,\n SnapMidVerticalLine,\n SnapVerticalLine,\n WorkflowSnapServiceOptions,\n AlignRects,\n AlignRect,\n AlignSpacing,\n SnapNodeRect,\n SnapEdgeLines,\n} from './type';\nimport { SnapDefaultOptions } from './constant';\n\n@injectable()\nexport class WorkflowSnapService {\n @inject(WorkflowDocument) private readonly document: WorkflowDocument;\n\n @inject(EntityManager) private readonly entityManager: EntityManager;\n\n @inject(WorkflowDragService)\n private readonly dragService: WorkflowDragService;\n\n @inject(PlaygroundConfigEntity)\n private readonly playgroundConfig: PlaygroundConfigEntity;\n\n private disposers: Disposable[] = [];\n\n private options: WorkflowSnapServiceOptions;\n\n private snapEmitter = new Emitter<SnapEvent>();\n\n public readonly onSnap = this.snapEmitter.event;\n\n private _disabled = false;\n\n public init(params: Partial<WorkflowSnapServiceOptions> = {}): void {\n this.options = {\n ...SnapDefaultOptions,\n ...params,\n };\n this.mountListener();\n }\n\n public dispose(): void {\n this.disposers.forEach((disposer) => disposer.dispose());\n }\n\n public get disabled(): boolean {\n return this._disabled;\n }\n\n public disable(): void {\n if (this._disabled) {\n return;\n }\n this._disabled = true;\n this.clear();\n }\n\n public enable(): void {\n if (!this._disabled) {\n return;\n }\n this._disabled = false;\n this.clear();\n }\n\n private mountListener(): void {\n const dragAdjusterDisposer = this.dragService.registerPosAdjuster((params) => {\n const { selectedNodes: targetNodes, position } = params;\n const isMultiSnapping = this.options.enableMultiSnapping ? false : targetNodes.length !== 1;\n if (this._disabled || !this.options.enableEdgeSnapping || isMultiSnapping) {\n return {\n x: 0,\n y: 0,\n };\n }\n return this.snapping({\n targetNodes,\n position,\n });\n });\n const dragEndDisposer = this.dragService.onNodesDrag((event) => {\n if (event.type !== 'onDragEnd' || this._disabled) {\n return;\n }\n if (this.options.enableGridSnapping) {\n this.gridSnapping({\n targetNodes: event.nodes,\n gridSize: this.options.gridSize,\n });\n }\n if (this.options.enableEdgeSnapping) {\n this.clear();\n }\n });\n this.disposers.push(dragAdjusterDisposer, dragEndDisposer);\n }\n\n private snapping(params: { targetNodes: WorkflowNodeEntity[]; position: IPoint }): IPoint {\n const { targetNodes, position } = params;\n\n const targetBounds = this.getBounds(targetNodes);\n\n const targetRect = new Rectangle(\n position.x,\n position.y,\n targetBounds.width,\n targetBounds.height\n );\n\n const snapNodeRects = this.getSnapNodeRects({\n targetNodes,\n targetRect,\n });\n\n const { alignOffset, alignRects, alignSpacing } = this.calcAlignOffset({\n targetRect,\n alignThreshold: this.options.edgeThreshold,\n snapNodeRects,\n });\n\n const { snapOffset, snapEdgeLines } = this.calcSnapOffset({\n targetRect,\n edgeThreshold: this.options.edgeThreshold,\n snapNodeRects,\n });\n\n const offset: IPoint = {\n x: snapOffset.x || alignOffset.x,\n y: snapOffset.y || alignOffset.y,\n };\n\n const snapRect = new Rectangle(\n position.x + offset.x,\n position.y + offset.y,\n targetRect.width,\n targetRect.height\n );\n\n this.snapEmitter.fire({\n snapRect,\n snapEdgeLines,\n alignRects,\n alignSpacing,\n });\n\n return offset;\n }\n\n private calcSnapOffset(params: {\n snapNodeRects: SnapNodeRect[];\n targetRect: Rectangle;\n edgeThreshold: number;\n }): {\n snapOffset: IPoint;\n snapEdgeLines: SnapEdgeLines;\n } {\n const { snapNodeRects, edgeThreshold, targetRect } = params;\n\n const snapLines = this.getSnapLines({\n snapNodeRects,\n });\n\n // 找到最近的线条\n const topYClosestLine = snapLines.horizontal.find((line) =>\n isLessThanOrEqual(Math.abs(line.y - targetRect.top), edgeThreshold)\n );\n const bottomYClosestLine = snapLines.horizontal.find((line) =>\n isLessThanOrEqual(Math.abs(line.y - targetRect.bottom), edgeThreshold)\n );\n const leftXClosestLine = snapLines.vertical.find((line) =>\n isLessThanOrEqual(Math.abs(line.x - targetRect.left), edgeThreshold)\n );\n const rightXClosestLine = snapLines.vertical.find((line) =>\n isLessThanOrEqual(Math.abs(line.x - targetRect.right), edgeThreshold)\n );\n const midYClosestLine = snapLines.midHorizontal.find((line) =>\n isLessThanOrEqual(Math.abs(line.y - targetRect.center.y), edgeThreshold)\n );\n const midXClosestLine = snapLines.midVertical.find((line) =>\n isLessThanOrEqual(Math.abs(line.x - targetRect.center.x), edgeThreshold)\n );\n\n // 计算最近坐标\n const topYClosest = topYClosestLine?.y;\n const bottomYClosest = isNumber(bottomYClosestLine?.y)\n ? bottomYClosestLine!.y - targetRect.height\n : undefined;\n const leftXClosest = leftXClosestLine?.x;\n const rightXClosest = isNumber(rightXClosestLine?.x)\n ? rightXClosestLine!.x - targetRect.width\n : undefined;\n const midYClosest = isNumber(midYClosestLine?.y)\n ? midYClosestLine!.y - targetRect.height / 2\n : undefined;\n const midXClosest = isNumber(midXClosestLine?.x)\n ? midXClosestLine!.x - targetRect.width / 2\n : undefined;\n\n // 吸附后坐标,按优先级取值\n const snappingPosition = {\n x: midXClosest ?? leftXClosest ?? rightXClosest ?? targetRect.x,\n y: midYClosest ?? topYClosest ?? bottomYClosest ?? targetRect.y,\n };\n\n // 吸附修正偏移量\n const snapOffset: IPoint = {\n x: snappingPosition.x - targetRect.x,\n y: snappingPosition.y - targetRect.y,\n };\n\n // 生效的吸附线条\n const snapEdgeLines: SnapEdgeLines = {\n top: isEqual(topYClosest, snappingPosition.y) ? topYClosestLine : undefined,\n bottom: isEqual(bottomYClosest, snappingPosition.y) ? bottomYClosestLine : undefined,\n left: isEqual(leftXClosest, snappingPosition.x) ? leftXClosestLine : undefined,\n right: isEqual(rightXClosest, snappingPosition.x) ? rightXClosestLine : undefined,\n midVertical: isEqual(midXClosest, snappingPosition.x) ? midXClosestLine : undefined,\n midHorizontal: isEqual(midYClosest, snappingPosition.y) ? midYClosestLine : undefined,\n };\n\n return { snapOffset, snapEdgeLines };\n }\n\n private gridSnapping(params: { gridSize: number; targetNodes: WorkflowNodeEntity[] }): void {\n const { gridSize, targetNodes } = params;\n const rect = this.getBounds(targetNodes);\n const snap = (value: number) => Math.round(value / gridSize) * gridSize;\n const snappedPosition: IPoint = {\n x: snap(rect.x),\n y: snap(rect.y),\n };\n const offset: IPoint = {\n x: snappedPosition.x - rect.x,\n y: snappedPosition.y - rect.y,\n };\n targetNodes.forEach((node) =>\n this.updateNodePositionWithOffset({\n node,\n offset,\n })\n );\n }\n\n private clear() {\n this.snapEmitter.fire({\n snapEdgeLines: {},\n snapRect: Rectangle.EMPTY,\n alignRects: {\n top: [],\n bottom: [],\n left: [],\n right: [],\n },\n alignSpacing: {},\n });\n }\n\n private getSnapLines(params: { snapNodeRects: SnapNodeRect[] }): SnapLines {\n const { snapNodeRects } = params;\n const horizontalLines: SnapHorizontalLine[] = [];\n const verticalLines: SnapVerticalLine[] = [];\n const midHorizontalLines: SnapMidHorizontalLine[] = [];\n const midVerticalLines: SnapMidVerticalLine[] = [];\n\n snapNodeRects.forEach((snapNodeRect) => {\n const nodeBounds = snapNodeRect.rect;\n const nodeCenter = nodeBounds.center;\n // 边缘横线\n const top: SnapHorizontalLine = {\n y: nodeBounds.top,\n sourceNodeId: snapNodeRect.id,\n };\n const bottom: SnapHorizontalLine = {\n y: nodeBounds.bottom,\n sourceNodeId: snapNodeRect.id,\n };\n // 边缘竖线\n const left: SnapVerticalLine = {\n x: nodeBounds.left,\n sourceNodeId: snapNodeRect.id,\n };\n const right: SnapVerticalLine = {\n x: nodeBounds.right,\n sourceNodeId: snapNodeRect.id,\n };\n // 中间横线\n const midHorizontal: SnapMidHorizontalLine = {\n y: nodeCenter.y,\n sourceNodeId: snapNodeRect.id,\n };\n // 中间竖线\n const midVertical: SnapMidVerticalLine = {\n x: nodeCenter.x,\n sourceNodeId: snapNodeRect.id,\n };\n horizontalLines.push(top, bottom);\n verticalLines.push(left, right);\n midHorizontalLines.push(midHorizontal);\n midVerticalLines.push(midVertical);\n });\n\n return {\n horizontal: horizontalLines,\n vertical: verticalLines,\n midHorizontal: midHorizontalLines,\n midVertical: midVerticalLines,\n };\n }\n\n private getAvailableNodes(params: {\n targetNodes: WorkflowNodeEntity[];\n targetRect: Rectangle;\n }): WorkflowNodeEntity[] {\n const { targetNodes, targetRect } = params;\n\n const targetCenter = targetRect.center;\n const targetContainerId = targetNodes[0].parent?.id ?? this.document.root.id;\n\n const disabledNodeIds = targetNodes.map((n) => n.id);\n disabledNodeIds.push(FlowNodeBaseType.ROOT);\n const availableNodes = this.nodes\n .filter((n) => n.parent?.id === targetContainerId)\n .filter((n) => !disabledNodeIds.includes(n.id))\n .sort((nodeA, nodeB) => {\n const nodeCenterA = nodeA.getData(FlowNodeTransformData)!.bounds.center;\n const nodeCenterB = nodeB.getData(FlowNodeTransformData)!.bounds.center;\n // 距离越近优先级越高\n const distanceA =\n Math.abs(nodeCenterA.x - targetCenter.x) + Math.abs(nodeCenterA.y - targetCenter.y);\n const distanceB =\n Math.abs(nodeCenterB.x - targetCenter.x) + Math.abs(nodeCenterB.y - targetCenter.y);\n return distanceA - distanceB;\n });\n return availableNodes;\n }\n\n private viewRect(): Rectangle {\n const { width, height, scrollX, scrollY, zoom } = this.playgroundConfig.config;\n return new Rectangle(scrollX / zoom, scrollY / zoom, width / zoom, height / zoom);\n }\n\n private getSnapNodeRects(params: {\n targetNodes: WorkflowNodeEntity[];\n targetRect: Rectangle;\n }): SnapNodeRect[] {\n const availableNodes = this.getAvailableNodes(params);\n const viewRect = this.viewRect();\n return availableNodes\n .map((node) => {\n const snapNodeRect: SnapNodeRect = {\n id: node.id,\n rect: node.getData(FlowNodeTransformData).bounds,\n entity: node,\n };\n if (\n this.options.enableOnlyViewportSnapping &&\n node.parent?.flowNodeType === FlowNodeBaseType.ROOT &&\n !Rectangle.intersects(viewRect, snapNodeRect.rect)\n ) {\n // 最外层节点仅包含当前可见节点\n return;\n }\n return snapNodeRect;\n })\n .filter(Boolean) as SnapNodeRect[];\n }\n\n private get nodes(): WorkflowNodeEntity[] {\n return this.entityManager.getEntities<WorkflowNodeEntity>(WorkflowNodeEntity);\n }\n\n private getBounds(nodes: WorkflowNodeEntity[]): Rectangle {\n if (nodes.length === 0) {\n return Rectangle.EMPTY;\n }\n return Rectangle.enlarge(nodes.map((n) => n.getData(FlowNodeTransformData)!.bounds));\n }\n\n private updateNodePositionWithOffset(params: { node: WorkflowNodeEntity; offset: IPoint }): void {\n const { node, offset } = params;\n const transform = node.getData(TransformData);\n const positionWithOffset: IPoint = {\n x: transform.position.x + offset.x,\n y: transform.position.y + offset.y,\n };\n transform.update({\n position: positionWithOffset,\n });\n this.document.layout.updateAffectedTransform(node);\n }\n\n private calcAlignOffset(params: {\n snapNodeRects: SnapNodeRect[];\n targetRect: Rectangle;\n alignThreshold: number;\n }): {\n alignOffset: IPoint;\n alignRects: AlignRects;\n alignSpacing: AlignSpacing;\n } {\n const { snapNodeRects, targetRect, alignThreshold } = params;\n\n const alignRects = this.getAlignRects({\n targetRect,\n snapNodeRects,\n });\n\n const alignSpacing = this.calcAlignSpacing({\n targetRect,\n alignRects,\n });\n\n let topY: number | undefined;\n let bottomY: number | undefined;\n let leftX: number | undefined;\n let rightX: number | undefined;\n let midY: number | undefined;\n let midX: number | undefined;\n\n if (alignSpacing.top) {\n const topAlignY = alignRects.top[0].rect.bottom + alignSpacing.top;\n const isAlignTop = isLessThanOrEqual(Math.abs(targetRect.top - topAlignY), alignThreshold);\n if (isAlignTop) {\n // 生效\n topY = topAlignY;\n } else {\n // 失效\n alignSpacing.top = undefined;\n }\n }\n if (alignSpacing.bottom) {\n const bottomAlignY = alignRects.bottom[0].rect.top - alignSpacing.bottom;\n const isAlignBottom = isLessThan(Math.abs(targetRect.bottom - bottomAlignY), alignThreshold);\n if (isAlignBottom) {\n bottomY = bottomAlignY - targetRect.height;\n } else {\n alignSpacing.bottom = undefined;\n }\n }\n if (alignSpacing.left) {\n const leftAlignX = alignRects.left[0].rect.right + alignSpacing.left;\n const isAlignLeft = isLessThanOrEqual(Math.abs(targetRect.left - leftAlignX), alignThreshold);\n if (isAlignLeft) {\n leftX = leftAlignX;\n } else {\n alignSpacing.left = undefined;\n }\n }\n if (alignSpacing.right) {\n const rightAlignX = alignRects.right[0].rect.left - alignSpacing.right;\n const isAlignRight = isLessThanOrEqual(\n Math.abs(targetRect.right - rightAlignX),\n alignThreshold\n );\n if (isAlignRight) {\n rightX = rightAlignX - targetRect.width;\n } else {\n alignSpacing.right = undefined;\n }\n }\n if (alignSpacing.midHorizontal) {\n const leftAlignX = alignRects.left[0].rect.right + alignSpacing.midHorizontal;\n const isAlignMidHorizontal = isLessThanOrEqual(\n Math.abs(targetRect.left - leftAlignX),\n alignThreshold\n );\n if (isAlignMidHorizontal) {\n midX = leftAlignX;\n } else {\n alignSpacing.midHorizontal = undefined;\n }\n }\n if (alignSpacing.midVertical) {\n const topAlignY = alignRects.top[0].rect.bottom + alignSpacing.midVertical;\n const isAlignMidVertical = isLessThanOrEqual(\n Math.abs(targetRect.top - topAlignY),\n alignThreshold\n );\n if (isAlignMidVertical) {\n midY = topAlignY;\n } else {\n alignSpacing.midVertical = undefined;\n }\n }\n\n const alignPosition: IPoint = {\n x: midX ?? leftX ?? rightX ?? targetRect.x,\n y: midY ?? topY ?? bottomY ?? targetRect.y,\n };\n\n const alignOffset: IPoint = {\n x: alignPosition.x - targetRect.x,\n y: alignPosition.y - targetRect.y,\n };\n\n return { alignOffset, alignRects, alignSpacing };\n }\n\n private calcAlignSpacing(params: {\n targetRect: Rectangle;\n alignRects: AlignRects;\n }): AlignSpacing {\n const { targetRect, alignRects } = params;\n\n const topSpacing = this.getDirectionAlignSpacing({\n rects: alignRects.top,\n isHorizontal: false,\n });\n const bottomSpacing = this.getDirectionAlignSpacing({\n rects: alignRects.bottom,\n isHorizontal: false,\n });\n const leftSpacing = this.getDirectionAlignSpacing({\n rects: alignRects.left,\n isHorizontal: true,\n });\n const rightSpacing = this.getDirectionAlignSpacing({\n rects: alignRects.right,\n isHorizontal: true,\n });\n const midHorizontalSpacing = this.getMidAlignSpacing({\n rectA: alignRects.left[0]?.rect,\n rectB: alignRects.right[0]?.rect,\n targetRect,\n isHorizontal: true,\n });\n const midVerticalSpacing = this.getMidAlignSpacing({\n rectA: alignRects.top[0]?.rect,\n rectB: alignRects.bottom[0]?.rect,\n targetRect,\n isHorizontal: false,\n });\n return {\n top: topSpacing,\n bottom: bottomSpacing,\n left: leftSpacing,\n right: rightSpacing,\n midHorizontal: midHorizontalSpacing,\n midVertical: midVerticalSpacing,\n };\n }\n\n private getAlignRects(params: {\n targetRect: Rectangle;\n snapNodeRects: SnapNodeRect[];\n }): AlignRects {\n const { targetRect, snapNodeRects } = params;\n\n const topVerticalRects: AlignRect[] = [];\n const bottomVerticalRects: AlignRect[] = [];\n const leftHorizontalRects: AlignRect[] = [];\n const rightHorizontalRects: AlignRect[] = [];\n\n snapNodeRects.forEach((snapNodeRect) => {\n const nodeRect = snapNodeRect.rect;\n const { isVerticalIntersection, isHorizontalIntersection, isIntersection } =\n this.intersection(nodeRect, targetRect);\n if (isIntersection) {\n // 忽略重叠的节点\n return;\n } else if (isVerticalIntersection) {\n // 垂直重合\n if (isGreaterThan(nodeRect.center.y, targetRect.center.y)) {\n // 下方\n bottomVerticalRects.push({\n rect: nodeRect,\n sourceNodeId: snapNodeRect.id,\n });\n } else {\n // 上方\n topVerticalRects.push({\n rect: nodeRect,\n sourceNodeId: snapNodeRect.id,\n });\n }\n } else if (isHorizontalIntersection) {\n // 水平重合\n if (isGreaterThan(nodeRect.center.x, targetRect.center.x)) {\n // 右方\n rightHorizontalRects.push({\n rect: nodeRect,\n sourceNodeId: snapNodeRect.id,\n });\n } else {\n // 左方\n leftHorizontalRects.push({\n rect: nodeRect,\n sourceNodeId: snapNodeRect.id,\n });\n }\n }\n });\n\n return {\n top: topVerticalRects,\n bottom: bottomVerticalRects,\n left: leftHorizontalRects,\n right: rightHorizontalRects,\n };\n }\n\n private getMidAlignSpacing(params: {\n rectA?: Rectangle;\n rectB?: Rectangle;\n targetRect: Rectangle;\n isHorizontal: boolean;\n }): number | undefined {\n const { rectA, rectB, targetRect, isHorizontal } = params;\n if (!rectA || !rectB) {\n return;\n }\n const { isVerticalIntersection, isHorizontalIntersection, isIntersection } = this.intersection(\n rectA,\n rectB\n );\n if (isIntersection) {\n return;\n }\n if (isHorizontal && isHorizontalIntersection && !isVerticalIntersection) {\n const betweenSpacing = Math.min(\n Math.abs(rectA.left - rectB.right),\n Math.abs(rectA.right - rectB.left)\n );\n return (betweenSpacing - targetRect.width) / 2;\n } else if (!isHorizontal && isVerticalIntersection && !isHorizontalIntersection) {\n const betweenSpacing = Math.min(\n Math.abs(rectA.top - rectB.bottom),\n Math.abs(rectA.bottom - rectB.top)\n );\n return (betweenSpacing - targetRect.height) / 2;\n }\n }\n\n private getDirectionAlignSpacing(params: {\n rects: AlignRect[];\n isHorizontal: boolean;\n }): number | undefined {\n const { rects, isHorizontal } = params;\n if (rects.length < 2) {\n // 非法情况\n return;\n }\n const rectA = rects[0].rect;\n const rectB = rects[1].rect;\n\n const { isVerticalIntersection, isHorizontalIntersection, isIntersection } = this.intersection(\n rectA,\n rectB\n );\n\n if (isIntersection) {\n // 非法情况:重叠\n return;\n }\n if (isHorizontal && isHorizontalIntersection && !isVerticalIntersection) {\n return Math.min(Math.abs(rectA.left - rectB.right), Math.abs(rectA.right - rectB.left));\n } else if (!isHorizontal && isVerticalIntersection && !isHorizontalIntersection) {\n return Math.min(Math.abs(rectA.top - rectB.bottom), Math.abs(rectA.bottom - rectB.top));\n }\n return;\n }\n\n private intersection(\n rectA: Rectangle,\n rectB: Rectangle\n ): {\n isHorizontalIntersection: boolean;\n isVerticalIntersection: boolean;\n isIntersection: boolean;\n } {\n const isVerticalIntersection =\n isLessThan(rectA.left, rectB.right) && isGreaterThan(rectA.right, rectB.left);\n const isHorizontalIntersection =\n isLessThan(rectA.top, rectB.bottom) && isGreaterThan(rectA.bottom, rectB.top);\n const isIntersection = isHorizontalIntersection && isVerticalIntersection;\n\n return {\n isHorizontalIntersection,\n isVerticalIntersection,\n isIntersection,\n };\n }\n}\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport type { WorkflowSnapLayerOptions, WorkflowSnapServiceOptions } from './type';\n\nexport const SnapDefaultOptions: WorkflowSnapServiceOptions & WorkflowSnapLayerOptions = {\n enableEdgeSnapping: true,\n edgeThreshold: 7,\n enableGridSnapping: false,\n gridSize: 20,\n enableMultiSnapping: true,\n enableOnlyViewportSnapping: true,\n edgeColor: '#4E40E5',\n alignColor: '#4E40E5',\n edgeLineWidth: 2,\n alignLineWidth: 2,\n alignCrossWidth: 16,\n};\n\nexport const Epsilon = 0.00001;\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { Epsilon } from './constant';\n\n/** 检查浮点数 a 是否等于 b */\nexport const isEqual = (a: number | undefined, b: number | undefined): boolean => {\n if (a === undefined || b === undefined) {\n return false;\n }\n // 检查 a 和 b 的差的绝对值是否小于 Epsilon\n return Math.abs(a - b) < Epsilon;\n};\n\n/** 检查浮点数 a 是否小于 b */\nexport const isLessThan = (a: number | undefined, b: number | undefined): boolean => {\n if (a === undefined || b === undefined) {\n return false;\n }\n // 检查 a 是否显著小于 b\n return b - a > Epsilon;\n};\n\n/** 检查浮点数 a 是否大于 b */\nexport const isGreaterThan = (a: number | undefined, b: number | undefined): boolean => {\n if (a === undefined || b === undefined) {\n return false;\n }\n return a - b > Epsilon;\n};\n\n/** 检查浮点数 a 是否小于等于 b */\nexport const isLessThanOrEqual = (a: number | undefined, b: number | undefined): boolean =>\n isEqual(a, b) || isLessThan(a, b);\n\n/** 检查浮点数 a 是否大于等于 b */\nexport const isGreaterThanOrEqual = (a: number | undefined, b: number | undefined): boolean =>\n isEqual(a, b) || isGreaterThan(a, b);\n\n/** 检查值是否是数字类型 */\nexport const isNumber = (value: unknown): value is number =>\n typeof value === 'number' && !isNaN(value);\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport React from 'react';\n\nimport { inject, injectable } from 'inversify';\nimport { domUtils } from '@flowgram.ai/utils';\nimport { Rectangle } from '@flowgram.ai/utils';\nimport { WorkflowDocument } from '@flowgram.ai/free-layout-core';\nimport { FlowNodeTransformData } from '@flowgram.ai/document';\nimport { Layer } from '@flowgram.ai/core';\n\nimport { isEqual, isGreaterThan, isNumber } from './utils';\nimport { AlignRect, SnapEvent, WorkflowSnapLayerOptions } from './type';\nimport { WorkflowSnapService } from './service';\n\ninterface SnapRenderLine {\n className: string;\n sourceNode: string;\n top: number;\n left: number;\n width: number;\n height: number;\n dashed?: boolean;\n}\n\n@injectable()\nexport class WorkflowSnapLayer extends Layer<WorkflowSnapLayerOptions> {\n public static type = 'WorkflowSnapLayer';\n\n @inject(WorkflowDocument) private readonly document: WorkflowDocument;\n\n @inject(WorkflowSnapService) private readonly service: WorkflowSnapService;\n\n public readonly node = domUtils.createDivWithClass(\n 'gedit-playground-layer gedit-flow-snap-layer'\n );\n\n private edgeLines: SnapRenderLine[] = [];\n\n private alignLines: SnapRenderLine[] = [];\n\n public onReady(): void {\n this.node.style.zIndex = '9999';\n this.toDispose.pushAll([\n this.service.onSnap((event: SnapEvent) => {\n this.edgeLines = this.calcEdgeLines(event);\n this.alignLines = this.calcAlignLines(event);\n this.render();\n }),\n ]);\n }\n\n public render(): JSX.Element {\n return (\n <>\n {this.alignLines.length > 0 && (\n <div className=\"workflow-snap-align-lines\">{this.renderAlignLines()}</div>\n )}\n {this.edgeLines.length > 0 && (\n <div className=\"workflow-snap-edge-lines\">{this.renderEdgeLines()}</div>\n )}\n </>\n );\n }\n\n public onZoom(scale: number): void {\n this.node.style.transform = `scale(${scale})`;\n }\n\n private renderEdgeLines(): JSX.Element[] {\n return this.edgeLines.map((renderLine: SnapRenderLine) => {\n const { className, sourceNode, top, left, width, height, dashed } = renderLine;\n const id = `${className}-${sourceNode}-${top}-${left}-${width}-${height}`;\n const isHorizontal = width < height;\n const border = `${this.options.edgeLineWidth}px ${dashed ? 'dashed' : 'solid'} ${\n this.options.edgeColor\n }`;\n return (\n <div\n className={`workflow-snap-edge-line ${className}`}\n data-testid=\"sdk.workflow.canvas.snap.edgeLine\"\n data-snap-line-id={id}\n data-snap-line-source-node={sourceNode}\n key={id}\n style={{\n top,\n left,\n width,\n height,\n position: 'absolute',\n borderLeft: isHorizontal ? border : 'none',\n borderTop: !isHorizontal ? border : 'none',\n }}\n />\n );\n });\n }\n\n private renderAlignLines(): JSX.Element[] {\n return this.alignLines.map((renderLine: SnapRenderLine) => {\n const id = `${renderLine.className}-${renderLine.sourceNode}-${renderLine.top}-${renderLine.left}-${renderLine.width}-${renderLine.height}`;\n const isHorizontal = isGreaterThan(renderLine.width, renderLine.height);\n const alignLineWidth = this.options.alignLineWidth; // 整体线条粗细\n const alignCrossWidth = this.options.alignCrossWidth; // 工字形横线的长度\n\n // 调整渲染位置以保持居中\n const adjustedTop = isHorizontal ? renderLine.top - alignLineWidth / 2 : renderLine.top;\n const adjustedLeft = isHorizontal ? renderLine.left : renderLine.left - alignLineWidth / 2;\n\n return (\n <div\n className={`workflow-snap-align-line ${renderLine.className}`}\n data-testid=\"sdk.workflow.canvas.snap.alignLine\"\n data-snap-line-id={id}\n data-snap-line-source-node={renderLine.sourceNode}\n key={id}\n style={{\n position: 'absolute',\n }}\n >\n {/* 主线 */}\n <div\n style={{\n position: 'absolute',\n top: adjustedTop,\n left: adjustedLeft,\n width: isHorizontal ? renderLine.width : alignLineWidth,\n height: isHorizontal ? alignLineWidth : renderLine.height,\n backgroundColor: this.options.alignColor,\n }}\n />\n {/* 左端或上端横线 */}\n <div\n style={{\n position: 'absolute',\n top: isHorizontal\n ? adjustedTop - (alignCrossWidth - alignLineWidth) / 2\n : adjustedTop,\n left: isHorizontal\n ? adjustedLeft\n : adjustedLeft - (alignCrossWidth - alignLineWidth) / 2,\n width: isHorizontal ? alignLineWidth : alignCrossWidth,\n height: isHorizontal ? alignCrossWidth : alignLineWidth,\n backgroundColor: this.options.alignColor,\n }}\n />\n {/* 右端或下端横线 */}\n <div\n style={{\n position: 'absolute',\n top: isHorizontal\n ? adjustedTop - (alignCrossWidth - alignLineWidth) / 2\n : adjustedTop + renderLine.height - alignLineWidth,\n left: isHorizontal\n ? adjustedLeft + renderLine.width - alignLineWidth\n : adjustedLeft - (alignCrossWidth - alignLineWidth) / 2,\n width: isHorizontal ? alignLineWidth : alignCrossWidth,\n height: isHorizontal ? alignCrossWidth : alignLineWidth,\n backgroundColor: this.options.alignColor,\n }}\n />\n </div>\n );\n });\n }\n\n private calcEdgeLines(event: SnapEvent): SnapRenderLine[] {\n const { alignRects, snapRect, snapEdgeLines } = event;\n const edgeLines: SnapRenderLine[] = [];\n\n const topFullAlign = this.directionFullAlign({\n alignRects: alignRects.top,\n targetRect: snapRect,\n isVertical: true,\n });\n const bottomFullAlign = this.directionFullAlign({\n alignRects: alignRects.bottom,\n targetRect: snapRect,\n isVertical: true,\n });\n const leftFullAlign = this.directionFullAlign({\n alignRects: alignRects.left,\n targetRect: snapRect,\n isVertical: false,\n });\n const rightFullAlign = this.directionFullAlign({\n alignRects: alignRects.right,\n targetRect: snapRect,\n isVertical: false,\n });\n\n // 处理顶部对齐\n if (topFullAlign) {\n const top = topFullAlign.rect.top;\n const height = bottomFullAlign\n ? snapRect.bottom - snapRect.height / 2 - top\n : snapRect.bottom - top;\n const width = this.options.edgeLineWidth;\n const lineData = {\n top,\n width,\n height,\n };\n edgeLines.push({\n className: 'edge-full-top-left',\n sourceNode: topFullAlign.sourceNodeId,\n left: snapRect.left,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-top-right',\n sourceNode: topFullAlign.sourceNodeId,\n left: snapRect.right - 1,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-top-mid',\n sourceNode: topFullAlign.sourceNodeId,\n left: snapRect.left + snapRect.width / 2,\n dashed: true,\n ...lineData,\n });\n }\n\n // 处理底部对齐\n if (bottomFullAlign) {\n const top = topFullAlign ? snapRect.top + snapRect.height / 2 : snapRect.top;\n const height = bottomFullAlign.rect.bottom - top;\n const width = this.options.edgeLineWidth;\n const lineData = {\n top,\n width,\n height,\n };\n edgeLines.push({\n className: 'edge-full-bottom-left',\n sourceNode: bottomFullAlign.sourceNodeId,\n left: snapRect.left,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-bottom-right',\n sourceNode: bottomFullAlign.sourceNodeId,\n left: snapRect.right - 1,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-bottom-mid',\n sourceNode: bottomFullAlign.sourceNodeId,\n left: snapRect.left + snapRect.width / 2,\n dashed: true,\n ...lineData,\n });\n }\n\n // 处理左侧对齐\n if (leftFullAlign) {\n const left = leftFullAlign.rect.left;\n const width = rightFullAlign\n ? snapRect.right - snapRect.width / 2 - left\n : snapRect.right - left;\n const height = this.options.edgeLineWidth;\n const lineData = {\n left,\n width,\n height,\n };\n edgeLines.push({\n className: 'edge-full-left-top',\n sourceNode: leftFullAlign.sourceNodeId,\n top: snapRect.top,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-left-bottom',\n sourceNode: leftFullAlign.sourceNodeId,\n top: snapRect.bottom - 1,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-left-mid',\n sourceNode: leftFullAlign.sourceNodeId,\n top: snapRect.top + snapRect.height / 2,\n dashed: true,\n ...lineData,\n });\n }\n\n // 处理右侧对齐\n if (rightFullAlign) {\n const left = leftFullAlign ? snapRect.left + snapRect.width / 2 : snapRect.left;\n const width = rightFullAlign.rect.right - left;\n const height = this.options.edgeLineWidth;\n const lineData = {\n left,\n width,\n height,\n };\n edgeLines.push({\n className: 'edge-full-right-top',\n sourceNode: rightFullAlign.sourceNodeId,\n top: snapRect.top,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-right-bottom',\n sourceNode: rightFullAlign.sourceNodeId,\n top: snapRect.bottom - 1,\n ...lineData,\n });\n edgeLines.push({\n className: 'edge-full-right-mid',\n sourceNode: rightFullAlign.sourceNodeId,\n top: snapRect.top + snapRect.height / 2,\n dashed: true,\n ...lineData,\n });\n }\n\n const snappedEdgeLines = Object.entries(snapEdgeLines)\n .map(([direction, snapLine]) => {\n if (!snapLine) {\n return;\n }\n const sourceNode = this.document.getNode(snapLine.sourceNodeId);\n if (!sourceNode) {\n return;\n }\n const nodeRect = sourceNode.getData(FlowNodeTransformData).bounds;\n if (isNumber(snapLine.x)) {\n // 垂直\n const top = Math.min(nodeRect.top, snapRect.top);\n const bottom = Math.max(nodeRect.bottom, snapRect.bottom);\n const height = bottom - top;\n const left = direction === 'right' ? snapLine.x - 1 : snapLine.x;\n const width = this.options.edgeLineWidth;\n const isMidX = direction === 'midVertical';\n const lineData: SnapRenderLine = {\n className: `edge-snapped-${direction}`,\n sourceNode: snapLine.sourceNodeId,\n top,\n left,\n width,\n height,\n dashed: isMidX,\n };\n const onTop = top === nodeRect.top;\n if (onTop && topFullAlign) {\n return;\n }\n if (!onTop && bottomFullAlign) {\n return;\n }\n return lineData;\n } else if (isNumber(snapLine.y)) {\n // 水平\n const left = Math.min(nodeRect.left, snapRect.left);\n const right = Math.max(nodeRect.right, snapRect.right);\n const width = right - left;\n const top = direction === 'bottom' ? snapLine.y - 1 : snapLine.y;\n const height = this.options.edgeLineWidth;\n const isMidY = direction === 'midHorizontal';\n const lineData: SnapRenderLine = {\n className: `edge-snapped-${direction}`,\n sourceNode: snapLine.sourceNodeId,\n top,\n left,\n width,\n height,\n dashed: isMidY,\n };\n const onLeft = left === nodeRect.left;\n if (onLeft && leftFullAlign) {\n return;\n }\n if (!onLeft && rightFullAlign) {\n return;\n }\n return lineData;\n }\n })\n .filter(Boolean) as SnapRenderLine[];\n\n edgeLines.push(...snappedEdgeLines);\n\n return edgeLines;\n }\n\n private directionFullAlign(params: {\n alignRects: AlignRect[];\n targetRect: Rectangle;\n isVertical: boolean;\n }): AlignRect | undefined {\n const { alignRects, targetRect, isVertical } = params;\n let fullAlignIndex = -1;\n for (let i = 0; i < alignRects.length; i++) {\n const alignRect = alignRects[i];\n const prevRect = alignRects[i - 1]?.rect ?? targetRect;\n const isFullAlign = this.rectFullAlign(alignRect.rect, prevRect, isVertical);\n if (!isFullAlign) {\n // 未对齐则中断\n break; // 用 for 循环 + break 反而比 Array.findIndex 实现可读性更好\n }\n fullAlignIndex = i;\n }\n const fullAlignRect = alignRects[fullAlignIndex];\n return fullAlignRect;\n }\n\n private rectFullAlign(rectA: Rectangle, rectB: Rectangle, isVertical: boolean): boolean {\n if (isVertical) {\n return isEqual(rectA.left, rectB.left) && isEqual(rectA.right, rectB.right);\n } else {\n return isEqual(rectA.top, rectB.top) && isEqual(rectA.bottom, rectB.bottom);\n }\n }\n\n private calcAlignLines(event: SnapEvent): SnapRenderLine[] {\n const { alignRects, alignSpacing, snapRect } = event;\n\n const topAlignLines = this.calcDirectionAlignLines({\n alignRects: alignRects.top,\n targetRect: snapRect,\n isVertical: true,\n spacing: alignSpacing.midVertical ?? alignSpacing.top,\n });\n\n const bottomAlignLines = this.calcDirectionAlignLines({\n alignRects: alignRects.bottom,\n targetRect: snapRect,\n isVertical: true,\n spacing: alignSpacing.midVertical ?? alignSpacing.bottom,\n });\n\n const leftAlignLines = this.calcDirectionAlignLines({\n alignRects: alignRects.left,\n targetRect: snapRect,\n isVertical: false,\n spacing: alignSpacing.midHorizontal ?? alignSpacing.left,\n });\n\n const rightAlignLines = this.calcDirectionAlignLines({\n alignRects: alignRects.right,\n targetRect: snapRect,\n isVertical: false,\n spacing: alignSpacing.midHorizontal ?? alignSpacing.right,\n });\n\n return [...topAlignLines, ...bottomAlignLines, ...leftAlignLines, ...rightAlignLines];\n }\n\n private calcDirectionAlignLines(params: {\n alignRects: AlignRect[];\n targetRect: Rectangle;\n isVertical: boolean;\n spacing?: number;\n }) {\n const { alignRects, targetRect, isVertical, spacing } = params;\n const alignLines: SnapRenderLine[] = [];\n if (!spacing) {\n return alignLines;\n }\n for (let i = 0; i < alignRects.length; i++) {\n const alignRect = alignRects[i];\n const rect = alignRect.rect;\n const prevRect = alignRects[i - 1]?.rect ?? targetRect;\n\n const betweenSpacing = isVertical\n ? Math.min(Math.abs(prevRect.top - rect.bottom), Math.abs(prevRect.bottom - rect.top))\n : Math.min(Math.abs(prevRect.left - rect.right), Math.abs(prevRect.right - rect.left));\n if (!isEqual(betweenSpacing, spacing)) {\n // 不连续,需要中断\n break; // 因为要用到 break,所以不能用 Array.map()\n }\n if (isVertical) {\n const centerX = this.calcHorizontalIntersectionCenter(rect, targetRect);\n alignLines.push({\n className: 'align-vertical',\n sourceNode: alignRect.sourceNodeId,\n top: Math.min(rect.bottom, prevRect.bottom),\n left: centerX,\n width: 1,\n height: spacing,\n });\n } else {\n const centerY = this.calcVerticalIntersectionCenter(rect, targetRect);\n alignLines.push({\n className: 'align-horizontal',\n sourceNode: alignRect.sourceNodeId,\n top: centerY,\n left: Math.min(rect.right, prevRect.right),\n width: spacing,\n height: 1,\n });\n }\n }\n return alignLines;\n }\n\n private calcVerticalIntersectionCenter(rectA: Rectangle, rectB: Rectangle): number {\n const top = Math.max(rectA.top, rectB.top);\n const bottom = Math.min(rectA.bottom, rectB.bottom);\n return (top + bottom) / 2;\n }\n\n private calcHorizontalIntersectionCenter(rectA: Rectangle, rectB: Rectangle): number {\n const left = Math.max(rectA.left, rectB.left);\n const right = Math.min(rectA.right, rectB.right);\n return (left + right) / 2;\n }\n}\n"],"mappings":"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","names":["import_core","import_free_layout_core","import_document","import_inversify","import_utils","import_free_layout_core","import_document","import_core","React"]}
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowgram.ai/free-snap-plugin",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.31",
|
|
4
4
|
"homepage": "https://flowgram.ai/",
|
|
5
5
|
"repository": "https://github.com/bytedance/flowgram.ai",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"exports": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
8
9
|
"import": "./dist/esm/index.js",
|
|
9
10
|
"require": "./dist/index.js"
|
|
10
11
|
},
|
|
@@ -17,34 +18,31 @@
|
|
|
17
18
|
"dependencies": {
|
|
18
19
|
"inversify": "^6.0.1",
|
|
19
20
|
"reflect-metadata": "~0.2.2",
|
|
20
|
-
"
|
|
21
|
-
"@flowgram.ai/core": "0.1.0-alpha.
|
|
22
|
-
"@flowgram.ai/
|
|
23
|
-
"@flowgram.ai/
|
|
24
|
-
"@flowgram.ai/utils": "0.1.0-alpha.3",
|
|
25
|
-
"@flowgram.ai/free-layout-core": "0.1.0-alpha.3"
|
|
21
|
+
"@flowgram.ai/core": "0.1.0-alpha.31",
|
|
22
|
+
"@flowgram.ai/free-layout-core": "0.1.0-alpha.31",
|
|
23
|
+
"@flowgram.ai/utils": "0.1.0-alpha.31",
|
|
24
|
+
"@flowgram.ai/document": "0.1.0-alpha.31"
|
|
26
25
|
},
|
|
27
26
|
"devDependencies": {
|
|
28
27
|
"@types/bezier-js": "4.1.3",
|
|
29
|
-
"@types/lodash": "^4.14.137",
|
|
30
28
|
"@types/react": "^18",
|
|
31
29
|
"@types/react-dom": "^18",
|
|
32
30
|
"@types/styled-components": "^5",
|
|
33
|
-
"@vitest/coverage-v8": "^
|
|
34
|
-
"eslint": "^
|
|
31
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
32
|
+
"eslint": "^9.0.0",
|
|
35
33
|
"react": "^18",
|
|
36
34
|
"react-dom": "^18",
|
|
37
35
|
"styled-components": "^5",
|
|
38
36
|
"tsup": "^8.0.1",
|
|
39
|
-
"typescript": "^5.
|
|
40
|
-
"vitest": "^
|
|
41
|
-
"@flowgram.ai/eslint-config": "0.1.0-alpha.
|
|
42
|
-
"@flowgram.ai/ts-config": "0.1.0-alpha.
|
|
37
|
+
"typescript": "^5.8.3",
|
|
38
|
+
"vitest": "^3.2.4",
|
|
39
|
+
"@flowgram.ai/eslint-config": "0.1.0-alpha.31",
|
|
40
|
+
"@flowgram.ai/ts-config": "0.1.0-alpha.31"
|
|
43
41
|
},
|
|
44
42
|
"peerDependencies": {
|
|
45
|
-
"react": ">=
|
|
46
|
-
"react-dom": ">=
|
|
47
|
-
"styled-components": ">=
|
|
43
|
+
"react": ">=16.8",
|
|
44
|
+
"react-dom": ">=16.8",
|
|
45
|
+
"styled-components": ">=5"
|
|
48
46
|
},
|
|
49
47
|
"publishConfig": {
|
|
50
48
|
"access": "public",
|