@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 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: false,
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
- (params) => this.snapping({
82
- targetNodes: params.selectedNodes,
83
- position: params.position
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 isMultiSnapping = this.options.enableMultiSnapping ? false : targetNodes.length !== 1;
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
- selectedBounds.width,
116
- selectedBounds.height
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-sourceNode": sourceNode,
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-sourceNode": renderLine.sourceNode,
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 = {
@@ -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 src_exports = {};
40
- __export(src_exports, {
39
+ var index_exports = {};
40
+ __export(index_exports, {
41
41
  WorkflowSnapService: () => WorkflowSnapService,
42
42
  createFreeSnapPlugin: () => createFreeSnapPlugin
43
43
  });
44
- module.exports = __toCommonJS(src_exports);
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: false,
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
- (params) => this.snapping({
116
- targetNodes: params.selectedNodes,
117
- position: params.position
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 isMultiSnapping = this.options.enableMultiSnapping ? false : targetNodes.length !== 1;
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
- selectedBounds.width,
150
- selectedBounds.height
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-sourceNode": sourceNode,
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-sourceNode": renderLine.sourceNode,
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",
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
- "lodash": "^4.17.21",
21
- "@flowgram.ai/core": "0.1.0-alpha.3",
22
- "@flowgram.ai/document": "0.1.0-alpha.3",
23
- "@flowgram.ai/renderer": "0.1.0-alpha.3",
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": "^0.32.0",
34
- "eslint": "^8.54.0",
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.0.4",
40
- "vitest": "^0.34.6",
41
- "@flowgram.ai/eslint-config": "0.1.0-alpha.3",
42
- "@flowgram.ai/ts-config": "0.1.0-alpha.3"
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": ">=17",
46
- "react-dom": ">=17",
47
- "styled-components": ">=4"
43
+ "react": ">=16.8",
44
+ "react-dom": ">=16.8",
45
+ "styled-components": ">=5"
48
46
  },
49
47
  "publishConfig": {
50
48
  "access": "public",