This commit is contained in:
StyleZhang 2024-03-14 21:07:38 +08:00
parent bcce53a929
commit 05ac27dfa8
7 changed files with 108 additions and 12 deletions

View File

@ -4,9 +4,16 @@ import type {
EdgeMouseHandler,
OnEdgesChange,
} from 'reactflow'
import { useStoreApi } from 'reactflow'
import {
getConnectedEdges,
useStoreApi,
} from 'reactflow'
import { useStore } from '../store'
import type { Node } from '../types'
import type {
Edge,
Node,
} from '../types'
import { getNodesConnectedSourceOrTargetHandleIdsMap } from '../utils'
import { useNodesSyncDraft } from './use-nodes-sync-draft'
export const useEdgesInteractions = () => {
@ -140,11 +147,63 @@ export const useEdgesInteractions = () => {
setEdges(newEdges)
}, [store])
const handleVariableAssignerEdgesChange = useCallback((nodeId: string, variables: any) => {
const {
getNodes,
setNodes,
edges,
setEdges,
} = store.getState()
const nodes = getNodes()
const newEdgesTargetHandleIds = variables.map((item: any) => item[0])
const connectedEdges = getConnectedEdges([{ id: nodeId } as Node], edges).filter(edge => edge.target === nodeId)
const needDeleteEdges = connectedEdges.filter(edge => !newEdgesTargetHandleIds.includes(edge.targetHandle))
const needAddEdgesTargetHandleIds = newEdgesTargetHandleIds.filter((targetHandle: string) => !connectedEdges.some(edge => edge.targetHandle === targetHandle))
const needAddEdges = needAddEdgesTargetHandleIds.map((targetHandle: string) => {
return {
id: `${targetHandle}-${nodeId}`,
type: 'custom',
source: targetHandle,
sourceHandle: 'source',
target: nodeId,
targetHandle,
}
})
const nodesConnectedSourceOrTargetHandleIdsMap = getNodesConnectedSourceOrTargetHandleIdsMap(
[
...needDeleteEdges.map(edge => ({ type: 'remove', edge })),
...needAddEdges.map((edge: Edge) => ({ type: 'add', edge })),
],
nodes,
)
const newNodes = produce(nodes, (draft) => {
draft.forEach((node) => {
if (nodesConnectedSourceOrTargetHandleIdsMap[node.id]) {
node.data = {
...node.data,
...nodesConnectedSourceOrTargetHandleIdsMap[node.id],
}
}
})
})
setNodes(newNodes)
const newEdges = produce(edges, (draft) => {
const filtered = draft.filter(edge => !needDeleteEdges.map(needDeleteEdge => needDeleteEdge.id).includes(edge.id))
filtered.push(...needAddEdges)
return filtered
})
setEdges(newEdges)
}, [store])
return {
handleEdgeEnter,
handleEdgeLeave,
handleEdgeDeleteByDeleteBranch,
handleEdgeDelete,
handleEdgesChange,
handleVariableAssignerEdgesChange,
}
}

View File

@ -12,10 +12,10 @@ import {
} from 'reactflow'
import type { ToolDefaultValue } from '../block-selector/types'
import type {
BlockEnum,
Node,
OnNodeAdd,
} from '../types'
import { BlockEnum } from '../types'
import { useStore } from '../store'
import {
NODE_WIDTH_X_OFFSET,
@ -362,6 +362,9 @@ export const useNodesInteractions = () => {
if (runningStatus)
return
if (nodeType === BlockEnum.VariableAssigner)
targetHandle = 'varNotSet'
const {
getNodes,
setNodes,
@ -369,9 +372,11 @@ export const useNodesInteractions = () => {
setEdges,
} = store.getState()
const nodes = getNodes()
const nodesWithSameType = nodes.filter(node => node.data.type === nodeType)
const newNode = generateNewNode({
data: {
...nodesInitialData[nodeType],
title: nodesWithSameType.length > 0 ? `${nodesInitialData[nodeType].title} ${nodesWithSameType.length + 1}` : nodesInitialData[nodeType].title,
...(toolDefaultValue || {}),
selected: true,
},
@ -386,6 +391,7 @@ export const useNodesInteractions = () => {
const outgoers = getOutgoers(prevNode, nodes, edges).sort((a, b) => a.position.y - b.position.y)
const lastOutgoer = outgoers[outgoers.length - 1]
newNode.data._connectedTargetHandleIds = [targetHandle]
newNode.data._connectedSourceHandleIds = []
newNode.position = {
x: lastOutgoer ? lastOutgoer.position.x : prevNode.position.x + NODE_WIDTH_X_OFFSET,
y: lastOutgoer ? lastOutgoer.position.y + lastOutgoer.height! + Y_OFFSET : prevNode.position.y,
@ -418,6 +424,7 @@ export const useNodesInteractions = () => {
const nextNodeIndex = nodes.findIndex(node => node.id === nextNodeId)
const nextNode = nodes[nextNodeIndex]!
newNode.data._connectedSourceHandleIds = [sourceHandle]
newNode.data._connectedTargetHandleIds = []
newNode.position = {
x: nextNode.position.x,
y: nextNode.position.y,
@ -534,10 +541,14 @@ export const useNodesInteractions = () => {
const nodes = getNodes()
const currentNode = nodes.find(node => node.id === currentNodeId)!
const connectedEdges = getConnectedEdges([currentNode], edges)
const nodesWithSameType = nodes.filter(node => node.data.type === nodeType)
const newCurrentNode = generateNewNode({
data: {
...nodesInitialData[nodeType],
title: nodesWithSameType.length > 0 ? `${nodesInitialData[nodeType].title} ${nodesWithSameType.length + 1}` : nodesInitialData[nodeType].title,
...(toolDefaultValue || {}),
_connectedSourceHandleIds: [],
_connectedTargetHandleIds: [],
selected: currentNode.data.selected,
},
position: {

View File

@ -45,10 +45,10 @@ export const useWorkflow = () => {
edges,
setNodes,
} = store.getState()
const nodes = getNodes()
const layout = getLayoutByDagre(nodes, edges)
const layout = getLayoutByDagre(getNodes(), edges)
const newNodes = produce(getNodes(), (draft) => {
const newNodes = produce(nodes, (draft) => {
draft.forEach((node) => {
const nodeWithPosition = layout.node(node.id)
node.position = {
@ -168,11 +168,21 @@ export const useWorkflow = () => {
return list
}, [store])
const getIncomersNodes = useCallback((currentNode: Node) => {
const {
getNodes,
edges,
} = store.getState()
return getIncomers(currentNode, getNodes(), edges)
}, [store])
return {
handleLayout,
getTreeLeafNodes,
getBeforeNodesInSameBranch,
getAfterNodesInSameBranch,
getIncomersNodes,
}
}

View File

@ -2,21 +2,26 @@ import { useCallback } from 'react'
import produce from 'immer'
import type { VariableAssignerNodeType } from '../../types'
import type { ValueSelector } from '@/app/components/workflow/types'
import { useEdgesInteractions } from '@/app/components/workflow/hooks'
type Params = {
id: string
inputs: VariableAssignerNodeType
setInputs: (newInputs: VariableAssignerNodeType) => void
}
function useVarList({
id,
inputs,
setInputs,
}: Params) {
const { handleVariableAssignerEdgesChange } = useEdgesInteractions()
const handleVarListChange = useCallback((newList: ValueSelector[]) => {
const newInputs = produce(inputs, (draft) => {
draft.variables = newList
})
handleVariableAssignerEdgesChange(id, newList)
setInputs(newInputs)
}, [inputs, setInputs])
}, [inputs, setInputs, id, handleVariableAssignerEdgesChange])
const handleAddVariable = useCallback(() => {
const newInputs = produce(inputs, (draft) => {

View File

@ -13,8 +13,9 @@ const i18nPrefix = 'workflow.nodes.variableAssigner'
const Node: FC<NodeProps<VariableAssignerNodeType>> = (props) => {
const { t } = useTranslation()
const { data } = props
const { variables, output_type } = data
const { variables: originVariables, output_type } = data
const variables = originVariables.filter(item => item.length > 0)
// TODO: get var type through node and value
const getVarType = () => {
return 'string'
@ -25,8 +26,13 @@ const Node: FC<NodeProps<VariableAssignerNodeType>> = (props) => {
<div className='mb-0.5 leading-4 text-xs font-medium text-gray-500 uppercase'>{t(`${i18nPrefix}.title`)}</div>
{
variables.length === 0 && (
<div className='flex items-center h-6 justify-between bg-gray-100 rounded-md px-1 space-x-1 text-xs font-normal text-gray-400 uppercase'>
<div className='relative flex items-center h-6 justify-between bg-gray-100 rounded-md px-1 space-x-1 text-xs font-normal text-gray-400 uppercase'>
{t(`${i18nPrefix}.varNotSet`)}
<NodeTargetHandle
{...props}
handleId='varNotSet'
handleClassName='!top-1/2 !-translate-y-1/2 !-left-[21px]'
/>
</div>
)
}
@ -36,12 +42,13 @@ const Node: FC<NodeProps<VariableAssignerNodeType>> = (props) => {
{variables.map((item, index) => {
const node = getNodeInfoById([], item[0]) // TODO: can not get all nodes
const varName = item[item.length - 1]
return (
<div key={index} className='relative flex items-center h-6 bg-gray-100 rounded-md px-1 text-xs font-normal text-gray-700' >
<NodeTargetHandle
{...props}
handleId={varName}
handleClassName='!top-1 !-left-[21px]'
handleId={item[0]}
handleClassName='!top-1/2 !-translate-y-1/2 !-left-[21px]'
/>
<div className='flex items-center'>
<div className='p-[1px]'>

View File

@ -15,6 +15,7 @@ const useConfig = (id: string, payload: VariableAssignerNodeType) => {
}, [inputs, setInputs])
const { handleVarListChange, handleAddVariable } = useVarList({
id,
inputs,
setInputs,
})

View File

@ -4,6 +4,7 @@ import {
getOutgoers,
} from 'reactflow'
import dagre from 'dagre'
import { cloneDeep } from 'lodash-es'
import type {
Edge,
Node,
@ -145,7 +146,9 @@ export const getNodesPositionMap = (nodes: Node[], edges: Edge[]) => {
return positionMap
}
export const getLayoutByDagre = (nodes: Node[], edges: Edge[]) => {
export const getLayoutByDagre = (originNodes: Node[], originEdges: Edge[]) => {
const nodes = cloneDeep(originNodes)
const edges = cloneDeep(originEdges)
const dagreGraph = new dagre.graphlib.Graph()
dagreGraph.setGraph({
rankdir: 'LR',