Commit 94cda3e8 authored by StyleZhang's avatar StyleZhang

chore

parent f09f91e2
...@@ -34,16 +34,19 @@ const initialNodes = [ ...@@ -34,16 +34,19 @@ const initialNodes = [
const initialEdges = [ const initialEdges = [
{ {
id: '0', id: '0',
type: 'custom',
source: '1', source: '1',
target: '2', target: '2',
}, },
{ {
id: '1', id: '1',
type: 'custom',
source: '2', source: '2',
target: '3', target: '3',
}, },
{ {
id: '2', id: '2',
type: 'custom',
source: '2', source: '2',
target: '4', target: '4',
}, },
......
...@@ -24,6 +24,7 @@ type NodeSelectorProps = { ...@@ -24,6 +24,7 @@ type NodeSelectorProps = {
trigger?: (open: boolean) => React.ReactNode trigger?: (open: boolean) => React.ReactNode
placement?: Placement placement?: Placement
offset?: OffsetOptions offset?: OffsetOptions
triggerStyle?: React.CSSProperties
popupClassName?: string popupClassName?: string
asChild?: boolean asChild?: boolean
} }
...@@ -32,6 +33,7 @@ const NodeSelector: FC<NodeSelectorProps> = ({ ...@@ -32,6 +33,7 @@ const NodeSelector: FC<NodeSelectorProps> = ({
trigger, trigger,
placement = 'right', placement = 'right',
offset = 6, offset = 6,
triggerStyle,
popupClassName, popupClassName,
asChild, asChild,
}) => { }) => {
...@@ -50,10 +52,10 @@ const NodeSelector: FC<NodeSelectorProps> = ({ ...@@ -50,10 +52,10 @@ const NodeSelector: FC<NodeSelectorProps> = ({
: ( : (
<div <div
className={` className={`
hidden absolute -right-2 top-4 items-center justify-center flex items-center justify-center
w-4 h-4 rounded-full bg-primary-600 cursor-pointer z-10 group-hover:flex w-4 h-4 rounded-full bg-primary-600 cursor-pointer z-10 group-hover:flex
${open && '!flex'}
`} `}
style={triggerStyle}
> >
<Plus02 className='w-2.5 h-2.5 text-white' /> <Plus02 className='w-2.5 h-2.5 text-white' />
</div> </div>
......
'use client' 'use client'
import { createContext, useContext } from 'use-context-selector' import { createContext, useContext } from 'use-context-selector'
import type { import type { Edge } from 'reactflow'
Edge,
ReactFlowInstance,
} from 'reactflow'
import type { import type {
BlockEnum, BlockEnum,
Node, Node,
...@@ -12,11 +9,8 @@ import type { ...@@ -12,11 +9,8 @@ import type {
export type WorkflowContextValue = { export type WorkflowContextValue = {
mode: string mode: string
reactFlow: ReactFlowInstance
nodes: Node[] nodes: Node[]
edges: Edge[] edges: Edge[]
selectedNodeId?: string
handleSelectedNodeIdChange: (nodeId: string) => void
selectedNode?: Node selectedNode?: Node
handleAddNextNode: (prevNode: Node, nextNodeType: BlockEnum) => void handleAddNextNode: (prevNode: Node, nextNodeType: BlockEnum) => void
handleUpdateNodeData: (nodeId: string, data: Node['data']) => void handleUpdateNodeData: (nodeId: string, data: Node['data']) => void
...@@ -24,10 +18,8 @@ export type WorkflowContextValue = { ...@@ -24,10 +18,8 @@ export type WorkflowContextValue = {
export const WorkflowContext = createContext<WorkflowContextValue>({ export const WorkflowContext = createContext<WorkflowContextValue>({
mode: 'workflow', mode: 'workflow',
reactFlow: null as any,
nodes: [], nodes: [],
edges: [], edges: [],
handleSelectedNodeIdChange: () => {},
handleAddNextNode: () => {}, handleAddNextNode: () => {},
handleUpdateNodeData: () => {}, handleUpdateNodeData: () => {},
}) })
......
...@@ -3,8 +3,11 @@ import type { EdgeProps } from 'reactflow' ...@@ -3,8 +3,11 @@ import type { EdgeProps } from 'reactflow'
import { import {
BaseEdge, BaseEdge,
EdgeLabelRenderer, EdgeLabelRenderer,
getSmoothStepPath, Position,
getSimpleBezierPath,
} from 'reactflow' } from 'reactflow'
import BlockSelector from './block-selector'
import { useStore } from './store'
const CustomEdge = ({ const CustomEdge = ({
id, id,
...@@ -12,38 +15,50 @@ const CustomEdge = ({ ...@@ -12,38 +15,50 @@ const CustomEdge = ({
sourceY, sourceY,
targetX, targetX,
targetY, targetY,
selected,
}: EdgeProps) => { }: EdgeProps) => {
const hoveringEdgeId = useStore(state => state.hoveringEdgeId)
const [ const [
edgePath, edgePath,
labelX, labelX,
labelY, labelY,
] = getSmoothStepPath({ ] = getSimpleBezierPath({
sourceX, sourceX,
sourceY, sourceY,
sourcePosition: Position.Right,
targetX, targetX,
targetY, targetY,
borderRadius: 30, targetPosition: Position.Left,
offset: -20,
}) })
return ( return (
<> <>
<BaseEdge id={id} path={edgePath} style={{ strokeWidth: 5 }} /> <BaseEdge
id={id}
path={edgePath}
style={{
stroke: selected ? '#2970FF' : '#D0D5DD',
strokeWidth: 2,
}}
/>
<EdgeLabelRenderer> <EdgeLabelRenderer>
<div {
className={` hoveringEdgeId === id && (
flex items-center px-2 h-6 bg-white rounded-lg shadow-xs <div
text-[10px] font-semibold text-gray-700 className='nopan nodrag'
nodrag nopan style={{
`} position: 'absolute',
style={{ transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
position: 'absolute', pointerEvents: 'all',
transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`, }}
pointerEvents: 'all', >
}} <BlockSelector
> asChild
Topic 2 onSelect={() => {}}
</div> />
</div>
)
}
</EdgeLabelRenderer> </EdgeLabelRenderer>
</> </>
) )
......
...@@ -2,13 +2,11 @@ import type { ...@@ -2,13 +2,11 @@ import type {
Dispatch, Dispatch,
SetStateAction, SetStateAction,
} from 'react' } from 'react'
import { import { useCallback } from 'react'
useCallback,
useMemo,
useState,
} from 'react'
import produce from 'immer' import produce from 'immer'
import type { Edge } from 'reactflow' import type {
Edge,
} from 'reactflow'
import type { import type {
BlockEnum, BlockEnum,
Node, Node,
...@@ -20,16 +18,7 @@ export const useWorkflow = ( ...@@ -20,16 +18,7 @@ export const useWorkflow = (
edges: Edge[], edges: Edge[],
setNodes: Dispatch<SetStateAction<Node[]>>, setNodes: Dispatch<SetStateAction<Node[]>>,
setEdges: Dispatch<SetStateAction<Edge[]>>, setEdges: Dispatch<SetStateAction<Edge[]>>,
initialSelectedNodeId?: string,
) => { ) => {
const [selectedNodeId, setSelectedNodeId] = useState(initialSelectedNodeId)
const handleSelectedNodeIdChange = useCallback((nodeId: string) => setSelectedNodeId(nodeId), [])
const selectedNode = useMemo(() => {
return nodes.find(node => node.id === selectedNodeId)
}, [nodes, selectedNodeId])
const handleAddNextNode = useCallback((prevNode: Node, nextNodeType: BlockEnum) => { const handleAddNextNode = useCallback((prevNode: Node, nextNodeType: BlockEnum) => {
const nextNode = { const nextNode = {
id: `node-${Date.now()}`, id: `node-${Date.now()}`,
...@@ -68,9 +57,6 @@ export const useWorkflow = ( ...@@ -68,9 +57,6 @@ export const useWorkflow = (
}, [setNodes]) }, [setNodes])
return { return {
selectedNodeId,
selectedNode,
handleSelectedNodeIdChange,
handleAddNextNode, handleAddNextNode,
handleUpdateNodeData, handleUpdateNodeData,
} }
......
import type { FC } from 'react' import type { FC } from 'react'
import { memo } from 'react'
import type { Edge } from 'reactflow' import type { Edge } from 'reactflow'
import ReactFlow, { import ReactFlow, {
Background, Background,
ReactFlowProvider, ReactFlowProvider,
useEdgesState, useEdgesState,
useNodesState, useNodesState,
useReactFlow,
} from 'reactflow' } from 'reactflow'
import 'reactflow/dist/style.css' import 'reactflow/dist/style.css'
import './style.css' import './style.css'
...@@ -20,6 +20,7 @@ import ZoomInOut from './zoom-in-out' ...@@ -20,6 +20,7 @@ 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,
...@@ -28,11 +29,13 @@ const edgeTypes = { ...@@ -28,11 +29,13 @@ const edgeTypes = {
custom: CustomEdge, custom: CustomEdge,
} }
const Workflow = () => { const Workflow = memo(() => {
const { const {
nodes, nodes,
edges, edges,
} = useWorkflowContext() } = useWorkflowContext()
const handleEnterEdge = useStore(state => state.handleEnterEdge)
const handleLeaveEdge = useStore(state => state.handleLeaveEdge)
return ( return (
<div className='relative w-full h-full'> <div className='relative w-full h-full'>
...@@ -44,6 +47,8 @@ const Workflow = () => { ...@@ -44,6 +47,8 @@ const Workflow = () => {
edgeTypes={edgeTypes} edgeTypes={edgeTypes}
nodes={nodes} nodes={nodes}
edges={edges} edges={edges}
onEdgeMouseEnter={handleEnterEdge}
onEdgeMouseLeave={handleLeaveEdge}
> >
<Background <Background
gap={[14, 14]} gap={[14, 14]}
...@@ -52,24 +57,23 @@ const Workflow = () => { ...@@ -52,24 +57,23 @@ const Workflow = () => {
</ReactFlow> </ReactFlow>
</div> </div>
) )
} })
Workflow.displayName = 'Workflow'
type WorkflowWrapProps = { type WorkflowWrapProps = {
selectedNodeId?: string selectedNodeId?: string
nodes: Node[] nodes: Node[]
edges: Edge[] edges: Edge[]
} }
const WorkflowWrap: FC<WorkflowWrapProps> = ({ const WorkflowWrap: FC<WorkflowWrapProps> = memo(({
nodes: initialNodes, nodes: initialNodes,
edges: initialEdges, edges: initialEdges,
selectedNodeId: initialSelectedNodeId, selectedNodeId: initialSelectedNodeId,
}) => { }) => {
const reactFlow = useReactFlow()
const [nodes, setNodes] = useNodesState(initialNodes) const [nodes, setNodes] = useNodesState(initialNodes)
const [edges, setEdges] = useEdgesState(initialEdges) const [edges, setEdges] = useEdgesState(initialEdges)
const { const {
selectedNodeId,
handleSelectedNodeIdChange,
selectedNode,
handleAddNextNode, handleAddNextNode,
handleUpdateNodeData, handleUpdateNodeData,
} = useWorkflow( } = useWorkflow(
...@@ -77,16 +81,11 @@ const WorkflowWrap: FC<WorkflowWrapProps> = ({ ...@@ -77,16 +81,11 @@ const WorkflowWrap: FC<WorkflowWrapProps> = ({
edges, edges,
setNodes, setNodes,
setEdges, setEdges,
initialSelectedNodeId,
) )
return ( return (
<WorkflowContext.Provider value={{ <WorkflowContext.Provider value={{
mode: 'workflow', mode: 'workflow',
reactFlow,
selectedNodeId,
handleSelectedNodeIdChange,
selectedNode,
nodes, nodes,
edges, edges,
handleAddNextNode, handleAddNextNode,
...@@ -95,7 +94,9 @@ const WorkflowWrap: FC<WorkflowWrapProps> = ({ ...@@ -95,7 +94,9 @@ const WorkflowWrap: FC<WorkflowWrapProps> = ({
<Workflow /> <Workflow />
</WorkflowContext.Provider> </WorkflowContext.Provider>
) )
} })
WorkflowWrap.displayName = 'WorkflowWrap'
const WorkflowWrapWithReactFlowProvider: FC<WorkflowWrapProps> = ({ const WorkflowWrapWithReactFlowProvider: FC<WorkflowWrapProps> = ({
selectedNodeId, selectedNodeId,
...@@ -114,4 +115,4 @@ const WorkflowWrapWithReactFlowProvider: FC<WorkflowWrapProps> = ({ ...@@ -114,4 +115,4 @@ const WorkflowWrapWithReactFlowProvider: FC<WorkflowWrapProps> = ({
) )
} }
export default WorkflowWrapWithReactFlowProvider export default memo(WorkflowWrapWithReactFlowProvider)
...@@ -8,7 +8,9 @@ import { ...@@ -8,7 +8,9 @@ import {
useMemo, useMemo,
} from 'react' } from 'react'
import type { NodeProps } from 'reactflow' import type { NodeProps } from 'reactflow'
import { useWorkflowContext } from '../../context' import { useNodes } from 'reactflow'
import { useStore } from '../../store'
import type { NodeData } from '../../types'
import BlockIcon from '../../block-icon' import BlockIcon from '../../block-icon'
import BlockSelector from '../../block-selector' import BlockSelector from '../../block-selector'
import NodeControl from './components/node-control' import NodeControl from './components/node-control'
...@@ -22,11 +24,9 @@ const BaseNode: FC<BaseNodeProps> = ({ ...@@ -22,11 +24,9 @@ const BaseNode: FC<BaseNodeProps> = ({
data, data,
children, children,
}) => { }) => {
const { const nodes = useNodes<NodeData>()
nodes, const selectedNodeId = useStore(state => state.selectedNodeId)
selectedNodeId, const handleSelectedNodeId = useStore(state => state.handleSelectedNodeId)
handleSelectedNodeIdChange,
} = useWorkflowContext()
const currentNode = useMemo(() => { const currentNode = useMemo(() => {
return nodes.find(node => node.id === nodeId) return nodes.find(node => node.id === nodeId)
}, [nodeId, nodes]) }, [nodeId, nodes])
...@@ -38,7 +38,7 @@ const BaseNode: FC<BaseNodeProps> = ({ ...@@ -38,7 +38,7 @@ const BaseNode: FC<BaseNodeProps> = ({
hover:shadow-lg hover:shadow-lg
${selectedNodeId === nodeId ? 'border-[2px] border-primary-600' : 'border border-white'} ${selectedNodeId === nodeId ? 'border-[2px] border-primary-600' : 'border border-white'}
`} `}
onClick={() => handleSelectedNodeIdChange(nodeId || '')} onClick={() => handleSelectedNodeId(nodeId || '')}
> >
<NodeControl /> <NodeControl />
<div className='flex items-center px-3 pt-3 pb-2'> <div className='flex items-center px-3 pt-3 pb-2'>
......
...@@ -5,9 +5,11 @@ import type { ...@@ -5,9 +5,11 @@ import type {
import { import {
cloneElement, cloneElement,
memo, memo,
useMemo,
} from 'react' } from 'react'
import type { NodeProps } from 'reactflow' import type { NodeProps } from 'reactflow'
import { useWorkflowContext } from '../../context' import { useWorkflowContext } from '../../context'
import { useStore } from '../../store'
import BlockIcon from '../../block-icon' import BlockIcon from '../../block-icon'
import NextStep from './components/next-step' import NextStep from './components/next-step'
import { import {
...@@ -26,9 +28,13 @@ const BasePanel: FC<BasePanelProps> = ({ ...@@ -26,9 +28,13 @@ const BasePanel: FC<BasePanelProps> = ({
children, children,
}) => { }) => {
const { const {
handleSelectedNodeIdChange, nodes,
selectedNode,
} = useWorkflowContext() } = 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'>
...@@ -47,7 +53,7 @@ const BasePanel: FC<BasePanelProps> = ({ ...@@ -47,7 +53,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={() => handleSelectedNodeIdChange('')} onClick={() => handleSelectedNodeId('')}
> >
<XClose className='w-4 h-4' /> <XClose className='w-4 h-4' />
</div> </div>
......
...@@ -3,16 +3,22 @@ import { ...@@ -3,16 +3,22 @@ import {
memo, memo,
useMemo, useMemo,
} from 'react' } from 'react'
import { useNodes } from 'reactflow'
import { useWorkflowContext } from '../context' import { useWorkflowContext } from '../context'
import { Panel as NodePanel } from '../nodes' import { Panel as NodePanel } from '../nodes'
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, mode,
selectedNode,
} = useWorkflowContext() } = 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,
...@@ -29,7 +35,7 @@ const Panel: FC = () => { ...@@ -29,7 +35,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!} /> <NodePanel node={selectedNode as any} />
) )
} }
{ {
......
import { create } from 'zustand'
import type { EdgeMouseHandler } from 'reactflow'
type State = {
selectedNodeId: string
hoveringEdgeId: string
}
type Action = {
handleSelectedNodeId: (selectedNodeId: State['selectedNodeId']) => void
handleEnterEdge: EdgeMouseHandler
handleLeaveEdge: EdgeMouseHandler
}
export const useStore = create<State & Action>(set => ({
selectedNodeId: '',
handleSelectedNodeId: selectedNodeId => set(() => ({ selectedNodeId })),
hoveringEdgeId: '',
handleEnterEdge: (_, edge) => set(() => ({ hoveringEdgeId: edge.id })),
handleLeaveEdge: () => set(() => ({ hoveringEdgeId: '' })),
}))
...@@ -4,7 +4,7 @@ import { ...@@ -4,7 +4,7 @@ import {
memo, memo,
useState, useState,
} from 'react' } from 'react'
import { useWorkflowContext } from './context' import { useReactFlow } from 'reactflow'
import { import {
PortalToFollowElem, PortalToFollowElem,
PortalToFollowElemContent, PortalToFollowElemContent,
...@@ -43,7 +43,7 @@ const ZOOM_IN_OUT_OPTIONS = [ ...@@ -43,7 +43,7 @@ const ZOOM_IN_OUT_OPTIONS = [
] ]
const ZoomInOut: FC = () => { const ZoomInOut: FC = () => {
const { reactFlow } = useWorkflowContext() const reactFlow = useReactFlow()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const handleZoom = (type: string) => { const handleZoom = (type: string) => {
......
...@@ -79,7 +79,8 @@ ...@@ -79,7 +79,8 @@
"sharp": "^0.33.2", "sharp": "^0.33.2",
"sortablejs": "^1.15.0", "sortablejs": "^1.15.0",
"swr": "^2.1.0", "swr": "^2.1.0",
"use-context-selector": "^1.4.1" "use-context-selector": "^1.4.1",
"zustand": "^4.5.1"
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "^0.36.0", "@antfu/eslint-config": "^0.36.0",
......
This diff is collapsed.
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