Skip to content

Commit f1eeaff

Browse files
committed
nodelayout port ok,
todo: * resize * edge * new type: subgraph
1 parent 6014aa7 commit f1eeaff

File tree

3 files changed

+155
-34
lines changed

3 files changed

+155
-34
lines changed

CustomNode.tsx

Lines changed: 151 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,173 @@
11
// Graph/CustomNode.tsx
2-
32
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';
55

66
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;
1420
}
1521

1622

17-
const handleStyle = { left: 10 };
23+
const handleStyle = {
24+
width: 6,
25+
height: 6,
26+
borderRadius: '50%',
27+
background: '#555',
28+
};
29+
1830

19-
const CustomNode: React.FC<CustomNodeProps> = ({ id, data, isConnectable = true, onNodeDataChange }) => {
31+
const CustomNode: React.FC<CustomNodeProps> = ({ id, data, isConnectable = true, onNodeDataChange, onResize }) => {
2032

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 });
2436
}, [id, data, onNodeDataChange]);
2537

38+
const handleResize = useCallback<OnResize>(
39+
(_, { width, height }) => {
40+
onResize?.(id, width, height);
41+
},
42+
[id, onResize]
43+
);
2644

2745
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+
/>
4181
<Handle
4282
type="source"
4383
position={Position.Bottom}
44-
id="b"
84+
id="false"
4585
isConnectable={isConnectable}
86+
className="absolute bottom-[-5px] left-1/2 -translate-x-1/2 bg-red-500"
87+
style={handleStyle}
4688
/>
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>
48169
</div>
49170
);
50171
};
51172

52-
export default CustomNode;
173+
export default React.memo(CustomNode);

GraphApp.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ const GraphApp: React.FC = () => {
4646
const handleAddNode = useCallback(() => {
4747
if (contextMenu && contextMenu.type === 'panel') {
4848
const newPosition = screenToFlowPosition({ x: contextMenu.mouseX, y: contextMenu.mouseY });
49-
const newNodeId = String(currentGraph.serial_number + 1);
49+
const newNodeId = String(currentGraph.serial_number + 1);
5050
const newNode = {
5151
id: newNodeId,
5252
type: 'custom',

svgResize.tsx renamed to ResizeIcon.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// svgResize.tsx
1+
// ResizeIcon.tsx
22

3-
function svgResize(): JSX.Element {
3+
function ResizeIcon(): JSX.Element {
44
return (
55
<svg
66
xmlns="http://www.w3.org/2000/svg"
@@ -23,4 +23,4 @@ function svgResize(): JSX.Element {
2323
);
2424
}
2525

26-
export default svgResize;
26+
export default ResizeIcon;

0 commit comments

Comments
 (0)