mirror of
https://gitee.com/ByteDance/flowgram.ai.git
synced 2025-07-07 17:43:29 +08:00
322 lines
9.3 KiB
Plaintext
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())
|
|
}
|
|
```
|
|
|
|
|