1
1
// Graph/CustomNode.tsx
2
-
3
2
import React , { useCallback } from 'react' ;
4
- import { Handle , Position } from '@xyflow/react' ;
3
+ import { Handle , Position , NodeResizeControl , OnResize } from '@xyflow/react' ;
4
+ import ResizeIcon from './ResizeIcon' ;
5
5
6
6
interface CustomNodeProps {
7
- id : string ;
8
- data : {
9
- label : string ;
10
- value : string | number ;
11
- } ;
12
- isConnectable ?: boolean ;
13
- onNodeDataChange ?: ( id : string , newData : any ) => void ;
7
+ id : string ;
8
+ data : {
9
+ type : string ;
10
+ name ?: string ;
11
+ tool ?: string ;
12
+ description ?: string ;
13
+ info ?: string ;
14
+ width ?: number ;
15
+ height ?: number ;
16
+ } ;
17
+ isConnectable ?: boolean ;
18
+ onNodeDataChange ?: ( id : string , newData : any ) => void ;
19
+ onResize ?: ( id : string , width : number , height : number ) => void ;
14
20
}
15
21
16
22
17
- const handleStyle = { left : 10 } ;
23
+ const handleStyle = {
24
+ width : 6 ,
25
+ height : 6 ,
26
+ borderRadius : '50%' ,
27
+ background : '#555' ,
28
+ } ;
29
+
18
30
19
- const CustomNode : React . FC < CustomNodeProps > = ( { id, data, isConnectable = true , onNodeDataChange } ) => {
31
+ const CustomNode : React . FC < CustomNodeProps > = ( { id, data, isConnectable = true , onNodeDataChange, onResize } ) => {
20
32
21
- const onChange = useCallback ( ( evt : React . ChangeEvent < HTMLInputElement > ) => {
22
- const newValue = evt . target . value ;
23
- onNodeDataChange ?.( id , { ...data , value : newValue } ) ;
33
+ const handleChange = useCallback ( ( evt : React . ChangeEvent < HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement > ) => {
34
+ const { name , value } = evt . target ;
35
+ onNodeDataChange ?.( id , { ...data , [ name ] : value } ) ;
24
36
} , [ id , data , onNodeDataChange ] ) ;
25
37
38
+ const handleResize = useCallback < OnResize > (
39
+ ( _ , { width, height } ) => {
40
+ onResize ?.( id , width , height ) ;
41
+ } ,
42
+ [ id , onResize ]
43
+ ) ;
26
44
27
45
return (
28
- < div className = "custom-node" >
29
- < Handle type = "target" position = { Position . Top } isConnectable = { isConnectable } />
30
-
31
- < div >
32
- < label htmlFor = "text" > Label:</ label >
33
- < input
34
- id = "text"
35
- className = "nodrag"
36
- value = { data . value }
37
- onChange = { onChange }
38
- />
39
- </ div >
40
- < Handle type = "source" position = { Position . Bottom } id = "a" style = { handleStyle } isConnectable = { isConnectable } />
46
+ < div
47
+ className = "border border-gray-500 p-2 rounded-xl bg-white overflow-visible relative flex flex-col text-black" // Added text-black here
48
+ style = { { width : data . width || 200 , height : data . height || 200 } }
49
+ >
50
+ < NodeResizeControl
51
+ className = "absolute right-1 bottom-1"
52
+ minWidth = { 200 }
53
+ minHeight = { 200 }
54
+ onResize = { handleResize }
55
+ >
56
+ < ResizeIcon />
57
+ </ NodeResizeControl >
58
+ < Handle
59
+ type = "target"
60
+ position = { Position . Left }
61
+ isConnectable = { isConnectable }
62
+ className = "absolute left-[-5px] top-1/2 -translate-y-1/2"
63
+ style = { handleStyle }
64
+ />
65
+ < Handle
66
+ type = "source"
67
+ position = { Position . Right }
68
+ id = "a"
69
+ isConnectable = { isConnectable }
70
+ className = "absolute right-[-5px] top-1/2 -translate-y-1/2"
71
+ style = { handleStyle }
72
+ />
73
+ < Handle
74
+ type = "source"
75
+ position = { Position . Top }
76
+ id = "true"
77
+ isConnectable = { isConnectable }
78
+ className = "absolute top-[-5px] left-1/2 -translate-x-1/2 bg-green-500"
79
+ style = { handleStyle }
80
+ />
41
81
< Handle
42
82
type = "source"
43
83
position = { Position . Bottom }
44
- id = "b "
84
+ id = "false "
45
85
isConnectable = { isConnectable }
86
+ className = "absolute bottom-[-5px] left-1/2 -translate-x-1/2 bg-red-500"
87
+ style = { handleStyle }
46
88
/>
47
-
89
+ < div className = "flex flex-col h-full flex-grow" >
90
+ < div >
91
+ < label htmlFor = "type" className = "block text-xs" >
92
+ Type:
93
+ </ label >
94
+ < select
95
+ id = "type"
96
+ name = "type"
97
+ defaultValue = { data . type }
98
+ onChange = { handleChange }
99
+ className = "nodrag w-full bg-white border border-gray-300 rounded focus:outline-none"
100
+ >
101
+ < option value = "START" > START</ option >
102
+ < option value = "STEP" > STEP</ option >
103
+ < option value = "TOOL" > TOOL</ option >
104
+ < option value = "CONDITION" > CONDITION</ option >
105
+ < option value = "INFO" > INFO</ option >
106
+ </ select >
107
+ </ div >
108
+ { data . type !== 'START' && (
109
+ < >
110
+ { [ 'STEP' , 'CONDITION' , 'INFO' ] . includes ( data . type ) && (
111
+ < div >
112
+ < label htmlFor = "name" className = "block text-xs" >
113
+ Name:
114
+ </ label >
115
+ < input
116
+ id = "name"
117
+ name = "name"
118
+ defaultValue = { data . name }
119
+ onChange = { handleChange }
120
+ className = "nodrag w-full bg-white border border-gray-300 rounded focus:outline-none"
121
+ />
122
+ </ div >
123
+ ) }
124
+ { data . type === 'STEP' && (
125
+ < div >
126
+ < label htmlFor = "tool" className = "block text-xs" >
127
+ Tool:
128
+ </ label >
129
+ < input
130
+ id = "tool"
131
+ name = "tool"
132
+ defaultValue = { data . tool }
133
+ onChange = { handleChange }
134
+ className = "nodrag w-full bg-white border border-gray-300 rounded focus:outline-none"
135
+ />
136
+ </ div >
137
+ ) }
138
+ { [ 'STEP' , 'TOOL' , 'CONDITION' , 'INFO' ] . includes ( data . type ) && (
139
+ < div className = "flex-grow" >
140
+ < label htmlFor = "description" className = "block text-xs" >
141
+ Description:
142
+ </ label >
143
+ < textarea
144
+ id = "description"
145
+ name = "description"
146
+ defaultValue = { data . description }
147
+ onChange = { handleChange }
148
+ className = "nodrag w-full h-full resize-none bg-white border border-gray-300 rounded focus:outline-none"
149
+ />
150
+ </ div >
151
+ ) }
152
+ { data . type === 'INFO' && (
153
+ < div >
154
+ < label htmlFor = "info" className = "block text-xs" >
155
+ Question:
156
+ </ label >
157
+ < input
158
+ id = "info"
159
+ name = "info"
160
+ defaultValue = { data . info }
161
+ onChange = { handleChange }
162
+ className = "nodrag w-full h-8 bg-white border border-gray-300 rounded focus:outline-none"
163
+ />
164
+ </ div >
165
+ ) }
166
+ </ >
167
+ ) }
168
+ </ div >
48
169
</ div >
49
170
) ;
50
171
} ;
51
172
52
- export default CustomNode ;
173
+ export default React . memo ( CustomNode ) ;
0 commit comments