Commit f6c07c99 authored by StyleZhang's avatar StyleZhang

workflow store

parent 7ba0bfff
'use client'
import { createContext, useContext } from 'use-context-selector'
import type { Edge } from 'reactflow'
import type {
BlockEnum,
Node,
} from './types'
export type WorkflowContextValue = {
mode: string
nodes: Node[]
edges: Edge[]
selectedNode?: Node
handleAddNextNode: (prevNode: Node, nextNodeType: BlockEnum) => void
handleUpdateNodeData: (nodeId: string, data: Node['data']) => void
}
export const WorkflowContext = createContext<WorkflowContextValue>({
mode: 'workflow',
nodes: [],
edges: [],
handleAddNextNode: () => {},
handleUpdateNodeData: () => {},
})
export const useWorkflowContext = () => useContext(WorkflowContext)
import type {
Dispatch,
SetStateAction,
} from 'react'
import { useCallback } from 'react' import { useCallback } from 'react'
import produce from 'immer' import produce from 'immer'
import type { import type { EdgeMouseHandler } from 'reactflow'
Edge, import { useStoreApi } from 'reactflow'
EdgeMouseHandler, import type { SelectedNode } from './types'
} from 'reactflow' import { useStore } from './store'
import type {
BlockEnum,
Node,
} from './types'
import { NodeInitialData } from './constants'
export const useWorkflow = ( export const useWorkflow = () => {
nodes: Node[], const store = useStoreApi()
edges: Edge[], const setSelectedNode = useStore(state => state.setSelectedNode)
setNodes: Dispatch<SetStateAction<Node[]>>,
setEdges: Dispatch<SetStateAction<Edge[]>>,
) => {
const handleAddNextNode = useCallback((prevNode: Node, nextNodeType: BlockEnum) => {
const nextNode = {
id: `node-${Date.now()}`,
type: 'custom',
position: {
x: prevNode.position.x + 304,
y: prevNode.position.y,
},
data: NodeInitialData[nextNodeType],
}
const newEdge = {
id: `edge-${Date.now()}`,
source: prevNode.id,
target: nextNode.id,
}
setNodes((oldNodes) => {
return produce(oldNodes, (draft) => {
draft.push(nextNode)
})
})
setEdges((oldEdges) => {
return produce(oldEdges, (draft) => {
draft.push(newEdge)
})
})
}, [setNodes, setEdges])
const handleUpdateNodeData = useCallback((nodeId: string, data: Node['data']) => {
setNodes((oldNodes) => {
return produce(oldNodes, (draft) => {
const node = draft.find(node => node.id === nodeId)
if (node)
node.data = data
})
})
}, [setNodes])
const handleEnterEdge = useCallback<EdgeMouseHandler>((_, edge) => { const handleEnterEdge = useCallback<EdgeMouseHandler>((_, edge) => {
setEdges((oldEdges) => { const {
return produce(oldEdges, (draft) => { edges,
const currentEdge = draft.find(e => e.id === edge.id) setEdges,
if (currentEdge) } = store.getState()
currentEdge.data = { ...currentEdge.data, hovering: true } const newEdges = produce(edges, (draft) => {
}) const currentEdge = draft.find(e => e.id === edge.id)
if (currentEdge)
currentEdge.data = { ...currentEdge.data, hovering: true }
}) })
}, [setEdges]) setEdges(newEdges)
}, [store])
const handleLeaveEdge = useCallback<EdgeMouseHandler>((_, edge) => { const handleLeaveEdge = useCallback<EdgeMouseHandler>((_, edge) => {
setEdges((oldEdges) => { const {
return produce(oldEdges, (draft) => { edges,
const currentEdge = draft.find(e => e.id === edge.id) setEdges,
if (currentEdge) } = store.getState()
currentEdge.data = { ...currentEdge.data, hovering: false } const newEdges = produce(edges, (draft) => {
}) const currentEdge = draft.find(e => e.id === edge.id)
if (currentEdge)
currentEdge.data = { ...currentEdge.data, hovering: false }
}) })
}, [setEdges]) setEdges(newEdges)
}, [store])
const handleSelectNode = useCallback((selectNode: SelectedNode, cancelSelection?: boolean) => {
const {
getNodes,
setNodes,
} = store.getState()
if (cancelSelection) {
setSelectedNode(null)
const newNodes = produce(getNodes(), (draft) => {
const currentNode = draft.find(n => n.id === selectNode.id)
if (currentNode)
currentNode.data = { ...currentNode.data, selected: false }
})
setNodes(newNodes)
}
else {
setSelectedNode(selectNode)
const newNodes = produce(getNodes(), (draft) => {
const currentNode = draft.find(n => n.id === selectNode.id)
if (currentNode)
currentNode.data = { ...currentNode.data, selected: true }
})
setNodes(newNodes)
}
}, [setSelectedNode, store])
return { return {
handleAddNextNode,
handleUpdateNodeData,
handleEnterEdge, handleEnterEdge,
handleLeaveEdge, handleLeaveEdge,
handleSelectNode,
} }
} }
...@@ -8,11 +8,6 @@ import ReactFlow, { ...@@ -8,11 +8,6 @@ import ReactFlow, {
useNodesState, useNodesState,
} from 'reactflow' } from 'reactflow'
import 'reactflow/dist/style.css' import 'reactflow/dist/style.css'
import './style.css'
import {
WorkflowContext,
useWorkflowContext,
} from './context'
import { useWorkflow } from './hooks' import { useWorkflow } from './hooks'
import Header from './header' import Header from './header'
import CustomNode from './nodes' import CustomNode from './nodes'
...@@ -20,7 +15,6 @@ import ZoomInOut from './zoom-in-out' ...@@ -20,7 +15,6 @@ import ZoomInOut from './zoom-in-out'
import CustomEdge from './custom-edge' import CustomEdge from './custom-edge'
import Panel from './panel' import Panel from './panel'
import type { Node } from './types' import type { Node } from './types'
import { useStore } from './store'
const nodeTypes = { const nodeTypes = {
custom: CustomNode, custom: CustomNode,
...@@ -29,13 +23,33 @@ const edgeTypes = { ...@@ -29,13 +23,33 @@ const edgeTypes = {
custom: CustomEdge, custom: CustomEdge,
} }
const Workflow = memo(() => { type WorkflowProps = {
selectedNodeId?: string
nodes: Node[]
edges: Edge[]
}
const Workflow: FC<WorkflowProps> = memo(({
nodes: initialNodes,
edges: initialEdges,
selectedNodeId: initialSelectedNodeId,
}) => {
const [nodes] = useNodesState(initialNodes)
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)
const { const {
nodes, handleEnterEdge,
edges, handleLeaveEdge,
} = useWorkflowContext() handleSelectNode,
const handleEnterEdge = useStore(state => state.handleEnterEdge) } = useWorkflow()
const handleLeaveEdge = useStore(state => state.handleLeaveEdge)
useEffect(() => {
if (initialSelectedNodeId) {
const initialSelectedNode = nodes.find(n => n.id === initialSelectedNodeId)
if (initialSelectedNode)
handleSelectNode({ id: initialSelectedNodeId, data: initialSelectedNode.data })
}
}, [initialSelectedNodeId])
return ( return (
<div className='relative w-full h-full'> <div className='relative w-full h-full'>
...@@ -47,8 +61,10 @@ const Workflow = memo(() => { ...@@ -47,8 +61,10 @@ const Workflow = memo(() => {
edgeTypes={edgeTypes} edgeTypes={edgeTypes}
nodes={nodes} nodes={nodes}
edges={edges} edges={edges}
onEdgesChange={onEdgesChange}
onEdgeMouseEnter={handleEnterEdge} onEdgeMouseEnter={handleEnterEdge}
onEdgeMouseLeave={handleLeaveEdge} onEdgeMouseLeave={handleLeaveEdge}
multiSelectionKeyCode={null}
> >
<Background <Background
gap={[14, 14]} gap={[14, 14]}
...@@ -58,74 +74,10 @@ const Workflow = memo(() => { ...@@ -58,74 +74,10 @@ const Workflow = memo(() => {
</div> </div>
) )
}) })
Workflow.displayName = 'Workflow'
type WorkflowWrapProps = { Workflow.displayName = 'Workflow'
selectedNodeId?: string
nodes: Node[]
edges: Edge[]
}
const WorkflowWrap: FC<WorkflowWrapProps> = memo(({
nodes: initialNodes,
edges: initialEdges,
selectedNodeId: initialSelectedNodeId,
}) => {
const [nodes, setNodes] = useNodesState(initialNodes)
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)
const {
handleAddNextNode,
handleUpdateNodeData,
handleEnterEdge,
handleLeaveEdge,
} = useWorkflow(
nodes,
edges,
setNodes,
setEdges,
)
const handleSelectedNodeId = useStore(state => state.handleSelectedNodeId)
useEffect(() => {
if (initialSelectedNodeId)
handleSelectedNodeId(initialSelectedNodeId)
}, [initialSelectedNodeId, handleSelectedNodeId])
// const handleEnterEdge = useStore(state => state.handleEnterEdge)
// const handleLeaveEdge = useStore(state => state.handleLeaveEdge)
return (
<WorkflowContext.Provider value={{
mode: 'workflow',
nodes,
edges,
handleAddNextNode,
handleUpdateNodeData,
}}>
<div className='relative w-full h-full'>
<Header />
<Panel />
<ZoomInOut />
<ReactFlow
nodeTypes={nodeTypes}
edgeTypes={edgeTypes}
nodes={nodes}
edges={edges}
onEdgesChange={onEdgesChange}
onEdgeMouseEnter={handleEnterEdge}
onEdgeMouseLeave={handleLeaveEdge}
>
<Background
gap={[14, 14]}
size={1}
/>
</ReactFlow>
</div>
</WorkflowContext.Provider>
)
})
WorkflowWrap.displayName = 'WorkflowWrap'
const WorkflowWrapWithReactFlowProvider: FC<WorkflowWrapProps> = ({ const WorkflowWrap: FC<WorkflowProps> = ({
selectedNodeId, selectedNodeId,
nodes, nodes,
edges, edges,
...@@ -133,7 +85,7 @@ const WorkflowWrapWithReactFlowProvider: FC<WorkflowWrapProps> = ({ ...@@ -133,7 +85,7 @@ const WorkflowWrapWithReactFlowProvider: FC<WorkflowWrapProps> = ({
return ( return (
<ReactFlowProvider> <ReactFlowProvider>
{selectedNodeId} {selectedNodeId}
<WorkflowWrap <Workflow
selectedNodeId={selectedNodeId} selectedNodeId={selectedNodeId}
nodes={nodes} nodes={nodes}
edges={edges} edges={edges}
...@@ -142,4 +94,4 @@ const WorkflowWrapWithReactFlowProvider: FC<WorkflowWrapProps> = ({ ...@@ -142,4 +94,4 @@ const WorkflowWrapWithReactFlowProvider: FC<WorkflowWrapProps> = ({
) )
} }
export default memo(WorkflowWrapWithReactFlowProvider) export default memo(WorkflowWrap)
import type { FC } from 'react'
import { import {
memo, memo,
useCallback, useCallback,
useMemo,
} from 'react' } from 'react'
import { getOutgoers } from 'reactflow'
import BlockIcon from '../../../block-icon' import BlockIcon from '../../../block-icon'
import type { Node } from '../../../types' import type { Node } from '../../../types'
import { BlockEnum } from '../../../types' import { BlockEnum } from '../../../types'
import { useWorkflowContext } from '../../../context' import { useStore } from '../../../store'
import BlockSelector from '../../../block-selector' import BlockSelector from '../../../block-selector'
import { Plus } from '@/app/components/base/icons/src/vender/line/general' import { Plus } from '@/app/components/base/icons/src/vender/line/general'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
type NextStepProps = { const NextStep = () => {
selectedNode: Node const selectedNode = useStore(state => state.selectedNode)
} const outgoers: Node[] = []
const NextStep: FC<NextStepProps> = ({
selectedNode,
}) => {
const {
nodes,
edges,
} = useWorkflowContext()
const outgoers = useMemo(() => {
return getOutgoers(selectedNode, nodes, edges)
}, [selectedNode, nodes, edges])
const renderAddNextNodeTrigger = useCallback((open: boolean) => { const renderAddNextNodeTrigger = useCallback((open: boolean) => {
return ( return (
...@@ -60,7 +47,7 @@ const NextStep: FC<NextStepProps> = ({ ...@@ -60,7 +47,7 @@ const NextStep: FC<NextStepProps> = ({
return ( return (
<div className='flex py-1'> <div className='flex py-1'>
<div className='shrink-0 relative flex items-center justify-center w-9 h-9 bg-white rounded-lg border-[0.5px] border-gray-200 shadow-xs'> <div className='shrink-0 relative flex items-center justify-center w-9 h-9 bg-white rounded-lg border-[0.5px] border-gray-200 shadow-xs'>
<BlockIcon type={selectedNode.data.type} /> <BlockIcon type={selectedNode!.data.type} />
</div> </div>
<div className='shrink-0 w-6'></div> <div className='shrink-0 w-6'></div>
<div className='grow'> <div className='grow'>
...@@ -74,7 +61,7 @@ const NextStep: FC<NextStepProps> = ({ ...@@ -74,7 +61,7 @@ const NextStep: FC<NextStepProps> = ({
type={outgoer.data.type} type={outgoer.data.type}
className='shrink-0 mr-1.5' className='shrink-0 mr-1.5'
/> />
<div className='grow'>{outgoer.data.name}</div> <div className='grow'>{outgoer.data.title}</div>
<BlockSelector <BlockSelector
onSelect={() => {}} onSelect={() => {}}
placement='top-end' placement='top-end'
...@@ -89,7 +76,7 @@ const NextStep: FC<NextStepProps> = ({ ...@@ -89,7 +76,7 @@ const NextStep: FC<NextStepProps> = ({
)) ))
} }
{ {
(!outgoers.length || selectedNode.data.type === BlockEnum.IfElse) && ( (!outgoers.length || selectedNode!.data.type === BlockEnum.IfElse) && (
<BlockSelector <BlockSelector
onSelect={() => {}} onSelect={() => {}}
placement='top' placement='top'
......
...@@ -5,13 +5,10 @@ import type { ...@@ -5,13 +5,10 @@ import type {
import { import {
cloneElement, cloneElement,
memo, memo,
useMemo,
} from 'react' } from 'react'
import type { NodeProps } from 'reactflow' import type { NodeProps } from 'reactflow'
import { useNodes } from 'reactflow'
import { useStore } from '../../store'
import type { NodeData } from '../../types'
import BlockIcon from '../../block-icon' import BlockIcon from '../../block-icon'
import { useWorkflow } from '../../hooks'
import BlockSelector from '../../block-selector' import BlockSelector from '../../block-selector'
import NodeControl from './components/node-control' import NodeControl from './components/node-control'
...@@ -25,27 +22,22 @@ const BaseNode: FC<BaseNodeProps> = ({ ...@@ -25,27 +22,22 @@ const BaseNode: FC<BaseNodeProps> = ({
selected, selected,
children, children,
}) => { }) => {
const nodes = useNodes<NodeData>() const { handleSelectNode } = useWorkflow()
const selectedNodeId = useStore(state => state.selectedNodeId)
const handleSelectedNodeId = useStore(state => state.handleSelectedNodeId)
const currentNode = useMemo(() => {
return nodes.find(node => node.id === nodeId)
}, [nodeId, nodes])
return ( return (
<div <div
className={` className={`
group relative pb-2 w-[240px] bg-[#fcfdff] rounded-2xl shadow-xs group relative pb-2 w-[240px] bg-[#fcfdff] rounded-2xl shadow-xs
hover:shadow-lg hover:shadow-lg
${selectedNodeId === nodeId ? 'border-[2px] border-primary-600' : 'border border-white'} ${(data.selected && selected) ? 'border-[2px] border-primary-600' : 'border border-white'}
`} `}
onClick={() => handleSelectedNodeId(nodeId || '')} onClick={() => handleSelectNode({ id: nodeId, data })}
> >
<NodeControl /> <NodeControl />
<div className='flex items-center px-3 pt-3 pb-2'> <div className='flex items-center px-3 pt-3 pb-2'>
<BlockIcon <BlockIcon
className='mr-2' className='mr-2'
type={currentNode!.data.type} type={data.type}
size='md' size='md'
/> />
<div className='text-[13px] font-semibold text-gray-700'> <div className='text-[13px] font-semibold text-gray-700'>
......
...@@ -5,12 +5,10 @@ import type { ...@@ -5,12 +5,10 @@ import type {
import { import {
cloneElement, cloneElement,
memo, memo,
useMemo,
} from 'react' } from 'react'
import type { NodeProps } from 'reactflow' import type { SelectedNode } from '../../types'
import { useWorkflowContext } from '../../context'
import { useStore } from '../../store'
import BlockIcon from '../../block-icon' import BlockIcon from '../../block-icon'
import { useWorkflow } from '../../hooks'
import NextStep from './components/next-step' import NextStep from './components/next-step'
import { import {
DotsHorizontal, DotsHorizontal,
...@@ -20,21 +18,14 @@ import { GitBranch01 } from '@/app/components/base/icons/src/vender/line/develop ...@@ -20,21 +18,14 @@ import { GitBranch01 } from '@/app/components/base/icons/src/vender/line/develop
type BasePanelProps = { type BasePanelProps = {
children: ReactElement children: ReactElement
} & Pick<NodeProps, 'id' | 'data'> } & SelectedNode
const BasePanel: FC<BasePanelProps> = ({ const BasePanel: FC<BasePanelProps> = ({
id, id,
data, data,
children, children,
}) => { }) => {
const { const { handleSelectNode } = useWorkflow()
nodes,
} = useWorkflowContext()
const selectedNodeId = useStore(state => state.selectedNodeId)
const handleSelectedNodeId = useStore(state => state.handleSelectedNodeId)
const selectedNode = useMemo(() => {
return nodes.find(node => node.id === selectedNodeId)
}, [nodes, selectedNodeId])
return ( return (
<div className='mr-2 w-[420px] h-full bg-white shadow-lg border-[0.5px] border-gray-200 rounded-2xl z-10 overflow-y-auto'> <div className='mr-2 w-[420px] h-full bg-white shadow-lg border-[0.5px] border-gray-200 rounded-2xl z-10 overflow-y-auto'>
...@@ -42,7 +33,7 @@ const BasePanel: FC<BasePanelProps> = ({ ...@@ -42,7 +33,7 @@ const BasePanel: FC<BasePanelProps> = ({
<div className='flex items-center px-4 pt-3'> <div className='flex items-center px-4 pt-3'>
<BlockIcon <BlockIcon
className='shrink-0 mr-2' className='shrink-0 mr-2'
type={selectedNode!.data.type} type={data.type}
size='md' size='md'
/> />
<div className='grow py-1 text-base text-gray-900 font-semibold '>{data.title}</div> <div className='grow py-1 text-base text-gray-900 font-semibold '>{data.title}</div>
...@@ -53,7 +44,7 @@ const BasePanel: FC<BasePanelProps> = ({ ...@@ -53,7 +44,7 @@ const BasePanel: FC<BasePanelProps> = ({
<div className='mx-3 w-[1px] h-3.5 bg-gray-200' /> <div className='mx-3 w-[1px] h-3.5 bg-gray-200' />
<div <div
className='flex items-center justify-center w-6 h-6 cursor-pointer' className='flex items-center justify-center w-6 h-6 cursor-pointer'
onClick={() => handleSelectedNodeId('')} onClick={() => handleSelectNode({ id, data }, true)}
> >
<XClose className='w-4 h-4' /> <XClose className='w-4 h-4' />
</div> </div>
...@@ -76,7 +67,7 @@ const BasePanel: FC<BasePanelProps> = ({ ...@@ -76,7 +67,7 @@ const BasePanel: FC<BasePanelProps> = ({
<div className='mb-2 text-xs text-gray-400'> <div className='mb-2 text-xs text-gray-400'>
Add the next block in this workflow Add the next block in this workflow
</div> </div>
<NextStep selectedNode={selectedNode!} /> <NextStep />
</div> </div>
</div> </div>
) )
......
import type { FC } from 'react'
import { memo } from 'react' import { memo } from 'react'
import type { NodeProps } from 'reactflow' import type { NodeProps } from 'reactflow'
import { import {
Handle, Handle,
Position, Position,
} from 'reactflow' } from 'reactflow'
import type { Node } from '../types' import type { SelectedNode } from '../types'
import { BlockEnum } from '../types' import { BlockEnum } from '../types'
import { import {
NodeComponentMap, NodeComponentMap,
...@@ -14,7 +13,7 @@ import { ...@@ -14,7 +13,7 @@ import {
import BaseNode from './_base/node' import BaseNode from './_base/node'
import BasePanel from './_base/panel' import BasePanel from './_base/panel'
const CustomNode = (props: NodeProps) => { const CustomNode = memo((props: NodeProps) => {
const nodeData = props.data const nodeData = props.data
const NodeComponent = NodeComponentMap[nodeData.type] const NodeComponent = NodeComponentMap[nodeData.type]
...@@ -43,21 +42,15 @@ const CustomNode = (props: NodeProps) => { ...@@ -43,21 +42,15 @@ const CustomNode = (props: NodeProps) => {
/> />
</> </>
) )
} })
CustomNode.displayName = 'CustomNode'
type PanelProps = { export const Panel = memo((props: SelectedNode) => {
node: Node const nodeData = props.data
} const PanelComponent = PanelComponentMap[nodeData.type]
export const Panel: FC<PanelProps> = memo(({
node,
}) => {
const PanelComponent = PanelComponentMap[node.data.type]
return ( return (
<BasePanel <BasePanel {...props}>
id={node.id}
data={node.data}
>
<PanelComponent /> <PanelComponent />
</BasePanel> </BasePanel>
) )
......
...@@ -3,22 +3,14 @@ import { ...@@ -3,22 +3,14 @@ import {
memo, memo,
useMemo, useMemo,
} from 'react' } from 'react'
import { useNodes } from 'reactflow'
import { useWorkflowContext } from '../context'
import { Panel as NodePanel } from '../nodes' import { Panel as NodePanel } from '../nodes'
import { useStore } from '../store' import { useStore } from '../store'
import WorkflowInfo from './workflow-info' import WorkflowInfo from './workflow-info'
import DebugAndPreview from './debug-and-preview' import DebugAndPreview from './debug-and-preview'
const Panel: FC = () => { const Panel: FC = () => {
const { const mode = useStore(state => state.mode)
mode, const selectedNode = useStore(state => state.selectedNode)
} = useWorkflowContext()
const nodes = useNodes()
const selectedNodeId = useStore(state => state.selectedNodeId)
const selectedNode = useMemo(() => {
return nodes.find(node => node.id === selectedNodeId)
}, [nodes, selectedNodeId])
const { const {
showWorkflowInfoPanel, showWorkflowInfoPanel,
showNodePanel, showNodePanel,
...@@ -26,7 +18,7 @@ const Panel: FC = () => { ...@@ -26,7 +18,7 @@ const Panel: FC = () => {
} = useMemo(() => { } = useMemo(() => {
return { return {
showWorkflowInfoPanel: mode === 'workflow' && !selectedNode, showWorkflowInfoPanel: mode === 'workflow' && !selectedNode,
showNodePanel: selectedNode, showNodePanel: !!selectedNode,
showDebugAndPreviewPanel: mode === 'chatbot' && !selectedNode, showDebugAndPreviewPanel: mode === 'chatbot' && !selectedNode,
} }
}, [mode, selectedNode]) }, [mode, selectedNode])
...@@ -35,7 +27,7 @@ const Panel: FC = () => { ...@@ -35,7 +27,7 @@ const Panel: FC = () => {
<div className='absolute top-14 right-0 bottom-2 flex'> <div className='absolute top-14 right-0 bottom-2 flex'>
{ {
showNodePanel && ( showNodePanel && (
<NodePanel node={selectedNode as any} /> <NodePanel {...selectedNode!} />
) )
} }
{ {
......
import { create } from 'zustand' import { create } from 'zustand'
import type { EdgeMouseHandler } from 'reactflow' import type { SelectedNode } from './types'
type State = { type State = {
mode: string mode: string
selectedNodeId: string selectedNode: SelectedNode | null
hoveringEdgeId: string
} }
type Action = { type Action = {
handleSelectedNodeId: (selectedNodeId: State['selectedNodeId']) => void setSelectedNode: (node: SelectedNode | null) => void
handleEnterEdge: EdgeMouseHandler
handleLeaveEdge: EdgeMouseHandler
} }
export const useStore = create<State & Action>(set => ({ export const useStore = create<State & Action>(set => ({
mode: 'workflow', mode: 'workflow',
selectedNodeId: '', selectedNode: null,
handleSelectedNodeId: selectedNodeId => set(() => ({ selectedNodeId })), setSelectedNode: node => set(() => ({ selectedNode: node })),
hoveringEdgeId: '',
handleEnterEdge: (_, edge) => set(() => ({ hoveringEdgeId: edge.id })),
handleLeaveEdge: () => set(() => ({ hoveringEdgeId: '' })),
})) }))
.react-flow__edge-path {
stroke-width: 2px !important;
}
\ No newline at end of file
...@@ -15,20 +15,16 @@ export enum BlockEnum { ...@@ -15,20 +15,16 @@ export enum BlockEnum {
Tool = 'tool', Tool = 'tool',
} }
export type NodeData = {
type: BlockEnum
name?: string
icon?: any
description?: string
}
export type Node = ReactFlowNode<NodeData>
export type CommonNodeType = { export type CommonNodeType = {
title: string title: string
desc: string desc: string
type: string type: BlockEnum
selected?: boolean
} }
export type Node = ReactFlowNode<CommonNodeType>
export type SelectedNode = Pick<Node, 'id' | 'data'>
export type ValueSelector = string[] // [nodeId, key | obj key path] export type ValueSelector = string[] // [nodeId, key | obj key path]
export type Variable = { export type Variable = {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment