小智 02e48ebdd9
feat(core): add flowing field to LineColor interface (#370)
Co-authored-by: husky-dot <xiaozhi@xiaozhideMacBook-Pro.local>
2025-06-12 11:18:12 +00:00

322 lines
9.3 KiB
Plaintext

# Lines
- [WorkflowLinesManager](https://github.com/bytedance/flowgram.ai/blob/main/packages/canvas-engine/free-layout-core/src/workflow-lines-manager.ts) manages all lines
- [WorkflowNodeLinesData](https://github.com/bytedance/flowgram.ai/blob/main/packages/canvas-engine/free-layout-core/src/entity-datas/workflow-node-lines-data.ts) manages lines connected to nodes
- [WorkflowLineEntity](https://github.com/bytedance/flowgram.ai/blob/main/packages/canvas-engine/free-layout-core/src/entities/workflow-line-entity.ts) line entity
## Get All Line Entities
```ts pure
const allLines = ctx.document.linesManager.getAllLines() // line entities containing from/to representing connected nodes
```
## Create/Delete Lines
```ts pure
// from and to are the node IDs to connect, fromPort, toPort are port IDs, if node has single port can be omitted
const line = ctx.document.linesManager.createLine({ from, to, fromPort, toPort })
// delete line
line.dispose()
```
## Export Line Data
:::note Basic line structure:
- sourceNodeID: `string` source node id
- targetNodeID: `string` target node id
- sourcePortID?: `string | number` source port id, defaults to node's default port if omitted
- targetPortID?: `string | number` target port id, defaults to node's default port if omitted
:::
```ts pure
const json = ctx.document.linesManager.toJSON()
```
## Get Input/Output Nodes or Lines for Current Node
```ts pure
import { WorkflowNodeLinesData } from '@flowgram.ai/free-layout-editor'
// get input nodes (calculated through connection lines)
node.getData(WorkflowNodeLinesData).inputNodes
// get all input nodes (recursively gets all upstream nodes)
node.getData(WorkflowNodeLinesData).allInputNodes
// get output nodes
node.getData(WorkflowNodeLinesData).outputNodes
// get all output nodes
node.getData(WorkflowNodeLinesData).allOutputNodes
// input lines
node.getData(WorkflowNodeLinesData).inputLines
// output lines
node.getData(WorkflowNodeLinesData).outputLines
```
## Line Configuration
We provide rich line configuration parameters for `FreeLayoutEditorProvider`, see details in [FreeLayoutProps](https://github.com/bytedance/flowgram.ai/blob/main/packages/client/free-layout-editor/src/preset/free-layout-props.ts)
```tsx pure
interface FreeLayoutProps {
/**
* Line color configuration
*/
lineColor?: LineColor;
/**
* Determine if line should be marked red
* @param ctx
* @param fromPort
* @param toPort
* @param lines
*/
isErrorLine?: (ctx: FreeLayoutPluginContext, fromPort: WorkflowPortEntity, toPort: WorkflowPortEntity | undefined, lines: WorkflowLinesManager) => boolean;
/**
* Determine if port should be marked red
* @param ctx
* @param port
*/
isErrorPort?: (ctx: FreeLayoutPluginContext, port: WorkflowPortEntity) => boolean;
/**
* Determine if port should be disabled
* @param ctx
* @param port
*/
isDisabledPort?: (ctx: FreeLayoutPluginContext, port: WorkflowPortEntity) => boolean;
/**
* Determine if line arrow should be reversed
* @param ctx
* @param line
*/
isReverseLine?: (ctx: FreeLayoutPluginContext, line: WorkflowLineEntity) => boolean;
/**
* Determine if line arrow should be hidden
* @param ctx
* @param line
*/
isHideArrowLine?: (ctx: FreeLayoutPluginContext, line: WorkflowLineEntity) => boolean;
/**
* Determine if line should show flowing effect
* @param ctx
* @param line
*/
isFlowingLine?: (ctx: FreeLayoutPluginContext, line: WorkflowLineEntity) => boolean;
/**
* Determine if line should be disabled
* @param ctx
* @param line
*/
isDisabledLine?: (ctx: FreeLayoutPluginContext, line: WorkflowLineEntity) => boolean;
/**
* Determine if line should be vertical
* @param ctx
* @param line
*/
isVerticalLine?: (ctx: FreeLayoutPluginContext, line: WorkflowLineEntity) => boolean;
/**
* Line drag end
* @param ctx
* @param params
*/
onDragLineEnd?: (ctx: FreeLayoutPluginContext, params: onDragLineEndParams) => Promise<void>;
/**
* Set line renderer type
* @param ctx
* @param line
*/
setLineRenderType?: (ctx: FreeLayoutPluginContext, line: WorkflowLineEntity) => LineRenderType | undefined;
/**
* Set line style
* @param ctx
* @param line
*/
setLineClassName?: (ctx: FreeLayoutPluginContext, line: WorkflowLineEntity) => string | undefined;
/**
* Whether to allow line creation
* @param ctx
* @param fromPort - start point
* @param toPort - target point
*/
canAddLine?: (ctx: FreeLayoutPluginContext, fromPort: WorkflowPortEntity, toPort: WorkflowPortEntity, lines: WorkflowLinesManager, silent?: boolean) => boolean;
/**
* Whether to allow line deletion
* @param ctx
* @param line - target line
* @param newLineInfo - new line info
* @param silent - if false, can show toast
*/
canDeleteLine?: (ctx: FreeLayoutPluginContext, line: WorkflowLineEntity, newLineInfo?: Required<WorkflowLinePortInfo>, silent?: boolean) => boolean;
/**
* Whether to allow line reset
* @param fromPort - start point
* @param oldToPort - old connection point
* @param newToPort - new connection point
* @param lines - line manager
*/
canResetLine?: (ctx: FreeLayoutPluginContext, fromPort: WorkflowPortEntity, oldToPort: WorkflowPortEntity, newToPort: WorkflowPortEntity, lines: WorkflowLinesManager) => boolean;
}
```
### 1. Custom Colors
```tsx pure
function App() {
const editorProps: FreeLayoutProps = {
lineColor: {
hidden: 'transparent',
default: '#4d53e8',
drawing: '#5DD6E3',
hovered: '#37d0ff',
selected: '#37d0ff',
error: 'red',
flowing: '#ff6b35', // Color for flowing lines (e.g., during workflow execution)
},
// ...others
}
return (
<FreeLayoutEditorProvider {...editorProps}>
<EditorRenderer className="demo-editor" />
</FreeLayoutEditorProvider>
)
}
```
### 2. Limit Single Output Port to One Line
<img loading="lazy" style={{ width: 500, margin: '0 auto' }} className="invert-img" src="/free-layout/line-limit.gif"/>
```tsx pure
function App() {
const editorProps: FreeLayoutProps = {
/*
* Check whether the line can be added
*/
canAddLine(ctx, fromPort, toPort) {
// not the same node
if (fromPort.node === toPort.node) {
return false;
}
// control number of lines
if (fromPort.availableLines.length >= 1) {
return false
}
return true;
},
// ...others
}
return (
<FreeLayoutEditorProvider {...editorProps}>
<EditorRenderer className="demo-editor" />
</FreeLayoutEditorProvider>
)
}
```
### 3. Add Node by Connecting to Empty Space
See code in free layout best practices
<img loading="lazy" style={{ width: 500, margin: '0 auto' }} className="invert-img" src="/free-layout/line-add-panel.gif"/>
```tsx pure
function App() {
const editorProps: FreeLayoutProps = {
/**
* Drag the end of the line to create an add panel (feature optional)
*/
async onDragLineEnd(ctx, params) {
const { fromPort, toPort, mousePos, line, originLine } = params;
if (originLine || !line) {
return;
}
if (toPort) {
return;
}
// Can open add panel based on mousePos here
await ctx.get(WorkflowNodePanelService).call({
fromPort,
toPort: undefined,
panelPosition: mousePos,
enableBuildLine: true,
panelProps: {
enableNodePlaceholder: true,
enableScrollClose: true,
},
});
},
// ...others
}
return (
<FreeLayoutEditorProvider {...editorProps}>
<EditorRenderer className="demo-editor" />
</FreeLayoutEditorProvider>
)
}
```
## Add Label to Line
See code in free layout best practices
<img loading="lazy" style={{ width: 500, margin: '0 auto' }} className="invert-img" src="/free-layout/line-add-button.gif"/>
```ts pure
import { createFreeLinesPlugin } from '@flowgram.ai/free-lines-plugin';
const editorProps = {
plugins: () => [
/**
* Line render plugin
*/
createFreeLinesPlugin({
renderInsideLine: LineAddButton,
}),
]
}
```
## Node Listening to Its Own Line Changes and Refresh
```tsx pure
import {
useRefresh,
WorkflowNodeLinesData,
} from '@flowgram.ai/free-layout-editor';
function NodeRender({ node }) {
const refresh = useRefresh()
const linesData = node.get(WorkflowNodeLinesData)
useEffect(() => {
const dispose = linesData.onDataChange(() => refresh())
return () => dispose.dispose()
}, [])
return <div>xxxx</div>
}
```
## Listen to All Line Connection Changes
This scenario is used when you want to monitor line connections in external components
```ts pure
import { useEffect } from 'react'
import { WorkflowLinesManager, useRefresh } from '@flowgram.ai/free-layout-editor'
function SomeReact() {
const refresh = useRefresh()
const linesManager = useService(WorkflowLinesManager)
useEffect(() => {
const dispose = linesManager.onAvailableLinesChange(() => refresh())
return () => dispose.dispose()
}, [])
console.log(ctx.document.linesManager.getAllLines())
}
```