feat: [US-032] - Display conditions on edges
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
e686719f29
commit
c431b212ac
|
|
@ -14,6 +14,7 @@ import ReactFlow, {
|
||||||
Node,
|
Node,
|
||||||
Edge,
|
Edge,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
|
EdgeTypes,
|
||||||
MarkerType,
|
MarkerType,
|
||||||
NodeMouseHandler,
|
NodeMouseHandler,
|
||||||
EdgeMouseHandler,
|
EdgeMouseHandler,
|
||||||
|
|
@ -24,6 +25,7 @@ import Toolbar from '@/components/editor/Toolbar'
|
||||||
import DialogueNode from '@/components/editor/nodes/DialogueNode'
|
import DialogueNode from '@/components/editor/nodes/DialogueNode'
|
||||||
import ChoiceNode from '@/components/editor/nodes/ChoiceNode'
|
import ChoiceNode from '@/components/editor/nodes/ChoiceNode'
|
||||||
import VariableNode from '@/components/editor/nodes/VariableNode'
|
import VariableNode from '@/components/editor/nodes/VariableNode'
|
||||||
|
import ConditionalEdge from '@/components/editor/edges/ConditionalEdge'
|
||||||
import ContextMenu, { ContextMenuType } from '@/components/editor/ContextMenu'
|
import ContextMenu, { ContextMenuType } from '@/components/editor/ContextMenu'
|
||||||
import ConditionEditor from '@/components/editor/ConditionEditor'
|
import ConditionEditor from '@/components/editor/ConditionEditor'
|
||||||
import type { FlowchartData, FlowchartNode, FlowchartEdge, Condition } from '@/types/flowchart'
|
import type { FlowchartData, FlowchartNode, FlowchartEdge, Condition } from '@/types/flowchart'
|
||||||
|
|
@ -65,7 +67,7 @@ function toReactFlowEdges(edges: FlowchartEdge[]): Edge[] {
|
||||||
target: edge.target,
|
target: edge.target,
|
||||||
targetHandle: edge.targetHandle,
|
targetHandle: edge.targetHandle,
|
||||||
data: edge.data,
|
data: edge.data,
|
||||||
type: 'smoothstep',
|
type: 'conditional',
|
||||||
markerEnd: {
|
markerEnd: {
|
||||||
type: MarkerType.ArrowClosed,
|
type: MarkerType.ArrowClosed,
|
||||||
},
|
},
|
||||||
|
|
@ -84,6 +86,14 @@ function FlowchartEditorInner({ initialData }: FlowchartEditorProps) {
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Define custom edge types - memoized to prevent re-renders
|
||||||
|
const edgeTypes: EdgeTypes = useMemo(
|
||||||
|
() => ({
|
||||||
|
conditional: ConditionalEdge,
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
const { getViewport, screenToFlowPosition } = useReactFlow()
|
const { getViewport, screenToFlowPosition } = useReactFlow()
|
||||||
|
|
||||||
const [contextMenu, setContextMenu] = useState<ContextMenuState>(null)
|
const [contextMenu, setContextMenu] = useState<ContextMenuState>(null)
|
||||||
|
|
@ -105,7 +115,7 @@ function FlowchartEditorInner({ initialData }: FlowchartEditorProps) {
|
||||||
target: params.target,
|
target: params.target,
|
||||||
sourceHandle: params.sourceHandle,
|
sourceHandle: params.sourceHandle,
|
||||||
targetHandle: params.targetHandle,
|
targetHandle: params.targetHandle,
|
||||||
type: 'smoothstep',
|
type: 'conditional',
|
||||||
markerEnd: {
|
markerEnd: {
|
||||||
type: MarkerType.ArrowClosed,
|
type: MarkerType.ArrowClosed,
|
||||||
},
|
},
|
||||||
|
|
@ -376,6 +386,7 @@ function FlowchartEditorInner({ initialData }: FlowchartEditorProps) {
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
edges={edges}
|
edges={edges}
|
||||||
nodeTypes={nodeTypes}
|
nodeTypes={nodeTypes}
|
||||||
|
edgeTypes={edgeTypes}
|
||||||
onNodesChange={onNodesChange}
|
onNodesChange={onNodesChange}
|
||||||
onEdgesChange={onEdgesChange}
|
onEdgesChange={onEdgesChange}
|
||||||
onEdgesDelete={onEdgesDelete}
|
onEdgesDelete={onEdgesDelete}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
'use client'
|
||||||
|
|
||||||
|
import {
|
||||||
|
BaseEdge,
|
||||||
|
EdgeLabelRenderer,
|
||||||
|
EdgeProps,
|
||||||
|
getSmoothStepPath,
|
||||||
|
} from 'reactflow'
|
||||||
|
import type { Condition } from '@/types/flowchart'
|
||||||
|
|
||||||
|
type ConditionalEdgeData = {
|
||||||
|
condition?: Condition
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ConditionalEdge({
|
||||||
|
id,
|
||||||
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
sourcePosition,
|
||||||
|
targetPosition,
|
||||||
|
data,
|
||||||
|
markerEnd,
|
||||||
|
selected,
|
||||||
|
}: EdgeProps<ConditionalEdgeData>) {
|
||||||
|
const [edgePath, labelX, labelY] = getSmoothStepPath({
|
||||||
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
sourcePosition,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
targetPosition,
|
||||||
|
})
|
||||||
|
|
||||||
|
const hasCondition = !!data?.condition
|
||||||
|
|
||||||
|
// Format condition as readable label
|
||||||
|
const conditionLabel = hasCondition
|
||||||
|
? `${data.condition!.variableName} ${data.condition!.operator} ${data.condition!.value}`
|
||||||
|
: null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<BaseEdge
|
||||||
|
id={id}
|
||||||
|
path={edgePath}
|
||||||
|
markerEnd={markerEnd}
|
||||||
|
style={{
|
||||||
|
strokeDasharray: hasCondition ? '5 5' : undefined,
|
||||||
|
stroke: selected ? '#3b82f6' : hasCondition ? '#f59e0b' : '#64748b',
|
||||||
|
strokeWidth: selected ? 2 : 1.5,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{conditionLabel && (
|
||||||
|
<EdgeLabelRenderer>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
|
||||||
|
pointerEvents: 'all',
|
||||||
|
}}
|
||||||
|
className="rounded border border-amber-300 bg-amber-50 px-2 py-0.5 text-xs font-medium text-amber-800 dark:border-amber-600 dark:bg-amber-900 dark:text-amber-200"
|
||||||
|
>
|
||||||
|
{conditionLabel}
|
||||||
|
</div>
|
||||||
|
</EdgeLabelRenderer>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue