11import { cn } from '@/lib/utils'
2- import { TNode } from '@/types/flow'
2+ import { EActionTypes , TNode } from '@/types/flow'
33import {
44 Bolt ,
55 Check ,
@@ -10,16 +10,8 @@ import {
1010 Webhook ,
1111 X ,
1212} from 'lucide-react'
13- import { useMemo } from 'react'
14- import {
15- Handle ,
16- HandleProps ,
17- NodeProps ,
18- Position ,
19- getConnectedEdges ,
20- useNodeId ,
21- useStore ,
22- } from 'reactflow'
13+ import { Handle , HandleProps , NodeProps , Position } from 'reactflow'
14+ import { useFlowCtx } from '.'
2315import { SOURCE_HANDLE_PROMPT_NO , SOURCE_HANDLE_PROMPT_YES } from './constant'
2416
2517type CustomNodeProps = NodeProps <
@@ -38,36 +30,75 @@ const HandleCustom = ({
3830 style ?: React . CSSProperties
3931 children ?: React . ReactNode
4032} ) => {
41- const { nodeInternals, edges } = useStore ( ( s ) => ( {
42- nodeInternals : s . nodeInternals ,
43- edges : s . edges ,
44- } ) )
45-
46- const nodeId = useNodeId ( )
47-
48- const isHandleConnectable = useMemo ( ( ) => {
49- if ( typeof props . isConnectable === 'function' ) {
50- const node = nodeInternals . get ( nodeId as string ) as any
51- const connectedEdges = getConnectedEdges ( [ node ] , edges )
52-
53- return props . isConnectable ( { node, connectedEdges } )
54- }
55-
56- if ( typeof props . isConnectable === 'number' ) {
57- const node = nodeInternals . get ( nodeId as string ) as any
58- const connectedEdges = getConnectedEdges ( [ node ] , edges )
59-
60- return connectedEdges . length <= props . isConnectable
61- }
62-
63- return props . isConnectable
64- } , [ props , nodeInternals , nodeId , edges ] )
33+ const { nodes, edges } = useFlowCtx ( )
6534
6635 return (
6736 < Handle
6837 { ...props }
6938 className = { cn ( ' !bg-stone-600 !w-2 !h-2' , className ) }
70- isConnectable = { isHandleConnectable }
39+ isValidConnection = { ( connection ) => {
40+ const sourcesFromHandleInState = edges . filter (
41+ ( edge ) => edge . source === connection . source ,
42+ ) . length
43+ const sourceNode = nodes . find ( ( node ) => node . id === connection . source )
44+
45+ const targetFromHandleInState = edges . filter (
46+ ( edge ) => edge . target === connection . target ,
47+ ) . length
48+
49+ if (
50+ sourceNode ?. data . action === EActionTypes . START &&
51+ sourcesFromHandleInState < 2
52+ )
53+ return true
54+
55+ if ( sourceNode ?. data . action === EActionTypes . PROMPT_AND_COLLECT ) {
56+ const numberOfYes = edges . filter ( ( edge ) => {
57+ return (
58+ edge . source === connection . source &&
59+ edge . sourceHandle === SOURCE_HANDLE_PROMPT_YES
60+ )
61+ } ) . length
62+ const numberOfNo = edges . filter (
63+ ( edge ) =>
64+ edge . source === connection . source &&
65+ edge . sourceHandle === SOURCE_HANDLE_PROMPT_NO ,
66+ ) . length
67+
68+ if (
69+ numberOfYes === 1 &&
70+ connection . sourceHandle === SOURCE_HANDLE_PROMPT_YES
71+ )
72+ return false
73+
74+ if (
75+ numberOfNo === 1 &&
76+ connection . sourceHandle === SOURCE_HANDLE_PROMPT_NO
77+ )
78+ return false
79+
80+ const targetEdge = edges . find (
81+ ( edge ) =>
82+ edge . target === connection . target &&
83+ edge . source === connection . source &&
84+ edge . sourceHandle !== null ,
85+ )
86+
87+ if ( targetEdge ) return false
88+
89+ return true
90+ }
91+
92+ if (
93+ sourceNode ?. data . action === EActionTypes . CHECK_VARIABLES &&
94+ sourcesFromHandleInState < 2
95+ )
96+ return true
97+
98+ if ( targetFromHandleInState === 1 ) return false
99+ if ( sourcesFromHandleInState < 1 ) return true
100+ return false
101+ } }
71102 >
72103 { children ? children : null }
73104 </ Handle >
@@ -82,7 +113,7 @@ export const StartNode = (props?: CustomNodeProps) => {
82113 < Bolt className = 'w-4 h-4' />
83114 < span className = 'leading-none' > { data ?. label } </ span >
84115 </ div >
85- < HandleCustom type = 'source' position = { Position . Right } isConnectable = { 2 } />
116+ < HandleCustom type = 'source' position = { Position . Right } />
86117 </ div >
87118 )
88119}
@@ -95,7 +126,7 @@ export const FallBackNode = (props?: CustomNodeProps) => {
95126 < Webhook className = 'w-4 h-4' />
96127 < span className = 'leading-none' > { data ?. label } </ span >
97128 </ div >
98- < HandleCustom type = 'target' position = { Position . Left } isConnectable = { 1 } />
129+ < HandleCustom type = 'target' position = { Position . Left } />
99130 </ div >
100131 )
101132}
@@ -109,11 +140,7 @@ export const NodeWrapper = (props?: {
109140 < div className = { cn ( 'bg-card shadow rounded-md p-2 border-card' , className ) } >
110141 { children }
111142 < HandleCustom type = 'target' position = { Position . Top } isConnectable = { 1 } />
112- < HandleCustom
113- type = 'source'
114- position = { Position . Bottom }
115- isConnectable = { 1 }
116- />
143+ < HandleCustom type = 'source' position = { Position . Bottom } />
117144 </ div >
118145 )
119146}
@@ -138,11 +165,10 @@ export const PromptAndCollectNode = (props?: CustomNodeProps) => {
138165 < HelpCircle className = 'w-4 h-4' />
139166 < span className = 'leading-none' > { data ?. name || data ?. label } </ span >
140167 </ div >
141- < HandleCustom type = 'target' position = { Position . Top } isConnectable = { 1 } />
168+ < HandleCustom type = 'target' position = { Position . Top } />
142169 < HandleCustom
143170 type = 'source'
144171 position = { Position . Bottom }
145- isConnectable = { 2 }
146172 id = { SOURCE_HANDLE_PROMPT_NO }
147173 className = '!w-4 !h-4 flex items-center justify-center !bg-red-500 !-bottom-2 text-white'
148174 style = { {
@@ -154,7 +180,6 @@ export const PromptAndCollectNode = (props?: CustomNodeProps) => {
154180 < HandleCustom
155181 type = 'source'
156182 position = { Position . Bottom }
157- isConnectable = { 2 }
158183 id = { SOURCE_HANDLE_PROMPT_YES }
159184 style = { {
160185 left : '20%' ,
@@ -184,7 +209,6 @@ export const CheckVariablesNode = (props?: CustomNodeProps) => {
184209 style = { {
185210 left : '80%' ,
186211 } }
187- isConnectable = { 2 }
188212 >
189213 < X className = 'w-2 h-2 pointer-events-none' />
190214 </ HandleCustom >
@@ -196,7 +220,6 @@ export const CheckVariablesNode = (props?: CustomNodeProps) => {
196220 left : '20%' ,
197221 } }
198222 className = '!w-4 !h-4 flex items-center justify-center !bg-green-500 !-bottom-2 text-white'
199- isConnectable = { 2 }
200223 >
201224 < Check className = 'w-2 h-2 pointer-events-none' />
202225 </ HandleCustom >
0 commit comments