Commit 94cda3e8 authored by StyleZhang's avatar StyleZhang

chore

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