@flowgram.ai/free-snap-plugin 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +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"]}
@@ -0,0 +1,103 @@
1
+ import * as _flowgram_ai_core from '@flowgram.ai/core';
2
+ import * as _flowgram_ai_utils from '@flowgram.ai/utils';
3
+ import { Rectangle } from '@flowgram.ai/utils';
4
+
5
+ interface SnapLine {
6
+ x?: number;
7
+ y?: number;
8
+ sourceNodeId: string;
9
+ }
10
+ interface SnapHorizontalLine extends SnapLine {
11
+ y: number;
12
+ }
13
+ interface SnapVerticalLine extends SnapLine {
14
+ x: number;
15
+ }
16
+ interface SnapMidHorizontalLine extends SnapLine {
17
+ y: number;
18
+ }
19
+ interface SnapMidVerticalLine extends SnapLine {
20
+ x: number;
21
+ }
22
+ interface SnapEdgeLines {
23
+ top?: SnapHorizontalLine;
24
+ bottom?: SnapHorizontalLine;
25
+ left?: SnapVerticalLine;
26
+ right?: SnapVerticalLine;
27
+ midHorizontal?: SnapMidHorizontalLine;
28
+ midVertical?: SnapMidVerticalLine;
29
+ }
30
+ interface AlignRect {
31
+ rect: Rectangle;
32
+ sourceNodeId: string;
33
+ }
34
+ interface AlignRects {
35
+ top: AlignRect[];
36
+ bottom: AlignRect[];
37
+ left: AlignRect[];
38
+ right: AlignRect[];
39
+ }
40
+ interface AlignSpacing {
41
+ top?: number;
42
+ bottom?: number;
43
+ left?: number;
44
+ right?: number;
45
+ midHorizontal?: number;
46
+ midVertical?: number;
47
+ }
48
+ interface SnapEvent {
49
+ snapEdgeLines: SnapEdgeLines;
50
+ snapRect: Rectangle;
51
+ alignRects: AlignRects;
52
+ alignSpacing: AlignSpacing;
53
+ }
54
+ interface WorkflowSnapServiceOptions {
55
+ edgeThreshold: number;
56
+ gridSize: number;
57
+ enableGridSnapping: boolean;
58
+ enableEdgeSnapping: boolean;
59
+ enableMultiSnapping: boolean;
60
+ enableOnlyViewportSnapping: boolean;
61
+ }
62
+ interface WorkflowSnapLayerOptions {
63
+ edgeColor: string;
64
+ alignColor: string;
65
+ edgeLineWidth: number;
66
+ alignLineWidth: number;
67
+ alignCrossWidth: number;
68
+ }
69
+
70
+ declare const createFreeSnapPlugin: _flowgram_ai_core.PluginCreator<Partial<WorkflowSnapServiceOptions & WorkflowSnapLayerOptions>>;
71
+
72
+ declare class WorkflowSnapService {
73
+ private readonly document;
74
+ private readonly entityManager;
75
+ private readonly dragService;
76
+ private readonly playgroundConfig;
77
+ private disposers;
78
+ private options;
79
+ private snapEmitter;
80
+ readonly onSnap: _flowgram_ai_utils.Event<SnapEvent>;
81
+ init(params?: Partial<WorkflowSnapServiceOptions>): void;
82
+ dispose(): void;
83
+ private mountListener;
84
+ private snapping;
85
+ private calcSnapOffset;
86
+ private gridSnapping;
87
+ private clear;
88
+ private getSnapLines;
89
+ private getAvailableNodes;
90
+ private viewRect;
91
+ private getSnapNodeRects;
92
+ private get nodes();
93
+ private getBounds;
94
+ private updateNodePositionWithOffset;
95
+ private calcAlignOffset;
96
+ private calcAlignSpacing;
97
+ private getAlignRects;
98
+ private getMidAlignSpacing;
99
+ private getDirectionAlignSpacing;
100
+ private intersection;
101
+ }
102
+
103
+ export { WorkflowSnapService, createFreeSnapPlugin };
@@ -0,0 +1,103 @@
1
+ import * as _flowgram_ai_core from '@flowgram.ai/core';
2
+ import * as _flowgram_ai_utils from '@flowgram.ai/utils';
3
+ import { Rectangle } from '@flowgram.ai/utils';
4
+
5
+ interface SnapLine {
6
+ x?: number;
7
+ y?: number;
8
+ sourceNodeId: string;
9
+ }
10
+ interface SnapHorizontalLine extends SnapLine {
11
+ y: number;
12
+ }
13
+ interface SnapVerticalLine extends SnapLine {
14
+ x: number;
15
+ }
16
+ interface SnapMidHorizontalLine extends SnapLine {
17
+ y: number;
18
+ }
19
+ interface SnapMidVerticalLine extends SnapLine {
20
+ x: number;
21
+ }
22
+ interface SnapEdgeLines {
23
+ top?: SnapHorizontalLine;
24
+ bottom?: SnapHorizontalLine;
25
+ left?: SnapVerticalLine;
26
+ right?: SnapVerticalLine;
27
+ midHorizontal?: SnapMidHorizontalLine;
28
+ midVertical?: SnapMidVerticalLine;
29
+ }
30
+ interface AlignRect {
31
+ rect: Rectangle;
32
+ sourceNodeId: string;
33
+ }
34
+ interface AlignRects {
35
+ top: AlignRect[];
36
+ bottom: AlignRect[];
37
+ left: AlignRect[];
38
+ right: AlignRect[];
39
+ }
40
+ interface AlignSpacing {
41
+ top?: number;
42
+ bottom?: number;
43
+ left?: number;
44
+ right?: number;
45
+ midHorizontal?: number;
46
+ midVertical?: number;
47
+ }
48
+ interface SnapEvent {
49
+ snapEdgeLines: SnapEdgeLines;
50
+ snapRect: Rectangle;
51
+ alignRects: AlignRects;
52
+ alignSpacing: AlignSpacing;
53
+ }
54
+ interface WorkflowSnapServiceOptions {
55
+ edgeThreshold: number;
56
+ gridSize: number;
57
+ enableGridSnapping: boolean;
58
+ enableEdgeSnapping: boolean;
59
+ enableMultiSnapping: boolean;
60
+ enableOnlyViewportSnapping: boolean;
61
+ }
62
+ interface WorkflowSnapLayerOptions {
63
+ edgeColor: string;
64
+ alignColor: string;
65
+ edgeLineWidth: number;
66
+ alignLineWidth: number;
67
+ alignCrossWidth: number;
68
+ }
69
+
70
+ declare const createFreeSnapPlugin: _flowgram_ai_core.PluginCreator<Partial<WorkflowSnapServiceOptions & WorkflowSnapLayerOptions>>;
71
+
72
+ declare class WorkflowSnapService {
73
+ private readonly document;
74
+ private readonly entityManager;
75
+ private readonly dragService;
76
+ private readonly playgroundConfig;
77
+ private disposers;
78
+ private options;
79
+ private snapEmitter;
80
+ readonly onSnap: _flowgram_ai_utils.Event<SnapEvent>;
81
+ init(params?: Partial<WorkflowSnapServiceOptions>): void;
82
+ dispose(): void;
83
+ private mountListener;
84
+ private snapping;
85
+ private calcSnapOffset;
86
+ private gridSnapping;
87
+ private clear;
88
+ private getSnapLines;
89
+ private getAvailableNodes;
90
+ private viewRect;
91
+ private getSnapNodeRects;
92
+ private get nodes();
93
+ private getBounds;
94
+ private updateNodePositionWithOffset;
95
+ private calcAlignOffset;
96
+ private calcAlignSpacing;
97
+ private getAlignRects;
98
+ private getMidAlignSpacing;
99
+ private getDirectionAlignSpacing;
100
+ private intersection;
101
+ }
102
+
103
+ export { WorkflowSnapService, createFreeSnapPlugin };