Bläddra i källkod

feat(free-layout): output port support left location and refactor arrow render (#766)

xiamidaxia 4 månader sedan
förälder
incheckning
c208758f75

+ 3 - 1
packages/canvas-engine/free-layout-core/src/entities/workflow-line-entity.ts

@@ -435,7 +435,9 @@ export class WorkflowLineEntity extends Entity<WorkflowLineEntityOpts> {
     return this.linesManager.isDisabledLine(this, this.uiState.disabled);
   }
 
-  /** 是否竖向 */
+  /**
+   * @deprecated
+   */
   get vertical(): boolean {
     const fromLocation = this.fromPort.location;
     const toLocation = this.toPort?.location;

+ 2 - 1
packages/canvas-engine/free-layout-core/src/entity-datas/workflow-node-ports-data.ts

@@ -8,7 +8,7 @@ import { FlowNodeRenderData } from '@flowgram.ai/document';
 import { EntityData, SizeData } from '@flowgram.ai/core';
 
 import { type WorkflowPortType, getPortEntityId } from '../utils/statics';
-import { type LinePoint, type WorkflowNodeMeta } from '../typings';
+import { type LinePoint, LinePointLocation, type WorkflowNodeMeta } from '../typings';
 import { WorkflowPortEntity } from '../entities/workflow-port-entity';
 import { type WorkflowNodeEntity, type WorkflowPort, type WorkflowPorts } from '../entities';
 
@@ -96,6 +96,7 @@ export class WorkflowNodePortsData extends EntityData {
         ...Array.from(elements).map((element) => ({
           portID: element.getAttribute('data-port-id')!,
           type: element.getAttribute('data-port-type')! as WorkflowPortType,
+          location: element.getAttribute('data-port-location')! as LinePointLocation,
           targetElement: element,
         }))
       );

+ 15 - 2
packages/canvas-engine/free-layout-core/src/service/workflow-drag-service.ts

@@ -38,7 +38,7 @@ import { WorkflowLinesManager } from '../workflow-lines-manager';
 import { WorkflowDocumentOptions } from '../workflow-document-option';
 import { WorkflowDocument } from '../workflow-document';
 import { LineEventProps, NodesDragEvent, OnDragLineEnd } from '../typings/workflow-drag';
-import { type WorkflowNodeJSON, type WorkflowNodeMeta } from '../typings';
+import { LinePointLocation, type WorkflowNodeJSON, type WorkflowNodeMeta } from '../typings';
 import { WorkflowNodePortsData } from '../entity-datas';
 import {
   type WorkflowLineEntity,
@@ -67,6 +67,19 @@ function checkDragSuccess(
   return false;
 }
 
+function reverseLocation(sourceLocation: LinePointLocation): LinePointLocation {
+  switch (sourceLocation) {
+    case 'bottom':
+      return 'top';
+    case 'left':
+      return 'right';
+    case 'top':
+      return 'bottom';
+    case 'right':
+      return 'left';
+  }
+}
+
 @injectable()
 export class WorkflowDragService {
   @inject(PlaygroundConfigEntity)
@@ -669,7 +682,7 @@ export class WorkflowDragService {
           line.drawingTo = {
             x: dragPos.x,
             y: dragPos.y,
-            location: line.fromPort.location === 'right' ? 'left' : 'top',
+            location: reverseLocation(line.fromPort.location),
           };
         }
 

+ 25 - 23
packages/plugins/free-lines-plugin/src/components/workflow-line-render/arrow.tsx

@@ -5,35 +5,37 @@
 
 import React from 'react';
 
+import { IPoint } from '@flowgram.ai/utils';
+import { LinePointLocation } from '@flowgram.ai/free-layout-core';
+
 import { type ArrowRendererProps } from '../../types/arrow-renderer';
 import { LINE_OFFSET } from '../../constants/lines';
 
-export function ArrowRenderer({
-  id,
-  pos,
-  reverseArrow,
-  strokeWidth,
-  vertical,
-  hide,
-}: ArrowRendererProps) {
+function getArrowPath(pos: IPoint, location: LinePointLocation): string {
+  switch (location) {
+    case 'left':
+      return `M ${pos.x - LINE_OFFSET},${pos.y - LINE_OFFSET} L ${pos.x},${pos.y} L ${
+        pos.x - LINE_OFFSET
+      },${pos.y + LINE_OFFSET}`;
+    case 'right':
+      return `M ${pos.x + LINE_OFFSET},${pos.y + LINE_OFFSET} L ${pos.x},${pos.y} L ${
+        pos.x + LINE_OFFSET
+      },${pos.y - LINE_OFFSET}`;
+    case 'bottom':
+      return `M ${pos.x - LINE_OFFSET},${pos.y + LINE_OFFSET} L ${pos.x},${pos.y} L ${
+        pos.x + LINE_OFFSET
+      },${pos.y + LINE_OFFSET}`;
+    case 'top':
+      return `M ${pos.x - LINE_OFFSET},${pos.y - LINE_OFFSET} L ${pos.x},${pos.y} L ${
+        pos.x + LINE_OFFSET
+      },${pos.y - LINE_OFFSET}`;
+  }
+}
+export function ArrowRenderer({ id, pos, strokeWidth, location, hide }: ArrowRendererProps) {
   if (hide) {
     return null;
   }
-  const arrowPath = vertical
-    ? reverseArrow
-      ? `M ${pos.x - LINE_OFFSET},${pos.y} L ${pos.x},${pos.y - LINE_OFFSET} L ${
-          pos.x + LINE_OFFSET
-        },${pos.y}`
-      : `M ${pos.x - LINE_OFFSET},${pos.y - LINE_OFFSET} L ${pos.x},${pos.y} L ${
-          pos.x + LINE_OFFSET
-        },${pos.y - LINE_OFFSET}`
-    : reverseArrow
-    ? `M ${pos.x},${pos.y + LINE_OFFSET} L ${pos.x - LINE_OFFSET},${pos.y} L ${pos.x},${
-        pos.y - LINE_OFFSET
-      }`
-    : `M ${pos.x - LINE_OFFSET},${pos.y - LINE_OFFSET} L ${pos.x},${pos.y} L ${
-        pos.x - LINE_OFFSET
-      },${pos.y + LINE_OFFSET}`;
+  const arrowPath = getArrowPath(pos, location);
 
   return (
     <path

+ 4 - 12
packages/plugins/free-lines-plugin/src/components/workflow-line-render/line-svg.tsx

@@ -7,13 +7,12 @@ import React from 'react';
 
 import clsx from 'clsx';
 import { type IPoint } from '@flowgram.ai/utils';
-import { POINT_RADIUS } from '@flowgram.ai/free-layout-core';
 import { WorkflowLineRenderData } from '@flowgram.ai/free-layout-core';
 
 import { type ArrowRendererComponent } from '../../types/arrow-renderer';
 import { LineRenderProps } from '../../type';
+import { posWithShrink } from '../../contributions/utils';
 import { STROKE_WIDTH_SLECTED, STROKE_WIDTH } from '../../constants/points';
-import { LINE_OFFSET } from '../../constants/lines';
 import { LineStyle } from './index.style';
 import { ArrowRenderer } from './arrow';
 
@@ -36,14 +35,8 @@ export const LineSVG = (props: LineRenderProps) => {
   const toPos = toRelative(position.to);
 
   // 箭头位置计算
-  const arrowToPos: IPoint =
-    position.to.location === 'top'
-      ? { x: toPos.x, y: toPos.y - POINT_RADIUS }
-      : { x: toPos.x - POINT_RADIUS, y: toPos.y };
-  const arrowFromPos: IPoint =
-    position.from.location === 'bottom'
-      ? { x: fromPos.x, y: fromPos.y + POINT_RADIUS + LINE_OFFSET }
-      : { x: fromPos.x + POINT_RADIUS + LINE_OFFSET, y: fromPos.y };
+  const arrowToPos: IPoint = posWithShrink(toPos, position.to.location, line.uiState.shrink);
+  const arrowFromPos: IPoint = posWithShrink(fromPos, position.from.location, line.uiState.shrink);
 
   const strokeWidth = selected
     ? line.uiState.strokeWidthSelected ?? STROKE_WIDTH_SLECTED
@@ -95,10 +88,9 @@ export const LineSVG = (props: LineRenderProps) => {
           {path}
           <ArrowComponent
             id={strokeID}
-            reverseArrow={reverse}
             pos={reverse ? arrowFromPos : arrowToPos}
             strokeWidth={strokeWidth}
-            vertical={vertical}
+            location={reverse ? position.from.location : position.to.location}
             hide={hideArrow}
             line={line}
           />

+ 3 - 9
packages/plugins/free-lines-plugin/src/contributions/bezier/index.ts

@@ -13,7 +13,7 @@ import {
 } from '@flowgram.ai/free-layout-core';
 import { LineType } from '@flowgram.ai/free-layout-core';
 
-import { toRelative } from '../utils';
+import { posWithShrink, toRelative } from '../utils';
 import { getBezierControlPoints } from './bezier-controls';
 
 export interface BezierData {
@@ -111,15 +111,9 @@ export class WorkflowBezierLineContribution implements WorkflowLineRenderContrib
     const controls = params.controls.map((c) => toRelative(c, bbox));
     const shrink = this.entity.uiState.shrink;
 
-    const renderFromPos: IPoint =
-      params.fromPos.location === 'bottom'
-        ? { x: fromPos.x, y: fromPos.y + shrink }
-        : { x: fromPos.x + shrink, y: fromPos.y };
+    const renderFromPos: IPoint = posWithShrink(fromPos, params.fromPos.location, shrink);
 
-    const renderToPos: IPoint =
-      params.toPos.location === 'top'
-        ? { x: toPos.x, y: toPos.y - shrink }
-        : { x: toPos.x - shrink, y: toPos.y };
+    const renderToPos: IPoint = posWithShrink(toPos, params.toPos.location, shrink);
 
     const controlPoints = controls.map((s) => `${s.x} ${s.y}`).join(',');
     return `M${renderFromPos.x} ${renderFromPos.y} C ${controlPoints}, ${renderToPos.x} ${renderToPos.y}`;

+ 5 - 13
packages/plugins/free-lines-plugin/src/contributions/fold/index.ts

@@ -12,7 +12,7 @@ import {
 } from '@flowgram.ai/free-layout-core';
 import { LineType } from '@flowgram.ai/free-layout-core';
 
-import { toRelative } from '../utils';
+import { posWithShrink, toRelative } from '../utils';
 import { FoldLine } from './fold-line';
 
 export interface FoldData {
@@ -60,24 +60,16 @@ export class WorkflowFoldLineContribution implements WorkflowLineRenderContribut
     const shrink = this.entity.uiState.shrink;
 
     // 根据方向预先计算源点和目标点的偏移
-    const sourceOffset = {
-      x: fromPos.location === 'bottom' ? 0 : shrink,
-      y: fromPos.location === 'bottom' ? shrink : 0,
-    };
-    const targetOffset = {
-      x: toPos.location === 'top' ? 0 : -shrink,
-      y: toPos.location === 'top' ? -shrink : 0,
-    };
+    const source = posWithShrink(fromPos, fromPos.location, shrink);
+    const target = posWithShrink(toPos, toPos.location, shrink);
 
     const { points, center } = FoldLine.getPoints({
       source: {
-        x: fromPos.x + sourceOffset.x,
-        y: fromPos.y + sourceOffset.y,
+        ...source,
         location: fromPos.location,
       },
       target: {
-        x: toPos.x + targetOffset.x,
-        y: toPos.y + targetOffset.y,
+        ...target,
         location: toPos.location,
       },
     });

+ 4 - 18
packages/plugins/free-lines-plugin/src/contributions/straight/index.ts

@@ -15,6 +15,7 @@ import {
 
 import { LINE_PADDING } from '../../constants/lines';
 import { projectPointOnLine } from './point-on-line';
+import { posWithShrink } from '../utils';
 
 export interface StraightData {
   points: IPoint[];
@@ -62,25 +63,10 @@ export class WorkflowStraightLineContribution implements WorkflowLineRenderContr
     const shrink = this.entity.uiState.shrink;
 
     // 根据方向预先计算源点和目标点的偏移
-    const sourceOffset = {
-      x: fromPos.location === 'bottom' ? 0 : shrink,
-      y: fromPos.location === 'bottom' ? shrink : 0,
-    };
-    const targetOffset = {
-      x: toPos.location === 'top' ? 0 : -shrink,
-      y: toPos.location === 'top' ? -shrink : 0,
-    };
+    const source = posWithShrink(fromPos, fromPos.location, shrink);
+    const target = posWithShrink(toPos, toPos.location, shrink);
 
-    const points = [
-      {
-        x: fromPos.x + sourceOffset.x,
-        y: fromPos.y + sourceOffset.y,
-      },
-      {
-        x: toPos.x + targetOffset.x,
-        y: toPos.y + targetOffset.y,
-      },
-    ];
+    const points = [source, target];
 
     const bbox = Rectangle.createRectangleWithTwoPoints(points[0], points[1]);
 

+ 22 - 0
packages/plugins/free-lines-plugin/src/contributions/utils.ts

@@ -4,6 +4,7 @@
  */
 
 import { IPoint, Rectangle } from '@flowgram.ai/utils';
+import { LinePointLocation } from '@flowgram.ai/free-layout-core';
 
 import { LINE_PADDING } from '../constants/lines';
 
@@ -13,3 +14,24 @@ export function toRelative(p: IPoint, bbox: Rectangle): IPoint {
     y: p.y - bbox.y + LINE_PADDING,
   };
 }
+
+export function getShrinkOffset(location: LinePointLocation, shrink: number): IPoint {
+  switch (location) {
+    case 'left':
+      return { x: -shrink, y: 0 };
+    case 'right':
+      return { x: shrink, y: 0 };
+    case 'bottom':
+      return { x: 0, y: shrink };
+    case 'top':
+      return { x: 0, y: -shrink };
+  }
+}
+
+export function posWithShrink(pos: IPoint, location: LinePointLocation, shrink: number): IPoint {
+  const offset = getShrinkOffset(location, shrink);
+  return {
+    x: pos.x + offset.x,
+    y: pos.y + offset.y,
+  };
+}

+ 2 - 4
packages/plugins/free-lines-plugin/src/types/arrow-renderer.ts

@@ -6,6 +6,7 @@
 import React from 'react';
 
 import { type IPoint } from '@flowgram.ai/utils';
+import { LinePointLocation } from '@flowgram.ai/free-layout-core/src';
 import { type WorkflowLineEntity } from '@flowgram.ai/free-layout-core';
 
 /**
@@ -16,12 +17,9 @@ export interface ArrowRendererProps {
   id: string;
   /** 箭头位置 */
   pos: IPoint;
-  /** 是否反转箭头方向 */
-  reverseArrow: boolean;
+  location: LinePointLocation;
   /** 描边宽度 */
   strokeWidth: number;
-  /** 是否为垂直方向 */
-  vertical?: boolean;
   /** 是否隐藏箭头 */
   hide?: boolean;
   /** 线条实体,提供更多上下文信息 */