Sfoglia il codice sorgente

feat(snap): node snapping and snap line rending can be disabled

liuyangxing 10 mesi fa
parent
commit
5f1f2a6a03
1 ha cambiato i file con 78 aggiunte e 57 eliminazioni
  1. 78 57
      packages/plugins/free-snap-plugin/src/service.ts

+ 78 - 57
packages/plugins/free-snap-plugin/src/service.ts

@@ -1,11 +1,11 @@
 import { inject, injectable } from 'inversify';
+import { Disposable, Emitter, Rectangle } from '@flowgram.ai/utils';
+import { IPoint } from '@flowgram.ai/utils';
+import { WorkflowNodeEntity, WorkflowDocument } from '@flowgram.ai/free-layout-core';
+import { WorkflowDragService } from '@flowgram.ai/free-layout-core';
 import { FlowNodeTransformData } from '@flowgram.ai/document';
 import { FlowNodeBaseType } from '@flowgram.ai/document';
 import { EntityManager, PlaygroundConfigEntity, TransformData } from '@flowgram.ai/core';
-import { WorkflowNodeEntity, WorkflowDocument } from '@flowgram.ai/free-layout-core';
-import { WorkflowDragService } from '@flowgram.ai/free-layout-core';
-import { Disposable, Emitter, Rectangle } from '@flowgram.ai/utils';
-import { IPoint } from '@flowgram.ai/utils';
 
 import { isEqual, isGreaterThan, isLessThan, isLessThanOrEqual, isNumber } from './utils';
 import type {
@@ -44,6 +44,8 @@ export class WorkflowSnapService {
 
   public readonly onSnap = this.snapEmitter.event;
 
+  private _disabled = false;
+
   public init(params: Partial<WorkflowSnapServiceOptions> = {}): void {
     this.options = {
       ...SnapDefaultOptions,
@@ -53,18 +55,46 @@ export class WorkflowSnapService {
   }
 
   public dispose(): void {
-    this.disposers.forEach(disposer => disposer.dispose());
+    this.disposers.forEach((disposer) => disposer.dispose());
+  }
+
+  public get disabled(): boolean {
+    return this._disabled;
+  }
+
+  public disable(): void {
+    if (this._disabled) {
+      return;
+    }
+    this._disabled = true;
+    this.clear();
+  }
+
+  public enable(): void {
+    if (!this._disabled) {
+      return;
+    }
+    this._disabled = false;
+    this.clear();
   }
 
   private mountListener(): void {
-    const dragAdjusterDisposer = this.dragService.registerPosAdjuster(params =>
-      this.snapping({
-        targetNodes: params.selectedNodes,
-        position: params.position,
-      }),
-    );
-    const dragEndDisposer = this.dragService.onNodesDrag(event => {
-      if (event.type !== 'onDragEnd') {
+    const dragAdjusterDisposer = this.dragService.registerPosAdjuster((params) => {
+      const { selectedNodes: targetNodes, position } = params;
+      const isMultiSnapping = this.options.enableMultiSnapping ? false : targetNodes.length !== 1;
+      if (this._disabled || !this.options.enableEdgeSnapping || isMultiSnapping) {
+        return {
+          x: 0,
+          y: 0,
+        };
+      }
+      return this.snapping({
+        targetNodes,
+        position,
+      });
+    });
+    const dragEndDisposer = this.dragService.onNodesDrag((event) => {
+      if (event.type !== 'onDragEnd' || this._disabled) {
         return;
       }
       if (this.options.enableGridSnapping) {
@@ -81,24 +111,15 @@ export class WorkflowSnapService {
   }
 
   private snapping(params: { targetNodes: WorkflowNodeEntity[]; position: IPoint }): IPoint {
-    const { targetNodes: targetNodes, position } = params;
+    const { targetNodes, position } = params;
 
-    const isMultiSnapping = this.options.enableMultiSnapping ? false : targetNodes.length !== 1;
-
-    if (!this.options.enableEdgeSnapping || isMultiSnapping) {
-      return {
-        x: 0,
-        y: 0,
-      };
-    }
-
-    const selectedBounds = this.getBounds(targetNodes);
+    const targetBounds = this.getBounds(targetNodes);
 
     const targetRect = new Rectangle(
       position.x,
       position.y,
-      selectedBounds.width,
-      selectedBounds.height,
+      targetBounds.width,
+      targetBounds.height
     );
 
     const snapNodeRects = this.getSnapNodeRects({
@@ -127,7 +148,7 @@ export class WorkflowSnapService {
       position.x + offset.x,
       position.y + offset.y,
       targetRect.width,
-      targetRect.height,
+      targetRect.height
     );
 
     this.snapEmitter.fire({
@@ -155,23 +176,23 @@ export class WorkflowSnapService {
     });
 
     // 找到最近的线条
-    const topYClosestLine = snapLines.horizontal.find(line =>
-      isLessThanOrEqual(Math.abs(line.y - targetRect.top), edgeThreshold),
+    const topYClosestLine = snapLines.horizontal.find((line) =>
+      isLessThanOrEqual(Math.abs(line.y - targetRect.top), edgeThreshold)
     );
-    const bottomYClosestLine = snapLines.horizontal.find(line =>
-      isLessThanOrEqual(Math.abs(line.y - targetRect.bottom), edgeThreshold),
+    const bottomYClosestLine = snapLines.horizontal.find((line) =>
+      isLessThanOrEqual(Math.abs(line.y - targetRect.bottom), edgeThreshold)
     );
-    const leftXClosestLine = snapLines.vertical.find(line =>
-      isLessThanOrEqual(Math.abs(line.x - targetRect.left), edgeThreshold),
+    const leftXClosestLine = snapLines.vertical.find((line) =>
+      isLessThanOrEqual(Math.abs(line.x - targetRect.left), edgeThreshold)
     );
-    const rightXClosestLine = snapLines.vertical.find(line =>
-      isLessThanOrEqual(Math.abs(line.x - targetRect.right), edgeThreshold),
+    const rightXClosestLine = snapLines.vertical.find((line) =>
+      isLessThanOrEqual(Math.abs(line.x - targetRect.right), edgeThreshold)
     );
-    const midYClosestLine = snapLines.midHorizontal.find(line =>
-      isLessThanOrEqual(Math.abs(line.y - targetRect.center.y), edgeThreshold),
+    const midYClosestLine = snapLines.midHorizontal.find((line) =>
+      isLessThanOrEqual(Math.abs(line.y - targetRect.center.y), edgeThreshold)
     );
-    const midXClosestLine = snapLines.midVertical.find(line =>
-      isLessThanOrEqual(Math.abs(line.x - targetRect.center.x), edgeThreshold),
+    const midXClosestLine = snapLines.midVertical.find((line) =>
+      isLessThanOrEqual(Math.abs(line.x - targetRect.center.x), edgeThreshold)
     );
 
     // 计算最近坐标
@@ -227,11 +248,11 @@ export class WorkflowSnapService {
       x: snappedPosition.x - rect.x,
       y: snappedPosition.y - rect.y,
     };
-    targetNodes.forEach(node =>
+    targetNodes.forEach((node) =>
       this.updateNodePositionWithOffset({
         node,
         offset,
-      }),
+      })
     );
   }
 
@@ -256,7 +277,7 @@ export class WorkflowSnapService {
     const midHorizontalLines: SnapMidHorizontalLine[] = [];
     const midVerticalLines: SnapMidVerticalLine[] = [];
 
-    snapNodeRects.forEach(snapNodeRect => {
+    snapNodeRects.forEach((snapNodeRect) => {
       const nodeBounds = snapNodeRect.rect;
       const nodeCenter = nodeBounds.center;
       // 边缘横线
@@ -310,11 +331,11 @@ export class WorkflowSnapService {
     const targetCenter = targetRect.center;
     const targetContainerId = targetNodes[0].parent?.id ?? this.document.root.id;
 
-    const disabledNodeIds = targetNodes.map(n => n.id);
+    const disabledNodeIds = targetNodes.map((n) => n.id);
     disabledNodeIds.push(FlowNodeBaseType.ROOT);
     const availableNodes = this.nodes
-      .filter(n => n.parent?.id === targetContainerId)
-      .filter(n => !disabledNodeIds.includes(n.id))
+      .filter((n) => n.parent?.id === targetContainerId)
+      .filter((n) => !disabledNodeIds.includes(n.id))
       .sort((nodeA, nodeB) => {
         const nodeCenterA = nodeA.getData(FlowNodeTransformData)!.bounds.center;
         const nodeCenterB = nodeB.getData(FlowNodeTransformData)!.bounds.center;
@@ -340,7 +361,7 @@ export class WorkflowSnapService {
     const availableNodes = this.getAvailableNodes(params);
     const viewRect = this.viewRect();
     return availableNodes
-      .map(node => {
+      .map((node) => {
         const snapNodeRect: SnapNodeRect = {
           id: node.id,
           rect: node.getData(FlowNodeTransformData).bounds,
@@ -367,7 +388,7 @@ export class WorkflowSnapService {
     if (nodes.length === 0) {
       return Rectangle.EMPTY;
     }
-    return Rectangle.enlarge(nodes.map(n => n.getData(FlowNodeTransformData)!.bounds));
+    return Rectangle.enlarge(nodes.map((n) => n.getData(FlowNodeTransformData)!.bounds));
   }
 
   private updateNodePositionWithOffset(params: { node: WorkflowNodeEntity; offset: IPoint }): void {
@@ -379,7 +400,7 @@ export class WorkflowSnapService {
     };
     if (node.collapsedChildren?.length > 0) {
       // 嵌套情况下需将子节点 transform 设为 dirty
-      node.collapsedChildren.forEach(childNode => {
+      node.collapsedChildren.forEach((childNode) => {
         const childNodeTransformData =
           childNode.getData<FlowNodeTransformData>(FlowNodeTransformData);
         childNodeTransformData.fireChange();
@@ -451,7 +472,7 @@ export class WorkflowSnapService {
       const rightAlignX = alignRects.right[0].rect.left - alignSpacing.right;
       const isAlignRight = isLessThanOrEqual(
         Math.abs(targetRect.right - rightAlignX),
-        alignThreshold,
+        alignThreshold
       );
       if (isAlignRight) {
         rightX = rightAlignX - targetRect.width;
@@ -463,7 +484,7 @@ export class WorkflowSnapService {
       const leftAlignX = alignRects.left[0].rect.right + alignSpacing.midHorizontal;
       const isAlignMidHorizontal = isLessThanOrEqual(
         Math.abs(targetRect.left - leftAlignX),
-        alignThreshold,
+        alignThreshold
       );
       if (isAlignMidHorizontal) {
         midX = leftAlignX;
@@ -475,7 +496,7 @@ export class WorkflowSnapService {
       const topAlignY = alignRects.top[0].rect.bottom + alignSpacing.midVertical;
       const isAlignMidVertical = isLessThanOrEqual(
         Math.abs(targetRect.top - topAlignY),
-        alignThreshold,
+        alignThreshold
       );
       if (isAlignMidVertical) {
         midY = topAlignY;
@@ -552,7 +573,7 @@ export class WorkflowSnapService {
     const leftHorizontalRects: AlignRect[] = [];
     const rightHorizontalRects: AlignRect[] = [];
 
-    snapNodeRects.forEach(snapNodeRect => {
+    snapNodeRects.forEach((snapNodeRect) => {
       const nodeRect = snapNodeRect.rect;
       const { isVerticalIntersection, isHorizontalIntersection, isIntersection } =
         this.intersection(nodeRect, targetRect);
@@ -612,7 +633,7 @@ export class WorkflowSnapService {
     }
     const { isVerticalIntersection, isHorizontalIntersection, isIntersection } = this.intersection(
       rectA,
-      rectB,
+      rectB
     );
     if (isIntersection) {
       return;
@@ -620,13 +641,13 @@ export class WorkflowSnapService {
     if (isHorizontal && isHorizontalIntersection && !isVerticalIntersection) {
       const betweenSpacing = Math.min(
         Math.abs(rectA.left - rectB.right),
-        Math.abs(rectA.right - rectB.left),
+        Math.abs(rectA.right - rectB.left)
       );
       return (betweenSpacing - targetRect.width) / 2;
     } else if (!isHorizontal && isVerticalIntersection && !isHorizontalIntersection) {
       const betweenSpacing = Math.min(
         Math.abs(rectA.top - rectB.bottom),
-        Math.abs(rectA.bottom - rectB.top),
+        Math.abs(rectA.bottom - rectB.top)
       );
       return (betweenSpacing - targetRect.height) / 2;
     }
@@ -646,7 +667,7 @@ export class WorkflowSnapService {
 
     const { isVerticalIntersection, isHorizontalIntersection, isIntersection } = this.intersection(
       rectA,
-      rectB,
+      rectB
     );
 
     if (isIntersection) {
@@ -663,7 +684,7 @@ export class WorkflowSnapService {
 
   private intersection(
     rectA: Rectangle,
-    rectB: Rectangle,
+    rectB: Rectangle
   ): {
     isHorizontalIntersection: boolean;
     isVerticalIntersection: boolean;